Python overriding setter to an attribute with no setter? - python

I have a class A made by someone else, that I cannot edit:
class A:
def __init__(self, x):
self.x = x
Now I'm trying to inherit my own class B from A, and have x as a property instead of an attribute.
Is this possible?
I already tried:
class B(A):
def __init__(self, x):
super().__init__(x)
#property
def x(self):
return super().x
#x.setter
def x(self, x):
super().x = x
print(x) # my stuff goes here
But as I expected, it's not possible: AttributeError: 'super' object has no attribute 'x'
Is there any other method, some workaroud maybe?

No, you cannot use super() for anything other than class attributes; x is an instance attribute, and there is no inheritance mechanism for attributes.
Instance attributes live in a single namespace; there is no 'parent instance' attribute namespace.
You can still reach the attribute in the instance __dict__ object:
class B(A):
#property
def x(self):
return self.__dict__['x']
#x.setter
def x(self, x):
self.__dict__['x'] = x
print(x) # my stuff goes here
A property is a data descriptor, meaning that it is looked up before the instance attributes are consulted (see the descriptor howto), but you can still access it directly.

Related

How does python builtin #property receives the overloaded setter function ?

When defining a builtin python property using the #property, how does the property object differentiates the setter from the getter method, provided that they are overloaded (have the same name)?
class A:
def __init__(self):
self._x = 12
#property
def x(self) -> int:
return self._x
#notifiable # strangely this stacks on both setter and getter
#x.setter
def x(self, val: int):
self._x = val
If I define a custom property decorator, say:
class my_property:
def __init__(self, getter):
print("__init__ getter %s:" % getter)
def setter(self, setter: FunctionType):
print("setter: %s" % setter)
class B:
def __init__(self):
self._val = 44
#my_property
def x(self):
return self._val
#x.setter
def x(self, val):
self._val = val
Executing the code results in the following output
__init__ getter <function B.x at 0x7ff80c5e1620>:
setter: <function B.x at 0x7ff80c5e1620>
Both the getter and the setter funtions passed to the decorator are the same funtion, but they should be different functions.
If I use the annotation like this:
class C:
def __init__(self):
self._val = 44
#my_property
def x(self):
return self._val
#x.setter
def set_x(self, val):
self._val = val
A different function is printed, as expected.
__init__ getter <function C.x at 0x7f529132c6a8>:
setter: <function C.set_x at 0x7f529132c6a8>
How does python solves this issue with the builtin #property? Is the decorator treated differently from user decorators ?
The reason you're seeing what you're seeing here is because you don't keep a reference to the getter anywhere. This means that once the __init__ method ends, there's no more reference to the first B.x, (i.e. the refcount is zero), so the function is released. Since the original getter function has been released, Python is free to reuse the exact same memory address for another object/function, which is what happens in this case.
If you modify my_property to keep a reference to the original getter method as such:
class my_property:
def __init__(self, getter):
print("__init__ getter %s:" % getter)
self.getter = getter
def setter(self, setter: FunctionType):
print("setter: %s" % setter)
you'll see that the function name (B.x) is still the same (which is ok, as python doesn't use the function name to uniquely identify a function), however the memory address of the two functions are different:
__init__ getter <function B.x at 0x7f9870d60048>
setter: <function B.x at 0x7f9870d600d0>
Is the decorator treated differently from user decorators ?
No, property just a regular decorator. However, if you want to reimplement the property decorator, you'd probably be interested in the descriptor protocol (there's a pure python reimplementation of #property in that page).

using __del__ with property methods in place

Property wrapper methods is a nice feature to have in python, this question is not the subject of such question, I need to know if it is possible to use it with python destructor __del__, a practical example could be a database connection, for simplification purposes let's say we have the following class:
class Point(object):
"""docstring for Point"""
def __init__(self, x, y):
self.x = x
self.y = y
#property
def x(self):
print('x getter got called')
return self._x
#x.setter
def x(self, x):
print('x setter got called')
self._x = x
def __str__(self):
return '[%s:%s]' % (self.x, self.y)
def __del__(self):
print('destructor got called')
del self.x
del self.y
as a test case let's say we have:
a = Point(4, 5)
del a
The output is:
Exception AttributeError: "can't delete attribute" in <bound method Point.__del__ of <__main__.Point object at 0x7f8bcc7e5e10>> ignored
if we deleted the property part, everything goes smooth.
can someone show where's the problem?
Add a deleter to your property x that does the clean up. By default, if no fdel is defined for the property, the AttributeError you see is raised:
#x.deleter
def x(self):
print("x deleter got called")
del self._x
If you don't use #x.deleter to define the delete behavior (like you did with #x.setter) then it's impossible to delete the property.

Class property returning property object

Before this is flagged as a duplicate, I know this question has been answered before, but the solutions provided there don't seem to apply to my case. I'm trying to programmatically set class properties. I know I can use property for that, so I thought about doing this:
class Foo:
def __init__(self, x):
self._x = x
def getx(): return self._x
def setx(y): self._x = y
self.x = property(fget=getx, fset=setx)
However, when I run this interactively, I get:
>>> f = Foo(42)
>>> f.x
<property object at 0x0000000>
>>> f._x
42
>>> f.x = 1
>>> f.x
1
Is there any way to solve this?
Edit:
I feel I may have left out too much, so here's what I am actually trying to reach. I have a class with a class variable called config, which contains configuration values to set as properties. The class should be subclassed to implement the config variable:
class _Base:
config = ()
def __init__(self, obj, **kwargs):
self._obj = obj()
for kwarg in kwargs:
# Whatever magic happens here to make these properties
# Sample implementation
class Bar(_Base):
config = (
"x",
"y"
)
def __init__(self, obj, x, y):
super().__init__(obj, x=x, y=y)
Which now allows for manipulation:
>>> b = Bar(x=3, y=4)
>>> b.x
3
>>> # Etc.
I'm trying to keep this as DRY as possible because I have to subclass _Base a lot.
property objects are descriptors, and descriptors are only invoked when defined on the class or metaclass. You can't put them directly on an instance; the __getattribute__ implementation for classes simply don't invoke the binding behaviour needed.
You need to put the property on the class, not on each instance:
class Foo:
def __init__(self, x):
self._x = x
#property
def x(self): return self._x
#x.setter
def x(self, y): self._x = y
If you have to have a property that only works on some instances, you'll have to alter your getter and setter methods to vary behaviour (like raise an AttributeError for when the state of the instance is such that the attribute should 'not exist').
class Bar:
def __init__(self, has_x_attribute=False):
self._has_x_attribute = has_x_attribute
self._x = None
#property
def x(self):
if not self._has_x_attribute:
raise AttributeError('x')
return self._x
#x.setter
def x(self, y):
if not self._has_x_attribute:
raise AttributeError('x')
self._x = y
The property object still exists and is bound, but behaves as if the attribute does not exist when a flag is set to false.

Setting Values Of Inherited Properties

I want to be able to create a concrete instance of a class that inherits from another concrete class, which in turn inherits from an abstract class.
The basic pattern is:
from abc import ABCMeta, abstractproperty
class Foo(object):
__metaclass__ = ABCMeta
#abstractproperty
def x(self):
pass
#abstractproperty
def y(self):
pass
class Bar(Foo):
x = None
y = None
def __init__(self, x, y):
self.x = x
self.y = y
#property
def x(self):
return self.x
#x.setter
def x(self, value):
self.x = value
#property
def y(self):
return self.y
#y.setter
def y(self, value):
self.y = value
class Baz(Bar):
def __init__(self):
super().__init__(x=2, y=6)
a = Baz()
When I try to create the instance of Baz I get a RecursionError: maximum recursion depth exceeded error. (As well as a pylint warning telling me that the signatures of the setter methods don't match the signatures of the base class)
However, if I remove the setters, I get an error self.x = x AttributeError: can't set attribute
What's the correct pattern to do this?
You need to change names for your x() / y() methods or for your x / y properties, for example rename
class Bar(Foo):
x = None
y = None
To:
class Bar(Foo):
x_val = None
y_val = None
And rename the references to x / y as well.
What you did is basically:
def x():
return x()
It happened because your def x overridden the x = None, so x is a function(property) that is calling itself. Avoid this by using another attribute(named differently) for storing the actual value of x.
Example from python docs (https://docs.python.org/3.5/library/functions.html#property):
class C:
def __init__(self):
self._x = None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
Note: attribute names starting with underscore should be considered "private" and should not be directly accessed outside of the class. But it's only a convention for programmers, technically they are just normal attributes and you can do whatever you want, but it's nice to follow some convention, isn't it?

python: why getter & setter? how can I set attribute quickly? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is the benefit to using a ‘get function’ for a python class?
I just started to read Python, but I wonder why does Python need setter and getter at all? it already have object variables which act like property
Consider
class C(object):
def _init_(self):
self._x = None
def get_x(self):
return self._x
def set_x(self, value):
self._x = valu
x = property(get_x, set_x)
Can we just use C.x = "value" to do what we want to do here? what is the benefit of property?
BTW, creating property/setter/getter in this way is cumbersome to me, is there any way to simplify this? like
class C()
has_attributes("x", "y", "z")
You can use a property to obtain what you want:
class C(object):
def _init_(self):
self._x = None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
Then you can access the attribute with the usual attribute syntax:
c = C()
c.x = 10 #calls the setter
print(c.x) #calls the getter
There are some reasons to use a property instead of a plain data attribute:
You can document the attribute
You can control the access to the attribute, either by making it read-only or by checking the type/value being set
You do not break backwards compatibility: if something was a plain instance attribute and then you decide to transform it into a property the code that worked with the attribute will still work. If you used get/set explicit methods all the code that used the old API would have to change
It's more readable then using explicit get/set methods.
Use plain attributes:
class C(object):
def __init__(self):
self.x = None
Later, when and if it would be necessary, x can be changed to a property. The beauty of "#property" it that is allows developer not to use getters, setters and "#property".
class C(object):
def _init_(self):
self._x = None
def get_x(self):
return self._x
def set_x(self, value):
self._x = value
x = property(get_x, set_x)
now you can use c.x = "foo" with the set and gets, transparently.
The purpose of a set and getter is don't expose the class internals.
Imagine that in the future
self._x
changes to
sql.save(id(self), value)
and get_x to:
value= sql.sql(id(self))
return convertFromDbFormatToExpectedApiFormat(value)
You will only have to change the code of getter and setters in only that class, not change
all the classes that communicates with it.
class C(object):
def _init_(self):
self.sql = DDBB()
def get_x(self):
dbregistry = self.sql.fetch(id(self))
return convertFromDbFormatToExpectedApiFormat(dbregistry)
def set_x(self, value):
self.sql.save(id(self), value)
x = property(get_x, set_x)

Categories

Resources