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:
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
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:
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. 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?
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
). If I don't get hold of self
, I cannot generate the router function yet. When I get hold of self
, I am already inside the method, and generating it there entails generating every time the method is called.
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.
That's because not only is your route
function produced a-new each time, *so are the odd
and even
methods. 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.
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:
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. Each time you access instance_of_your_decorated_class.route
, a method object is created through the descriptor protocol. And the same happens when calling odd.__get__()
and 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.