classmethods, class variables and subclassing

Steven Bethard steven.bethard at gmail.com
Fri Oct 21 13:34:05 EDT 2005


Andrew Jaffe wrote:
> Hi,
> 
> I have a class with various class-level variables which are used to 
> store global state information for all instances of a class. These are 
> set by a classmethod as in the following (in reality the setcvar method 
> is more complicated than this!):
> 
> class sup(object):
>     cvar1 = None
>     cvar2 = None
> 
>     @classmethod
>     def setcvar1(cls, val):
>         cls.cvar1 = val
> 
>     @classmethod
>     def setcvar2(cls, val):
>         cls.cvar2 = val
> 
>     @classmethod
>     def printcvars(cls):
>     print cls.cvar1, cls.cvar2
> 
> 
> I can then call setcvar on either instances of the class or the class 
> itself.
> 
> Now, the problem comes when I want to subclass this class. If I override 
> the setcvar1 method to do some new things special to this class, and 
> then call the sup.setcvar1() method, it all works fine:
> 
> class sub(sup):
>     cvar1a = None
> 
>     @classmethod
>     def setcvar1(cls, val, vala):
>         cls.cvar1a = vala
>         sup.setcvar1(val)
> 
>     @classmethod
>     def printcvars(cls):
>         print cls.cvar1a
>         sup.printcvars()
> 
> This works fine, and sets cvar and cvar2 for both classes.
> 
> However, if  I *don't* override the setcvar2 method, but I call 
> sub.setcvar2(val) directly, then only sub.cvar2 gets set; it is no 
> longer identical to sup.cvar1!
> 
> In particular,
>     sub.setcvar1(1,10)
>     sub.setcvar2(2)
>     sub.printcvars()
> prints
>   10
>   1 None
> 
> i.e. sub.cvar1, sub.cvar1a, sub.cvar2= 1 10 2
> but sup.cvar1, cvar2= 1 None

I'm not sure if I understand your goal here, but you can get different 
behavior using super().

py> class sup(object):
...     cvar1 = None
...     cvar2 = None
...     @classmethod
...     def setcvar1(cls, val):
...         cls.cvar1 = val
...     @classmethod
...     def setcvar2(cls, val):
...         cls.cvar2 = val
...     @classmethod
...     def printcvars(cls):
...         print cls.cvar1, cls.cvar2
...
py> class sub(sup):
...     cvar1a = None
...     @classmethod
...     def setcvar1(cls, val, vala):
...         cls.cvar1a = vala
...         super(sub, cls).setcvar1(val)
...     @classmethod
...     def printcvars(cls):
...         print cls.cvar1a
...         super(sub, cls).printcvars()
...
py> sub.setcvar1(1, 10); sub.setcvar2(2); sub.printcvars()
10
1 2
py> sup.printcvars()
None None

I'm not sure what you want sup.printcvars() to print afterwards. If you 
want it to print out "1 2" instead of "None None", then what you're 
trying to do is to set every cvar in every superclass.  You'll need to 
be explicit about this, perhaps something like:

py> class sup(object):
...     cvar1 = None
...     cvar2 = None
...     @classmethod
...     def setcvar1(cls, val):
...         for cls in cls.mro()[:-1]: # search through superclasses
...             cls.cvar1 = val
...     @classmethod
...     def setcvar2(cls, val):
...         for cls in cls.mro()[:-1]: # search through superclasses
...             cls.cvar2 = val
...     @classmethod
...     def printcvars(cls):
...         print cls.cvar1, cls.cvar2
...
py> class sub(sup):
...     cvar1a = None
...     @classmethod
...     def setcvar1(cls, val, vala):
...         for cls in cls.mro()[:-2]: # search through superclasses
...             cls.cvar1a = vala
...         super(sub, cls).setcvar1(val)
...     @classmethod
...     def printcvars(cls):
...         print cls.cvar1a
...         super(sub, cls).printcvars()
...
py> sub.setcvar1(1, 10); sub.setcvar2(2); sub.printcvars()
10
1 2
py> sup.printcvars()
1 2

That is, if you want the cvar set on every superclass, you need an 
assignment statement for every superclass.  There's probably a way to 
factor out the for-loop so you don't have to write it every time, but I 
haven't thought about it too much yet.  Perhaps an appropriate 
descriptor in the metaclass...

STeVe



More information about the Python-list mailing list