Вычисление имени функции из другого имени функции

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

Например:

myTestList = (
    "myDrawFromTo",
    "myDrawLineDir"
)

myTestDict = {
    "myDrawFromTo": test_myDrawFromTo,
    "myDrawLineDir": test_myDrawLineDir
}

for myTest in myTestList:
    result = myTestDict[myTest]()

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

С другой стороны, если бы я мог сделать то же самое без словаря, это тоже было бы хорошо. Я попытался просто создать новую строку из записей в myTestList, а затем использовать local() для настройки вызова, но мне не повезло. Идея словаря пришла из документации Python 3.x.


person PDX Mark    schedule 12.09.2014    source источник
comment
Подойдет ли вам создание класса ваших функций?   -  person Charles Clayton    schedule 12.09.2014
comment
@crclayton: Это хорошая идея… но на самом деле это не помогает решить проблему.   -  person abarnert    schedule 12.09.2014


Ответы (2)


Проблема состоит из двух частей.

Простая часть — просто добавить префикс 'text_' к каждой строке:

tests = {test: 'test_'+test for test in myTestDict}

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

tests = {test: globals()['test_'+test] for test in myTestList}

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

tester = TestClass()
tests = {test: getattr(tester, 'test_'+test) for test in myTestList}

(Хотя более вероятно, что этот код будет внутри TestClass, поэтому он будет использовать self, а не tester.)


Если вам на самом деле не нужен dict, конечно, вы можете изменить понимание на явный оператор for:

for test in myTestList:
    globals()['test_'+test]()

И еще: прежде чем заново изобретать велосипед, просмотрели ли вы среды тестирования, встроенные в stdlib или доступны в PyPI?

person abarnert    schedule 12.09.2014
comment
@abamert: спасибо, я думаю, что у меня это получится. На самом деле я преподаю курс обслуживания CS для студентов, в основном не изучающих CS (но определенно студентов, не имеющих опыта программирования), и было предложено использовать модуль черепахи. Я хотел бы, чтобы студенты создавали простые, но содержательные функции, и в то же время я хотел бы сделать упор на написание тестов. Это был бы очень простой способ представить очень простые идеи тестирования без необходимости погружаться в кучу других тем. Только около 30% студентов обычно поступают на программу CS. - person PDX Mark; 12.09.2014
comment
@PDXMark: я думаю, что это делает использование среды модульного тестирования еще более привлекательным, потому что тогда им не нужно изучать такие вещи, как globals или getattr (или, что еще хуже, eval). Они просто определяют тесты, и они волшебным образом запускаются. Хороший фреймворк также облегчит написание тестов, которые в противном случае они могли бы не знать, как писать (или просто не захотеть тратить время на написание), таких как проверка того, что функция вызывает исключение, или распечатка фактических результатов. отличается от ожидаемых результатов. - person abarnert; 12.09.2014

Ответ Абарнерта кажется полезным, но чтобы ответить на ваш первоначальный вопрос о том, как вызвать все тестовые функции для списка имен функций:

def test_f():
    print("testing f...")

def test_g():
    print("testing g...")

myTestList = ['f', 'g']

for funcname in myTestList:
    eval('test_' + funcname + '()')
person 5gon12eder    schedule 12.09.2014
comment
5gon12eder: работает отлично. Это, вероятно, будет легче для моей аудитории. - person PDX Mark; 12.09.2014
comment
Если вам ни для чего не нужен dict, имеет смысл просто зациклить функции явно так… но использование eval вместо globals — плохая идея. - person abarnert; 12.09.2014
comment
как уже отмечалось, использование eval представляет собой угрозу безопасности. также см. stackoverflow.com/questions /1933451/ - person Ruperto; 28.12.2020