【问题标题】:"Unwraping" and wrapping again @staticmethod in meta class在元类中“解包”并再次包装@staticmethod
【发布时间】:2019-05-10 16:04:12
【问题描述】:

我想创建一个元类,用跟踪装饰器装饰每个函数。

所以我得到了这个:

from functools import wraps
from inspect import getfile

from arrow import now


def trace(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print(
            '{timestamp} - {file} - {function} - CALL *{args} **    {kwargs}'.format(timestamp=now().isoformat(sep=' '),
                                                                                                 file=getfile(f),
                                                                                 function=f.__name__, args=args[1:],
                                                                                 kwargs=kwargs))
        result = f(*args, **kwargs)
        print(
            '{timestamp} - {file} - {function} - RESULT     {result}'.format(timestamp=now().isoformat(sep=' '),
                                                                         file=getfile(f),
                                                                         function=f.__name__,
                                                                         result=result))
        return result

    return wrapper


class TraceLogger(type):
    def __new__(mcs, name, bases, dct):
        for attr in dct:
            value = dct[attr]
            if callable(value):
                dct[attr] = trace(value)
        return super(TraceLogger, mcs).__new__(mcs, name, bases, dct)


class ExampleClass(object):
    __metaclass__ = TraceLogger

    def foo(self):
        print('foo')

    @staticmethod
    def bar():
        print('bar')

example = ExampleClass()
example.foo()
example.bar()

跟踪适用于任何非静态函数,因为静态方法不可调用。 我怎样才能解开 staticmethod 然后在 new metclass 这样包装两次:

dct[attr] = staticmethod(trace(value))

【问题讨论】:

  • staticmethods are not callable 是什么意思?它们是,否则你将无法调用它们
  • 嗨@DeepSpace staticmethod 对象不满足这个条件如果 callable(value): dct[attr] = trace(value)
  • 我明白了。您的问题归结为“为什么callable(ExampleClass.bar) 返回Truecallable(ExampleClass.__dict__['bar']) 返回False”,这是一个非常有趣的问题。也许您想稍微简化一下您的问题,因为这与元类无关。
  • staticmethod 对象是描述符,当访问它们时,Python 返回一个可调用函数。它们本身确实是不可调用的。

标签: python python-2.7 python-2.x


【解决方案1】:

(我在这个答案中链接到三个不同的问题/答案,因为我想提供尽可能多的细节,而不是仅仅关闭重复。如果你赞成这个答案,请考虑也赞成链接的答案)

您偶然发现了 Python 的一个有趣的“功能”,this 问题的答案中对此进行了解释。

您可以检查if callable(value) 而不是if isinstance(value, (function, staticmethod, classmethod)),但这只会导致另一个有趣的极端情况:NameError: name 'function' is not defined(看看为什么here)(即使这样做import builtins ; ... ; builtins.function 也会导致错误)。

您需要检查属性名称是方法、静态方法还是类方法,并且(可以说,请参阅here 为什么)在您的情况下正确的方法是使用 @ 987654328@:

import types

...

if type(value) == types.FunctionType: # or isinstance(value, types.FunctionType)
    dct[attr] = trace(value)

...

【讨论】:

    【解决方案2】:

    您可以通过调用 __get__ 来解开 staticmethod 对象。

    @staticmethod
    def func(*args):
        print('func called:', args)
        return 42
    
    print(func)
    print(func.__get__(None, object))
    print(func.__get__(None, object)(1, 2, 3))
    

    它输出:

    <staticmethod object at 0x7f8d42835ac0>
    <function func at 0x7f8d429561f0>
    func called: (1, 2, 3)
    42
    

    至于为什么会这样,你可能有兴趣了解描述符协议是什么,我推荐this link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多