Take the following class
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
How can I prevent the following usage?
p1 = Person('Foo', 'Bar')
p1.firstname='Fooooooo'
The code above will execute successfully in Python, however, a mistake was made with the name of the property, i.e. its missing _
between first
and name
UPDATE: This sounds like "Monkey Patching", why do I want to do this?
My intention is to simply help avoid the user from setting the wrong property, have the code execute, and see unexpected behavior and not be aware of the mistake immediately.
What does the Pythonic way recommend here?
First of all, it's almost always a bad idea to do such a thing. If only reason why you want that is making sure you don't make typos - there are better tools for that (think IDE or pylint). If you are a 100% positive that you need such a thing, here are two ways to do it:
First way - you can do this with using __setattr__
method. See python __setattr__
documentation
class Person(object):
def __init__(self, first_name, last_name):
self.__dict__['first_name'] = first_name
self.__dict__['last_name'] = last_name
def __setattr__(self, name, value):
if name in self.__dict__:
super(Person, self).__setattr__(name, value)
else:
raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name))
and output:
In [49]: a = Person(1, 2)
In [50]: a.a = 2
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>()
----> 1 a.a = 2
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in __setattr__(self, name, value)
8 super(Person, self).__setattr__(name, value)
9 else:
---> 10 raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name))
AttributeError: Person has no attribute a
Alternatively, you can do this using __slots__
(python __slots__
documentation):
class Person(object):
__slots__ = ("first_name", "last_name")
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
output:
In [32]: a = Person("a", "b")
In [33]: a.first_name
Out[33]: 'a'
In [34]: a.a = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>()
----> 1 a.a = 1
AttributeError: 'Person' object has no attribute 'a'
The first way is more flexible as it allows hacking this code even further through using __dict__
directly, but that would be even more wrong than it is now. Second approach preallocates space for certain number of instance variables (references), which means less memory consumption.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With