在 setter 和 @property 装饰器中遇到一些麻烦和困惑,并非所有在 `__init__` 中定义的属性都被更新

问题描述

class Employee:
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.annual_pay = 12*pay
        self.email = self.first + '.' + self.last + '@email.com'

    @property                   
    def fullname(self):
        return ('{} {}'.format(self.first,self.last))

    @fullname.setter            
    def fullname(self,name):
        first,last = name.split(' ')
        self.first = first
        self.last = last

emp1 = Employee('test1','subject1',10000)
emp1.fullname='test2 subject2'
print(emp1.fullname)
print(emp1.email)

我使用 setter 为用户分配一个 fullname() 并覆盖顶部已经由 init 设置的属性,但在输出中它只将 fullname 更改为 test2 subject2 同时电子邮件未更改为 test1 [email protected](我从教程中学到,我认为我们可以通过这个 setter 功能覆盖属性)(而且那个家伙没有像我那样在 init 中使用电子邮件,而是他有一个单独的函数,带有@property 装饰器)

解决方法

那是因为当您运行 __init__() 时,电子邮件的值被计算并保存。仅仅因为您更改 self.firstself.last 并不意味着 self.email 会受到影响,因为它没有保存与这些变量的关系。保存为字符串。

示例:

a = "hello"
b = "world"
c = a + b
a = "goodbye"
print(c)
>>> "hello world"

此处,c 设置为 a+b,并且在执行该行时保存该值。在我们已经赋予 a 其值之后更改 c 不会改变它。它已经设置,我们必须再次执行 c = a+b 才能打印出 goodbye world

在你的情况下,你必须在 setter 中修改它:

@fullname.setter            
def fullname(self,name):
    first,last = name.split(' ')
    self.first = first
    self.last = last
    self.email = self.first + '.' + self.last + '@email.com'

然而,这不是一个好习惯,因为 setter 应该只更改您正在访问的特定变量。这就是为什么您正在阅读的教程有一个电子邮件函数的原因:如果您希望根据 firstlast 的任何值动态计算它当你检查它的时候,你应该总是使用一个函数:

def email(self):
    return self.first + '.' + self.last + '@email.com'

print(emp1.email())

如果需要,您可以像教程一样使用 property 装饰器:

@property
def email(self):
    return self.first + '.' + self.last + '@email.com'

这只是语法糖,让您可以像以前一样调用它:

print(emp1.email)

不必像函数一样调用它,尽管仅此而已。

作为旁注,我建议使用 string formatting,在这种情况下,通常首选使用 + 将字符串连接在一起:

return f"{self.first}.{self.last}@email.com"