Asked  7 Months ago    Answers:  5   Viewed   40 times

What is the difference between the following class methods?

Is it that one is static and the other is not?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()

 Answers

74

In Python, there is a distinction between bound and unbound methods.

Basically, a call to a member function (like method_one), a bound function

a_test.method_one()

is translated to

Test.method_one(a_test)

i.e. a call to an unbound method. Because of that, a call to your version of method_two will fail with a TypeError

>>> a_test = Test() 
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

You can change the behavior of a method using a decorator

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

The decorator tells the built-in default metaclass type (the class of a class, cf. this question) to not create bound methods for method_two.

Now, you can invoke static method both on an instance or on the class directly:

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two
Tuesday, June 1, 2021
 
MDDY
answered 7 Months ago
38

All functions are also descriptors, so you can bind them by calling their __get__ method:

bound_handler = handler.__get__(self, MyWidget)

Here's R. Hettinger's excellent guide to descriptors.


As a self-contained example pulled from Keith's comment:

def bind(instance, func, as_name=None):
    """
    Bind the function *func* to *instance*, with either provided name *as_name*
    or the existing name of *func*. The provided *func* should accept the 
    instance as the first argument, i.e. "self".
    """
    if as_name is None:
        as_name = func.__name__
    bound_method = func.__get__(instance, instance.__class__)
    setattr(instance, as_name, bound_method)
    return bound_method

class Thing:
    def __init__(self, val):
        self.val = val

something = Thing(21)

def double(self):
    return 2 * self.val

bind(something, double)
something.double()  # returns 42
Tuesday, June 1, 2021
 
Avicinnian
answered 7 Months ago
57

This is not how staticmethod is supposed to be used. staticmethod objects are descriptors that return the wrapped object, so they only work when accessed as classname.staticmethodname. Example

class A(object):
    @staticmethod
    def f():
        pass
print A.f
print A.__dict__["f"]

prints

<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

Inside the scope of A, you would always get the latter object, which is not callable.

I'd strongly recommend to move the decorator to the module scope -- it does not seem to belong inside the class. If you want to keep it inside the class, don't make it a staticmethod, but rather simply del it at the end of the class body -- it's not meant to be used from outside the class in this case.

Thursday, June 17, 2021
 
tdous
answered 6 Months ago
15

I was looking for something similar for chaining Class functions and found no good answer, so here is what I did and thought was a very simple way of chaining: Simply return the self object.

So here is my setup:

Class Car():
    def __init__(self, name=None):
        self.name = name
        self.mode = 'init'

    def set_name(self, name):
        self.name = name
        return self

    def drive(self):
        self.mode = 'drive'
        return self

And now I can name the car and put it in drive state by calling:

my_car = Car()
my_car.set_name('Porche').drive()

Caveat: This only works on the class functions that do not intend to return any data.

Hope this helps!

Tuesday, July 27, 2021
 
JohnnyW
answered 5 Months ago
79

The choice of the type of method depends on other factors.

You have two cases. The first case is when the method has to be part of the class interface - e.g. it has to be called by users, or it has to be overridable in subclasses, or it uses the information in self, or it's likely that in a future version of the software you might need any of those.

In this first case you'd often either use a normal method (this is when the method relates to the instances and not to the class) or a classmethod (when the method relates to the class, e.g. it's an alternative constructor, a method for discovering class features, etc.). In both cases you can use a staticmethod instead if no information from the class/instance is used by the method, but you don't gain anything from doing so. And it would also break your ability to do cls.method(instance, *args) which is already too much for gaining nothing.

The second case is when the method isn't a part of the class in any way. Then it is generally advisable to use a function - the method is not really part of the interface, so it has no place there. Your example seems to be that case unless you want to override the tree/money calculator in subclasses, but that largely depends on what you're doing with it.

A special case is private methods - then you might want to use a method even if it's not really related to the class, private methods aren't part of the interface so it doesn't matter where you put them. Using a staticmethod still doesn't gain you much but there isn't any reason not to use it either.

There's actually one case where staticmethod is very helpful - when you're putting external functions (or other objects) in the class.

class Foo(object):
     trees2money = staticmethod(calculators.trees2money)
     foo = staticmethod(calculators.bar)

But when you have a static definition of the class, that's not really great, because you can always do the following instead.

class Foo(object):
     def trees2money(self, trees):
         """Calculator for trees2money, you can override when subclassing"""
         return calculators.trees2money(trees)
     @property
     def foo(self):
         """The foo of the object"""
         return calculators.bar

Which gives you a better idea what these objects do when reading the source and even allows you to add documentation. But it might still come in handy when you're building classes dynamically, or you're adding them in a metaclass (creating a wrapper method manually isn't very convenient).

Monday, August 16, 2021
 
Aducci
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share