2013-04-03 2 views
0

У меня есть класс (node), к которому я хочу присвоить определенный набор функций (в данном случае a1, a2, b1 и b2) на основе параметр класса (operatingMode).Как добавить функции к классу на основе параметра

Ситуация в том, что у меня есть двигатель, который имеет много разных режимов работы. Каждый режим работы позволяет выполнять определенные функции, но не другие. Назначение функций в различные режимы осуществляется таким образом, чтобы не создавать приятные условия для создания классов для каждого режима работы.

Ниже мой пропуск по решению, но он не работает.

Любые мысли?

def a1(self): 
    return 'a1' 

def a2(self): 
    return 'a2' 

def b1(self): 
    return 'b1' 

def b2(self): 
    return b2 




class node(object): 
    def __init__(self,operatingMode): 
    self.operatingMode=operatingMode 

    if self.operatingMode=='A': 
     self.a1function=a1 
     self.a2function=a2 
     print 'Operating Mode \'A\' functions loaded' 

    if self.operatingMode=='B': 
     self.b1function=b1 
     self.b2function=b2 
     print 'Operating Mode \'B\' functions loaded' 

    def setOperatingMode(self,operatingMode): 
     self.operatingMode=operatingMode 
     self.__init__(self,operatingMode) 

Запуск этого в моем терминале позволяет мне назвать это, но я должен заявить локоть дважды:

In [65]: elbow=node('A') 
Operating Mode 'A' functions loaded 

In [66]: elbow.a1function(elbow) 
Out[66]: 'a1' 

пытается запустить elbow.setOperatingMode('B') дает ошибку.

+1

это не совсем понятно, что вы пытаетесь достичь, но вы пытаетесь 'себя .a1function = a1' и т. д.? – BrenBarn

+0

+1 к @BrenBarn. Или, альтернативно, возможно, 'self.functions = {}', затем 'self.functions ['a1'] = a1функция'? Не зная, как вы надеетесь _call_ эти методы, трудно сказать, как это сделать правильно. – abarnert

+0

На другом узле: вызов '__init__' из другого метода, как правило, очень плохая идея. Если у вас есть код, который вы хотите вызывать как '__init__', так и' setOperatingMode', абстрагируйте его в методе, который они оба вызывают (или имеют '__init__' вызов' setOperatingMode', а не наоборот). – abarnert

ответ

1

В ответ на этот комментарий:

Это для управления двигателем, поэтому я стараюсь ограничить накладные расходов ... рабочих режимы будут изменены гораздо реже, чем отдельная команды будет называться.

экземпляры Python могут менять свой класс. Вы можете использовать это, чтобы изменить режимы работы без необходимости использовать if -clause для проверки режима:

class Base(object): 
    # Put any methods shared by ANode and BNode here. 
    pass 

class ANode(Base): 
    def a1(self): 
     return 'a1' 

    def a2(self): 
     return 'a2' 

class BNode(Base): 
    def b1(self): 
     return 'b1' 

    def b2(self): 
     return 'b2' 


elbow = ANode() 
print(elbow.a1()) 
# a1 

knee = ANode() 
print(knee.a1()) 
# a1 

elbow.__class__ = BNode 
print(knee.a1()) 
# a1 
print(elbow.b2()) 
# b2 

elbow.a1() 
# AttributeError: 'BNode' object has no attribute 'a1' 

С положительной стороны, это самое быстрое предложением я отправил. Обратите внимание, что в приведенном выше коде не указано if.Как только класс изменяется, все доступные методы изменяются вместе с ним «мгновенно», только из-за обычного метода Python, вызывающего семантику.

Если Node определяется как в растворе декоратора,

In [33]: elbow = Node('A') 

In [34]: %timeit elbow.a1() 
1000000 loops, best of 3: 288 ns per loop 

Хотя, если knee определяется с помощью ANode,

In [36]: knee = ANode() 

In [37]: %timeit knee.a1() 
10000000 loops, best of 3: 126 ns per loop 

Таким образом, это решение является более чем в 2 раза, как быстро при вызове методов, чем решение декоратора.

скорость переключения сравнима:

In [38]: %timeit elbow.operatingMode = 'B' 
10000000 loops, best of 3: 71.7 ns per loop 

In [39]: %timeit knee.__class__ = BNode 
10000000 loops, best of 3: 78.7 ns per loop 

Caveat: Одна вещь, которая будет изводить все решения, которые я отправил, что после переключателя имена на доступные методы изменение. Это означает, что при программировании этих классов вы должны отслеживать состояние экземпляра, прежде чем сможете узнать, какие методы доступны. Это неудобно.

Ваша программа будет значительно проще, если ANode и BNode имеют точно тот же интерфейс - все тот же атрибут и методы имен - только с определениями этих методов изменения (когда режим - или класс - изменяется).


Что касается этого комментария:

У меня есть около ста функций и 7 режимов работы. Из этих функций около 10 разделены между всеми режимами работы, 75 - , которые совместно используются несколькими режимами, а 15 - исключительно для определенного режима. Проблема состоит в том, что 75 не выделяются в режимах красиво: Некоторые могут быть в режимах 1,4 и 7, в других 2,4,5 и 7, другие в 1 и 5.

вы можете определить методы вне классов, а затем «зацепить их» к классам режима на основе, как это:

def a1(self): 
    return 'a1' 

def a2(self): 
    return 'a2' 

def b1(self): 
    return 'b1' 

def b2(self): 
    return 'b2' 

class Base(object): 
    # Put the 10 methods share by all modes here 
    def common1(self): 
     pass 

class ANode(Base): 
    a1 = a1 
    a2 = a2 

class BNode(Base): 
    b1 = b1 
    b2 = b2 

class CNode(Base): 
    a1 = a1 
    b2 = b2 
+0

Это замечательно. Я думаю, что это сработает отлично ... Большое спасибо – Chris

0

Весь дизайн очень странный, и вам, вероятно, нужно сделать шаг назад и объяснить, что вы пытаетесь сделать, чтобы кто-то мог помочь вам с лучшим дизайном.

Но если вы просто хотите, чтобы сделать вашу работу проекта, есть две проблемы с текущим кодом:

self.a1function = a1 

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

self.a1function=types.MethodType(a1, self, self.__class__) 

Или вы можете установить a1function быть оберткой a1. Или, проще говоря, сделать обертку динамически, что означает, что вы можете подделать гранично-метод-Несс с крышкой, которая, возможно, более читаемым:

def __getattr__(self, attr): 
    if attr == 'a1function' and self.operating_mode == 'A': 
     return lambda: a1(self) 

Тем:

пытается бежать локоть .setOperatingMode ('B') дает ошибку.

Вам действительно нужно опубликовать трассировку или, по крайней мере, строку ошибки, вместо того, чтобы просто сказать «дает ошибку». В этом случае, он говорит вам, очень явно, что ошибка, так что вам не нужно угадать:

TypeError: __init__() takes exactly 2 arguments (3 given) 

Проблема заключается в том, что в этой строке:

self.__init__(self,operatingMode) 

... вы передаете self дважды. Это объект, на который вы вызываете метод, и это также первый параметр.

Вызов __init__ из другого метода является плохой идеей в любом случае, но если вы действительно хотите, это то же самое, как вызов любой другой метод:

self.__init__(operatingMode) 
2

Использование types.MethodType и __getattr__:

import types 

def a1(self): 
    return 'a1' 

def a2(self): 
    return 'a2' 

def b1(self): 
    return 'b1' 

def b2(self): 
    return 'b2' 

class Node(object): 
    def __init__(self, operatingMode): 
     self.operatingMode = operatingMode 
    def __getattr__(self, attr): 
     if self.operatingMode=='A': 
      if attr == 'a1function': 
       return types.MethodType(a1, self) 
      if attr == 'a2function': 
       return types.MethodType(a2, self) 

     elif self.operatingMode=='B': 
      if attr == 'b1function': 
       return types.MethodType(b1, self) 
      if attr == 'b2function': 
       return types.MethodType(b2, self) 
     else: 
      raise AttributeError() 

Затем

elbow = Node('A') 
print(elbow.a1function()) 
elbow.operatingMode = 'B' 
print(elbow.b2function()) 

дает

a1 
b2 
1

Возможно используя checkMode декоратор будет более чистый способ - это позволяет избежать __getattr__ и type.MethodType магии:

def checkMode(mode): 
    def deco(func): 
     def wrapper(self): 
      if self.operatingMode == mode: 
       return func(self) 
      else: 
       raise TypeError('Wrong operating Mode') 
     return wrapper 
    return deco 

class Node(object): 
    def __init__(self, operatingMode): 
     self.operatingMode = operatingMode 

    @checkMode('A') 
    def a1(self): 
     return 'a1' 

    @checkMode('A') 
    def a2(self): 
     return 'a2' 

    @checkMode('B') 
    def b1(self): 
     return 'b1' 

    @checkMode('B') 
    def b2(self): 
     return 'b2' 

с кодом выше, мы можем сделать это:

elbow = Node('A') 
print(elbow.a1()) 
# a1 

knee = Node('A') 
print(knee.a1()) 
# a1 

elbow.operatingMode = 'B' 
print(knee.a1()) # knee is still in operatingMode A 
# a1 

print(elbow.b2()) 
# b2 

elbow.a1() 
# TypeError: Wrong operating Mode 

Пояснение:

Синтаксис декоратор работает следующим образом:

@deco 
def func(): ... 

эквивалентно

def func(): ... 
func = deco(func) 

Выше checkMode это функция, которая возвращает декоратора, deco. deco затем украшает методы a1, a2 и т.д., так что

a1 = deco(a1) 

Таким образом, a1 это FUNC передается deco. deco(a1), в свою очередь, возвращает новый метод, в общем называемый wrapper. Этот новый метод присваивается a1 оператором a1 = deco(a1). Таким образом, a1 - это теперь метод wrapper. Поэтому, когда вы вызываете elbow.a1(), выполняется код в wrapper.

+0

Кажется, что это работает очень хорошо, и примерно в 2 раза быстрее, чем метод типа. Я должен сделать некоторые копания на декораторах, чтобы понять их немного лучше. – Chris

+0

Возможно, вы захотите проверить решение коммутации '__class__'. Вызов методов с помощью этого решения по моим меркам более чем в 2 раза быстрее, чем это решение для декоратора, так как инструкция 'if' должна выполняться. – unutbu

+0

К сожалению, мой список функций не так разбит, что он вмещает дискретные классы, как показано ... что является импульсом для этого вопроса в первую очередь. – Chris

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