2015-07-03 2 views
0

Итак, у меня есть класс с несколькими (нормальными) методами. В зависимости от значения я хочу вызвать разные методы. Такое поведение методов выбора является статическим (то же самое для всех экземпляров классов). Как вы порекомендовали бы это сделать?Справочные методы из статического члена

Будет ли ответ изменяться наилучшим образом для достижения этого, если состояние экземпляра является постоянным и никогда не изменяется после инициализации ?

Пример:

PLUS = 0 
MINUS = 1 
OPERATIONS = [PLUS, MINUS] 

class Generator(object): 

    operations = { 
     PLUS: self.plus, # Not possible 
     MINUS: self.minus, 
    } 

    def __init__(self, state): 
     self._state = state 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 

    def generate(self): 
     a, b = give_me_numbers() 
     for op in OPERATIONS: 
      print self.operations[op](a, b) 

ответ

1
PLUS = 0 
MINUS = 1 
OPERATIONS = [PLUS, MINUS] 

class Generator: 
    operations = {} 

    def __init__(self, state): 
     self._state = state 

    @classmethod 
    def init_operations(cls): 
     cls.operations = { 
      PLUS: cls.plus,  
      MINUS: cls.minus 
     } 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 


    def generate(self): 
     a, b = 5, 10 
     for op in self.operations: 
      print(self.operations[op](self, a, b)) 

gen = Generator(1) 
gen.init_operations() 
gen.generate() 

Для того, чтобы operations хранить функции определения класса это не может быть сделано в верхней части класса, как вы сделали. Это связано с тем, что синтаксический анализатор не найдет функции, о которых вы говорите, потому что они еще не разобрали их. Поэтому вместо этого я добавил «статические» init_operations().

Обратите внимание, что эти операции хранятся как несвязанные методы (поскольку он вызван из статического); поэтому при вызове этих функций необходимо включить переменную self в качестве 1-го аргумента.

+0

Кажется хорошим решением. Это эквивалентно ответу EOL? – Moberg

+0

его эквивалентно моему :) – Richard

1

Одним из вариантов является превращение операций в метод:

def operations(self, op): 
     dictMethods = { 
      "PLUS" : self.plus 
      "MINUS" : self.minus 
     } 
     return dictMethods[op] 

затем вызвать его LIK е:

self.operations(op)(a, b) 
1

Что вы хотите сделать на самом деле работает (нет необходимости в более сложном растворе), но вы должны (1) написать словарное определение operations должным образом (с : вместо =) и (2) поместить его определение, когда оно может быть понято (после того, как методы, которые она ссылается определены):

PLUS = 0 
MINUS = 1 
OPERATIONS = [PLUS, MINUS] 

class Generator(object): 

    def __init__(self, state): 
     self._state = state 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 

    def generate(self): 
     a, b = give_me_numbers() 
     for op in OPERATIONS: 
      print operations[op](a, b) 

    operations = { # plus and minus are defined, at this point 
     PLUS: plus, 
     MINUS: minus 
    } 

Боковые ноты:

  • Обратите внимание на Generator(object) синтаксис (не Generator()) -или просто Generator в Python 3.

  • Вы можете проверить модуль enum, который обрабатывает константы PLUS, MINUS и OPERATIONS, что вы определяете в чистом и удобном виде.

PS: Как отмечено PM 2Ring, используя значения из operations может быть сделано через self.operations[op](self, a, b). Я лично сделал бы Generator.operations[op](self, a, b), так как operations не относится ни к какому экземпляру и является словарем, связанным с классом Generator.

+0

Я считаю, что мне нужно вызвать функции и передать ссылку на себя тоже? 'print operations [op] (self, a, b)' я прав? – Moberg

+0

Это дает мне 'NameError: глобальное имя 'операции' не определено' на' print operations [op] (a, b) 'в Python 2.6.6. Но это работает правильно: 'print self.operations [op] (self, a, b)' –

+1

@Moberg: Да, вам нужно явно передать 'self', поскольку методы в' operations [op] 'являются методами класса , Без ссылки на «self» им невозможно узнать, к какому экземпляру класса нужно получить доступ, чтобы оценить «self.state». –

1

Вот небольшая модификация кода Ричарда, которая автоматически вызывает метод init_operations при создании экземпляра Generator.

class Generator: 
    def __init__(self, state): 
     self._state = state 
     if not hasattr(self, 'operations'): 
      self.init_operations() 

    @classmethod 
    def init_operations(cls): 
     cls.operations = { 
      PLUS: cls.plus, 
      MINUS: cls.minus, 
     } 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 

    def generate(self): 
     a, b = give_me_numbers() 
     for op in self.operations: 
      print self.operations[op](self, a, b) 

Вот альтернатива, которая делает operations простой атрибут экземпляра. Это отнимает немного места, но это означает, что вам не нужно явно пропускать self при вызове операции.

class Generator(object): 
    def __init__(self, state=False): 
     self._state = state 
     self.operations = { 
      PLUS: self.plus, 
      MINUS: self.minus 
     } 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 

    def generate(self): 
     a, b = give_me_numbers() 
     for op in OPERATIONS: 
      print self.operations[op](a, b) 

И, наконец, эта вариация использует имена методов для идентификации операций вместо целых чисел.

#!/usr/bin/env python 

from random import seed, randint 

def give_me_numbers(): 
    a, b = randint(1, 99), randint(1, 99) 
    print 'a=%d, b=%d' % (a, b) 
    return a, b 


OPERATIONS = ('plus', 'minus') 

class Generator(object): 
    def __init__(self, state=False): 
     self._state = state 

    def plus(self, a, b): 
     # Depends on state 
     return a + b 

    def minus(self, a, b): 
     return a - b if self._state else b - a 

    def operations(self, op): 
     return getattr(self, op) 

    def generate(self): 
     a, b = give_me_numbers() 
     for op in OPERATIONS: 
      #print getattr(self, op)(a, b) 
      print self.operations(op)(a, b)  

seed(42) 

g1 = Generator(False) 
g1.generate() 

g2 = Generator(True) 
g2.generate() 

выход

a=64, b=3 
67 
-61 
a=28, b=23 
51 
5 

Вы не действительно нужен метод operations здесь - я просто оставил его, чтобы остаться (относительно) в соответствии с кодом ОП. Вместо этого вы можете просто позвонить getattr(self, op)(a, b) напрямую. OTOH, это is очиститель для подачи operations метод, если вы хотите назвать его вне класса.

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