Reputation: 13
I'm wondering how I can update an attribute that has been set using the @property decorator in this case. (the code below will tell you more than words...)
When I try to update the email without a setter, I get an AttributeError: can't set attribute. When I do use a setter, nothing changes. The new email doesn't use neither the firstname nor the lastname.
Could anybody help?
class Employee:
def __init__(self, first, last):
self.first = first
self.last = last
@property
def email(self):
return f"{self.first}.{self.last}@email.com".lower()
# @email.setter
# def email(self, new_email):
# return new_email
Upvotes: 1
Views: 123
Reputation: 2063
I think the most straightforward path here would be to do away with the property, and instead make email
an optional paramater that defaults to first.last
:
class Employee:
def __init__(self, first, last, email=None):
self.first = first
self.last = last
self.email = email if email else f"{first}.{last}@email.com".lower()
Now you can modify the email address for an existing instance with the usual dot notation:
>>> e = Employee('John', 'Doe')
>>> e.email
'[email protected]'
>>>
>>> e.email = '[email protected]'
>>> e.email
'[email protected]'
If you're really intent on keeping the property, the setter needs to update an instance attribute:
class Employee:
def __init__(self, first, last):
self.first = first
self.last = last
self._email = f"{first}.{last}@email.com".lower()
@property
def email(self):
return self._email
@email.setter
def email(self, addr):
self._email = addr
You might choose this pattern if you need to do some validation on the way in - say, confirm the new email has an @
sign:
@email.setter
def email(self, addr):
if '@' not in addr:
raise ValueError('nope!')
self._email = addr
but otherwise the first alternative is a bit simpler.
Upvotes: 2
Reputation: 27485
As I posted in my comment you need to determine if the new email is in [email protected]
then just set the first
and last
attributes.
Although I wouldn't use email as a property if you're creating it based on the name you should change the name itself.
class Employee:
def __init__(self, first, last):
self.first = first
self.last = last
def email(self):
return f"{self.first}.{self.last}@email.com".lower()
def __repr__(self):
return f"<Employee | {self.first} {self.last}>"
john = Employee('John', 'Smith')
print(john)
#<Employee | John Smith>
john.first = "Joe"
print(john)
#<Employee | Joe Smith>
Otherwise if you want a setter for email then I suggest using it to set the first and last attributes but you don't need a return value as you're just setting the email you'd already know it. I'd use the re
library to check if the email is in correct format. This is a very rough example:
@email.setter
def email(self, new_email):
try:
self.first, self.last = re.search('(?P<first>\w+).(?P<last>\w+)@\S+', email).group('first', 'last'))
except AttributeError:
raise ValueError("Email must be in "[email protected]")
Upvotes: 0