python decorator更改“self”参数:工作但看起来很糟糕

时间:2014-08-11 01:40:36

标签: python python-3.x decorator

我的目标是创建一个带有Log类的简单日志模块,我可以在其他项目中使用它来跟踪事物。特别是,我想创建一个" @ tracemethod"装饰器记录所有方法调用(方法名称和参数)。这是一个非常简化(但功能性)的类:

class Log :

    def __init__(self, path) :
        self.path = path
        # here is a whole path-checking routine.

    def log(self, msg):
        with open(self.path, mode="a+") as log_file :
            log_file.write(msg + "\n")

    def tracemethod(self, f):
        def wrapped(self2, *args, **kwargs) :
            outstring = "{}({},{})".format(f.__name__, str(args), str(kwargs))
            self.log(outstring)
            return f(self2, *args, **kwargs)
        return wrapped

它有效,我可以在另一个模块中调用该类并使用它:

from log import Log

log1 = Log("/Path/to/existing_log_file.txt")

class Foo() :

    @log1.tracemethod
    def __init__(self, a, b, c) :
        self.a = a
        self.b = b
        self.c = c

    @log1.tracemethod
    def a_method(self, c, d, e):
        return c+d+e

foo = Foo(4,5, c=5 )
foo.a_method(32, 64, e=9)

在existing_log_file.txt中给出了:

__init__((4, 5),{'c': 5})
a_method((32, 64),{'e': 9})

但正如你所看到的," tracemethod" Log类中的方法使用" self2"用于区分Log实例和Foo实例的参数。我听说过重命名" self"是一个很大的禁忌。什么是实现这一目标的正确/ pythonic方式?

2 个答案:

答案 0 :(得分:2)

当你真正需要访问被包裹的self时,这是一个两难的问题 - 你有一个指导方针告诉你不要重命名self个参数,而是更强的指导方针你不要用另一个self遮蔽。{/ p>

PEP 8中没有任何内容可以告诉你该怎么做;文档中甚至都没有示例。


但这是有原因的。实际上,您需要访问已包装的self非常罕见。看看你的代码:你不能对self2做任何事情:

def tracemethod(self, f):
    def wrapped(self2, *args, **kwargs) :
        outstring = "{}({},{})".format(f.__name__, str(args), str(kwargs))
        self.log(outstring)
        return f(self2, *args, **kwargs)
    return wrapped

你显然想在输出中跳过它,但你可以通过切片[1:]来做到这一点。因此,就像文档中的所有示例一样,将其保留在*args中,而不是将其作为单独的参数拉出来:

def tracemethod(self, f):
    def wrapped(*args, **kwargs) :
        outstring = "{}({},{})".format(f.__name__, str(args[1:]), str(kwargs))
        self.log(outstring)
        return f(*args, **kwargs)
    return wrapped

所以,在你的情况下,避免了问题。


什么时候你可以逃脱这个?

在这种情况下,似乎并不是一个被广泛接受的标准。真的,任何可以清楚地了解哪个是哪个,并且容易在视觉上区分两者的东西都可以; self2并没有完全消除它,但是AMADANON的wrappedself确实如此。

我认为我见过的两个最常见的名字是inner_selfwrapped_self(有或没有下划线),但没有进行更科学的调查,我不会过分依赖。

我也看过一个教程(虽然我不记得在哪里),建议将所有内容包装在另一个函数调用中以避免问题出现,但这对我来说似乎很愚蠢。

答案 1 :(得分:1)

我认为最狡猾的方式是跳过内心" self"总之,只需使用* args和** args(args [0]将是" self",如果你需要它):

def tracemethod(self, f):
    def wrapped(*args, **kwargs) :
        outstring = "{}({},{})".format(f.__name__, str(args), str(kwargs))
        self.log(outstring)
        return f(*args, **kwargs)
    return wrapped

毕竟,self是其中一个参数。这样你的装饰器也可以用于常规(非对象)函数。

或者,更换" self"用"包裹自己" (或类似的东西),那么你可以使用" self"而不是" self2":

def tracemethod(wrappedself, f):
    def wrapped(self, *args, **kwargs) :
        outstring = "{}({},{})".format(f.__name__, str(args), str(kwargs))
        wrappedself.log(outstring)
        return f(self, *args, **kwargs)
    return wrapped