即使我清楚地为其分配了一个不同的值,方法属性也不会自动更新

时间:2019-02-17 20:45:23

标签: python class

在更新值时, init 方法仍然使用旧的属性值。

class Email:

    def __init__(self, name):
        self.name = name
        self.email = self.name + "@hotmail.com"

    def details(self):
        return f'{self.name} | {self.email}'


person = Email("James")
print(person.details())

person.name = "Michael"
print(person.details())

得到的输出:

James | James@hotmail.com
Michael | James@hotmail.com

预期输出:

James | James@hotmail.com
Michael | Michael@hotmail.com

我在做什么错了?

谢谢

4 个答案:

答案 0 :(得分:4)

OP中出了什么问题?

如果我们在用行person实例化person = Email("James")对象之后立即打开它,则将是这样的:

person {
"name"  : "James", 
"email" : "James@hotmail.com"
}

如果像您使用name一样更新此person对象中的person.name = "Michael"变量,然后再次打开person对象,则为:

person {
"name"  : "Michael", 
"email" : "James@hotmail.com"
}

请注意,在此阶段,它仍然是我们先前实例化的同一person对象,并且email变量没有从其先前状态更改,因为我们对此没有做任何事情,但是。


通过将email字段设置为property,并使用name变量将其动态设置,

The answer posted by @chepner非常好而且干净。以下是@chepner的代码的副本:

class Email:

    def __init__(self, name):
        self.name = name
        # self.email = self.name + "@hotmail.com"

    @property
    def email(self):
        return f'{self.name}@hotmail.com'

    def details(self):
        return f'{self.name} | {self.email}'

property是什么,如何使用?

property是Python Built-in Functionproperty对象具有以下方法:

  • getter:用于获取属性值
  • setter:用于设置属性值
  • deleter:用于删除属性值

现在,@chepner 's answer中到底发生了什么?在下面的几行中,我们将email设置为property

    @property
    def email(self):
        return f'{self.name}@hotmail.com'

此修饰功能也可以用作getter,例如person.email

请注意,在这里我们没有将其链接到变量(如Python documentation example所示),如果需要/需要,我们可以通过替换return语句并设置一个_email中的__init__变量:

    def __init__(self, input_name):
        self._name = input_name
        self._email = f'{self._name}@hotmail.com'

    @property
    def email(self):
        return self._email

然后,对于setterdeleter,我们可以将它们创建为:

    @email.setter
    def email(self, input_email):
        self._email = input_email

    @email.deleter
    def email(self):
        del self._email

请注意,gettersetterdeleter方法的名称与property相同,只是装饰器不同。

从问题描述中并不需要支持单独更新电子邮件地址,而是仅在此示例之后,如果我们运行person.email = "Michael@hotmail.com",因为emailproperty,因此它正在触发setter将值设置为_email变量。


返回@chepner 's answer中的details方法:

    def details(self):
        return f'{self.name} | {self.email}'

通过执行self.email,我们触发getter返回电子邮件地址,该电子邮件地址是在return语句return f'{self.name}@hotmail.com'中动态生成的。

答案 1 :(得分:2)

在此示例中,在类的self.email方法中分配了__init__,该方法仅在创建Email的实例时调用。这样,当重新分配self.name时,self.email不会更改。要解决此问题,您可以使用property装饰器:

class Email:
  def __init__(self, name):
    self._name = name
    self._email = f'{name}@hotmail.com'
  def details(self):
    return f'{self._name} | {self._email}'
  @property
  def name(self):
    return self._name
  @name.setter
  def name(self, _new_name):
    self._name = _new_name
    self._email = f'{_new_name}@hotmail.com'

person = Email("James")
print(person.details())
person.name = "Michael"
print(person.details())

输出:

James | James@hotmail.com
Michael | Michael@hotmail.com

答案 2 :(得分:1)

正如@ Ajax1234所说,创建新实例时将调用init函数。如果您熟悉面向对象的语言,就可以将其视为构造函数。

因此,如果仅更改person.name,则可以更改名称字段,但不能更改分配了较旧名称名称的电子邮件变量。

关于这种情况,您有几种选择:

  • 您可以创建一个名为Michael的新电子邮件对象。

  • 您可以像在init函数中一样直接重新分配电子邮件。

  • 您可以为setName(name)之类的名称创建一个“ setter”方法,并且可以在此功能中更新电子邮件。因此,当您呼叫设置器时,它会自动更新电子邮件。

答案 3 :(得分:1)

最简单的解决方法是将email设置为属性,而不是您在__init__中设置的属性。

class Email:

    def __init__(self, name):
        self.name = name
        # self.email = self.name + "@hotmail.com"

    @property
    def email(self):
        return f'{self.name}@hotmail.com'

    def details(self):
        return f'{self.name} | {self.email}'