File: classmethods.py
#-----------------------------------------------------------------------
# LP5E supplemental example: classmethod versus staticmethod
#
# Classmethods can work much like customizable module functions:
# they're callable with no instances, are localized to the containing
# class, and may be customized via normal class inheritance. However,
# they are functionally equivalent to staticmethods (and to simple
# class-level functions called only through classes in 3.X), unless
# they use per-class data. Here is the case for classmethods:
#-----------------------------------------------------------------------
from __future__ import print_function # 2.X compatibility
class C:
x = 1
@classmethod
def m1(c): # replaced
print(c.__name__, c.x)
@classmethod
def m2(c): # inherited
print(c.__name__, c.x) # c is most-specific (lowest) class
class D(C):
x = 2
@classmethod
def m1(c): # m1 replaced, m2 from C, m3 unique
print(c.__name__, c.x)
@classmethod
def m3(c): # inherited
print(c.__name__, c.x)
class E(D):
x = 3
@classmethod
def m1(c): # m1 replaced, m2 from C, m3 from D
print(c.__name__, c.x)
print('CLASSMETHOD')
print('class calls...')
C.m1(); C.m2() # local, local
D.m1(); D.m2(); D.m3() # local, inherited, local
E.m1(); E.m2(); E.m3() # local, inherited, inherited
print('instance calls...')
c = C(); d = D(); e = E()
c.m1(); c.m2() # ditto, but from instances
d.m1(); d.m2(); d.m3()
e.m1(); e.m2(); e.m3()
#-----------------------------------------------------------------------
# And here's the equivalent with staticmethods. As function calls,
# the routing is the same, but there is no notion of per-class data
# here: staticmethods have access to data in their own class (or
# other classes named specifically) only, while classmethods have
# access to data in the class that's the implied subject of a call.
# That's why LP5E states that classmethod is best for per-class data,
# and staticmethod is for local class data. The two are equivalent
# only if the classes are used to package just functions with no data.
#-----------------------------------------------------------------------
class C:
x = 1
@staticmethod
def m1(): # replaced
print(C.__name__, C.x)
@staticmethod
def m2(): # inherited
print(C.__name__, C.x) # there is no lowest class here
class D(C):
x = 2
@staticmethod
def m1(): # m1 replaced, m2 from C, m3 unique
print(D.__name__, D.x)
@staticmethod
def m3(): # inherited
print(D.__name__, D.x)
class E(D):
x = 3
@staticmethod
def m1(): # m1 replaced, m2 from C, m3 from D
print(E.__name__, E.x)
print('\nSTATICMETHOD')
print('class calls...')
C.m1(); C.m2() # local, local
D.m1(); D.m2(); D.m3() # local, inherited, local
E.m1(); E.m2(); E.m3() # local, inherited, inherited
print('instance calls...')
c = C(); d = D(); e = E()
c.m1(); c.m2() # ditto, but from instances
d.m1(); d.m2(); d.m3()
e.m1(); e.m2(); e.m3()
#-----------------------------------------------------------------------
# Output
#-----------------------------------------------------------------------
"""
c:\...\code> py -3 classmethods.py
CLASSMETHOD
class calls...
C 1
C 1
D 2
D 2
D 2
E 3
E 3
E 3
instance calls...
C 1
C 1
D 2
D 2
D 2
E 3
E 3
E 3
STATICMETHOD
class calls...
C 1
C 1
D 2
C 1
D 2
E 3
C 1
D 2
instance calls...
C 1
C 1
D 2
C 1
D 2
E 3
C 1
D 2
"""