Python: Regular method and static method with same name

Introduction

I have a Python class, which contains a number of methods. I want one of those methods to have a static counterpart—that is, a static method with the same name—which can handle more arguments. After some searching, I have found that I can use the @staticmethod decorator to create a static method.

Problem

For convenience, I have created a reduced test case which reproduces the issue:

class myclass:

    @staticmethod
    def foo():
        return 'static method'

    def foo(self):
        return 'public method'

obj = myclass()
print(obj.foo())
print(myclass.foo())

I expect that the code above will 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, I can only assume that calling myclass.foo() tries to call its non-static counterpart with no arguments (which won’t work because non-static methods always accept the argument self). This behavior baffles me, because I expect any call to the static method to actually call the static method.

I’ve tested the issue in both Python 2.7 and 3.3, only to receive the same error.

Questions

Why does this happen, and what can I do to fix my code so it prints:

public method
static method

as I would expect?

1

While it’s not strictly possible to do, as rightly pointed out, you could always “fake” it by redefining the method on instantiation, like this:

class YourClass(object):

    def __init__(self):
        self.foo = self._instance_foo

    @staticmethod
    def foo():
        print "Static!"

    def _instance_foo(self):
        print "Instance!"

which would produce the desired result:

>>> YourClass.foo()
Static!
>>> your_instance = YourClass()
>>> your_instance.foo()
Instance!

2

A similar question is here: override methods with same name in python programming

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

In other words, you can’t have a static version and an instance version of the same name. If you look at its vars you’ll 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>}

2

Because attribute lookup in Python is something within the programmer’s control, this sort of thing is technically possible. If you put any value into writing code in a “pythonic” way (using the preferred conventions and idioms of the python community), it is very likely the wrong way to frame a problem / design. But if you know how descriptors can allow you to control attribute lookup, and how functions become bound functions (hint: functions are descriptors), you can accomplish code that is roughly what you want.

For a given name, there is only one object that will be looked up on a class, regardless of whether you are looking the name up on an instance of the class, or the class itself. Thus, the thing that you’re looking up has to deal with the two cases, and dispatch appropriately.

(Note: this isn’t exactly true; if an instance has a name in its attribute namespace that collides with one in the namespace of its class, the value on the instance will win in some circumstances. But even in those circumstances, it won’t become a “bound method” in the way that you probably would wish it to.)

I don’t recommend designing your program using a technique such as this, but the following will do roughly what you asked. 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 does print out:

% python /tmp/sandbox.py
static method
public method

2

Ended up here from google so thought I would post my solution to this “problem”…

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

This way you can use the method like a static method or like an instance method.

4

When you try to call MyClass.foo(), Python will complain since you did not pass the one required self argument. @coderpatros’s answer has the right idea, where we provide a default value for self, so its no longer required. However, that won’t work if there are additional arguments besides self. Here’s a function that can handle almost all types of method signatures:

import inspect
from functools import wraps

def class_overload(cls, methods):
    """ Add classmethod overloads to one or more instance methods """
    for name in methods:
        func = getattr(cls, name)
        # required positional arguments
        pos_args = 1 # start at one, as we assume "self" is positional_only
        kwd_args = [] # [name:str, ...]
        sig = iter(inspect.signature(func).parameters.values())
        next(sig)
        for s in sig:
            if s.default is s.empty:
                if s.kind == s.POSITIONAL_ONLY:
                    pos_args += 1
                    continue
                elif s.kind == s.POSITIONAL_OR_KEYWORD:
                    kwd_args.append(s.name)
                    continue
            break
        @wraps(func)
        def overloaded(*args, **kwargs):
            # most common case: missing positional arg or 1st arg is not a cls instance
            isclass = len(args) < pos_args or not isinstance(args[0], cls)
            # handle ambiguous signatures, func(self, arg:cls, *args, **kwargs);
            # check if missing required positional_or_keyword arg
            if not isclass:
                for i in range(len(args)-pos_args,len(kwd_args)):
                    if kwd_args[i] not in kwargs:
                        isclass = True
                        break
            # class method
            if isclass:
                return func(cls, *args, **kwargs)
            # instance method
            return func(*args, **kwargs)
        setattr(cls, name, overloaded)

class Foo:
    def foo(self, *args, **kwargs):
        isclass = self is Foo
        print("foo {} method called".format(["instance","class"][isclass]))

class_overload(Foo, ["foo"])

Foo.foo() # "foo class method called"
Foo().foo() # "foo instance method called"

You can use the isclass bool to implement the different logic for class vs instance method.

The class_overload function is a bit beefy and will need to inspect the signature when the class is declared. But the actual logic in the runtime decorator (overloaded) should be quite fast.

There’s one signature that this solution won’t work for: a method with an optional, first, positional argument of type Foo. It’s impossible to tell if we are calling the static or instance method just by the signature in this case. For example:

def bad_foo(self, other:Foo=None):
    ...
bad_foo(f) # f.bad_foo(None) or Foo.bad_foo(f) ???

Note, this solution may also report an incorrect isclass value if you pass in incorrect arguments to the method (a programmer error, so may not be important to you).

We can get a possibly more robust solution by doing the reverse of this: first start with a classmethod, and then create an instance method overload of it. This is essentially the same idea as @Dologan’s answer, though I think mine is a little less boilerplatey if you need to do this on several methods:

from types import MethodType

def instance_overload(self, methods):
    """ Adds instance overloads for one or more classmethods"""
    for name in methods:
        setattr(self, name, MethodType(getattr(self, name).__func__, self))

class Foo:
    def __init__(self):
        instance_overload(self, ["foo"])

    @classmethod
    def foo(self, *args, **kwargs):
        isclass = self is Foo
        print("foo {} method called:".format(["instance","class"][isclass]))

Foo.foo() # "foo class method called"
Foo().foo() # "foo instance method called"

Not counting the code for class_overload or instance_overload, the code is equally succinct. Often signature introspection is touted as the “pythonic” way to do these kinds of things. But I think I’d recommend using the instance_method solution instead; isclass will be correct for any method signature, including cases where you call with incorrect arguments (a programmer error).

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật