“展开”并在元类中再次包装@staticmethod

时间:2018-12-09 16:00:28

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

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

所以我明白了:

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()

跟踪适用于任何非静态函数,因为静态方法不可调用。 我如何解开静态方法,然后在 new metclass中将其包装两次,如下所示:

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

2 个答案:

答案 0 :(得分:1)

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

@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

答案 1 :(得分:0)

(我在此答案中链接了三个不同的问题/答案,因为我想提供尽可能多的详细信息,而不是重复多次。如果您对此答案表示赞同,请考虑对链接的答案也进行投票)

您偶然发现了Python的一个有趣的“功能”,该问题在this问题的答案中得到了解释。

您可以检查if callable(value)而不是if isinstance(value, (function, staticmethod, classmethod)),但这只会导致另一个有趣的极端情况:NameError: name 'function' is not defined(请参阅here的原因)(即使这样做{{ 1}}会导致错误)。

您无法摆脱检查属性名称是方法,静态方法还是类方法的麻烦,而(可能会看到here的正确方法)是使用import builtins ; ... ; builtins.function

types.FunctionType