Upon updating a value, the __init__
method still uses the old attribute value.
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())
Output gotten:
James | James@hotmail.com
Michael | James@hotmail.com
Output expected:
James | James@hotmail.com
Michael | Michael@hotmail.com
What am I doing wrong?
In this example,
self.email
is being assigned in the__init__
method of the class, which is only called when an instance ofEmail
is created. As such, whenself.name
is reassigned,self.email
is not changed. To work around this, you can use aproperty
decorator:Output:
What went wrong in the OP?
If we open up the
person
object right after you instantiated it with the lineperson = Email("James")
, it would be something like this:If we update the
name
variable in thisperson
object as you did withperson.name = "Michael"
, and open up theperson
object again, it would be:Please note that at this stage, it is still the same
person
object we instantiated earlier, and theemail
variable is not changed from its previous state, because we didn't do anything about it, yet.The answer posted by @chepner is very nice and clean, by setting
email
field as aproperty
, and by setting it dynamically using thename
variable. Below is a copy of @chepner 's code:What is
property
and how is it used?property
is a Python Built-in Function, aproperty
object has these methods:getter
: used to get an attribute valuesetter
: used to set an attribute valuedeleter
: used to delete an attribute valueNow, what exactly is happening in @chepner 's answer? With the lines below, we are setting the
email
as aproperty
:And this decorated function can also be used as a
getter
, e.g.person.email
.Note that in here we didn't link it to a variable (as shown in the Python documentation example), if we want / need, we can do so by replacing the
return
statement and set an_email
variable in__init__
:And then, as for the
setter
anddeleter
, we can create them as:Note that the
getter
,setter
, anddeleter
methods all have the same name as theproperty
, just different decorators.From the question description it is not needed to support updating the email address separately, but just following this example here, if we run
person.email = "Michael@hotmail.com"
, becauseemail
is aproperty
, it is triggering thesetter
to set the value to the_email
variable.Going back to the
details
method in @chepner 's answer:By doing
self.email
we are triggering thegetter
to return the email address, which is dynamically generated in thatreturn
statementreturn f'{self.name}@hotmail.com'
.The simplest fix would be to make
email
a property, rather than an attribute you set in__init__
.As @Ajax1234 saying, the init function is calling when you create a new instance. Think it like a constructor if you are familiar with an object oriented language.
So, if you just change the person.name, you can change the name field but not the email variable which assigned with older value of name.
You have a few options about that case:
You can create a new Email object with name of Michael.
You can re-assign the e-mail directly, like you do in init function.
You can create a "setter" method for name like
setName(name)
and you can update the email in this function. So, when you call the setter, it automatically updates the email.