假设我有一个生成路由器函数的函数,该函数根据数字是奇数还是偶数来调用指定的回调:
def odd_even_router(odd, even):
def r(n):
if n % 2:
odd(n)
else:
even(n)
return r
我还有一个类装饰器,它将一个名为check_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_default_router
修饰的课程会自动定义check_number()
,只需实施on_odd()
和on_even()
:
@attach_default_router
class A(object):
def on_odd(self, n):
print 'Odd number'
def on_even(self, n):
print 'Even number'
如果我想在odd_even_router()
内重新使用路由器函数生成器attach_default_router()
,我可以这样做:
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()
时,都会生成一个新的(但相同的)路由器函数。所以这是我的问题:如何在odd_even_router()
装饰器中重新使用attach_default_router()
生成器,但每次都不生成新的路由器功能?
问题的核心是:odd_even_router()
返回一个接受一个参数的函数,但作为实例方法的check_number()
需要两个(第一个是对象的self
)。如果我没有抓住self
,我还无法生成路由器功能。当我抓住self
时,我已经在方法中,并且每次生成它都需要在每次调用方法时生成。
我怎样才能解决这个难题?
答案 0 :(得分:2)
您可以,但是您必须在运行时绑定odd
和even
挂钩,这需要您的工厂实现略有不同。
这是因为您的route
函数不仅每次都生成一个新的*,odd
和even
方法也是如此。 self.odd
每次执行该表达式时都会创建一个新的方法包装器,因为functions are descriptors,并且每次需要时都绑定到实例(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
方法对象。每次访问instance_of_your_decorated_class.route
时,都会通过描述符协议创建方法对象。调用odd.__get__()
和even.__get__()
时也是如此。您也可以坚持使用原始版本,并为每个调用生成一个新的route()
函数,传入self.odd
和self.even
,因为它可能更具可读性,并保留原始版本{{ 1}}工厂函数可用作方法和函数。