Is it possible to access superclass's attributes during class construction?
Here's my code:
class A:
x = 1
class B(A):
x += 1 # <- error
The increment line x += ... is not valid, because x is not defined at this moment. You may suggest doing it in __init__, but I need to initialize the values before the constructor and want to minimize the code.
The code x = A.x + 1 will not work in my case because the A is generated in run-time. So the real code for B looks like
class A:
x: int
def class_gen(params):
class SubA(A):
x = 1
return SubA
class B(class_gen(some_params)):
x += 1
I've found a weird workaround:
x = A.__subclasses__()[-1].x + 1 (indeed the last subclass of A will be the generated super), but it looks too dirty and unstable.
Another workaround is declaring the a 'stub' class variable:
...
_Stub = class_gen(some_params)
class B(_Stub):
x = _Stub.x + 1
but is it also looks urgly. Is there a better way for a perfectionist?
I think you can accomplish it by using __new__ in the subclass.
class A:
x = 1
class B(A):
def __new__(cls, *args, **kwargs):
cls.x += 1
return super().__new__(cls, *args, **kwargs)
b = B()
print(b.x) # 2
There's no easy way to do this. Probably the best is to give your B class a metaclass that handles incrementing of the x attribute for you:
class XIncrementerMeta(type):
def __new__(mcls, name, bases, namespace):
namespace['x'] = bases[0].x + 1
return super().__new__(mcls, name, bases, namespace)
class B(class_gen("some_params"), metaclass=XIncrementerMeta):
pass
print(B.x) # prints 2
If you need each B-like class to have its own kind of manipulation of the x value, you could put it in a classmethod and have an inherited metaclass call it. Or you could even have an __init_subclass__ method in A that calls methods to set the value on its subclasses:
class A:
def __init_subclass__(cls):
cls.set_X()
class SubA(A):
#classmethod
def set_X(cls):
cls.x = 1
class B(SubA):
#classmethod
def set_X(cls):
super().set_X() # get SubA to set an initial value
cls.x += 1 # then update it
print(B.x) # prints 2
I want to create a proxy in Python because of function and attributes access (something like private). I create the proxy with references to functions in the source object. But I have a problem, that functions have no problem with changing attributes but property yes. Here is an example:
A working example
class A:
def __init__(self):
self.value = 1
def get_value(self):
return self.value
class Proxy:
def __init__(self, cls):
self.get_value = cls.get_value
# del cls
a = A()
p = Proxy(a)
print(a.get_value(), p.get_value())
a.value = 2
print(a.get_value(), p.get_value())
Output:
1 1
2 2
Not working:
class A:
def __init__(self):
self.value = 1
#property
def get_value(self):
return self.value
class Proxy:
def __init__(self, cls):
self.get_value = cls.get_value
# del cls
a = A()
p = Proxy(a)
print(a.get_value, p.get_value)
a.value = 2
print(a.get_value, p.get_value)
Output:
1 1
2 1
Can someone explain me where the problem is and if there is any solution for this? I could use functions, but I think #property is more Python solution. And I really want to know what is the difference. Thank you
In Proxy.__init__, you end up executing:
self.get_value = a.get_value
and very different things happen in your two examples.
In the first case, a.get_value is a method of a. So, calling p.get_value() is the same as calling a.get_value(). You get identical results.
In the second case, you have already defined a.get_value as a property, so self.get_value = a.get_value is basically self.get_value = 2, it is just an int attribute of p.
I am trying to master (begin ;)) to understand how to properly work with decorators and descriptors on Python 3. I came up with an idea that i´m trying to figure how to code it.
I want to be able to create a class A decorated with certain "function" B or "class" B that allows me to create a instance of A, after delaring properties on A to be a component of certain type and assigning values on A __init__ magic function. For instance:
componentized is certain "function B" or "class B" that allows me to declarate a class Vector. I declare x and y to be a component(float) like this:
#componentized
class Vector:
x = component(float)
y = component(float)
def __init__ (self, x, y):
self.x = x
self.y = y
What I have in mind is to be able to this:
v = Vector(1,2)
v.x #returns 1
But the main goal is that I want do this for every marked component(float) property:
v.xy #returns a tuple (1,2)
v.xy = (3,4) #assigns to x the value 3 and y the value 4
My idea is to create a decorator #componentized that overrides the __getattr__ and __setattr__ magic methods. Sort of this:
def componentized(cls):
class Wrapper(object):
def __init__(self, *args):
self.wrapped = cls(*args)
def __getattr__(self, name):
print("Getting :", name)
if(len(name) == 1):
return getattr(self.wrapped, name)
t = []
for x in name:
t.append(getattr(self.wrapped, x))
return tuple(t)
#componentized
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y
And it kind of worked, but i don't think I quite understood what happened. Cause when I tried to do an assign and override the __setattr__ magic method it gets invoked even when I am instantiating the class. Two times in the following example:
vector = Vector(1,2)
vector.x = 1
How would could I achieve that sort of behavior?
Thanks in advance! If more info is needed don't hesitate to ask!
EDIT:
Following #Diego's answer I manage to do this:
def componentized(cls):
class wrappedClass(object):
def __init__(self, *args, **kwargs):
t = cls(*args,**kwargs)
self.wrappedInstance = t
def __getattr__(self, item):
if(len(item) == 1):
return self.wrappedInstance.__getattribute__(item)
else:
return tuple(self.wrappedInstance.__getattribute__(char) for char in item)
def __setattr__(self, attributeName, value):
if isinstance(value, tuple):
for char, val in zip(attributeName, value):
self.wrappedInstance.__setattr__(char, val)
elif isinstance(value, int): #EMPHASIS HERE
for char in attributeName:
self.wrappedInstance.__setattr__(char, value)
else:
object.__setattr__(self, attributeName, value)
return wrappedClass
And Having a class Vector like this:
#componentized
class Vector:
def __init__ (self, x, y):
self.x = x
self.y = y
It kind of behave like I wanted, but I still have no idea how to achieve:
x = component(float)
y = component(float)
inside the Vector class to somehow subscribe x and y of type float, so when I do the #EMPHASIS LINE(in the line I hardcoded a specific type) on the code I can check whether the value someone is assigning a value to x and/or y for an instance of Vector is of type I defined it with:
x = component(float)
So I tried this (a component (descriptor) class):
class component(object):
def __init__(self, t, initval=None):
self.val = initval
self.type = t
def __get__(self, obj, objtype):
return self.val
def __set__(self, obj, val):
self.val = val
To use component like a descriptor, but I couldn't managed to do a workaround to handle the type. I tried to do an array to hold val and type, but then didn't know how to get it from the decorator __setattr__ method.
Can you point me into the right direction?
PS: Hope you guys understand what I am trying to do and lend me a hand with it. Thanks in advance
Solution
Well, using #Diego´s answer (which I will be accepting) and some workarounds to achieve my personal needs I managed to this:
Decorator (componentized)
def componentized(cls):
class wrappedClass(object):
def __init__(self, *args):
self.wrappedInstance = cls(*args)
def __getattr__(self, name):
#Checking if we only request for a single char named value
#and return the value using getattr() for the wrappedInstance instance
#If not, then we return a tuple getting every wrappedInstance attribute
if(len(name) == 1):
return getattr(self.wrappedInstance, name)
else:
return tuple(getattr(self.wrappedInstance, char) for char in name)
def __setattr__(self, attributeName, value):
try:
#We check if there is not an instance created on the wrappedClass __dict__
#Meaning we are initializing the class
if len(self.__dict__) == 0:
self.__dict__[attributeName] = value
elif isinstance(value, tuple): # We get a Tuple assign
self.__checkMultipleAssign(attributeName)
for char, val in zip(attributeName, value):
setattr(self.wrappedInstance, char, val)
else:
#We get a value assign to every component
self.__checkMultipleAssign(attributeName)
for char in attributeName:
setattr(self.wrappedInstance, char, value)
except Exception as e:
print(e)
def __checkMultipleAssign(self, attributeName):
#With this we avoid assigning multiple values to the same property like this
# instance.xx = (2,3) => Exception
for i in range(0,len(attributeName)):
for j in range(i+1,len(attributeName)):
if attributeName[i] == attributeName[j]:
raise Exception("Multiple component assignment not allowed")
return wrappedClass
component (descriptor class)
class component(object):
def __init__(self, t):
self.type = t #We store the type
self.value = None #We set an initial value to None
def __get__(self, obj, objtype):
return self.value #Return the value
def __set__(self, obj, value):
try:
#We check whether the type of the component is diferent to the assigned value type and raise an exeption
if self.type != type(value):
raise Exception("Type \"{}\" do not match \"{}\".\n\t--Assignation never happened".format(type(value), self.type))
except Exception as e:
print(e)
else:
#If the type match we set the value
self.value = value
(The code comments are self explanatories)
With this design I can achieve what I wanted (explained above)
Thanks you all for your help.
I thing there is an easiest way to achive the behavior : overloading __getattr__and __setattr__ functions.
Getting vector.xy :
class Vector:
...
def __getattr__(self, item):
return tuple(object.__getattribute__(self, char) for char in item)
The __getattr__ function is called only when "normal" ways of accessing an atribute fails, as stated in the Python documentation.
So, when python doesn't find vector.xy, the __getattr__method is called and we return a tuple of every value (ie. x and y).
We use object.__getattribute__ to avoid infinite recurtion.
Setting vector.abc :
def __setattr__(self, key, value):
if isinstance(value, tuple) and len(key) == len(value):
for char, val in zip(key, value):
object.__setattr__(self, char, val)
else:
object.__setattr__(self, key, value)
The __setattr__ method is always called unlike __getattr__, so we set each value separately only when the item we want to set is of the same lenght as the tuple of value.
>>> vector = Vector(4, 2)
>>> vector.x
4
>>> vector.xy
(4, 2)
>>> vector.xyz = 1, 2, 3
>>> vector.xyxyxyzzz
(1, 2, 1, 2, 1, 2, 3, 3, 3)
The only drawback is that if you really want to asign a tuple like (suppose you have an attribute called size):
vector.size = (1, 2, 3, 4)
Then s, i, z and e will by assigned separately, and that's obviously not what you want !
FWIW, I've done something similar by abusing __slots__. I created an Abstract Base Class which read the subclass's slots and then used that for pickling (with __getstate__ and __setstate__). You could do something similar with get-set-attr but you will still need to muck about with the class's actual attr's vs the ones you want to use as get/set properties.
Previous answer:
Why not just use the #property decorator? See the third example in the docs. You would apply it by first changing the attr names to something different and private (like _x) and then use the actual name x as the property.
class Vector(object):
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 xy(self):
return (self._x, self._y) # returns a tuple
#xy.setter
def xy(self, value):
self._x, self._y = value # splits out `value` to _x and _y
And if you want this to happen with every attr automatically, then you will need to use a metaclass, as #kasramvd commented. If you don't have many such different classes where you want to do this or many properties, may not be worth the effort.
Lets say I have 2 class and I want to add the second classes attributes to first class I can make like that:
class first:
def __init__(self):
self.value_one = 2
self.value_two = 5
self.value_third = 7 #second class don't have that attribute
def sum_class(self, cls):
for attribute in cls.__dict__:
x = getattr(cls, attribute)
y = getattr(self, attribute)
setattr(self, attribute, x+y)
class second:
def __init__(self):
self.value_one = 3
self.value_two = 1
But it doesn't look pythonic is there any better way to do it?
My Classes will have more than 10 attributes so I don't want to add one by one that could be easy but massy code like:
def sum(self, cls):
self.value_one += cls.value_one
self.value_two += cls.value_two
Also my third class may have:
class ClassB:
def __init__(self):
self.value_one = 2
self.value_third = 3
I also want to able to add this class into my first class
The only class that have a behvaiour similar to what you are looking for is the Counter class:
>>> c = Counter()
>>> c['a'] = 1.0
>>> c + Counter('a')
Counter({'a': 2.0})
So you could store these "attributes" inside a Counter and use __getattr__ to use normal attribute access:
from collections import Counter
class ClassWithCounter:
def __init__(self, **kwargs):
self.counter = Counter(kwargs)
def __getattr__(self, attr):
# this allows to use the syntax: first().value_one
try:
return self.counter[attr]
except KeyError:
raise AttributeError(attr)
class first(ClasswithCounter):
def __init__(self):
super(first, self).__init__(value_one=2, value_two=5, value_third=7)
def sum_class(self, cls):
self.counter += cls.counter
class second(ClassWithCounter):
def __init__(self):
super(second, self).__init__(value_one=3, value_two=1)
Note however that the purpose of Counter is just to count things, so there may be some situations where it gives you strange results.
If that is the case you can simply implement your own dictionary-like class and use it in place of Counter.
Also a suggestion: given that you are writing this for a game, you should consider whether this kind of update is good or not. Because in this way the original "base values" for the player gets lost.
I personally would keep the "base values" separate and keep track of the "modifiers" to such values (e.g. bonuses or maluses provided by items, or temporary effects).
This apporach allows you to implement things like "the damage of this spell isn't affected by bonus armor" (so you just use the base value when computing the damage). Your current approach makes this more cumbersome.
you can make it shorter by using:
def sum_class(self, cls):
[setattr(self, attr, getattr(cls, attr) + getattr(self, attr)) for attr in cls.__dict__]
Edit 1:
It was unclear what you wanted, but after you sad in comments you want something like classA.__dict__ + classB.__dict_, maybe you can use this:
class sum_class:
def __init__(self, class_1, class_2):
self.sum = class_1.__class__()
self.class_2 = class_2
for attr in self.class_2.__dict__:
if attr in self.sum.__dict__:
setattr(self.sum, attr, getattr(self.class_2, attr) + getattr(self.sum, attr))
else:
setattr(self.sum, attr, getattr(self.class_2, attr))
class first:
def __init__(self):
self.value_one = 2
self.value_two = 5
self.value_third = 7 #second class don't have that attribute
def __add__(self, cls):
return sum_class(self, cls).sum
class second:
def __init__(self):
self.value_one = 3
self.value_two = 1
def __add__(self, cls):
return sum_class(self, cls).sum
when classes are defined like that then you can use it like this:
>>> f = first()
>>> s = second()
>>> x = f + s
>>> x.value_one
5
>>> x.value_two
6
>>> x.value_third
7
Hi I want to define an object value as a sum of values of two objects of the same class before I know what the value of those 2 objects is.
I need to do something like:
`A=: B+C
B=10
C=20
print A`
I have a class Set and I'm trying something like:
class Set(object):
def __init__(self, w):
self.value=w
a=Set
c=a
a(10)
print c
But c is still class instead of object instance. Can somebody help me?
a is a class Set and so is c. a(10) generates an instance but discards it right away. You need to create instances and assign values later. Something like that:
class Sum(object):
def __init__(self, *values):
self.values = values
#property
def value(self):
return sum(v.value for v in self.values)
class Value(object):
def __init__(self, value=0):
self.value=value
b = Value()
c = Value()
a = Sum(b,c)
b.value = 10
c.value = 20
print a.value