Reputation: 3924
I want to create a class with attributes that can be __setattr__
-ed by its methods internally, so an attempt like self.attr = value
would raise an AttributeError
. This is what I have so far:
class MyClass():
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def __repr__(self):
return '%r class with a=%s, b=%s, c=%s' % (self, self.a, self.b, self.c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
self.attr = value
>>> obj = MyClass(1,2,3)
>>> obj.setattr_(a,4) # obj.a = 4
AttributeError: 'obj' is read-only # __setattr__ method also applies internally
Upvotes: 1
Views: 149
Reputation: 17247
Please use the properties.
Anyway, it is good to understand the internals, here is a working code based on your question. Just to play with.
Once you redefine __setattr__
to fail, there is no way to set an attribute in that class. But there is still a working __setattr__
left in the parent class.
class MyClass():
def __init__(self, a, b, c):
self.setattr_('a', a)
self.setattr_('b', b)
self.setattr_('c', c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
super().__setattr__(attr, value)
obj = MyClass(1,2,3)
obj.setattr_('a',4) # note that a is a name (string)
Upvotes: 0
Reputation: 838
You can use properties in Python for this type of tasks. First, you make your attribute 'private' by adding two underscores, then you create a getter method with the @property
decorator:
class MyClass:
def __init__(self, a, b, c):
self.__a, self.__b, self.__c = a, b, c
@property
def a(self):
return self.__a
@property
def b(self):
return self.__b
@property
def c(self):
return self.__c
Now, you can use your class like this:
>>> my_object = MyClass('foo', 'bar', 'bar')
>>> print(my_object.b)
bar
>>> my_object.b = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Note
I wrote 'private' because you can still access it if you really want:
>>> my_object._MyClass__b = 42
>>> print(my_object.b)
42
This has to do with the Zen of Python: "We’re all consenting adults here".
Upvotes: 0
Reputation: 530960
This is a use case for properties. Properties without a setter are read-only. In the following, a
and b
are read-only, while c
is not.
class MyClass:
def __init__(self, a, b, c):
self._a = a
self.b = b
self._c = c
# a is a read-only property
@property
def a(self):
return self._a
# b is an ordinary attribute
# c is a property you can set
@property
def c(self):
return self._c
@c.setter
def c(self, value):
self._c = value
Since you have defined only getters for the a
, attempts to
change its value will fail. Attempts to change b
will succeed as expected. Attempts to change c
will succeed as
if it were a regular attribute.
>>> obj = MyClass(1,2,3)
>>> obj.a = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.b = 5
>>> obj.c = 6
>>> obj.c
6
Upvotes: 1