检查方法是否是静态的

问题描述 投票:0回答:6

以下类定义:

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
作为参数。

python static-methods
6个回答
24
投票

让我们尝试一下:

>>> 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
来区分静态方法。


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'>

14
投票

为了补充这里的答案,在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
进行类似的检查(例如使用
@property
装饰器定义的属性)

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

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

Test.test()

该示例不适用于 Test

instances
,因为该方法将绑定到实例并调用为
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__
上递归)以查看该方法是否绑定到类或实例。

对于那些进行更深入内省的人来说,另一个最后的警告是

staticmethod
可以在不同的类中定义。
__qualname__
将显示静态方法是否在其附加的类中定义。


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
投票

何苦呢?你可以像调用 f 一样调用 g:

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

1
投票

Python 中没有办法检查方法是否是静态的,

only with having the method object
。这是我的实现。

def isStaticmethod_withClass(classObj, methodName):
    return isinstance(inspect.getattr_static(classObj, methodName), staticmethod)


def isStaticmethod_onlyByStringDefinition(method):
    if not isinstance(method, types.FunctionType):
        return False

    return inspect.getsource(method).strip().startswith('@staticmethod')


def isStaticmethod(method):
    if isStaticmethod_onlyByStringDefinition(
            method):  # check being static method with it's string definition
        staticmethod_actualClass = getStaticmethod_actualClass(method)  # gets class object
        return isStaticmethod_withClass(staticmethod_actualClass, method.__name__)
    return False


def getStaticmethod_actualClass(method):
    if not isStaticmethod_onlyByStringDefinition(method):
        return None
    className = method.__qualname__.split('.')[0]
    moduleName = method.__module__
    actualClass = getattr(sys.modules[moduleName], className)
    return actualClass


def isClassMethod(method):
    bound_to = getattr(method, '__self__', None)
    if not isinstance(bound_to, type):
        # must be bound to a class
        return False
    name = method.__name__
    for cls in bound_to.__mro__:
        descriptor = vars(cls).get(name)
        if descriptor is not None:
            return isinstance(descriptor, classmethod)
    return False
    ```
    function `isStaticmethod` can get if the object is a static method or not. also you may get the actual class of that method with `getStaticmethod_actualClass`.

the `isStaticmethod_withClass` is from @Azmisov 's code here.
© www.soinside.com 2019 - 2024. All rights reserved.