Python переключатель по имени класса? - PullRequest
2 голосов
/ 28 февраля 2010

Я сейчас занимаюсь этим, чтобы делать разные вещи в зависимости от типа объекта:

    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]()

Можно ли вырезать цикл for и сделать что-то подобное?

actions[something to do with obj]()

Ответы [ 6 ]

6 голосов
/ 28 февраля 2010
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()

Simpler. Отчетливое. Более расширяемый. Меньше магии. Нет словаря. Без цикла.

1 голос
/ 28 февраля 2010

Я предполагаю, что у вас есть родительский класс для всех этих или, по крайней мере, миксин. Поместите функцию возврата по умолчанию в parent или 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...
1 голос
/ 28 февраля 2010
actions[obj.__class__]()

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

actions.get(obj.__class__, lambda: None)

, чтобы получить значение по умолчанию.

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

0 голосов
/ 28 февраля 2010
results = [func() for cls, func in actions.iteritems() if isinstance(obj, cls)]

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

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

0 голосов
/ 28 февраля 2010
actions[type(obj)]()
0 голосов
/ 28 февраля 2010

А как же

actions[obj.__class__]()
...