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