【问题标题】:Decorators unexpectedly change constructor behavior in Python装饰器意外更改 Python 中的构造函数行为
【发布时间】:2020-10-25 20:14:15
【问题描述】:

下面,我展示了一个更复杂代码的简化示例,但它完全代表了我遇到的问题。

第 1 部分:工作正常,没有问题:

class Animal():
    def __init__(self, animal_name = "no name given"):    
        self.set_name(animal_name) 
    
    def get_name(self):
        return self._animal_name
    
    def set_name(self, animal_name):
        self._animal_name = animal_name

class Dog(Animal):
    def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
        self._dog_breed = dog_breed
        super().__init__(dog_name)
        
    def get_breed(self):
        print(self._dog_breed)

x = Dog('Greyhound', 'Rich')

第 2 部分:引入 getter 和 setter 装饰器后,代码停止工作:

class Animal():
    def __init__(self, animal_name = "no name given"):
        #THE LINE BELOW SEEMS TO CAUSE AN ISSUE
        self.name(animal_name)
    
    @property
    def name(self):
        return self._animal_name
    
    @name.setter
    def name(self, animal_name):
        self._animal_name = animal_name

class Dog(Animal):
    def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
        self._dog_breed = dog_breed
        super().__init__(dog_name)
        
    def get_breed(self):
        print(self._dog_breed)

x = Dog('Greyhound', 'Rich')

输出:AttributeError:“狗”对象没有属性“_animal_name”

当我保留第 2 部分中的装饰器但将 Animal 类中的构造函数更改为:

class Animal():
    def __init__(self, animal_name = "no name given"):
        self._animal_name=animal_name

有效。

我只是好奇为什么它在上面 第 2 部分的示例中不起作用?

【问题讨论】:

  • 如果你使用self.name=animal_name而不是self.name(animal_name),它是否有效?
  • 是的,这解决了问题。因此,当我使用装饰器时,我需要将类中的 getter 和 setter 方法视为一个属性,即使在类构造函数中也是如此:我现在明白了。谢谢!

标签: python-3.x python-decorators


【解决方案1】:

简答:

线

self.name(animal_name)

可以分成两部分:

tmp = self.name
tmp(animal_name)

首先,self.name 调用 getter 并将结果视为一个函数。 getter 使用 return self._animal_name 并且由于从未调用过 setter,因此会发生相应的错误。

长答案:

让我们学习以下课程:

class Animal:
    def __init__(self, animal_name):
        self.name(animal_name)

    @property
    def name(self):
        return self._animal_name

    @name.setter
    def name(self, animal_name):
        self._animal_name = animal_name

要明白什么是行

self.name(animal_name)

其实,你首先需要了解装饰器。

代码

@dec
def func(a, b, ...):
    [...]

等价于

def func_impl(a, b, ...):
    [...]
func = dec(func_impl)

(除了不能直接调用func_impl)。例如,请参阅PEP 318 了解更多信息。

这意味着您可以在不使用装饰器的情况下从上面编写Animal 类:

class Animal:
    def __init__(self, animal_name):
        self.name(animal_name)

    def get_name(self):
        return self._animal_name
    name = property(get_name)

    def set_name(self, animal_name):
        self._animal_name = animal_name
    name = name.setter(set_name)

为了理解这段代码,你需要理解内置的property,它是一个类。有关详细信息,请参阅the python docs

name = property(get_name) 行创建了一个property 类型的对象。检索属性值时,会调用get_name

name = name.setter(set_name) 行首先调用name.setter(set_name),这会创建属性的副本,然后用此副本覆盖name。为副本赋值时,会调用set_name

总而言之,name 是一个 property 类型的对象,它使用 get_name 作为 getter,set_name 作为 setter。

这有什么帮助?

你需要明白这一点:name 不是一个函数。 它是一个属性。不可调用。

有问题的行

self.name(animal_name)

实际上等价于

self.get_name()(animal_name)

这解释了错误消息:构造函数调用getter,它尝试使用return self._animal_name。但由于尚未调用 setter,因此尚未设置 self._animal_name

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    • 2022-10-18
    • 2019-07-21
    • 2017-05-04
    • 1970-01-01
    相关资源
    最近更新 更多