Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python:class attribute/variable inheritance with polymorphism?

In my endeavours as a python-apprentice i got recently stuck at some odd (from my point of view) behaviour if i tried to work with class attributes. I'm not complaining, but would appreciate some helpful comments to shed some light on this issue.

To reduce a complex matter into a more concise question i would formulate it like this:

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

It seems to me like a class-attribute behaves like a "copy on read" default value with polymorphic characteristics. As long as i do "read-only" operations it stays a "singleton", but as soon, as i access the class-attribute with an assignment through the derived class or instance it gets morphed into a new reference loosing the relation to the inherited base-reference.

(It has sure potential for some interessting features, but you have to understand it to embrace it, so some insight is highly 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)

So either the direct (assigning) access object.classvar or mediated through self.__class__.classvar are not the same as BASECLASS.classvar.

Is this a scope issue or somethin totaly different.

Looking forward to your answers and thanks in forward. :-)


Edit: There was an answer for a very short time suggesting the use of class-descriptors like: How to make a class property?.

Unfortunatly that doesn't seem to work:

class Hotel(Bar):
    def __init__(self):        
        Hotel.bar += 1

hotel = Hotel()
assert hotel.bar == 51
assert hotel.bar == foo.bar

The 2nd assertion fails! hotel.bar doesn't reference the same object as foo.bar and hotel.bar references somethin other then Hotel.bar!


2nd Edit: I'm quite aware that singletons are considered an "antipattern" and i didn't intend to use them (extensivly). Therefore i didn't mention them in the question-titel. Even so there are many solutions discussing and providing solutions with and about singletons, my question stays: Why can a class-variable detach it's reference so easily? Ruby behaves more the way it feels natural to me: http://snippets.dzone.com/posts/show/6649

like image 620
Don Question Avatar asked Dec 20 '11 14:12

Don Question


People also ask

Are class attributes inherited Python?

It's good when you want to establish a subtype from an existing object. Objects are defined by classes, classes can inherit attributes and behavior from pre-existing classes. The resulting classes are known as derived classes or subclasses. A subclass “inherits” all the attributes (methods, etc) of the parent class.

Does Python have inheritance and polymorphism?

Polymorphism and Inheritance Like in other programming languages, the child classes in Python also inherit methods and attributes from the parent class. We can redefine certain methods and attributes specifically to fit the child class, which is known as Method Overriding.

Can a class attribute be used without instance?

while you can access class attributes using an instance it's not safe to do so. In python, the instance of a class is referred to by the keyword self. Using this keyword you can access not only all instance attributes but also the class attributes.

How do you assign a value to an attribute in a class in Python?

Use dot notation or setattr() function to set the value of a class attribute. Python is a dynamic language. Therefore, you can assign a class variable to a class at runtime. Python stores class variables in the __dict__ attribute.


1 Answers

a1.classvar = 'aaa'

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

That is a new instance variable in the object 'a1'.

An expression like A.classvar is the class variable. The class object (and it's superclasses) all have a class level dictionary (A.__dict__) with class-level objects defined in it. Name resolution works by checking the class, then all the super-classes in Method Resolution Order (MRO).

An expression like a.classvar is resolved by a search through the object's namespace. When this is a "reading" reference, the object and the class (and the superclasses) are searched.

When this appears on the left side of assignment, the instance variable ("classvar") is simply created on the referenced object ("a"). No searching through parent namespaces to resolve a name, since there's nothing to resolve. It's being created.

like image 111
S.Lott Avatar answered Nov 15 '22 10:11

S.Lott