Имена динамических функций Python

Я ищу лучший способ вызова функций на основе переменной в Python по сравнению с использованием операторов if/else, как показано ниже. Каждый код состояния имеет соответствующую функцию

if status == 'CONNECT':
    return connect(*args, **kwargs)
elif status == 'RAWFEED':
    return rawfeed(*args, **kwargs)
elif status == 'RAWCONFIG':
    return rawconfig(*args, **kwargs)
elif status == 'TESTFEED':
    return testfeed(*args, **kwargs)
...

Я предполагаю, что для этого потребуется какая-то фабричная функция, но я не уверен в синтаксисе


person drjeep    schedule 25.03.2009    source источник


Ответы (8)


Канонический способ сделать это — использовать словарь для эмуляции switch или if/elif. Здесь, на SO, вы найдете несколько вопросов к подобным проблемам.

Поместите свои функции в словарь с вашими кодами состояния в качестве ключей:

funcs = {
    'CONNECT': connect,
    'RAWFEED': rawfeed,
    'RAWCONFIG' : rawconfig,
    'TESTFEED': testfeed
}
funcs[status](*args, **kwargs)
person Community    schedule 25.03.2009
comment
funcs['status'] вызовет KeyError - person SilentGhost; 25.03.2009
comment
Я думаю, что это безопасный способ, он вызывает исключение, когда status является неожиданным именем метода. - person Andrew T; 25.03.2009
comment
DRY редко применяется в подобных случаях. Исходный список функций может иметь строковые имена; но это часто расходится в более сложные отношения. - person S.Lott; 25.03.2009
comment
@S.Lott: в заголовке вопроса указано «имена динамических функций». Это статическое отображение. - person vartec; 25.03.2009
comment
@goodrone: и как вы думаете, что он делает, когда вы используете getattr? кроме того, есть простой способ предоставить метод по умолчанию. - person SilentGhost; 25.03.2009
comment
Это не канонический питонический способ диспетчеризации функций. getattr — канонический метод в данном случае - person hasen; 25.03.2009
comment
Я бы использовал этот метод, когда переменная состояния не является внешней, то есть из веб-формы или чего-то еще, или если функции могут быть переименованы во что-то другое, чем версия строки состояния в нижнем регистре - вы также можете утверждать, что это более явный (и, следовательно, более питонический). Однако я был бы более склонен использовать метод getattr, когда список функций велик или изменяется в размере. - person Brendan; 07.12.2010

я думаю, вы можете найти getattr полезным

import module
getattr(module, status.lower())(*args, **kwargs)
person SilentGhost    schedule 25.03.2009
comment
Спасибо за этот пример, я не знал, что getattr() также работает с модулями - person drjeep; 25.03.2009

предполагая, что эти функции принадлежат некоторому модулю:

import module
return getattr(module, status.lower()).__call__(*args, **kwargs)
person vartec    schedule 25.03.2009

кажется, что вы можете использовать getattr немного по-другому (на мой взгляд, более элегантно)

import math
getattr(math, 'sin')(1)

или если функция импортирована, как показано ниже

from math import sin

sin теперь находится в пространстве имен, так что вы можете вызвать его

vars()['sin'](1)
person to-chomik    schedule 25.03.2009

Некоторое улучшение ответа SilentGhost:

globals()[status.lower()](*args, **kwargs)

если вы хотите вызвать функцию, определенную в текущем модуле.

Хотя выглядит некрасиво. Я бы использовал решение со словарем.

person Eugene Morozov    schedule 25.03.2009

Посмотрите на это: getattra как диспетчер функций

person hasen    schedule 25.03.2009
comment
Ссылка мертва, счет уменьшается. Простите за это. - person Drachenfels; 11.05.2012

Я столкнулся с той же проблемой ранее. Посмотрите на этот вопрос, я думаю, это то, что вы ищете.

Словарь или операторы If

Надеюсь, это полезно

Эф

person RailsSon    schedule 25.03.2009

некоторые изменения по сравнению с предыдущим:

funcs = {
'CONNECT': connect,
'RAWFEED': rawfeed,
'RAWCONFIG' : rawconfig,
'TESTFEED': testfeed
}

func = funcs.get('status')
if func:
    func(*args, **kwargs)
person linjunhalida    schedule 25.03.2009
comment
нет, это не так. это та же ошибка, что и у heikogerlach в начале. - person SilentGhost; 25.03.2009
comment
Я заменил «статус» на переменную, так что, полагаю, технически это не так. Меня больше интересует метод, чем семантика - person drjeep; 25.03.2009