【问题标题】:Python: check if method is staticPython:检查方法是否是静态的
【发布时间】:2012-02-02 08:46:49
【问题描述】:

假设以下类定义:

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


    【解决方案1】:

    让我们做一些实验:

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

    【讨论】:

    • 谢谢你指点我的“类型”模块,我差点忘了。
    【解决方案2】:

    你的方法对我来说似乎有点缺陷,但你可以检查类属性:

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

    【讨论】:

    • 请注意,这仅在对象被实例化时才有效(至少在 python 3 中),因此如果您想在不实例化对象的情况下检查方法是否是静态的,那么这将不起作用。跨度>
    • @DuckPuncher 谢谢!你是对的,我已经更新了答案。
    • @DuckPuncher 在 Python 3 中你可以检查 isinstance(A.__dict__['f'], types.FunctionType)isinstance(A.__dict__['g'], staticmethod)
    【解决方案3】:

    我碰巧有一个模块来解决这个问题。它是 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
    

    这是仅静态方法的解决方案。但是我推荐使用模块 posted here.

    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
    

    【讨论】:

    • 这有一个错误。它检查类的 MRO 中具有该名称的 any 方法是否是静态的。如果基类中的静态方法被子类中的非静态方法遮蔽,您的函数将输出True
    • 我修复了这个包中的错误github.com/MacHu-GWU/inspect_mate-project
    【解决方案4】:

    为了补充这里的答案,在 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 装饰器定义的属性)

    请注意,即使它是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 避免调用描述符协议!我今天学到了一些东西:) ++
    【解决方案5】:

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

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

    【讨论】:

    • 哇,确实,你是对的!我认为它不起作用,因为我在列表中有方法,比如l = [a.f, a.g],并用l[0]()l[1]() 调用它们。但它有效!奇怪,我一直认为普通方法需要引用它们的包含对象作为第一个参数。我的意思是,如果我使用A.f(),这是一个错误,而A.g() 可以正常工作,而A.f(a) 也可以正常工作。
    • 这显然是问题的好答案(不要检查方法是否静态!),但它没有回答标题中的问题(检查方法是否是静态的)。
    • 没错,吉里。但我认为有多种解决问题的方法很好,这就是我发布这个答案的原因。看来 OP 发现它很有用,所以赢/赢 :)
    • @DanielJung 之所以有效,是因为当您执行 a.f 时,python 会创建绑定方法实例(它绑定到被调用的对象)。当您执行 A.f 时,它返回未绑定的方法(您仍然可以使用 A.f(a) 调用它),而 A.g 返回一个简单的函数。
    • 何必呢?因为您正在实现元类、装饰器或编写代码分析工具。
    猜你喜欢
    • 2012-12-20
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多