When I update an instance property, that change is not reflected in the __str__ method of the parent class. This makes sense, as super().__init__ is called during the __init__ call, and when I make an update to the property, it's not getting called again.
class BulkData:
def __init__(self, fields):
self.fields = fields
def __str__(self):
return ''.join(str(f) for f in self.fields)
class Node(BulkData):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
fields = [self.x, self.y, self.z]
super().__init__(fields)
Example:
n1 = Node(1,2,3)
n1.x = 5
print(n1)
> '123'
I would like the result to be '523'.
You mention in your question "property" but those are just attributes. One way to achieve that is to actually use a property:
class BulkData:
def __init__(self, fields):
self.fields = fields
def __str__(self):
return ''.join(self.fields)
class Node(BulkData):
def __init__(self, x, y, z):
self._x = x
self._y = y
self._z = z
fields = [self._x, self._y, self._z]
super().__init__(fields)
#property
def x(self):
return self._x
#x.setter
def x(self, val):
self._x = val
self.fields[0] = val
And now:
>>> n1 = Node('1','2','3')
>>> n1.x = '5'
>>> print(n1)
523
You can define actual Python properties that reflect the state of fields:
class BulkData:
def __init__(self, fields, floats=None):
if floats is None:
floats = []
self.fields = fields
self.floats = floats
def __str__(self):
return ''.join([str(x) for x in self.fields])
class Node(BulkData):
def __init__(self, x, y, z):
super().__init__([x, y, z])
#property
def x(self):
return self.fields[0]
#x.setter(self, v):
self.fields[0] = v
#property
def y(self):
return self.fields[1]
#y.setter(self, v):
self.fields[1] = v
#property
def z(self):
return self.fields[2]
#z.setter(self, v):
self.fields[2] = v
Instead of instance attributes, you define property instances that "wrap" a particular slot of the underlying fields attribute.
If you are thinking, that's a lot of code... you're right. Let's fix that.
class FieldReference(property):
def __init__(self, pos):
def getter(self):
return self.fields[pos]
def setter(self, v):
self.fields[pos] = v
super().__init__(getter, setter)
class Node(BulkData):
def __init__(self, x, y, z):
super().__init__([x, y, z])
x = FieldReference(0)
y = FieldReference(1)
z = FieldReference(2)
Here, we subclass property and hard-code the getter and setter functions to operate on the position specified by the argument to the property. Note that self in getter and setter are distinct from self in __init__, but getter and setter are both closures with access to __init__'s argument pos.
You might also want to read the Descriptor how-to, specifically the section on properties to understand why FieldReference is defined the way it is.
Related
I'm writing a Python class A with a method square() that returns a new instance of that class with its first attribute squared. For example:
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
I would like to use this method in a subclass B so that it returns an instance of B with x squared but all additional attributes of B unchanged (i. e. taken from the instance). I can get it to work by overwriting square() like this:
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def square(self):
return self.__class__(self.x**2, self.y)
If I don't overwrite the square() method, this little code example will fail because I need to pass a value for y in the constructor of B:
#test.py
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
#def square(self):
# return self.__class__(self.x**2, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
$ python test.py
9
Traceback (most recent call last):
File "test.py", line 20, in <module>
b2 = b.square()
File "test.py", line 6, in square
return self.__class__(self.x**2)
TypeError: __init__() takes exactly 3 arguments (2 given)
Overwriting the method once isn't a problem. But A potentially has multiple methods similar to square() and there might be more sub(sub)classes. If possible, I would like to avoid overwriting all those methods in all those subclasses.
So my question is this:
Can I somehow implement the method square() in A so that it returns a new instance of the current subclass with x squared and all other attributes it needs for the constructor taken from self (kept constant)? Or do I have to go ahead and overwrite square() for each subclass?
Thanks in advance!
I'd suggest implementing .__copy__() (and possibly .__deepcopy__ as well) methods for both classes.
Then your squared can be simple method:
def squared(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
It will work with inheritance, assuming all child classes have correctly implemented __copy__ method.
EDIT: fixed typo with call to copy()
Full working example:
#test.py
from copy import copy
class A:
def __init__(self, x):
self.x = x
def square(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
def __copy__(self):
return A(self.x)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def __copy__(self):
return B(self.x, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
check if the object contains y then return the right class instance:
class A:
x: int
def __init__(self, x):
self.x = x
def square(self):
if hasattr(self, 'y'):
return self.__class__(self.x ** 2, self.y)
return self.__class__(self.x**2)
class B(A):
y: int
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
# def square(self):
# return self.__class__(self.x**2, self.y)
I'm following this code example from a python course:
class P:
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
And I tried to implement this pattern to my own code:
class PCAModel(object):
def __init__(self):
self.M_inv = None
#property
def M_inv(self):
return self.__M_inv
#M_inv.setter
def set_M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
Note that I want the M_inv property to be None before I have run the setter the first time. Also, the setter solely relies on other properties of the class object, and not on input arguments.
The setter decorator generates an error:
NameError: name 'M_inv' is not defined
Why is this?
Your setter method should be like below:
#M_inv.setter
def M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
The decorator #M_inv.setter and the function def M_inv(self): name should be same
The example is wrong.
EDIT: Example was using a setter in __init__ on purpose.
Getters and setters, even though they act like properties, are just methods that access a private attribute. That attribute must exist.
In the example, self.__x is never created.
Here is my suggested use :
class PCAModel(object):
def __init__(self):
# We create a private variable
self.__M_inv = None
#property
def M_inv(self):
# Accessing M_inv returns the value of the previously created variable
return self.__M_inv
#M_inv.setter
def M_inv(self): # Keep the same name than your propery
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
Thinking about this and I'm wondering if it is possible (and if so, how to make such a decorator etc.) to have a classmethod, that IF called from an instance, can retrieve data on the instance? Perhaps some more clarity on how the staticmethod and classmethod decorators work would be helpful too (looking at the implementation __builtin__.py did not help)
Example use would be:
class A(object):
def __init__(self, y):
self.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
So that we could do:
>>>A.f(3, 5)
8
>>>a = A(5)
>>>a.f(3)
8
I came up with this below to mimic the behavior but its pretty inconvenient to implement:
class A(object):
def __init__(self, y):
self.y = y
self.f = self.f_
def f_(self, x):
return x + self.y
#classmethod
def f(cls, x, y):
return x + y
To expand on the comments made by #Adirio You could make a decorator that accomplishes this dynamically.
In this particular implementation, when the decorated method is called it will do a partial bind of the provided arguments to the method and uses the method's signature to determine what parameters have not been provided.
For any unspecified argument, if the calling object has an attribute matching the unspecified parameter name, the object's attribute value will be injected into the function.
import inspect
class BindableConstructor(object):
def __init__(self, meth):
self.meth = meth
self.sig = inspect.signature(self.meth)
def __get__(self, obj, klass=None):
if obj is not None:
print('Method ', repr(self.meth), ' called from instance ', repr(obj))
if klass is None:
klass = type(obj)
def newmeth(*args, **kwargs):
ba = self.sig.bind_partial(*args, **kwargs)
ba.apply_defaults()
for paramname in self.sig.parameters:
if paramname not in ba.arguments and hasattr(obj, paramname):
ba.arguments[paramname] = getattr(obj, paramname)
return self.meth(klass, *ba.args, **ba.kwargs)
return newmeth
Then suppose you have the following class using this decorator
class MyClass(object):
def __init__(self, y):
self.y = y
#BindableConstructor
def my_constructor(cls, x, y):
return cls(x + y)
Then the following behavior would be observed
>>> a = MyClass(5)
>>> b = MyClass.my_constructor(3, 2)
>>> b
<__main__.MyClass object at 0x0605C770>
>>> b.y
5
>>> c = b.my_constructor(3) # c.y == b.y + 3
Method <function MyClass.my_constructor at 0x05396420> called from instance <__main__.MyClass object at 0x0605C770>
>>> c.y
8
In this particular case ba.apply_defaults is called before checking the object's attributes to inject. If you want the object's attributes to take precedence over defaults, call ba.apply_defaults after the parameter injection logic.
When you try you example, you get an error saying
AttributeError: type object 'A' has no attribute 'y', because in constructor, you assigned y as an attribute of the object and not of the class.
The trivial fix:
class A(object):
def __init__(self, y):
A.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
Would indeed solve the error, but as the class will only know one single object at a time, you would get weird result as soon as you use more than one:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3) # fine till there...
8
>>> b = A(7)
>>> a.f(3) # last created object wins here!
10
So the only foolproof way is to create an attribute with the name of the class function in each object. As you only call a class method, a lamdba is enough:
class A(object):
def __init__(self, y):
self.y = y
self.f = lambda x: A.f(x, y) # declare a shortcut for the class method
#classmethod
def f(cls, x, y=None):
return x + y
You can then safely do:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3)
8
>>> b = A(7)
>>> a.f(3)
8
>>> b.f(3)
10
Do not forget to handle error cases.
class InstanceAndClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, instance, owner=None):
if instance is None:
instance = owner
def newfunc(*args, **kwargs):
return self.f(instance, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#InstanceAndClassMethod
def f(cls, x, y=None):
try:
y = cls.y if y is None else y
except AttributeError:
raise TypeError("f() missing 1 required positional argument: 'y'")
return x + y
With the help of docs.python.org/3/howto/descriptor.html I came up with this, seems to work:
class CoolerClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass):
if obj is None:
self_ = klass
else:
self_ = obj
def newfunc(*args, **kwargs):
return self.f(self_, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#CoolerClassMethod
def f(cls, x, y=None):
y = cls.y if y is None else y
return x + y
Testing:
>>> a = A(5)
>>> A.f(3, 5)
8
>>> a.f(3)
8
>>> A.f(3, 5)
8
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.
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?