我们可以编写某种日志装饰器来回显函数/方法调用,如下所示:
def log(fn):
...
@log
def foo():
...
class Foo(object):
@log
def foo(self):
...
@log
def bar(self, a, b):
...
@log
def foobar(self, x, y, z):
...
Run Code Online (Sandbox Code Playgroud)
但是如果我们想要记录方法调用而不在每个方法定义的前面放置那么多的@log会怎样?有没有办法将一个装饰器放在类定义之上,以使其所有方法调用都被装饰/记录?还是有其他更好,更有趣的方法来做而不是装饰?
Ned*_*der 16
这可能有点矫枉过正,但是有一个跟踪功能工具可以告诉您程序中的大量活动:
import sys
def trace(frame, event, arg):
if event == "call":
filename = frame.f_code.co_filename
if filename == "path/to/myfile.py":
lineno = frame.f_lineno
# Here I'm printing the file and line number,
# but you can examine the frame, locals, etc too.
print "%s @ %s" % (filename, lineno)
return trace
sys.settrace(trace)
call_my_function()
sys.settrace(None)
Run Code Online (Sandbox Code Playgroud)
Mah*_*der 11
我不确定你的用例是什么,但一般来说,我会更多地考虑你要解决的问题究竟是什么.
也就是说,这是一个可能做你想要的但没有装饰者的例子:
#!/usr/bin/env python
import inspect
class Foo(object):
def foo(self):
pass
def bar(self, a, b):
pass
def foobar(self, x, y, z):
pass
def __getattribute__(self, name):
returned = object.__getattribute__(self, name)
if inspect.isfunction(returned) or inspect.ismethod(returned):
print 'called ', returned.__name__
return returned
if __name__ == '__main__':
a = Foo()
a.foo()
a.bar(1, 2)
a.foobar(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)
输出:
called foo
called bar
called foobar
Run Code Online (Sandbox Code Playgroud)
它可以通过许多不同的方式完成。我将展示如何通过元类、类装饰器和继承来实现。
通过改变元类
import functools
class Logger(type):
@staticmethod
def _decorator(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print(fun.__name__, args, kwargs)
return fun(*args, **kwargs)
return wrapper
def __new__(mcs, name, bases, attrs):
for key in attrs.keys():
if callable(attrs[key]):
# if attrs[key] is callable, then we can easily wrap it with decorator
# and substitute in the future attrs
# only for extra clarity (though it is wider type than function)
fun = attrs[key]
attrs[key] = Logger._decorator(fun)
# and then invoke __new__ in type metaclass
return super().__new__(mcs, name, bases, attrs)
class A(metaclass=Logger):
def __init__(self):
self.some_val = "some_val"
def method_first(self, a, b):
print(a, self.some_val)
def another_method(self, c):
print(c)
@staticmethod
def static_method(d):
print(d)
b = A()
# __init__ (<__main__.A object at 0x7f852a52a2b0>,) {}
b.method_first(5, b="Here should be 5")
# method_first (<__main__.A object at 0x7f852a52a2b0>, 5) {'b': 'Here should be 5'}
# 5 some_val
b.method_first(6, b="Here should be 6")
# method_first (<__main__.A object at 0x7f852a52a2b0>, 6) {'b': 'Here should be 6'}
# 6 some_val
b.another_method(7)
# another_method (<__main__.A object at 0x7f852a52a2b0>, 7) {}
# 7
b.static_method(7)
# 7
Run Code Online (Sandbox Code Playgroud)
此外,我将展示两种方法如何在不改变类元信息的情况下(通过类装饰器和类继承)。第一种方法通过类装饰器 put_decorator_on_all_methods
接受装饰器来包装类的所有成员可调用对象。
def logger(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
def put_decorator_on_all_methods(decorator, cls=None):
if cls is None:
return lambda cls: put_decorator_on_all_methods(decorator, cls)
class Decoratable(cls):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
return decorator(value)
return value
return Decoratable
@put_decorator_on_all_methods(logger)
class A:
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
@staticmethod
def static_method(d):
print(d)
b = A()
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(8)
# >>> static_method (8,) {}
# >>> 8
Run Code Online (Sandbox Code Playgroud)
而且,最近,我遇到了同样的问题,但我无法将装饰器放在类上或以任何其他方式更改它,除非我被允许仅通过继承添加此类行为(我不确定这是如果您可以根据需要更改代码库,则是最佳选择)。
这里 classLogger
强制子类的所有可调用成员编写有关其调用的信息,请参阅下面的代码。
class Logger:
def _decorator(self, f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
decorator = object.__getattribute__(self, '_decorator')
return decorator(value)
return value
class A(Logger):
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
@staticmethod
def static_method(d):
print(d)
b = A()
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(7)
# >>> static_method (7,) {}
# >>> 7
Run Code Online (Sandbox Code Playgroud)
或者更抽象地说,您可以基于一些装饰器实例化基类。
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
class Decoratable:
def __init__(self, dec):
self._decorator = dec
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
decorator = object.__getattribute__(self, '_decorator')
return decorator(value)
return value
class A(Decoratable):
def __init__(self, dec):
super().__init__(dec)
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
@staticmethod
def static_method(d):
print(d)
b = A(decorator)
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(7)
# >>> static_method (7,) {}
# >>> 7
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
11555 次 |
最近记录: |