如何通过python

时间:2019-05-21 18:34:12

标签: python python-3.x

如何设置子类的属性归因于父类的属性归因?对于归因,我知道我可以做类似的事情 setattr(self.name, 'nickname', object)。但是,如果我有一个类似Animal的类,它是Bird继承的,并且包含一个名为name的属性。我可以创建另一个财产吗 叫伯德班?

class Animal:
    def __init__(self):
        self._name = None
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

class Bird(Animal):
    def __init__(self):
        super().__init__()

    # I need to create the other property under name attribution from Animal class as nickname
    #so I can access as cat.name.nickname = 'i am nickname'
    #print(cat.name.nickname) # 'i am nickname
    #@property
    #def nickname(self):
    #    return self._name
    #
    #@name.setter
    #def name(self, value):
    #    self._name = value
cat = Animal()
cat.name = 'i am cat'
print(cat.name) # i am cat

1 个答案:

答案 0 :(得分:0)

属性获取器和设置器可以使用super-调用超类上的属性方法。

这意味着您可以在子类中重新创建name属性,为兼容性而检索超类值,并将其包装在具有所需属性的另一个类上。

但是,键_name将在实例字典中使用,以保持值Animal.name属性知道-因此我们需要在实例中使用另一个影子名称来保留该值,以排除子类的值。

也就是说,仍然需要构建一个聪明的类,该类可以将属性的原始值包装在超类上,并且知道如何在子类上处理属性设置和检索-Wrapper代码如下做到这一点:

class Wrapper(str):
    def __new__(cls, original_str, *args):
        return super().__new__(cls, original_str)
    def __init__(self, original_str, name_in_parent, parent):
        self._name = name_in_parent
        self._parent = parent

        # original_str is taken care of in `__new__`
    def __setattr__(self, attrname, value):
        if attrname.startswith("_"):
            return super().__setattr__(attrname, value)
        ns = getattr(self._parent, self._name, None)
        if ns is None:
            ns = {}
            setattr(self._parent, self._name, ns)
        ns[attrname] = value
    def __getattr__(self, attrname):
        return getattr(self._parent, self._name)[attrname]

这将在超类上使用一个简单的属性,如:

class Animal:
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self, value):
        # just so that the property is not 100% meaningless
        self._name = value.lower()

class Bird(Animal):
    @property 
    def name(self):
        return Wrapper(super().name, "_bird_name", self)
    @name.setter
    def name(self, value):
        # this turned out to be the trickiest part - to retrieve
        # the original property on the superclass so that we can 
        # call it's setter. `super()` did not work for this.
        # We set just the core value - the specialized class
        # with more attributes is only used upon reading the property back
        super_property = [getattr(val, "name")  for val in a.__class__.__mro__[1:] if hasattr(val, "name")][0]
        super_property.__set__(self, value)

这有效:

In [511]: b = Bird()                                                                                                              

In [512]: b.name = "Woodpecker"                                                                                                  

In [513]: b.name                                                                                                                  
Out[513]: 'woodpecker'

In [514]: b.name.nickname = "Woody"                                                                                               

In [515]: b.__dict__                                                                                                              
Out[515]: {'_name': 'woodpecker', '_bird_name': {'nickname': 'Woody'}}

In [516]: b.name.nickname                                                                                                         
Out[516]: 'Woody'

如果您想限制可接受的子属性,只需在if中使用普通的Wrapper.__setattr__语句即可。