Python 在类装饰器中重用全局函数
假设我有一个函数,它生成一个路由器函数,根据一个数字是奇数还是偶数调用指定的回调:Python 在类装饰器中重用全局函数,python,decorator,python-decorators,Python,Decorator,Python Decorators,假设我有一个函数,它生成一个路由器函数,根据一个数字是奇数还是偶数调用指定的回调: def odd_even_router(odd, even): def r(n): if n % 2: odd(n) else: even(n) return r 我还有一个类装饰器,它将一个类似的路由器方法(名为check\u number)附加到一个类: def attach_default_router(cls
def odd_even_router(odd, even):
def r(n):
if n % 2:
odd(n)
else:
even(n)
return r
我还有一个类装饰器,它将一个类似的路由器方法(名为check\u number
)附加到一个类:
def attach_default_router(cls):
def route(self, n):
if n % 2:
self.on_odd(n)
else:
self.on_even(n)
cls.check_number = route
return cls
然后,用@attach\u default\u router
修饰的类自动定义了check\u number()
,只需在奇数()上实现,在偶数()上实现:
如果我想重新使用奇偶路由器()
,路由器函数生成器,在附加默认路由器()
内,我可以这样做:
def attach_default_router(cls):
def route(self, n):
r = odd_even_router(self.on_odd, self.on_even)
r(n)
cls.check_number = route
return cls
然而,一个不希望出现的效果是,每次调用check_number()
时,都会生成一个新的(但相同的)路由器函数。因此,我的问题是:如何在附加默认路由器()
装饰器中重复使用奇偶路由器()
生成器,但不每次生成新的路由器函数?
核心问题是:奇偶路由器()
返回一个带一个参数的函数,但是检查编号()
,作为一个实例方法,需要两个参数(第一个是对象的自身
)。如果我没有获得self
,我还不能生成路由器函数。当我掌握了self
时,我已经在该方法中了,在那里生成它需要在每次调用该方法时生成它
我如何解决这个难题呢?您可以,但是您必须在运行时绑定奇数
和偶数
钩子,这需要对工厂进行稍微不同的实现
这是因为不仅您的route
函数每次都会产生一个新函数,*而且odd
和偶数
方法也是如此self.odd
在每次执行该表达式时都会创建一个新的方法包装器,因为每次需要时都会绑定到实例(self
)
因此,如果要生成一个route()
函数用于修饰类的所有实例,则必须手动确保绑定仍然发生:
def odd_even_router_method_factory(odd, even):
def route(self, n):
if n % 2:
odd.__get__(self)(n)
else:
even.__get__(self)(n)
return route
def attach_default_router(cls):
route = odd_even_router_method_factory(cls.on_odd, cls.on_even)
cls.check_number = route
return cls
请注意,Python现在仍将创建一个route
method对象。每次访问类.route的实例时,都会通过描述符协议创建一个方法对象。调用奇数和偶数时也会发生同样的情况。您也可以使用原始版本,为每个调用生成一个新的route()
函数,传入self.odd
和self.偶
,因为这可能更具可读性,并保留原始的奇偶路由器()
工厂函数既可用作方法也可用作函数。您考虑过使用mixin而不是类装饰器吗?感谢您向我介绍“mixin”这个术语。查找之后,我会说这只是多重继承。嗯,我确实考虑过多重继承,但我有一个(有点不合理)的逆境。在我的例子中,要修饰的类都是其他类的扩展,所以我的本能是避免继承多个类。但是的,这是一种选择。我会记住这一点。只是提醒一下:您忘记了工厂功能中的返回路线。非常感谢。我想,和大多数工程问题一样,并没有“完美”的解决方案,只有折衷。哦,返回语句补充道。的确虽然使用Python进行工程对我来说更有趣:-)
def odd_even_router_method_factory(odd, even):
def route(self, n):
if n % 2:
odd.__get__(self)(n)
else:
even.__get__(self)(n)
return route
def attach_default_router(cls):
route = odd_even_router_method_factory(cls.on_odd, cls.on_even)
cls.check_number = route
return cls