Python:检查方法是否为静态方法

22

假设以下类定义:

class A:
  def f(self):
    return 'this is f'

  @staticmethod
  def g():
    return 'this is g'

a = A() 

所以f是一个普通方法,g是一个静态方法。

现在,我如何检查函数对象a.f和a.g是否为静态方法?Python中是否有"isstatic"函数?

我必须知道这一点,因为我有包含许多不同函数(方法)对象的列表,并且要调用它们,我必须知道它们是否需要"self"作为参数。

5个回答

23

让我们来做一些实验:

>>> import types
>>> class A:
...   def f(self):
...     return 'this is f'
...   @staticmethod
...   def g():
...     return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False

看起来你可以使用types.FunctionType来区分静态方法。


1
非常感谢你指引我去找“types”模块,我差点忘记它了。 - proggy

14

我认为你的方法有点缺陷,但你可以检查类属性:

(在Python 2.7中):

>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>

例如Python 3.x中的实例属性

>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>

4
请注意,这只在对象被实例化后才起作用(至少在Python 3中是如此),所以如果你想检查一个方法是否为静态方法而不需要实例化对象,则这种方法将无法使用。 - RattleyCooper
@DuckPuncher 谢谢!你是正确的,我已经更新了答案。 - Jiri
4
在Python 3中,你可以检查isinstance(A.__dict__['f'], types.FunctionType)isinstance(A.__dict__['g'], staticmethod) - Marcin Raczyński

12
为了补充这里的答案,在Python 3中,最佳方法如下:
import inspect

class Test:
    @staticmethod
    def test(): pass

isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)

我们使用getattr_static而不是getattr,因为getattr将检索绑定的方法或函数,而不是staticmethod类对象。您可以对classmethod类型和使用@property装饰器定义的属性(例如)进行类似的检查。

请注意,即使它是staticmethod,也不要假设它是在类内部定义的。该方法源可能来自另一个类。要获得真正的源代码,您可以查看基础函数的限定名称和模块。例如:

class A:
    @staticmethod:
    def test(): pass

class B: pass
B.test = inspect.getattr_static(A, "test")

print("true source: ", B.test.__qualname__)

技术上说,任何方法都可以被用作“静态”方法,只要它们被在类本身上调用,所以请记住这一点。例如,这将完美地工作:

class Test:
    def test():
        print("works!")

Test.test()

那个例子不能用于Test类的实例,因为该方法将绑定到实例并被称为Test.test(self)

在某些情况下,实例和类方法也可以用作静态方法,只要第一个参数处理得当即可。

class Test:
    def test(self):
        print("works!")

Test.test(None)

也许另一个罕见的情况是一个staticmethod同时绑定到一个类或实例。例如:

class Test:
    @classmethod
    def test(cls): pass

Test.static_test = staticmethod(Test.test)

虽然严格来说它是一个 staticmethod,但它的行为更像一个 classmethod。因此,在您进行内省时,可以考虑检查 __self__(在 __func__ 上递归)以查看该方法是否绑定到类或实例。


非常感谢@Azmisov提供的get_attr_static,避免了调用描述符协议!我今天学到了新东西 :) ++ - smarie

5
我恰好有一个模块来解决这个问题。它是Python2/3兼容的解决方案。而且它允许使用从父类继承的方法进行测试。
此外,该模块还可以测试以下内容:
  1. 常规属性
  2. 属性样式方法
  3. 常规方法
  4. 静态方法
  5. 类方法
例如:
class Base(object):
    attribute = "attribute"

    @property
    def property_method(self):
        return "property_method"

    def regular_method(self):
        return "regular_method"

    @staticmethod
    def static_method():
        return "static_method"

    @classmethod
    def class_method(cls):
        return "class_method"

class MyClass(Base):
    pass

这里是仅使用静态方法的解决方案。但我建议使用此处发布的模块
import inspect

def is_static_method(klass, attr, value=None):
    """Test if a value of a class is static method.

    example::

        class MyClass(object):
            @staticmethod
            def method():
                ...

    :param klass: the class
    :param attr: attribute name
    :param value: attribute value
    """
    if value is None:
        value = getattr(klass, attr)
    assert getattr(klass, attr) == value

    for cls in inspect.getmro(klass):
        if inspect.isroutine(value):
            if attr in cls.__dict__:
                bound_value = cls.__dict__[attr]
                if isinstance(bound_value, staticmethod):
                    return True
    return False

1
这里有一个错误。它检查类的MRO中是否有任何具有该名称的方法是静态的。如果在基类中存在一个被子类中的非静态方法遮蔽的静态方法,则您的函数将输出“True”。 - Aran-Fey
我修复了这个包中的错误 https://github.com/MacHu-GWU/inspect_mate-project - MacSanhe

1

为什么要麻烦呢?你可以像调用 f 一样调用 g:

a = A()
a.f()
a.g()

哇,确实,你是对的!我以为它不起作用,因为我有一个方法列表,比如 l = [a.f, a.g],并使用 l[0]()l[1]() 调用它们。但它起作用了!奇怪的是,我一直认为普通方法需要一个包含对象的引用作为第一个参数。我的意思是,如果我执行 A.f(),那么会出错,而 A.g() 则可以正常工作,而 A.f(a) 也可以正常工作。 - proggy
1
这显然是一个很好的答案(不要检查方法是否为静态!)来解决问题,但它并没有回答标题中的问题(检查方法是否为静态)。 - Jiri
1
没错,Jiri。但我认为拥有多种解决问题的方法是好的,这就是为什么我发布了这个答案。而且似乎原帖作者也觉得很有用,所以双赢 :) - rzetterberg
1
@DanielJung 这个可以工作是因为当你执行 a.f 时,Python 会创建一个绑定方法实例(它绑定到调用它的对象上)。当你执行 A.f 时,它返回未绑定方法(你仍然可以使用 A.f(a) 调用它),而 A.g 返回一个简单函数。 - soulcheck
1
为什么要费心呢?因为你正在实现元类、装饰器或编写代码分析工具。 - Christopher Barber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接