Python: class attribute / variable inheritance with polymorphism?

In my endeavors, as a python student, I recently got stuck in some odd (from my point of view) behavior if I tried to work with class attributes. I am not complaining, but I will be grateful for some useful comments to shed light on this issue.

To reduce a complex question to a more concise question, I would formulate it like this:

What is the "pythonic" way to make sure that the class attribute behaves more like a static variable in an inheritance tree?

It seems to me that the class attribute behaves like the default “copy by read” value with polymorphic characteristics. While I perform read-only operations, it remains "single", but as soon as I access the class attribute with the assignment through a derived class or instance, it turns into a new link, losing contact with the inherited link base.

(It has the potential for some interesting features, but you should understand this by hugging it, so understanding is greatly appreciated.)

class A(object): classvar = 'A' def setclassvar(self, value): A.classvar = value def __str__(self): return "%s id(%s) " %(A.classvar, hex(id(A.classvar))[2:-1].upper()) class A1(A): pass class B(object): classvar = 'B' def setclassvar(self, value): self.__class__.classvar = value def __str__(self): cvar = self.__class__.classvar return "%s id(%s) " %(cvar, hex(id(cvar))[2:-1].upper()) class B1(B): def setclassvar(self, value): self.__class__.classvar = value a, a1 = A(), A1() a1.setclassvar('a') print "new instance A: %s" %a print "new instance A1: %s" %a b, b1 = B(), B1() b1.setclassvar('bb') print "new instance B: %s" %b print "new instance B1: %s" %b1 a1.setclassvar('aa') print "new value a1: %s" %a print "new value a: %s" %a a1.classvar = 'aaa' print "direct access a1: %s id(%s)" %(a1.classvar, hex(id(a1.classvar))[2:-1].upper()) print "method access a1: %s" %a1 print "direct access a: %s" %a 

produces the following:

 new instance A: a id(B73468A0) new instance A1: a id(B73468A0) new instance B: B id(B73551C0) new instance B1: bb id(AD1BFC) new value a1: aa id(AD1BE6) new value a: aa id(AD1BE6) direct access a1: aaa id(A3A494) method access a1: aa id(AD1BE6) direct access a: aa id(AD1BE6) 

Thus, direct (assigning) access to object.classvar or indirect via self.__class__.classvar does not match BASECLASS.classvar .

Is this a problem of the area or something completely different.

We are waiting for your answers and thank you in advance. :-)


Edit: for a short time there was an answer involving the use of class descriptors: How to create a class property? .

Unfortunately this does not work:

 class Hotel(Bar): def __init__(self): Hotel.bar += 1 hotel = Hotel() assert hotel.bar == 51 assert hotel.bar == foo.bar 

The second statement fails! hotel.bar does not refer to the same object as foo.bar , and hotel.bar refers to others other than Hotel.bar!


2nd Edit: I understand very well that singletones are considered an "antipatter", and I was not going to use them (extensively). Therefore, I did not mention them in the tile question. However, there are many solutions that discuss and provide solutions with and about singletones, my question remains: why does a class variable easily separate a link? Ruby behaves as it seems natural to me: http://snippets.dzone.com/posts/show/6649

+6
source share
2 answers

If the complexity of the implementation is hard to explain, this is a bad idea.

In this case, I turn on the implementation for the sake of completeness, and for this I am a complex thing that I like in Python.

So the snippet below seems to close a bit to come up with the Decorator class that fills the needs of OP: a class variable that remains “unified” for reading and writing to derived classes.

In addition, as a bonus, I wrap the attribute in a descriptor that makes the attribute unchanged inside the instances, so that whenever an attribute is written - either in a subclass or in an instance of a subclass of the source class, the class attribute is updated correctly.

As Zen of Python says, “If implementation complexity is hard to understand, this is a bad idea” - I don’t think I can come up with anything more complicated - we are talking about dynamically generated metaclasses Here. It will work, but it will lose, this is a "non-pythonic" code, because it is very cryptic due to the heavy use of classes, metaclass, closures and descriptors.

 def SingletonAttrs(**names): keys = names.keys() def class_decorator(cls): class Meta(type): def __getattribute__(cls, attr): if attr in keys: return type.__getattribute__(owner_cls, attr) return type.__getattribute__(cls, attr) def __setattr__(cls, attr, value): if attr in keys: class Wrapper(object): def __init__(self, value): self.__set__(None, value) __set__ = lambda self, instance, value: setattr(owner_cls,"__" + attr, value) __get__ = lambda self, instance, owner: type.__getattribute__(owner_cls, "__" + attr) return type.__setattr__(owner_cls, attr, Wrapper(value)) return type.__setattr__(cls, attr, value) owner_cls = Meta(cls.__name__, cls.__bases__, cls.__dict__.copy()) for key in keys: setattr(owner_cls, key, names[key]) return owner_cls return class_decorator if __name__ == "__main__": @SingletonAttrs(a="value 1", b="value 2") class Test(object): pass class TestB(Test): pass t = Test() print ta print tb tb = TestB() tb.a = "value 3" print Test.a 
+1
source
 a1.classvar = 'aaa' 

This is not a "reference" to a class variable.

This is the new instance variable in object 'a1'.

An expression of type A.classvar is a class variable. A class object (and its superclasses) has a class dictionary ( A.__dict__ ) with class-level objects defined in it. Name resolution works by checking the class, and then all the superclasses in method resolution order (MRO).

An expression of type A.classvar resolved by searching through the namespace of objects. When it is a read link, it searches for the object and class (and superclasses).

When this appears on the left side of the destination, an instance variable ("classvar") is simply created on the reference object ("a"). Do not search through parent namespaces to resolve the name, since nothing is resolved. He is being created.

+2
source

Source: https://habr.com/ru/post/904158/


All Articles