簡體   English   中英

Python:記錄所有類的方法而不修飾每個方法

[英]Python: Logging all of a class' methods without decorating each one

我想在某些類中記錄每個方法調用。 我本可以做到的

class Class1(object):
    @log
    def method1(self, *args):
        ...
    @log
    def method2(self, *args):
        ...

但是我在每個班級都有很多方法,我不想單獨裝飾每一個。 目前,我嘗試使用帶有元類的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):
        ...

但是,我使用的是Python 2.X,而super()語法不起作用。 在我調用super時,我沒有__getattribute__的類(但我確實有它的類名),所以我不能使用舊的超級語法super(Class, Inst)

我之前嘗試過使用元類,但是覆蓋所有方法而不是__getattribute__ ,但我也想記錄靜態方法調用,它們給了我一些麻煩。

我搜索了這類問題,但發現沒有人試圖以這種方式改變課程。

任何想法或幫助將非常感激。

編輯:我的解決方案是這個(主要取自這個線程):

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"

后來我把它清理了一下,但那是解決方案的本質。 我需要類,靜態和普通方法之間的這種區別,因為我想要

inst = MyClass()
assert type(inst.baz) == types.FunctionType
assert type(inst.foo) == types.MethodType
assert type(inst.bar) == types.MethodType

你為什么不改變類對象?

您可以使用dir(MyClass)查看類中的方法,並將其替換為包裝版本...類似於:

def logify(klass):
    for member in dir(klass):
        if not callable(getattr(klass, method))
            continue # skip attributes
        setattr(klass, method, log(method))

修補這樣的東西...應該工作......

類裝飾器可以在這里提供幫助。 裝飾整個類並將日志功能添加到該類具有的所有可調用屬性。

我建議從這個SO帖子中獲取for_all_methods裝飾器,然后你的代碼就可以了

@for_all_methods(log)
class Class1():
   def method1(self): pass
   ...

如果目標是通過簡單記錄調用和響應來使代碼更容易調試,請查看自動記錄模塊。 只需一個注釋即可=)

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM