2016-02-11 3 views
3

Предположим, что у меня есть функция, которая генерирует функцию маршрутизатора, который вызывающую предопределенные функции обратного вызова в зависимости от того, является ли число четным или нечетным:Повторное использование глобальной функции в пределах класса декоратора

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

Вы считали использование миксинов вместо декораторов класса? –

+0

Спасибо, что ознакомили меня с термином «mixin». Посмотрев на него, я бы сказал, что это просто множественное наследование. Ну, я рассматривал множественное наследование, но у меня есть (несколько иррациональная) невзгод. В моем случае классы, которые будут украшены, являются продолжениями от некоторых других классов, поэтому мой инстинкт состоит в том, чтобы избежать наследования более чем одного. Но да, это вариант. Я запомню это. –

ответ

2

Вы можете, но вам придется привязать крюки odd и even во время выполнения, требуя немного другой реализации вашей фабрики.

Это потому, что не только ваша route функция произвела a-new каждый раз, так и методы 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, так как это, вероятно, более читаемо и сохраняет заводские функции оригинала odd_even_router() для использования как в качестве методов, так и для функций.

+0

Напоминание: вы забыли «обратный маршрут» в заводской функции. Спасибо. Я думаю, как и большинство технических проблем, нет «идеальных» решений, только компромиссы. –

+0

К сожалению, добавлен оператор возврата. В самом деле; хотя инжиниринг с Python чувствует себя гораздо более интересным для меня :-) –

Смежные вопросы