python:class attribute/variable inheritance with polymorphism?

2024/10/7 22:20:37

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):passclass B(object):classvar = 'B'def setclassvar(self, value):self.__class__.classvar = value            def __str__(self):cvar = self.__class__.classvarreturn "%s id(%s) " %(cvar, hex(id(cvar))[2:-1].upper())class B1(B):def setclassvar(self, value):self.__class__.classvar = valuea, a1 = A(), A1()
a1.setclassvar('a')
print "new instance A: %s" %a
print "new instance A1: %s" %ab, b1 = B(), B1()
b1.setclassvar('bb')
print "new instance B: %s" %b
print "new instance B1: %s" %b1a1.setclassvar('aa')
print "new value a1: %s" %a
print "new value a: %s" %aa1.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 += 1hotel = 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

Answer
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.

https://en.xdnf.cn/q/70195.html

Related Q&A

Unable to load firefox in selenium webdriver in python

I have installed Python 3.6.2, Selenium 3.5.0 with GeckoDriver 0.18.0 and the firefox version is 54.0.1version on windows 7. I am trying to run a selenium script which is loading a firefox where i get …

Plot hyperplane Linear SVM python

I am trying to plot the hyperplane for the model I trained with LinearSVC and sklearn. Note that I am working with natural languages; before fitting the model I extracted features with CountVectorizer …

Calculating plugin dependencies

I have the need to create a plugin system that will have dependency support and Im not sure the best way to account for dependencies. The plugins will all be subclassed from a base class, each with i…

Vectorization: Not a valid collection

I wanna vectorize a txt file containing my training corpus for the OneClassSVM classifier. For that Im using CountVectorizer from the scikit-learn library. Heres below my code: def file_to_corpse(file…

Solve a simple packing combination with dependencies

This is not a homework question, but something that came up from a project I am working on. The picture above is a packing configuration of a set of boxes, where A,B,C,D is on the first layer and E,F,G…

ImportError: cannot import name FFProbe

I cant get the ffprobe package to work in Python 3.6. I installed it using pip, but when I type import ffprobe it saysTraceback (most recent call last): File "<stdin>", line 1, in <m…

Generate larger synthetic dataset based on a smaller dataset in Python

I have a dataset with 21000 rows (data samples) and 102 columns (features). I would like to have a larger synthetic dataset generated based on the current dataset, say with 100000 rows, so I can use it…

Executing python script in android terminal emulator

I installed python 2.7 in my Android device and I tried executing a python script by typing the command in terminal emulator. The problem is that although I use the full path for python the following e…

How to return error messages in JSON with Bottle HTTPError?

I have a bottle server that returns HTTPErrors as such:return HTTPError(400, "Object already exists with that name")When I receive this response in the browser, Id like to be able to pick out…

Cant execute msg (and other) Windows commands via subprocess

I have been having some problems with subprocess.call(), subprocess.run(), subprocess.Popen(), os.system(), (and other functions to run command prompt commands) as I cant seem to get the msg command to…