简体   繁体   English

在类装饰器中重用全局函数

[英]Re-use a global function within a class decorator

Suppose I have a function that generates a router function which invokes specified callbacks depending on whether a number is odd or even: 假设我有一个生成路由器函数的函数,该函数根据数字是奇数还是偶数来调用指定的回调:

def odd_even_router(odd, even):
    def r(n):
        if n % 2:
            odd(n)
        else:
            even(n)
    return r

I also have a class decorator that attaches a similar router method, named check_number , to a class: 我还有一个类装饰器,它将一个名为check_number的类似路由器方法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

Then, a class decorated with @attach_default_router has check_number() defined automatically, and only has to implement on_odd() and on_even() : 然后,用@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'

If I want to re-use odd_even_router() , the router function generator, inside attach_default_router() , I may do this: 如果我想重新使用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

However, an undesirable effect is that, every time check_number() gets called, a new (but identical) router function is generated. 然而,不期望的效果是,每次调用check_number()时,都会生成新的(但相同的)路由器功能。 So here is my question: How could I re-use odd_even_router() generator inside attach_default_router() decorator, but without generating a new router function every time? 所以这是我的问题: 如何在attach_default_router()装饰器中重新使用odd_even_router()生成器,但attach_default_router()生成新的路由器函数?

The issue at heart is: odd_even_router() returns a function that takes one argument, but check_number() , being an instance method, needs two (the first being the object's self ). 问题的核心是: odd_even_router()返回一个接受一个参数的函数,但check_number()是一个实例方法,需要两个(第一个是对象的self )。 If I don't get hold of self , I cannot generate the router function yet. 如果我没有掌握self ,我还无法生成路由器功能。 When I get hold of self , I am already inside the method, and generating it there entails generating every time the method is called. 当我抓住self ,我已经在方法中,并且在那里生成它需要在每次调用方法时生成。

How could I resolve this dilemma? 我怎么能解决这个难题?

You can, but you'd have to bind your odd and even hooks at runtime, requiring a slightly different implementation of your factory. 你可以,但是你必须在运行时绑定你的oddeven钩子,需要稍微不同的工厂实现。

That's because not only is your route function produced a-new each time, *so are the odd and even methods. 这是因为你的route功能不仅每次产生一个新的,* oddeven方法也是even self.odd creates a new method wrapper every time you execute that expression, because functions are descriptors , and are being bound to the instance ( self here) each time you need one. 每次执行该表达式时, self.odd都会创建一个新的方法包装器,因为函数是描述符 ,并且每次需要时都绑定到实例( self here)。

So, if you want to generate one route() function for use with all instances of your decorated class, you have to then manually make sure the binding still takes place: 因此,如果要生成一个 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

Note that Python still will create a route method object now. 请注意,Python现在仍将创建一个route方法对象 Each time you access instance_of_your_decorated_class.route , a method object is created through the descriptor protocol. 每次访问instance_of_your_decorated_class.route ,都会通过描述符协议创建方法对象。 And the same happens when calling odd.__get__() and even.__get__() . 当调用odd.__get__() even.__get__()发生同样的情况。 You may as well stick to your original version, and generate a new route() function for each call, passing in self.odd and self.even , as that's probably more readable, and keeps your original odd_even_router() factory function usable for use both as methods and for functions. 你也可以坚持你的原始版本,并为每个调用生成一个新的route()函数,传入self.oddself.even ,因为它可能更具可读性,并保持你原来的odd_even_router()工厂函数可以使用作为方法和功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM