Learning Python - Mark Lutz [436]
class Sub(Spam):
def printNumInstances(): # Override a static method
print("Extra stuff...") # But call back to original
Spam.printNumInstances()
printNumInstances = staticmethod(printNumInstances)
>>> a = Sub()
>>> b = Sub()
>>> a.printNumInstances() # Call from subclass instance
Extra stuff...
Number of instances: 2
>>> Sub.printNumInstances() # Call from subclass itself
Extra stuff...
Number of instances: 2
>>> Spam.printNumInstances()
Number of instances: 2
Moreover, classes can inherit the static method without redefining it—it is run without an instance, regardless of where it is defined in a class tree:
>>> class Other(Spam): pass # Inherit static method verbatim
>>> c = Other()
>>> c.printNumInstances()
Number of instances: 3
Counting Instances with Class Methods
Interestingly, a class method can do similar work here—the following has the same behavior as the static method version listed earlier, but it uses a class method that receives the instance’s class in its first argument. Rather than hardcoding the class name, the class method uses the automatically passed class object generically:
class Spam:
numInstances = 0 # Use class method instead of static
def __init__(self):
Spam.numInstances += 1
def printNumInstances(cls):
print("Number of instances:", cls.numInstances)
printNumInstances = classmethod(printNumInstances)
This class is used in the same way as the prior versions, but its printNumInstances method receives the class, not the instance, when called from both the class and an instance:
>>> a, b = Spam(), Spam()
>>> a.printNumInstances() # Passes class to first argument
Number of instances: 2
>>> Spam.printNumInstances() # Also passes class to first argument
Number of instances: 2
When using class methods, though, keep in mind that they receive the most specific (i.e., lowest) class of the call’s subject. This has some subtle implications when trying to update class data through the passed-in class. For example, if in module test.py we subclass to customize as before, augment Spam.printNumInstances to also display its cls argument, and start a new testing session:
class Spam:
numInstances = 0 # Trace class passed in
def __init__(self):
Spam.numInstances += 1
def printNumInstances(cls):
print("Number of instances:", cls.numInstances, cls)
printNumInstances = classmethod(printNumInstances)
class Sub(Spam):
def printNumInstances(cls): # Override a class method
print("Extra stuff...", cls) # But call back to original
Spam.printNumInstances()
printNumInstances = classmethod(printNumInstances)
class Other(Spam): pass # Inherit class method verbatim
the lowest class is passed in whenever a class method is run, even for subclasses that have no class methods of their own:
>>> x, y = Sub(), Spam()
>>> x.printNumInstances() # Call from subclass instance
Extra stuff... Number of instances: 2 >>> Sub.printNumInstances() # Call from subclass itself Extra stuff... Number of instances: 2 >>> y.printNumInstances() Number of instances: 2 In the first call here, a class method call is made through an instance of the Sub subclass, and Python passes the lowest class, Sub, to the class method. All is well in this case—since Sub’s redefinition of the method calls the Spam superclass’s version explicitly, the superclass method in Spam receives itself in its first argument. But watch what happens for an object that simply inherits the class method: >>> z = Other() >>> z.printNumInstances() Number of instances: 3 This last call here passes