Python: how to share class attribute between derived classes? - python

I want to share some information between all the instances of some class and all it's derived classes.
class Base():
cv = "some value" # information I want to share
def print_cv(self, note):
print("{}: {}".format(note, self.cv))
#classmethod
def modify_cv(cls, new_value):
# do some class-specific stuff
cls.cv = new_value
class Derived(Base):
pass
b = Base()
d = Derived()
b.print_cv("base")
d.print_cv("derived")
Output is as expected (instances of both classes see correct class attribute):
base: some value
derived: some value
I can change the value of this class attribute and everything is still fine:
# Base.cv = "new value"
b.modify_cv("new value")
b.print_cv("base") # -> base: new value
d.print_cv("derived") # -> derived: new value
So far so good. The problem is that the "connection" between Base and Derived classes can be broken if I access cv via derived class:
# Derived.cv = "derived-specific value"
d.modify_cv("derived-specific value")
b.print_cv("base") # -> base: new value
d.print_cv("derived") # -> derived: derived-specific value
This behavior is expected, but this is not what I want!
I understand why a and b see different values of cv - because they are instances of different classes. I have overridden cv value in derived class and now derived class behaves differently, I've used this feature many times.
But for my current task I need a and b always use the same cv!
UPDATE
I have updated the question and now it better describes the real-life situation. Actually I did not modify cv value like this:
Base.cv = "new value"
modifications were done in some classmethods (actually all these class methods were implemented in Base class).
And now solution became obvious, I just need to modify the method slightly:
class Base():
#classmethod
def modify_cv(cls, new_value):
#cls.cv = new_value
Base.cv = new_value
Thank you all for discussion and ideas (in the begining I was going to use getters/setters and module-level attribute)

classmethod is useful when you need to know which class is calling the method, but if you want the same behaviour regardless of the class that's calling the method, you could use staticmethod instead. You can then access the class variable simply through the base class's name with Base.cv:
class Base:
cv = "some value" # information I want to share
def print_cv(self, note):
print("{}: {}".format(note, self.cv))
#staticmethod
def modify_cv(new_value):
Base.cv = new_value
You can still call it on any instance or subclass, but it always changes Base.cv:
>>> b = Base()
>>> d = Derived()
>>> Base.cv == Derived.cv == b.cv == d.cv == "some value"
True
>>> d.modify_cv("new value")
>>> Base.cv == Derived.cv == b.cv == d.cv == "new value"
True
Update:
If you still need access to the class for other reasons, use classmethod with the cls argument as you did before, but still access the base class's variable through Base.cv rather than cls.cv:
#classmethod
def modify_cv(cls, new_value):
do_stuff_with(cls)
Base.cv = new_value

You have to override __setattr__ on the class of the class, i.e. a metaclass:
class InheritedClassAttributesMeta(type):
def __setattr__(self, key, value):
cls = None
if not hasattr(self, key):
# The attribute doesn't exist anywhere yet,
# so just set it here
cls = self
else:
# Find the base class that's actually storing it
for cls in self.__mro__:
if key in cls.__dict__:
break
type.__setattr__(cls, key, value)
class Base(metaclass=InheritedClassAttributesMeta):
cv = "some value"
class Derived(Base):
pass
print(Derived.cv)
Derived.cv = "other value"
print(Base.cv)
Using metaclasses is often overkill, so directly specifying Base might be better.
To avoid unwanted side effects with this solution, consider checking first if key is in some predefined set of attribute names before changing the behaviour.

In Python, inside a method, you can use the bare __class__ variable name to mean the actual class the method is defined in.
This differs from the cls arg that is passed to classmethods, or self.__class__ on regular methods, that will refer to a subclass if the method is invoked in a subclass. Thus, cls.attr = value would set the value on the subclass class' __dict__, and the attribute value will be independent on that subclass from that point on. This is what you are getting there.
Instead, you can use:
class MyClass:
cv = "value"
#classmethod # this is actually optional
def modify_cv(cls, new_value):
__class__.cv = new_value
__class__ is created automatically in Python 3 by
the mechanism that allows one to write
parameterless form of super

Related

Is there a way in python to create a class that inherits from another class/object passed by an object?

I want to create a class that inherits another class and instantiates it with the object from the prev. class to have a new object from the new class ( old class attrs. and methods with new attrs. and methods).
example:
class A():
attrs...
methods...
class B(A):
def __init__(self, a_obj):
...
A_attrs + B_attrs...
A_methods + B_methods...
a = A()
# assign some values to 'a'
b = B(a)
# a and b should have the same params and behaviors
Is there a way to implement such an alternative class and use the new object?
It is hard to tell what exactly you are trying to achieve, but having the exact things you want to forward to a, it is easy to achieve.
No need to use metaclasses, but depending on the behavior you want, the special __getattribute__ and __setattr__ methods might help.
Take in mind that as far as methods are concerned, the inheritance mechanism will already do that: any methods called in an instance of B will be forwarded to the method defined in A, unless there is an overriding implementation of it in B: in this case the overriding method have to explicitly run the method in A by using a super() call, or skip it altoghether: it is up to the implementation.
Method overriding is independent of instances. If you want them to "see" a particular instance of A passed at B object instantiation, it is just the attributes in that instance that matter.
Now, if you want instances of B to proxy over to the attributes a particular instance of A, the special methods I mentioned can do the same bridge. We can implement those in a way that if any attribute access is attempted in attribute that existis in the a instance, that one is used instead. Also, the special behavior can be implemented in a mixin class, so you are free to implement your business logic in B, and deffer all special attribute handling mechanisms to the mixin instead.
_SENTINEL = object()
class ProxyAttrMixins:
def __init__(self):
# Do nothing: just prevent the proxied class` __init__ from being run
pass
def _inner_get(self, attrname):
bound_getattr = super().__getattribute__
try:
proxied = bound_getattr("proxied")
except AttributeError:
# No associated object to proxy to!
# just pass an try to retrieve the attribute from `self`
pass
else: # no AttributeError: there is a proxied object
associated_attr = getattr(proxied, attrname, _SENTINEL)
if associated_attr is not _SENTINEL:
# if object is a callable: it is a method. A mehtod in the derived class should
# be called if it exists, and just otherwise in the proxied object:
if callable(associated_attr):
try:
own_method = bound_getattr(attrname)
except AttributeError:
pass
else:
return "own", own_method
return "proxy", associated_attr
# if there is no proxied object, or if the proxied does not have the desired attribute,
# return the regular instance attribute:
return "own", bound_getattr(attrname)
def __getattribute__(self, attrname):
bound_getattr = super().__getattribute__
whose, attr = bound_getattr("_inner_get")(attrname)
return attr
def __setattr__(self, attrname, value):
bound_getattr = super().__getattribute__
try:
whose, attr = bound_getattr("_inner_get")(attrname)
except AttributeError:
whose = "own"
if whose != "own":
proxied = bound_getattr("proxied")
return setattr(proxied, attrname, value)
super().__setattr__(attrname, value)
class A:
def __init__(self, c: int):
self.c = c
class B(ProxyAttrMixins, A):
def __init__(self, a: A):
self.proxied = a
super().__init__() # this ensure B can still have colaborative inheritance.
# The Mixin's __init__ prevents A __init__ from being run and
# report on the missing `c` argument
And this code allows this kind of scenario:
In [18]: b = B(a:=A(5))
In [19]: b.c
Out[19]: 5
In [20]: b.c = 10
In [21]: a.c
Out[21]: 10

How to overwrite self after reading yaml? [duplicate]

I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>

How to set `__class__.__name__` as class attribute?

I need to get class name from class:
class Cls:
notation = None
def __init__(self):
notation = self.__class__.__name__
print(Cls.notation) prints None but I need 'Cls'
How to fix it or how to define class attribute which returns a name of class?
You are assigning to a local variable, not the class attribute:
def __init__(self):
Cls.notation = self.__class__.__name__
Note that self.__class__ isn't necessarily Cls, if there is a subclass of Cls involved. You might want to use
def __init__(self):
type(self).notation = self.__class__.__name__
depending on your use case.
Assigning to self.notation won't work, because that creates an instance attribute that shadows the class attribute.
If you want Cls.notation == "Cls" immediately after the class is defined, you may as well just hard-code it:
class Cls:
notation = "Cls"
or
class Cls:
pass
Cls.notation = Cls.__name__
though you can also write
class Cls:
notation = __qualname__
to set its value based on the name used in the first line of the statement, though __qualname__ takes into account nesting as well:
class Cls1:
class Cls2:
notation = __qualname__ # "Cls1.Cls2", not "Cls2"

Possible to hijack class definition with decorators?

Say I have a
class A:
def __init__(self, *args):
pass
and I want an decorator that copies A's definition and extend it with the new class.
def decorator(cls): # some decorator here
# make a new class which inherits from A
# return it while preserving the original A
Is that possible? (PS: This is to avoid maintainence problems.)
When you invoke a function using decorator syntax:
#my_decorator_function
class A:
pass
The decorator function's return value will replace the existing definition of A. So if you want it to create a new class and "return it while preserving the original A", you've got a tricky challenge. What you return will replace A, so you need to decide if that should be the original A or the new class. You can put the other one somewhere else.
For instance, this decorator would replace A with a subclass, and the subclass will make the original A class available as a class attribute named _orig:
def class_decorator(cls):
class SubClass(cls):
_orig = cls
# add other stuff here?
return SubClass
You can add extra logic to copy the original class's __name__ and __doc__ into the new class if you want to. You could also turn the logic around, and add SubClass as an attribute of cls before returning the otherwise unmodified cls.
Using #decorator is not the only possible syntax. You can put B = decorator(A) after the class definition.
class A:
...
B = decorator(A)
Now you still have a reference on the undecorated A, and you have a decorated version B.
The other answers have done a good job, but to make it crystal clear why you don't want to do this:
def dec(cls):
new_cls = type(cls.__name__, (cls,), {})
return new_cls
#dec
class A():
pass
Now inspect the method resolution order class A:
>>> A.__mro__
(<class '__main__.A'>, <class '__main__.A'>, <class 'object'>)
>>> classes = A.__mro__
>>> classes[0].__name__
'A'
>>> classes[1].__name__
'A'
TWO class As! Are they the same?
>>> classes[0] is classes[1]
False
Nope; different. The current variable A is pointing to the lowest one of course:
>>> A is classes[0]
True
But now you've lost name-access to the parent. That's usually not optimal.
In short: you are creating a metric ton of confusion and ambiguity for yourself a few months from now when you have forgotten all about what you did. Do something else.
If you really want to, here is an idea for spinning out new subclasses:
def add_babymaker(cls):
'''Adds a method for making new child classes.'''
def babymaker(name=None):
'''Creates a new child class based on the parent class.'''
name = name if name is not None else cls.__name__
new_cls = type(name, (cls,), {})
return new_cls
cls.babymaker = babymaker
return cls
#add_babymaker
class A():
pass
B = A.babymaker('B')
C = A.babymaker('C')
ANew = A.babymaker()
I think I have worked it out. That's not really a good idea.
def make_variant(cls):
suffix='VARIANT'
new = type(cls.__name__+suffix, (cls, ), {})
# new.__repr__ = lambda self: 'HELLO' # Just do whatever needed here
assert cls.__name__ + suffix not in globals()
globals()[cls.__name__+suffix] = new # Think twice about this line
return cls
#make_variant
class A:
def __init__(self):
pass
print(AVARIANT(), A())

How to implement property() with dynamic name (in python)

I am programming a simulations for single neurons. Therefore I have to handle a lot of Parameters. Now the Idea is that I have two classes, one for a SingleParameter and a Collection of parameters. I use property() to access the parameter value easy and to make the code more readable. This works perfect for a sinlge parameter but I don't know how to implement it for the collection as I want to name the property in Collection after the SingleParameter. Here an example:
class SingleParameter(object):
def __init__(self, name, default_value=0, unit='not specified'):
self.name = name
self.default_value = default_value
self.unit = unit
self.set(default_value)
def get(self):
return self._v
def set(self, value):
self._v = value
v = property(fget=get, fset=set, doc='value of parameter')
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
# par1 and par2 I can access perfectly via 'p1.v = ...'
# or get its value with 'p1.v'
class Collection(object):
def __init__(self):
self.dict = {}
def __getitem__(self, name):
return self.dict[name] # get the whole object
# to get the value instead:
# return self.dict[name].v
def add(self, parameter):
self.dict[parameter.name] = parameter
# now comes the part that I don't know how to implement with property():
# It shoule be something like
# self.__dict__[parameter.name] = property(...) ?
col = Collection()
col.add(par1)
col.add(par2)
col['par1'] # gives the whole object
# Now here is what I would like to get:
# col.par1 -> should result like col['par1'].v
# col.par1 = 5 -> should result like col['par1'].v = 5
Other questions that I put to understand property():
Why do managed attributes just work for class attributes and not for instance attributes in python?
How can I assign a new class attribute via __dict__ in python?
Look at built-in functions getattr and setattr. You'll probably be a lot happier.
Using the same get/set functions for both classes forces you into an ugly hack with the argument list. Very sketchy, this is how I would do it:
In class SingleParameter, define get and set as usual:
def get(self):
return self._s
def set(self, value):
self._s = value
In class Collection, you cannot know the information until you create the property, so you define the metaset/metaget function and particularize them only later with a lambda function:
def metaget(self, par):
return par.s
def metaset(self, value, par):
par.s = value
def add(self, par):
self[par.name] = par
setattr(Collection, par.name,
property(
fget=lambda x : Collection.metaget(x, par),
fset=lambda x, y : Collection.metaset(x,y, par))
Properties are meant to dynamically evaluate attributes or to make them read-only. What you need is customizing attribute access. __getattr__ and __setattr__ do that really fine, and there's also __getattribute__ if __getattr__ is not enough.
See Python docs on customizing attribute access for details.
Have you looked at the traits package? It seems that you are reinventing the wheel here with your parameter classes. Traits also have additional features that might be useful for your type of application (incidently I know a person that happily uses traits in neural simulations).
Now I implemented a solution with set-/getattr:
class Collection(object):
...
def __setattr__(self, name, value):
if 'dict' in self.__dict__:
if name in self.dict:
self[name].v = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
return self[name].v
There is one thing I quite don't like that much: The attributes are not in the __dict__. And if I have them there as well I would have a copy of the value - which can be dangerous...
Finally I succeded to implement the classes with property(). Thanks a lot for the advice. It took me quite a bit to work it out - but I can promise you that this exercise helps you to understand better pythons OOP.
I implemented it also with __getattr__ and __setattr__ but still don't know the advantages and disadvantages to the property-solution. But this seems to be worth another question. The property-solutions seems to be quit clean.
So here is the code:
class SingleParameter(object):
def __init__(self, name, default_value=0, unit='not specified'):
self.name = name
self.default_value = default_value
self.unit = unit
self.set(default_value)
def get(*args):
self = args[0]
print "get(): "
print args
return self._v
def set(*args):
print "set(): "
print args
self = args[0]
value = args[-1]
self._v = value
v = property(fget=get, fset=set, doc='value of parameter')
class Collection(dict):
# inheriting from dict saves the methods: __getitem__ and __init__
def add(self, par):
self[par.name] = par
# Now here comes the tricky part.
# (Note: this property call the get() and set() methods with one
# more argument than the property of SingleParameter)
setattr(Collection, par.name,
property(fget=par.get, fset=par.set))
# Applying the classes:
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
col = Collection()
col.add(par1)
col.add(par2)
# Setting parameter values:
par1.v = 13
col.par1 = 14
# Getting parameter values:
par1.v
col.par1
# checking identity:
par1.v is col.par1
# to access the whole object:
col['par1']
As I am new I am not sure how to move on:
how to treat follow up questions (like this itself):
get() is seems to be called twice - why?
oop-design: property vs. "__getattr__ & __setattr__" - when should I use what?
is it rude to check the own answer to the own question as accepted?
is it recommended to rename the title in order to put correlated questions or questions elaborated with the same example into the same context?
Other questions that I put to understand property():
Why do managed attributes just work for class attributes and not for instance attributes in python?
How can I assign a new class attribute via __dict__ in python?
I have a class that does something similar, but I did the following in the collection object:
setattr(self, par.name, par.v)

Categories