在类“重大缺陷”内实现装饰器?

时间:2018-07-03 13:31:43

标签: python python-2.7 python-decorators

为什么这种装饰器策略被认为是不好的? (..还是!!)

class User(object):

    def __init__(self):
        self.thing = 5

    def __atomic_rate_change(fn):
        def wrapper(self,*args,**kwargs):
            print "start magic"
            self.thing += 1
            fn(self,*args,**kwargs)
            print "end magic"
        return wrapper

    @__atomic_rate_change
    def foo(self,add):
        print self.__atomic_rate_change # <bound method User.__atomic_rate_change of <__main__.User object at 0x6ffffe1ef50>>
        self.thing += add
        print "normal call {0}".format(self.thing)

test = User()
test.foo(1)

这有效。但是,根据下面的资源,这是错误的做法。原因是:

  

[...]这种方法有一个重大缺陷:atomic_rating_change变成了   User类的实例方法。那没有任何意义。更多   比这还不能作为一种方法:如果您调用它,   装饰参数将用作自我。

https://medium.com/@vadimpushtaev/decorator-inside-python-class-1e74d23107f6

我不明白为什么atomic_rate_change是一个实例方法是一个问题/错误/不好。我只打算在类中使用装饰器。也许在这种情况下还可以吗?

1 个答案:

答案 0 :(得分:6)

通常,将不是方法的函数定义放到类定义中是不合适的(恕我直言,它甚至可能是非Python的)。 Flat is better than nested,因此最好在类外部声明函数。这样,当读者查看您的类时,就不会感到困惑为什么为什么没有采用self作为参数的 method (因为该函数被声明为仅仅是装饰器的一种方法,尽管如果函数是@staticmethod,则略有不同。

如果您担心在类之外使用它,请在其前面加上_,然后from my_package import *不会导入它。仍然可以在该模块中使用它,但是除非明确导入,否则它不会在外部使用。

实际上,作者指的是偶然的范围界定行为(类似于Javascript中关于如何根据范围划分使用function() { ...() => { ...的争论。)如果不小心,并且在装饰器的错误部分中意外涉及到self的逻辑,则可能会遇到范围问题。

我能看到的在类内部使用函数的唯一优势可能是因为它更接近于方法(但这引入了不必要的嵌套,潜在的作用域问题以及实现的意识,这是装饰者而不是方法),如果函数的名称以___开头,则更好地隐藏了该函数。

TL; DR 对样式/ Pythonicity的关注以及潜在的范围界定问题。