[英]Python: Logging all of a class' methods without decorating each one
I want to log every method call in some classes. 我想在某些类中记录每个方法调用。 I could have done
我本可以做到的
class Class1(object):
@log
def method1(self, *args):
...
@log
def method2(self, *args):
...
But I have a lot of methods in every class, and I don't want to decorate every one separately. 但是我在每个班级都有很多方法,我不想单独装饰每一个。 Currently, I tried using a hack with metaclasses (overriding my logged class'
__getattribute__
so that if I try to get a method, it'll return a logging method instead): 目前,我尝试使用带有元类的hack(覆盖我记录的类'
__getattribute__
这样如果我尝试获取方法,它将返回一个日志记录方法):
class LoggedMeta(type):
def __new__(cls, name, bases, attrs):
def __getattribute__(self, name_):
attr = super().__getattribute__(name_)
if isinstance(attr, (types.MethodType, types.FunctionType)) and not name_.startswith("__"):
return makeLogged(attr) #This returns a method that first logs the method call, and then calls the original method.
return attr
attrs["__getattribute__"] = __getattribute__
return type.__new__(cls, name, bases, attrs)
class Class1(object):
__metaclass__ = LoggedMeta
def method1(self, *args):
...
However, I'm on Python 2.X, and the super() syntax doesn't work. 但是,我使用的是Python 2.X,而super()语法不起作用。 At the time I call super, I don't have the
__getattribute__
's class (but I do have its class name), so I can't use the old super syntax super(Class, Inst)
. 在我调用super时,我没有
__getattribute__
的类(但我确实有它的类名),所以我不能使用旧的超级语法super(Class, Inst)
。
I tried earlier to use metaclasses, but override all the methods instead of __getattribute__
, but I want to log static method calls also, and they gave me some trouble. 我之前尝试过使用元类,但是覆盖所有方法而不是
__getattribute__
,但我也想记录静态方法调用,它们给了我一些麻烦。
I searched for this type of question, but found no-one who tried changing a class this way. 我搜索了这类问题,但发现没有人试图以这种方式改变课程。
Any ideas or help would be very appreciated. 任何想法或帮助将非常感激。
EDIT: My solution was this (mostly taken from this thread): 编辑:我的解决方案是这个(主要取自这个线程):
import inspect, types
CLASS = 0
NORMAL = 1
STATIC = 2
class DecoratedMethod(object):
def __init__(self, func, type_):
self.func = func
self.type = type_
def __get__(self, obj, cls=None):
def wrapper(*args, **kwargs):
print "before"
if self.type == CLASS:
#classmethods (unlike normal methods) reach this stage as bound methods, but args still contains the class
#as a first argument, so we omit it.
ret = self.func(*(args[1:]), **kwargs)
else:
ret = self.func(*args, **kwargs)
print "after"
return ret
for attr in "__module__", "__name__", "__doc__":
setattr(wrapper, attr, getattr(self.func, attr))
if self.type == CLASS:
return types.MethodType(wrapper, cls, type)
elif self.type == NORMAL:
return types.MethodType(wrapper, obj, cls)
else:
return wrapper
def decorate_class(cls):
for name, meth in inspect.getmembers(cls):
if inspect.ismethod(meth):
if inspect.isclass(meth.im_self):
# meth is a classmethod
setattr(cls, name, DecoratedMethod(meth, CLASS))
else:
# meth is a regular method
setattr(cls, name, DecoratedMethod(meth, NORMAL))
elif inspect.isfunction(meth):
# meth is a staticmethod
setattr(cls, name, DecoratedMethod(meth, STATIC))
return cls
@decorate_class
class MyClass(object):
def __init__(self):
self.a = 10
print "__init__"
def foo(self):
print self.a
@staticmethod
def baz():
print "baz"
@classmethod
def bar(cls):
print "bar"
later I cleaned it up a bit, but that's the solution's essence. 后来我把它清理了一下,但那是解决方案的本质。 I need this difference between class, static and normal methods because I want to have
我需要类,静态和普通方法之间的这种区别,因为我想要
inst = MyClass()
assert type(inst.baz) == types.FunctionType
assert type(inst.foo) == types.MethodType
assert type(inst.bar) == types.MethodType
Why don't you alter the class object? 你为什么不改变类对象?
You can go through the methods in a class with dir(MyClass)
and replace them with a wrapped version... something like: 您可以使用
dir(MyClass)
查看类中的方法,并将其替换为包装版本...类似于:
def logify(klass):
for member in dir(klass):
if not callable(getattr(klass, method))
continue # skip attributes
setattr(klass, method, log(method))
tinker around with something like this... should work... 修补这样的东西...应该工作......
A class decorator can help here. 类装饰器可以在这里提供帮助。 Decorate the whole class and add you logging functionality to all callable attributes the class have.
装饰整个类并将日志功能添加到该类具有的所有可调用属性。
I suggest taking for_all_methods decorator from this SO post , then your code would be 我建议从这个SO帖子中获取for_all_methods装饰器,然后你的代码就可以了
@for_all_methods(log)
class Class1():
def method1(self): pass
...
If the goal is to make your code easier to debug by simply logging call and response, check out the Autologging module. 如果目标是通过简单记录调用和响应来使代码更容易调试,请查看自动记录模块。 A single annotation is all it takes =)
只需一个注释即可=)
https://pythonhosted.org/Autologging/examples-traced.html https://pythonhosted.org/Autologging/examples-traced.html
pip install Autologging
. 。
# my_module.py
from autologging import traced
@traced
class MyClass:
def __init__(self):
self._value = "ham"
def my_method(self, arg, keyword=None):
return "%s, %s, and %s" % (arg, self._value, keyword)
. 。
>>> import logging, sys
>>> from autologging import TRACE
>>> logging.basicConfig(level=TRACE, stream=sys.stdout,
... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s")
>>> from my_module import MyClass
>>> my_obj = MyClass()
TRACE:my_module.MyClass:__init__:CALL *() **{}
TRACE:my_module.MyClass:__init__:RETURN None
>>> my_obj.my_method("spam", keyword="eggs")
TRACE:my_module.MyClass:my_method:CALL *('spam',) **{'keyword': 'eggs'}
TRACE:my_module.MyClass:my_method:RETURN 'spam, ham, and eggs'
'spam, ham, and eggs'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.