An attribute of a class-object is implicitly mutated into an unbound-method object if it starts out as a Python-coded function; thus, such functions must be wrapped as other callables if "just calling them" (without an instance-argument) is desired.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# direct, naive approach -- doesn't work...: class Class1: def static1(name): print "Hello",name # ...but now, a call such as: Class1.static1("John") # will fail with a TypeError, as 'static1' has become # an unbound-method object, not a plain function. # This is easy to solve with a simple tiny wrapper: class Callable: def __init__(self, anycallable): self.__call__ = anycallable # toy-example usage: class Class2: def static2(name): print "Hi there",name static2 = Callable(static2) # now, a call such as: Class2.static2("Peter") # works just fine, and as-expected
Perhaps more often than warranted, Python programmers coming from other languages offering "class-methods" (aka static-methods: class-object attributes that are callable _without_ an instance of the class as an argument) want to use such constructs in Python. There are many Pythonic alternatives, of course, but the plain/naive approach to defining 'class methods' in Python doesn't work "right out of the box", since Python-coded function objects become unbound-method objects when accessed as attributes of a class-object.
However, if the 'class-methods' approach is desired, a simple wrapper makes it easy to have in Python, since the "mutation to unbound-method" only happens to function-objects, NOT to other callables -- thus, if we don't want the mutation to happen, it suffices to use a callable that's not a function-object; handiest is an instance of a class with a __call__ method, which delegates calls to another callable it holds -- class Callable in the above example recipe is fully general for this use (tx to Carel Fellinger for pointing out Callable's current short form, from the Python FAQ!).
In the body of the class which we want to have 'class-methods', then, we just need to wrap those attributes, which we want to be 'class-methods', into instances of Callable. The same name may be used, if desired, for both the wrapper attribute and the wrapped one -- the latter, in this case, becomes unaccessible by its original name, as that gets usurped by the wrapper, but that's typically just what we want!, so this sub-idiom is advisable.
To avoid the mutation that happens to function-objects held as attributes of a class-object, one clear alternative is to hold a callable that is not a function-object, as in this recipe; the other clear alternative is to hold a function-object, but NOT as a class-object attribute -- for example, it can be an attribute of the module-object (the most Pythonic approach).
If close association to the class-object IS desired anyway, the function object can be held by the class object, but indirectly -- for example, in a list, tuple, or dictionary (that, in turn, is an attribute of the class object). The disadvantage of this approach, when compared to the one suggested by this recipe, is that awareness of how the function-object is held becomes necessary at each call site -- the 'indirection' must be de-referenced at each call site, and that is obtrusive and syntactically gauche; the wrapper approach is syntactically transparent at call-sites, and only needs a very modest and 'clean' amount of modification at definition-sites.