Override a field in parent class with property in child class - python

Where I am now looks like this:
class A(object):
def __init__(self, val):
self.x=val
self.y=42
# other fields
class B(object):
def __init__(self):
self.a=22
# other fields
class C(A,B):
def __init__(self, val):
super(C,self).__init__(val)
#property
def x(self):
# if A.x is None return a value that I can compute from A.y and B.a
# if A.x is not None return it
#x.setter
def x(self, val):
# set the field value
Sometimes I just want to set an assumed value for x by hand, in which case I would just use an A. In other cases I want to use a more complicated approach that involves computing A.x's value on the basis of information that is organized into a B. The idea in this code is to make a C class that can look like an A (in terms of the x field) but doesn't need that field value to be set by hand, instead it just gets derived.
What I can't figure out is how to have the C.x property shadow the A.x field in a sensible way.

The line self.x = val in the A.__init__ method will simply invoke your C.x setter. You already have everything handled here. You are handling per instance attributes here, not attributes on a class that are inherited by subclasses.
All you need to do is to set a different attribute in the setter to represent the x value. You could name it _x, for example:
class C(A, B):
_x = None
#property
def x(self):
if self._x is not None:
return self._x
return self.a + self.y
#x.setter
def x(self, val):
self._x = val
Note that if all C.__init__ does is call super().__init__, you don't need it at all. However, you do need to make sure at least A.__init__() plays along in the inheritance structure; add in more calls to super().__init__():
class A(object):
def __init__(self, val, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
self.x = val
self.y = 42
class B(object):
def __init__(self, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
self.a = 22
Using *args and **kwargs allows these methods to pass on any extra arguments to other classes in the hierarchy.
Demo, using the above classes:
>>> c = C(None)
>>> c.x
64
>>> c.x = 15
>>> c.x
15

Related

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.

call a setter from __init__ in Python

How can I call a Python (v2.7) setter property from inside __init__? I written the following class but I dont know how to change it to make it work. I get an AttributeError: 'test' object has no attribute '_x' exception. There are a few similar questions around here but couldnt find an answer so far. The idea is when the initialiser is called to do some processing/slicing and assign the result to an attribute
class test(object):
def __init__(self, a,b):
self._x = self.x(a,b)
#property
def x(self):
return self._x
#x.setter
def x(self, a, b):
self._x = "Get this from {} and make a dataframe like {}".format(a,b)
self.x is a property, so you'd just assign directly to it like you would with a regular attribute:
def __init__(self, a, b):
self.x = (a, b)
However, the setter is given one object, always; in the above case, it is passed a tuple; you could unpack it:
#x.setter
def x(self, value):
a, b = value
self._x = "Get this from {} and make a dataframe like {}".format(a,b)
Note the value argument; that's the result of the assignment being passed to the setter.
Demo:
>>> class test(object):
... def __init__(self, a, b):
... self.x = (a, b)
... #property
... def x(self):
... return self._x
... #x.setter
... def x(self, value):
... a, b = value
... self._x = "Get this from {} and make a dataframe like {}".format(a,b)
...
>>> t = test(42, 'foo')
>>> t.x
'Get this from 42 and make a dataframe like foo'

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?

A property's `fdel` method is not invoked when `__delattr__` is overridden

I have a class like this, in which I have declared a property x, and overridden __delattr__:
class B(object):
def __init__(self, x):
self._x = x
def _get_x(self):
return self._x
def _set_x(self, x):
self._x = x
def _del_x(self):
print '_del_x'
x = property(_get_x, _set_x, _del_x)
def __delattr__(self, name):
print '__del_attr__'
Now when I run
b = B(1)
del b.x
Only __del_attr__ get invoked, anybody knows why and how to solve this problem?
You have to call the __delattr__ of your ancestor to achieve this correctly.
class B(object):
.....
def __delattr__(self, name):
print '__del_attr__'
super(B, self).__delattr__(name) # explicit call to ancestor (not automatic in python)
And then running :
b = B(1)
del b.x
Will output:
__del_attr__
_del_x
_del_x is called from the default __del_attr__. But since you have overridden __del_attr__, the onus is on you to call _del_x from inside your __del_attr__.

How to call a property of the base class if this property is being overwritten in the derived class?

I'm changing some classes of mine from an extensive use of getters and setters to a more pythonic use of properties.
But now I'm stuck because some of my previous getters or setters would call the corresponding method of the base class, and then perform something else. But how can this be accomplished with properties? How to call the property getter or setter in the parent class?
Of course just calling the attribute itself gives infinite recursion.
class Foo(object):
#property
def bar(self):
return 5
#bar.setter
def bar(self, a):
print a
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return self.bar # --> recursion!
#bar.setter
def bar(self, c):
# perform the same action
# as in the base class
self.bar = c # --> recursion!
# then do something else
print 'something else'
fb = FooBar()
fb.bar = 7
You might think you could call the base class function which is called by property:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
Though this is the most obvious thing to try I think - it does not work because bar is a property, not a callable.
But a property is just an object, with a getter method to find the corresponding attribute:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
super() should do the trick:
return super().bar
In Python 2.x you need to use the more verbose syntax:
return super(FooBar, self).bar
There is an alternative using super that does not require to explicitly reference the base class name.
Base class A:
class A(object):
def __init__(self):
self._prop = None
#property
def prop(self):
return self._prop
#prop.setter
def prop(self, value):
self._prop = value
class B(A):
# we want to extend prop here
pass
In B, accessing the property getter of the parent class A:
As others have already answered, it's:
super(B, self).prop
Or in Python 3:
super().prop
This returns the value returned by the getter of the property, not the getter itself but it's sufficient to extend the getter.
In B, accessing the property setter of the parent class A:
The best recommendation I've seen so far is the following:
A.prop.fset(self, value)
I believe this one is better:
super(B, self.__class__).prop.fset(self, value)
In this example both options are equivalent but using super has the advantage of being independent from the base classes of B. If B were to inherit from a C class also extending the property, you would not have to update B's code.
Full code of B extending A's property:
class B(A):
#property
def prop(self):
value = super(B, self).prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(B, self.__class__).prop.fset(self, value)
One caveat:
Unless your property doesn't have a setter, you have to define both the setter and the getter in B even if you only change the behaviour of one of them.
try
#property
def bar:
return super(FooBar, self).bar
Although I'm not sure if python supports calling the base class property. A property is actually a callable object which is set up with the function specified and then replaces that name in the class. This could easily mean that there is no super function available.
You could always switch your syntax to use the property() function though:
class Foo(object):
def _getbar(self):
return 5
def _setbar(self, a):
print a
bar = property(_getbar, _setbar)
class FooBar(Foo):
def _getbar(self):
# return the same value
# as in the base class
return super(FooBar, self)._getbar()
def bar(self, c):
super(FooBar, self)._setbar(c)
print "Something else"
bar = property(_getbar, _setbar)
fb = FooBar()
fb.bar = 7
Some small improvements to Maxime's answer:
Using __class__ to avoid writing B. Note that self.__class__ is the runtime type of self, but __class__ without self is the name of the enclosing class definition. super() is a shorthand for super(__class__, self).
Using __set__ instead of fset. The latter is specific to propertys, but the former applies to all property-like objects (descriptors).
class B(A):
#property
def prop(self):
value = super().prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(__class__, self.__class__).prop.__set__(self, value)
You can use the following template:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#property
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
Note! All of the property methods must be redefined together. If do not want to redefine all methods, use the following template instead:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#Parent.prop1.getter
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#Parent.prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#Parent.prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
class Base(object):
def method(self):
print "Base method was called"
class Derived(Base):
def method(self):
super(Derived,self).method()
print "Derived method was called"
d = Derived()
d.method()
(that is unless I am missing something from your explanation)

Categories

Resources