2010-02-28 4 views
2

В настоящее время я делаю это, чтобы делать разные вещи в зависимости от типа объекта:Переключатель python по имени класса?

actions = { 
     SomeClass: lambda: obj.name 
     AnotherClass: lambda: self.normalize(obj.identifier) 
     ...[5 more of these]... 
    } 

    for a in actions.keys(): 
     if isinstance(obj, a): 
      return actions[a]() 

Можно ли вырезать для цикла, и сделать что-то вроде этого?

actions[something to do with obj]() 
+7

Не делайте этого. Это ужасный способ реализации полиморфизма. –

ответ

1
actions[obj.__class__]() 

работает, если obj действительно является экземпляром (скажем) SomeClass и не подкласса - так, если это может быть дело, результат будет отличаться от вашего текущего способа его обработки. Также обратите внимание, что это может привести к возникновению KeyError, если класс не имеет соответствующего действия. Для того, чтобы обрабатывать этот случай так же, как вы делаете сейчас (то есть ничего не делать), вы можете использовать

иметь значение по умолчанию возвращается.

О, и послушайте комментарий С.Лотта по вашему вопросу. Во многих случаях есть лучшие способы добиться чего-то подобного. Например, у вас есть все классы, которые определяют do_whatever(self) и просто звоните obj.do_whatever().

0
results = [func() for cls, func in actions.iteritems() if isinstance(obj, cls)] 

Там будет ноль или больше результатов, если ваш объект isinstance нуля или более классов-ключей.

Использование type(obj) как ключ будет работать только в том случае, если ваш объект имеет такой тип. Если он будет дальше по дереву наследования, вы пропустите его.

6
class SomeClass(object): 
.... 
    def action(self): 
     return self.name 

class AnotherClass(object): 
.... 
    def action(self): 
     return self.normalize(self.identifier) 

[5 more classes like the above two] 

a.action() 

Упрощенный. Отчетливое. Более растяжимая. Меньше магии. Нет словаря. Нет петли.

+0

+1. Любой намек на случай, что 'action' зависит от * двух * объектов ('классов)? Это проблема, которая у меня есть в текущем проекте, и я еще не мог придумать лучшего способа, чем тесты isinstance. – balpha

+0

@balpha: Они называют это «двойной отправкой». Это одно и то же базовое упражнение, кроме иерархии классов. A выбирает метод из другого класса. Во-первых, исследование «Double Dispatch». Затем задайте отдельный вопрос, если у вас все еще есть проблемы. –

+0

Я думаю, что реальный вопрос заключается в том, как вы можете организовать код Python, чтобы все «действия» были близки друг к другу, вместо того, чтобы быть посажены по одному в каждом классе.В Scala конструкция match {} позволяет сделать именно это и даже выдает ошибки типа компиляции, если у вас есть непревзойденный тип, поэтому он выглядит как приемлемый способ выкладки полиморфного кода. – codewarrior

1

Я предполагаю, что у вас есть родительский класс для всех этих или, по крайней мере, для микширования. Поместите функцию возврата по умолчанию в родительский или mixin, а затем переопределите ее в тех, которые отличаются друг от друга ... Это единственный правильный способ сделать это.

Уверенный, он делает дополнительный код, но по крайней мере он инкапсулирован и масштабируется. Скажите, что вы хотите добавить поддержку еще пяти классов. Вместо того, чтобы изменять этот код, просто добавьте правильный код в новые классы. По внешнему виду это две строки для каждого класса (определение функции и обратная линия). Это не плохо, не так ли?

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

class MyMixin: 
    def my_return(self, *args): 
    return self.name 
    ... possibly other things... 

class SomeClass(MyMixin): 
    ... no alteration to the default ... 

class AnotherClass(MyParent, MyMixin): 
    def my_return(self, *args): 
    return args[0].normalize(self.identifier) 
    ... blabla 


# now, this is in the caller object... 
try: 
    rval = obj.my_return(self) # this is the caller object 'self', not the 'self' in the 'obj' 
    #dosomething with rval 
except Exception: 
    pass #no rval for this object type, skipping it... 
+0

С набивкой ути питона миксин не является строго необходимым. Пока все ваши классы реализуют 'my_return' с правильным набором аргументов, он должен работать нормально. Тем не менее, Tor прав, что полезно иметь общий родительский класс, поскольку он позволяет вам задавать по умолчанию поведение всех ваших классов, если не указано иное. – jcdyer

+0

Это было предположение, которое я сделал, что между конкретными классами существуют какие-то отношения. –

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