首页 > 解决方案 > 包括常见的属性装饰器

问题描述

我正在寻找一种将通用属性装饰器添加到类的速记。

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)

标签: python-3.xoopinheritancedecorator

解决方案


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.


推荐阅读