Python 装饰器作为静态方法

发布于 2024-11-16 04:41:12 字数 1001 浏览 5 评论 0原文

我正在尝试编写一个 python 类,它使用需要实例状态信息的装饰器函数。这是按预期工作的,但是如果我明确地将装饰器设为静态方法,则会收到以下错误:

Traceback (most recent call last):
  File "tford.py", line 1, in <module>
    class TFord(object):
  File "tford.py", line 14, in TFord
    @ensure_black
TypeError: 'staticmethod' object is not callable

为什么?

这是代码:

class TFord(object):
    def __init__(self, color):
        self.color = color

    @staticmethod
    def ensure_black(func):
        def _aux(self, *args, **kwargs):
            if self.color == 'black':
                return func(*args, **kwargs)
            else:
                return None
        return _aux

    @ensure_black
    def get():
        return 'Here is your shiny new T-Ford'

if __name__ == '__main__':
    ford_red = TFord('red')
    ford_black = TFord('black')

    print ford_red.get()
    print ford_black.get()

如果我只是删除行 @staticmethod ,一切正常,但我不明白为什么。它不应该需要 self 作为第一个参数吗?

I'm trying to write a python class which uses a decorator function that needs information of the instance state. This is working as intended, but if I explicitly make the decorator a staticmetod, I get the following error:

Traceback (most recent call last):
  File "tford.py", line 1, in <module>
    class TFord(object):
  File "tford.py", line 14, in TFord
    @ensure_black
TypeError: 'staticmethod' object is not callable

Why?

Here is the code:

class TFord(object):
    def __init__(self, color):
        self.color = color

    @staticmethod
    def ensure_black(func):
        def _aux(self, *args, **kwargs):
            if self.color == 'black':
                return func(*args, **kwargs)
            else:
                return None
        return _aux

    @ensure_black
    def get():
        return 'Here is your shiny new T-Ford'

if __name__ == '__main__':
    ford_red = TFord('red')
    ford_black = TFord('black')

    print ford_red.get()
    print ford_black.get()

And if I just remove the line @staticmethod, everything works, but I do not understand why. Shouldn't it need self as a first argument?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

小鸟爱天空丶 2024-11-23 04:41:12

这不是 staticmethod 应该如何使用的。 staticmethod 对象是返回包装对象的描述符,因此它们仅当作为 classname.staticmethodname 访问时才有效。 示例

class A(object):
    @staticmethod
    def f():
        pass
print A.f
print A.__dict__["f"]

打印

<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

A 范围内,您始终会得到后一个对象,该对象不可调用。

我强烈建议将装饰器移至模块范围——它似乎不属于类内部。如果您想将其保留在类中,请不要将其设为staticmethod,而是简单地将其del放在类主体的末尾——这并不是为了在这种情况下可以从类外部使用。

This is not how staticmethod is supposed to be used. staticmethod objects are descriptors that return the wrapped object, so they only work when accessed as classname.staticmethodname. Example

class A(object):
    @staticmethod
    def f():
        pass
print A.f
print A.__dict__["f"]

prints

<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

Inside the scope of A, you would always get the latter object, which is not callable.

I'd strongly recommend to move the decorator to the module scope -- it does not seem to belong inside the class. If you want to keep it inside the class, don't make it a staticmethod, but rather simply del it at the end of the class body -- it's not meant to be used from outside the class in this case.

白昼 2024-11-23 04:41:12

Python 类是在运行时评估类声明的内容后创建的。通过将所有声明的变量和函数分配给一个特殊的字典并使用该字典调用 type.__new__ 来评估该类(请参阅自定义类创建)。

因此,

class A(B):
    c = 1

相当于:

A = type.__new__("A", (B,), {"c": 1})

当您使用 @staticmethod 注解一个方法时,在使用 type.__new__ 创建类之后会发生一些特殊的魔法。在类声明范围内,@staticmethod 函数只是 staticmethod 对象的实例,您无法调用它。装饰器可能应该在同一模块中的类定义上方或在单独的“装饰”模块中声明(取决于您有多少个装饰器)。一般来说,装饰器应该在类之外声明。一个值得注意的例外是属性类(请参阅 properties)。在您的情况下,如果您有类似颜色类的内容,那么在类声明中使用装饰器可能是有意义的:

class Color(object):

    def ___init__(self, color):
        self.color = color

     def ensure_same_color(f):
         ...

black = Color("black")

class TFord(object):
    def __init__(self, color):
        self.color = color

    @black.ensure_same_color
    def get():
        return 'Here is your shiny new T-Ford'

Python classes are created at runtime, after evaluating the contents of the class declaration. The class is evaluated by assigned all declared variables and functions to a special dictionary and using that dictionary to call type.__new__ (see customizing class creation).

So,

class A(B):
    c = 1

is equivalent to:

A = type.__new__("A", (B,), {"c": 1})

When you annotate a method with @staticmethod, there is some special magic that happens AFTER the class is created with type.__new__. Inside class declaration scope, the @staticmethod function is just an instance of a staticmethod object, which you can't call. The decorator probably should just be declared above the class definition in the same module OR in a separate "decorate" module (depends on how many decorators you have). In general decorators should be declared outside of a class. One notable exception is the property class (see properties). In your case having the decorator inside a class declaration might make sense if you had something like a color class:

class Color(object):

    def ___init__(self, color):
        self.color = color

     def ensure_same_color(f):
         ...

black = Color("black")

class TFord(object):
    def __init__(self, color):
        self.color = color

    @black.ensure_same_color
    def get():
        return 'Here is your shiny new T-Ford'
够运 2024-11-23 04:41:12

解决方案确实存在!

问题是尝试用作装饰器的静态方法实际上是 staticmethod 对象并且不可调用。

解决方案:staticmethod对象具有方法__get__,它接受任何参数并返回真实方法:python 文档 Python 3.5 及更高版本:

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

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

    def __get__(self, obj, objtype=None):
        return self.f

我提供的最小解决方案是:

class A():
    def __init__(self):
        self.n =  2

    @staticmethod
    def _returnBaseAndResult(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*args, **kwargs):
            self = args[0]
            response = func(*args, **kwargs)
            return self.n, response
        return wrapper

    @_returnBaseAndResult.__get__('this can be anything')
    def square(self):
        return self.n**2

if __name__ == '__main__':
    a = A()
    print(a.square())

将打印 (2, 4)

Solution does exist!

Problem is that Static method that is trying to be used as decorator is in fact staticmethod object and is not callable.

Solution: staticmethod object has method __get__ which takes any argument and returns real method: python documentation Python 3.5 and up:

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

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

    def __get__(self, obj, objtype=None):
        return self.f

Min solution I came with is:

class A():
    def __init__(self):
        self.n =  2

    @staticmethod
    def _returnBaseAndResult(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*args, **kwargs):
            self = args[0]
            response = func(*args, **kwargs)
            return self.n, response
        return wrapper

    @_returnBaseAndResult.__get__('this can be anything')
    def square(self):
        return self.n**2

if __name__ == '__main__':
    a = A()
    print(a.square())

Will print (2, 4)

束缚m 2024-11-23 04:41:12

ensure_black 返回一个未由 @staticmethod 修饰的 _aux 方法

您可以将非静态方法返回给 static_method

http://docs.python.org/library/functions.html#staticmethod

ensure_black is returning a _aux method that isn't decorated by @staticmethod

You can return a non-static method to a static_method

http://docs.python.org/library/functions.html#staticmethod

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文