Вызов функции модуля с использованием его имени (строки)

1737

Каков наилучший способ вызова функции по заданной строке с именем функции в программе Python. Например, предположим, что у меня есть модуль foo, и у меня есть строка с содержимым "bar". Как лучше всего позвонить foo.bar()?

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

ricree
источник

Ответы:

2081

Предполагая модуль fooс методом bar:

import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()

Вы можете сократить строки 2 и 3 до:

result = getattr(foo, 'bar')()

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

getattrТаким образом вы можете использовать методы, связанные с экземплярами классов, методы уровня модулей, методы классов ... список можно продолжить.

Патрик Джонмейер
источник
9
hasattr или getattr могут использоваться для определения, определена ли функция. У меня было отображение базы данных (eventType и functionName), и я хотел убедиться, что я никогда не «забыл» определить обработчик событий в моем питоне
Shaun
9
Это работает, если вы уже знаете имя модуля. Однако, если вы хотите, чтобы пользователь предоставлял имя модуля в виде строки, это не сработает.
Blairg23
7
Если вам нужно избежать исключения NoneType, которое не вызывается, вы можете также использовать форму с тремя аргументами getattr: getattr (foo, 'bar', lambda: None). Я прошу прощения за форматирование; Android-приложение stackexchange явно ужасно.
geekofalltrades
3
См. Также ответ, предоставленный @sastanin, если вы заботитесь, например, только о функциях вашего локального / текущего модуля.
NuSkooler
6
@akki Да, если ты в в fooмодуле вы можете использовать , globals()чтобы сделать это:methodToCall = globals()['bar']
Бен Хойт
544
locals()["myfunction"]()

или

globals()["myfunction"]()

locals возвращает словарь с текущей таблицей локальных символов. globals возвращает словарь с глобальной таблицей символов.

sastanin
источник
53
Этот метод с глобальными / локальными данными хорош, если метод, который вам нужно вызвать, определен в том же модуле, из которого вы вызываете.
Джоэлмоб
@Joelmob есть ли другой способ получить объект за строкой из корневого пространства имен?
Ник Т
@NickT Я знаю только об этих методах, я не думаю, что есть другие, которые выполняют ту же функцию, что и эти, по крайней мере, я не могу придумать причину, почему должно быть больше.
Joelmob
У меня есть причина для вас (на самом деле, что привело меня сюда): Модуль A имеет функцию F, которая должна вызывать функцию по имени. Модуль B импортирует модуль A и вызывает функцию F с запросом вызова функции G, которая определена в модуле B. Этот вызов завершается ошибкой, поскольку, очевидно, функция F выполняется только с глобальными значениями, определенными в модуле F, поэтому globals () ['G'] = Нет.
Дэвид Стейн
338

Решение Патрика, наверное, самое чистое. Если вам нужно динамически подобрать модуль, вы можете импортировать его следующим образом:

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
источник
93
Я не понимаю этот последний комментарий. __import__ имеет свое право, и следующее предложение в упомянутых документах гласит: «Прямое использование __import __ () редко, за исключением случаев, когда вы хотите импортировать модуль, имя которого известно только во время выполнения». Итак: +1 за данный ответ.
Хоффмае
50
Использование importlib.import_module. Официальные документы говорят о __import__: «Это продвинутая функция, которая не нужна в повседневном программировании на Python, в отличие от importlib.import_module ()». docs.python.org/2/library/functions.html#__import__
glarrain
8
@glarrain До тех пор, пока вы в порядке с поддержкой только 2,7 и выше.
Xiong Chiamiov
1
@Xiong Chaimiov, importlib.import_moduleподдерживается в версии 3.6. См. Docs.python.org/3.6/library/…
cowlinator
7
@cowlinator Да, 3.6 является частью «2.7 и выше», как в строгой семантике версионирования, так и в датах выпуска (это произошло около шести лет спустя). Это также не существовало в течение трех лет после моего комментария. ;) В ветке 3.x модуль существует с 3.1. 2.7 и 3.1 сейчас довольно древние; вы все еще найдете серверы, которые поддерживают только 2.6, но, вероятно, в наши дни стандартным советом будет importlib.
Сюн Чямов
114

Просто простой вклад. Если класс, который нам нужен для экземпляра, находится в том же файле, мы можем использовать что-то вроде этого:

# Get class from globals and create an instance
m = globals()['our_class']()

# Get the function (from the instance) that we need to call
func = getattr(m, 'function_name')

# Call it
func()

Например:

class A:
    def __init__(self):
        pass

    def sampleFunc(self, arg):
        print('you called sampleFunc({})'.format(arg))

m = globals()['A']()
func = getattr(m, 'sampleFunc')
func('sample arg')

# Sample, all on one line
getattr(globals()['A'](), 'sampleFunc')('sample arg')

И, если не класс:

def sampleFunc(arg):
    print('you called sampleFunc({})'.format(arg))

globals()['sampleFunc']('sample arg')
Sourcegeek
источник
101

Получив строку с полным путем к Python пути к функции, я получил результат от указанной функции:

import importlib
function_string = 'mypackage.mymodule.myfunc'
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
result = func()
ferrouswheel
источник
1
Это помогло мне. Это облегченная версия __import__функции.
Панкадж Бхамбхани
Я думаю, что это был лучший ответ.
Саид
55

Лучший ответ в соответствии с часто задаваемыми вопросами по программированию на Python :

functions = {'myfoo': foo.bar}

mystring = 'myfoo'
if mystring in functions:
    functions[mystring]()

Основным преимуществом этого метода является то, что строки не должны совпадать с именами функций. Это также основной метод, используемый для эмуляции конструкции case.


источник
43

Ответ (надеюсь) никто никогда не хотел

Eval как поведение

getattr(locals().get("foo") or globals().get("foo"), "bar")()

Почему бы не добавить авто-импорт

getattr(
    locals().get("foo") or 
    globals().get("foo") or
    __import__("foo"), 
"bar")()

Если у нас есть дополнительные словари, которые мы хотим проверить

getattr(next((x for x in (f("foo") for f in 
                          [locals().get, globals().get, 
                           self.__dict__.get, __import__]) 
              if x)),
"bar")()

Нам нужно идти глубже

getattr(next((x for x in (f("foo") for f in 
              ([locals().get, globals().get, self.__dict__.get] +
               [d.get for d in (list(dd.values()) for dd in 
                                [locals(),globals(),self.__dict__]
                                if isinstance(dd,dict))
                if isinstance(d,dict)] + 
               [__import__])) 
        if x)),
"bar")()
00500005
источник
это можно улучшить, рекурсивно сканируя дерево каталогов и автоматически монтируя USB-диски
wilks
27

Для чего стоит, если вам нужно было передать имя функции (или класса) и имя приложения в виде строки, то вы можете сделать это:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
trubliphone
источник
Просто немного более общийhandler = getattr(sys.modules[__name__], myFnName)
одинокий
21

Попробуй это. Хотя он по-прежнему использует eval, он использует его только для вызова функции из текущего контекста . Тогда у вас есть реальная функция для использования по вашему желанию.

Основное преимущество для меня в том, что вы получите любые ошибки, связанные с eval, в момент вызова функции. Тогда вы получите только ошибки, связанные с функцией при вызове.

def say_hello(name):
    print 'Hello {}!'.format(name)

# get the function by name
method_name = 'say_hello'
method = eval(method_name)

# call it like a regular function later
args = ['friend']
kwargs = {}
method(*args, **kwargs)
tvt173
источник
1
Это было бы рискованно. Строка может иметь что-нибудь, и eval в конечном итоге оценит это без какого-либо рассмотрения.
Янкит
4
Конечно, вы должны помнить о контексте, в котором вы его используете, будет ли это уместным или нет, учитывая эти риски.
tvt173
1
Функция не должна отвечать за проверку своих параметров - это работа другой функции. Сказать, что использование eval со строкой рискованно, означает, что использование каждой функции рискованно.
red777
Вы никогда не должны использовать без evalкрайней необходимости. getattr(__module__, method_name)это гораздо лучший выбор в этом контексте.
Moi
14

ничто из того, что было предложено, не помогло мне. Я обнаружил это все же.

<object>.__getattribute__(<string name>)(<params>)

Я использую Python 2.66

Надеюсь это поможет

Natdrip
источник
13
В каком аспекте это лучше, чем getattr ()?
V13
1
Именно то, что я хотел. Работает как шарм! Отлично!! self.__getattribute__('title')self.title
невероятно похож
self.__getattribute__('title')в любом случае не работает (не знаю почему), но func = getattr(self, 'title'); func();работает. Так что, может быть, лучше использовать getattr()вместо этого
ioaniatr
10
Могут ли люди, которые не знают Python, прекратить голосовать за этот мусор? Используйте getattrвместо этого.
Аран-Фей
6

Как этот вопрос Как динамически вызывать методы внутри класса, используя присвоение имени метода переменной [duplicate] помеченной как дубликат, как этот, я публикую соответствующий ответ здесь:

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

class MyClass:
    def __init__(self, i):
        self.i = i

    def get(self):
        func = getattr(MyClass, 'function{}'.format(self.i))
        func(self, 12)   # This one will work
        # self.func(12)    # But this does NOT work.


    def function1(self, p1):
        print('function1: {}'.format(p1))
        # do other stuff

    def function2(self, p1):
        print('function2: {}'.format(p1))
        # do other stuff


if __name__ == "__main__":
    class1 = MyClass(1)
    class1.get()
    class2 = MyClass(2)
    class2.get()

Выход (Python 3.7.x)

функция1: 12

функция2: 12

Сержик
источник
-7

Это простой ответ, это позволит вам очистить экран, например. Ниже приведены два примера с eval и exec, которые после очистки будут печатать 0 вверху (если вы используете Windows, измените clearна cls, пользователи Linux и Mac оставьте как есть) или просто выполните его соответственно.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Номер файла
источник
3
Это не то, что оп просит.
Tuncay Göncüoğlu