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
Related
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 have a parent class with 3 items in it. I am trying to create a child class that when called updates a set item in the parent class.
class NOS:
def __init__(self):
self.Bike = 0
self.car = 0
self.plane = 0
class buy(NOS):
def __init__(self, mode):
NOS.__init__(self)
self.mode = mode
def buy_comp(self, value):
self.mode += value
if i called it like below
a = buy('bike')
a.buy_comp(4)
I am trying to get to a situation where bike would equal 4. The above did not work. Neither did the below where i tried to use buy as a function instead of a class.
def buy(self, mode, value):
self.mode += value
a= NOS()
a.buy('bike', 5)
Here i got the error - AttributeError: 'NOS' object has no attribute 'bike'
In the first example you posted, your child class "buy" is not actually a child class, because it is not inheriting from "NOS".
Not exactly sure what you're trying to achieve. Maybe this is helpful?
class Parent:
def __init__(self):
self.foo = "Parent Foo"
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def set_foo(self, new_foo):
self.foo = new_foo
child = Child()
print(child.foo)
child.set_foo("New Foo")
print(child.foo)
Output:
Parent Foo
New Foo
EDIT - Oh, I think I get it now. Something like this maybe?
class NOS:
def __init__(self):
self.bike = 0
self.car = 0
self.plane = 0
class Buy(NOS):
def __init__(self, item_name):
NOS.__init__(self)
self.item_name = item_name
def buy_comp(self, amount):
try:
old_value = getattr(self, self.item_name)
except NameError:
# No such item exists
pass
else:
setattr(self, self.item_name, old_value + amount)
a = Buy("bike")
print(a.bike)
a.buy_comp(4)
print(a.bike)
However, I think that if you're relying on getattr and setattr, there's bound to be a better way. I have a feeling that this may be an instance of an XY problem. Can you tell us more about the actual use case? I'm sure there's a more elegant solution you could benefit from.
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.
Consider following example
class Key:
def __init__(self, s):
self.s = s
d = {}
for x in range(1, 10000):
t = Key(x)
d[t] = x
This will create 10000 keys. Is it possible to control the object creation of class key, for example we cannot create more than 5 objects of key class. The loop should not be changed in any ways.
You can control how, or how many objects are created by giving your class a __new__ method:
class Key(object):
_count = 0
def __new__(cls, s):
if cls._count == 5:
raise TypeError('Too many keys created')
cls._count += 1
return super(Key, cls).__new__(cls, s)
def __init__(self,s):
self.s = s
Key.__new__() is called to create a new instance; here I keep a count of how many are created, and if there are too many, an exception is raised. You could also keep a pool of instances in a dictionary, or control creating of new instance in other ways.
Note that this only works for new-style classes, inheriting from object.
You can also use a metaclass approach
import weakref
import random
class FiveElementType(type):
def __init__(self, name, bases, d):
super(FiveElementType, self).__init__(name, bases, d)
self.__cache = weakref.WeakValueDictionary()
def __call__(self, *args):
if len(self.__cache) == 5:
return random.choice(self.__cache.values())
else:
obj = super(FiveElementType, self).__call__(*args)
self.__cache[len(self.__cache)] = obj
return obj
class Key(object):
__metaclass__ = FiveElementType
def __init__(self, s):
self.s = s
You can choose a random element or select it on the base of stored index. In this approach your loop don't fail with an exception that can be right or not, depending on your intention.
I am using properties to execute some code every time there is a change to an attribute, like this:
class SomeClass(object):
def __init__(self,attr):
self._attr = attr
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
And this works great:
>>> a = SomeClass(5)
>>> a.attr = 10
Do some code here every time attr changes
But if I store a mutable object in attr instead, attr can be modified directly, bypassing the setter and my change-detection code:
class Container(object):
def __init__(self,data):
self.data = data
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
>>>
Let's assume that attr is only ever going to be used to store an object of type Container. Is there an elegant way to modify SomeClass and/or Container to make SomeClass execute _on_change whenever the Container object stored in attr is modified? In other words, I want my output to be:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
Here is another solution. Some kind of proxy class. You dont need to modify any classes to monitor attributes changes in them, only wrap object in ChangeTrigger derived class with ovverriden _on_change function:
class ChangeTrigger(object):
def __getattr__(self, name):
obj = getattr(self.instance, name)
# KEY idea for catching contained class attributes changes:
# recursively create ChangeTrigger derived class and wrap
# object in it if getting attribute is class instance/object
if hasattr(obj, '__dict__'):
return self.__class__(obj)
else:
return obj
def __setattr__(self, name, value):
if getattr(self.instance, name) != value:
self._on_change(name, value)
setattr(self.instance, name, value)
def __init__(self, obj):
object.__setattr__(self, 'instance', obj)
def _on_change(self, name, value):
raise NotImplementedError('Subclasses must implement this method')
Example:
class MyTrigger(ChangeTrigger):
def _on_change(self, name, value):
print "New value for attr %s: %s" % (name, value)
class Container(object):
def __init__(self, data):
self.data = data
class SomeClass(object):
attr_class = 100
def __init__(self, attr):
self.attr = attr
self.attr_instance = 5
>>> a = SomeClass(5)
>>> a = MyTrigger(a)
>>>
>>> a.attr = 10
New value for attr attr: 10
>>>
>>> b = SomeClass(Container(5))
>>> b = MyTrigger(b)
>>>
>>> b.attr.data = 10
New value for attr data: 10
>>> b.attr_class = 100 # old value = new value
>>> b.attr_instance = 100
New value for attr attr_instance: 100
>>> b.attr.data = 10 # old value = new value
>>> b.attr.data = 100
New value for attr data: 100
Here is a version of SomeClass and Container that I think has the behavior you are looking for. The idea here being that modifications to Container will call the _on_change() function of the SomeClass instance that is associated with it:
class Container(object):
def __init__(self, data):
self.data = data
def __setattr__(self, name, value):
if not hasattr(self, name) or getattr(self, name) != value:
self.on_change()
super(Container, self).__setattr__(name, value)
def on_change(self):
pass
class SomeClass(object):
def __init__(self, attr):
self._attr = attr
self._attr.on_change = self._on_change
#property
def attr(self):
return self._attr
#attr.setter
def attr(self,value):
if self._attr != value:
self._on_change()
self._attr = value
def _on_change(self):
print "Do some code here every time attr changes"
Example:
>>> b = SomeClass(Container(5))
>>> b.attr.data = 10
Do some code here every time attr changes
>>> b.attr.data = 10 # on_change() not called if the value isn't changing
>>> b.attr.data2 = 'foo' # new properties being add result in an on_change() call
Do some code here every time attr changes
Note that the only change to SomeClass was the second line in __init__(), I just included the full code for completeness and easy testing.
If you want to track changes and don't want to mess with juggling with on_change() methods in different classes you could use functools.partial in the way shown starting here.
This way you can wrap your data and hide it totally. Get/change will be possible only via some methods melded inside that object.
NB: python has no private properties and on convention that we all are grownups and act against rules. In your case users of your api shouldn't change data on container (after creation) directly.
NB: here for those who may be interested in other ways...