python-3.x - 包括常见的属性装饰器
问题描述
我正在寻找一种将通用属性装饰器添加到类的速记。
class Animal:
def __init__(self):
self._attributes = {}
class Dog(Animal):
@property
def color(self):
return super()._attributes.get('color', None)
@color.setter
def color(self, value):
if value is not None:
super()._attributes['color'] = value
else:
super()._attributes.pop('color', None)
class Cat(Animal):
@property
def color(self):
return super()._attributes.get('color', None)
@color.setter
def color(self, value):
if value is not None:
super()._attributes['color'] = value
else:
super()._attributes.pop('color', None)
class InvisibleMan(Animal):
pass
我正在寻找“打包”颜色属性的最简单方法,以便可以将其分配给 Dog 和 Cat,但不能分配给 InvisibleMan。像这样的东西(虽然实际上会有~8个这样的属性和~15个这样的类)
class Dog(Animal):
def __init__(self):
super().__init__()
includeColorProperty(self)
解决方案
Have you considered descriptors, instead of a decorator?
In a nutshell, descriptors give you fine-grained control over attribute storage. (In fact, the property
decorator builds a descriptor under the hood!) Here are some Python docs that may be helpful.
Anyway, sticking with your pattern, a descriptor that manipulates _attributes
would look something like this:
class Color:
def __get__(self, obj, objtype=None):
return obj._attributes.get('color')
def __set__(self, obj, value):
if value is None:
obj._attributes.pop('color', None)
else:
obj._attributes['color'] = value
where obj
is a reference to the Dog
instance, et al.
(Note the __get__
and __set__
methods match your getter and setter, respectively.)
Then, plug the descriptor into your classes like this:
class Animal:
def __init__(self):
self._attributes = {}
class Dog(Animal):
color = Color()
class Cat(Animal):
color = Color()
class InvisibleMan(Animal):
pass
You can see in this example the behaviors you're looking for are preserved: instances maintain their own _attributes
, and InvisibleMan
has no color
:
>>> d1, d2 = Dog(), Dog()
>>> d1.color = 'blue'
>>> d1.color, d2.color
('blue', None)
>>>
>>>
>>> x = InvisibleMan()
>>> x.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'InvisibleMan' object has no attribute 'color'
Personally, I also find this a bit easier to read when many properties are involved, as you mentioned is true in your case. Want to know what properties are available for a given type? They're listed out right at the top, no surprises.