python decorator更改“self”参数:有效但看起来不好

2024-10-04 03:15:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我的目标是创建一个简单的日志模块,其中包含一个日志类,我可以在其他项目中使用它来跟踪事件。特别是,我想创建一个“@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)

在现有的日志中_文件.txt地址:

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

但是如您所见,Log类中的“tracemethod”方法使用“self2”参数来区分Log实例和使用它的Foo实例。我听说改名“self”是一个很大的禁忌。什么是正确的/Python式的方法来实现这一点?你知道吗


Tags: path方法selflogreturnfooinitdef
2条回答

当您实际需要访问包装的self时,这有点进退两难,因为有一条准则告诉您不要重命名任何一个self参数,但有一条更强大的准则告诉您不要用另一个self来隐藏一个self。你知道吗

在pep8中没有什么可以告诉你该做什么;文档中甚至没有一个例子。你知道吗


但这是有原因的。实际上,很少需要访问包装的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并不能很好地解决这个问题,但阿马达农的wrappedself确实做到了。你知道吗

我认为我见过的两个最常见的名字是inner_selfwrapped_self(带下划线或不带下划线),但如果不做更科学的调查,我不会太依赖于这一点。你知道吗

我也看过一个教程(虽然我不记得在哪里),它建议在另一个函数调用中包装所有内容,只是为了避免出现问题,但这在我看来是相当愚蠢的。你知道吗

我认为最具python风格的方法是完全跳过内在的“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是一个参数。这样,您的decorator也可以处理常规(非对象)函数。你知道吗

或者,用“wrappedself”替换“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

相关问题 更多 >