Setting Values Of Inherited Properties - python

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?

Related

Python abstractclass Inheritance

I have two class structured as below
from abc import ABCMeta, abstractmethod
class C(metaclass=ABCMeta):
""""""
def __init__(self, x, y):
self._x = x
self._y = y
#property
#abstractmethod
def x(self):
"""Get the _x"""
#x.setter
#abstractmethod
def x(self, value):
"""Set the x"""
#property
def y(self):
"""Get the _y"""
#y.setter
def y(self, value):
"""Set the _y"""
class D(C):
""""""
def __init__(self, x, y):
self._x = x
self._y = y
#property
def x(self):
return self._x
#C.x.setter
def x(self, value):
self._x = value
#property
def y(self):
return self._y
#C.y.setter
def y(self, value):
self._y = value
When I initialize an instance of D. It throws a error:
TypeError: Can't instantiate abstract class D with abstract methods x
When I rewrite setters decorator in D as
#x.setter
def x(self, value):
self._x = value
it works. But in python abc document https://docs.python.org/3/library/abc.html it states:
in disappreciated #abc.abstractproperty
If only some components are abstract, only those components need to be updated to create a concrete property in a subclass:
class D(C):
#C.x.setter
def x(self, val):
...
I don't know why write in this way will lead to error. Please help me understand the logic here. Thank you.
When you write #C.x.setter above your setter, you're setting x to a version of C.x with the setter replaced with your new setter function. Only the setter - the getter you wrote earlier is discarded. You're still using C.x's abstract getter.
The example in the docs uses #C.x.setter because they want the behavior it provides. In the doc example, C.x has a concrete getter, and they just want to replace the setter. That's not the case in your code.

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.

Is it true that once property is used in Python, it'd always be better initializing via property?

I used to initialize private attributes in __init__ like below (this way of initializing is also commonly seen),
class Duck():
def __init__(self, input_name):
self.__name = input_name
#property
def name(self):
return self.__name
#name.setter
def name(self, input_name):
self.__name = input_name
# Use private attribute __name internally for other purposes below...
But I just want to make sure if it is actually safer to just use property at the very beginning __init__, for example, in next example, for input greater than 1000 or less than 0, I want to evaluate to 1000 and 0, respectively, rather than the original input value. It seems I can't use self.__x = x,
class P:
def __init__(self,x):
# If self.__x = x, not desirable
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
I assume you work with python2. Properties are not supported for old-style classes. Just change the first line to
class P(object):
And whether you use self._x or self.__x for the attribute behind does not matter. Just do it consistent, i.e. change the constructor line back to
self.__x = x
Just don't call that self.x as the property.
Edit:
There was a misunderstanding I think. Here the complete code I propose:
class P(object):
def __init__(self,x):
self.__x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
p = P(3)
p.x = 1001
print p.x
Edit 2 - The actual question:
I apologize, did simply not grasp the heading and actual question here, but was focused on making your class work. My distraction was that if you are in python2 and use old-style classes, the setter would not really get called.
Then like indicated in the comment-conversation below, I don't have a definite answer on whether to initialize the attribute or the property, but I (personally) would say:
a. If the initialisation deserves the same validation as performed in
the setter, then use the property, as else you'd need to copy the
setter code.
b. If however the value to initialise doesn't need validation (for
instance, you set it to an a priori validated constant default
value), then there is no reason to use the property.

Override a field in parent class with property in child class

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

Categories

Resources