Python: 装饰的静态方法接收到不可调用的方法

7 投票
3 回答
2316 浏览
提问于 2025-04-16 13:55

我在用Python装饰一个静态方法时遇到了一点问题。我觉得下面的代码最能代表我的问题:

def decorator(func):
    print callable(func)
    return func

class Foo():
    @decorator
    @staticmethod
    def bar():
        return

# outputs False

print callable(Foo.bar) 
# outputs True

这看起来像是个bug。我想这可能是因为当方法Foo.bar被传给装饰器时,它实际上是一个函数,而不是一个方法。这是我能想到的唯一原因,导致它无法被调用,因为如果我们装饰一个普通函数,它也是无法被调用的,下面的例子就说明了这一点。

@staticmethod
def function():
    return

print callable(function) 
# outputs False

那么,这真的是静态方法装饰器实现上的一个bug吗?或者有没有什么简单的解决办法?我曾考虑过写一个装饰器来赋予一个__call__属性,但我不知道callable是怎么实现的,所以我无法判断这种方法的成功率。

3 个回答

1

我自己写了一个 staticmethod 的实现,这个实现是可以被调用的,似乎很好地解决了这个问题。

class staticmethod(object):
    """Make @staticmethods play nice with @memoize."""

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        """Call the static method with no instance."""
        return self.func(*args, **kwargs)

这个实现没有使用描述符协议,因此它的内部行为和内置的 staticmethod 有很大不同。不过在实际使用中,它可以让函数像类属性、实例属性,甚至像函数属性一样被调用(也就是说,如果你把类放在一个装饰函数里,这个静态方法的实现仍然允许你把静态方法当作装饰函数的属性来调用)。

4

好吧,不管你认为这算不算一个错误,它是有说明的:

静态方法对象提供了一种方式,可以避免将函数对象转换为方法对象。静态方法对象其实是一个包装器,包裹着其他对象,通常是用户自定义的方法对象。当你从一个类或者类的实例中获取静态方法对象时,实际上返回的是被包裹的对象,这个对象不会再被进一步转换。静态方法对象本身是不能被调用的,虽然它们包裹的对象通常是可以被调用的。静态方法对象是通过内置的 staticmethod() 构造函数创建的。

6

方法就是函数。但是,staticmethod 对象就不是函数了。它们是一些叫做描述符的东西,所以当你通过 Cls.static_method 来访问它时,会有一些额外的“魔法”,让它变得可以调用。但是这种“魔法”在你使用 static_method(比如传给装饰器)时,就没办法隐藏什么了。你不能轻易地绕过这个限制,至少不能做到很干净。一个更简单的解决办法是调整装饰器的顺序,让 staticmethod 最后应用——也就是说,把它放在最上面,放在所有其他装饰器的上面。

撰写回答