Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 24 lines
 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.

4 comments

Sim Harbert 21 years, 6 months ago  # | flag

Accessing class data? I am trying to write static class methods to class data, and this doesn't work for that. Is there a way to do that? Here is the code I was trying to use:

class Callable:

    def __init__(self, anycallable):
        self.__call__ = anycallable


class Num:

    num = 0

    def get():

        print "Num.get"
        return num

    def set(value = 0):

        print "Num.set"
        num = value

    get = Callable(get)
    set = Callable(set)

if __name__ == '__main__':

    print Num.set(10)
    print Num.get()
news reader 20 years, 10 months ago  # | flag

Accessing Class Data. Class data needs to be qualified, like so:

def get():
    print "Num.get"
    return Num.num  # qualify w/ classname

--A

Q Neill 15 years, 7 months ago  # | flag
Python 2.2.3 introduced unifying types and classes which provides hooks to create both static methods and class methods. See http://www.python.org/download/releases/2.2.3/descrintro/

Q

Noam 12 years, 11 months ago  # | flag

Just use @staticmethod above your function (and don't use the "self" argument inside the function).

See examples here: http://stackoverflow.com/questions/735975/static-methods-in-python

Created by Alex Martelli on Mon, 26 Mar 2001 (PSF)
Python recipes (4591)
Alex Martelli's recipes (27)

Required Modules

  • (none specified)

Other Information and Tasks