Python: regular method and static method with the same name

Introduction

I have a Python class that contains several methods. I want one of these methods to have a static copy, i.e. a static method with the same name that can handle more arguments. After some searching, I found that I can use the @staticmethod decorator to create a static method.

Problem

For convenience, I created a smaller test case that reproduces the problem:

 class myclass: @staticmethod def foo(): return 'static method' def foo(self): return 'public method' obj = myclass() print(obj.foo()) print(myclass.foo()) 

I expect the above code to print the following:

 public method static method 

However, the code prints the following:

 public method Traceback (most recent call last): File "sandbox.py", line 14, in <module> print(myclass.foo()) TypeError: foo() missing 1 required positional argument: 'self' 

From this, we can only assume that the call to myclass.foo() tries to call its non-static counter without arguments (which will not work, because non-static methods always accept the self argument). This behavior is confusing to me because I expect that any call to the static method will actually call the static method.

I tested the problem in both Python 2.7 and 3.3, only to get the same error.

Questions

Why is this happening and what can I do to fix my code so that it prints:

 public method static method 

how would i expect?

+7
python static class decorator
source share
5 answers

A similar question here: redefine methods with the same name in peony programming

functions are looked up by name, so you just override foo with the instance method. There is no such thing as an overloaded function in Python. You either write a new function with a separate name, or provide arguments in such a way that they can handle the logic for both.

In other words, you cannot have a static version and an instance version with the same name. If you look at vars , you will see one foo .

 In [1]: class Test: ...: @staticmethod ...: def foo(): ...: print 'static' ...: def foo(self): ...: print 'instance' ...: In [2]: t = Test() In [3]: t.foo() instance In [6]: vars(Test) Out[6]: {'__doc__': None, '__module__': '__main__', 'foo': <function __main__.foo>} 
+9
source share

Since attribute searching in Python is something inside a programmer control, it is technically possible. If you attach any value to writing the code in a "pythonic" way (using the preferred conventions and idioms of the python community), this is most likely the wrong way to create a problem / design. But if you know how descriptors can allow you to control the search for attributes and how functions become related functions (hint: functions are descriptors), you can execute code that roughly matches your desire.

For this name, there is only one object that will be viewed in the class, regardless of whether you look at the instance of the class or the class itself. Therefore, what you are looking for should deal with these two cases and send it properly.

(Note: this is not entirely true: if an instance has a name in its attribute namespace that collides with one in the namespace of its class, the value in the instance will benefit in some circumstances. But even in these circumstances, it will not become a โ€œrelated methodโ€ as you probably wish.)

I do not recommend developing your program using a technique such as this, but the following will do roughly what you requested. Understanding how this works requires a relatively deep understanding of python as a language.

 class StaticOrInstanceDescriptor(object): def __get__(self, cls, inst): if cls is None: return self.instance.__get__(self) else: return self.static def __init__(self, static): self.static = static def instance(self, instance): self.instance = instance return self class MyClass(object): @StaticOrInstanceDescriptor def foo(): return 'static method' @foo.instance def foo(self): return 'public method' obj = MyClass() print(obj.foo()) print(MyClass.foo()) 

which prints:

 % python /tmp/sandbox.py static method public method 
+4
source share

Although this is not entirely possible to do, as was correctly pointed out, you could always "fake" it by overriding the method when creating the instance, for example:

 class YourClass(object): def __init__(self): self.foo = self._instance_foo @staticmethod def foo() print "Static!" def _instance_foo(self): print "Instance!" 

which will give the desired result:

 >>> YourClass.foo() Static! >>> your_instance = YourClass() >>> your_instance.foo() Instance! 
+3
source share

It ends here from Google, so I decided to post my solution to this "problem" ...

 class Test(): def test_method(self=None): if self is None: print("static bit") else: print("instance bit") 

In this way, you can use the method as a static method or as an instance method.

+1
source share

I really donโ€™t know with Python when it comes to these types of questions, but I agree with a number of sentiments in the answers so far that it is not native, and you should not try.

So now for something completely different. I was just mocking that I have a similar problem:

 class Foo(object): def __init__(self, name): self.name = name @staticmethod def get_norm_name(name): # Normalize name return name.strip().lower() def norm_name(self): return Foo.get_norm_name(self.name) >>> Foo.get_norm_name('Robert') 'robert' >>> f = Foo('Robert') >>> f.norm_name() 'robert' 

The names of the methods are different, which you tried to avoid, but I think they are close enough. Again, not sure if this is Pythonic, but it seems clear.

0
source share

All Articles