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

avatar
ricree
6 августа 2008 в 03:36
860410
19
2049

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

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

Источник

Ответы (19)

avatar
Patrick Johnmeyer
6 августа 2008 в 03:57
2453

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

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

Строки 2 и 3 можно сократить до:

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

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

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

Shaun
3 июня 2014 в 13:20
14

hasattr или getattr могут использоваться, чтобы определить, определена ли функция. У меня было отображение базы данных (eventType и обработка functionName), и я хотел убедиться, что никогда не «забывал» определить обработчик событий в моем питоне.

Blairg23
21 июня 2014 в 07:39
12

Это работает, если вы уже знаете имя модуля. Однако, если вы хотите, чтобы пользователь указал имя модуля в виде строки, это не сработает.

geekofalltrades
16 августа 2014 в 18:01
9

Если вам нужно избежать исключения NoneType is not callable, вы также можете использовать форму с тремя аргументами getattr: getattr (foo, 'bar', lambda: None). Прошу прощения за форматирование; приложение для Android stackexchange, по-видимому, ужасно.

NuSkooler
19 июня 2015 в 22:19
4

См. Также ответ @sastanin, если вас интересуют только, например, функции вашего локального / текущего модуля.

azmeuk
26 августа 2015 в 23:20
2

Это не работает с функциями, использующими декоратор. getattr возвращает внешнюю функцию

tuned
29 августа 2015 в 17:54
2

Примечание: круто +1, это заставило меня еще раз понять, что в Python все является объектом. Следовательно, он работает также с переменными, вы можете получить доступ к переменным модуля как к любым другим переменным объекта.

Robert Parcus
5 ноября 2015 в 12:38
1

Если у вас есть глубокие структуры, может быть полезен следующий синтаксис: from functools import reduce reduce (getattr, "a.b.c.d.e.f.g" .split ('.'), DeepStructure)

akki
17 сентября 2016 в 06:07
1

Это очень полезный ответ, покупайте, я не мог его решить, когда модуль foo сам является текущим модулем. Кто-нибудь знает, как это сделать?

Ben Hoyt
20 декабря 2016 в 19:06
11

@akki Да, если вы в модуле foo, вы можете использовать globals() для этого: methodToCall = globals()['bar']

KeatsKelleher
23 октября 2017 в 01:12
1

Вы МОЖЕТЕ это сделать, но ДОЛЖНЫ ли вы? Как вы найдете все экземпляры вызова функции, когда ваша база кода станет большой? Нечего искать, и никакая IDE не найдет эту ссылку. Это делает такой динамический вызов действительно неприятным. В любом случае, не лучше ли говорить откровенно вашим коллегам? Меньше кода не всегда означает более читабельный.

Patrick Johnmeyer
13 ноября 2017 в 16:17
2

@KeatsKelleher, поскольку мы не знаем варианта использования, только вопрос, все, что я могу с уверенностью сказать, это то, что существует много вариантов использования, в которых ваша точка зрения применяется, а некоторые - нет. Обычно и разумно делать такие вещи в механизмах правил, DSL, сценариях в приложении и т. Д., В которых код, который вы пишете, существует для запуска другого кода. Вероятно, существует множество других «хороших» моментов для этого, о которых я сейчас не думаю. Итак, должны это сделать? Ответ, как и в случае с большинством языковых функций в большинстве языков, - «это зависит от обстоятельств».

KeatsKelleher
13 ноября 2017 в 16:21
2

@PatrickJohnmeyer Я никогда полностью не исключаю паттерны / парадигмы ... Вот почему я задал вопрос вместо того, чтобы делать общее заявление. Существует множество негативных последствий чрезмерного использования этого шаблона ... что (по крайней мере, по моему опыту) является обычным явлением.

Momooo
12 декабря 2019 в 19:28
1

В случае, если сам модуль является строкой и нужно преобразовать из str в класс, в этом случае "foo" является строкой, но ее нужно вызвать, тогда проверьте эту ссылку blender.stackexchange.com/questions/ 36781 /… PS: Я знаю, что это не относится к контексту заданного вопроса, я подумал, что кому-то может быть полезен этот комментарий.

conol
24 января 2021 в 21:47
0

мы можем получить это foo также из строки?

Patrick Johnmeyer
6 февраля 2021 в 23:11
0

@conol вы можете использовать что-то вроде m = __import__('foo'), где m теперь имя, используемое для модуля «foo». Затем вы выполните method_to_call = getattr(m, 'bar'). Звонок по номеру __import__ редко является правильным ответом, но возможен. coderhelper.com/a/4605/363

avatar
Franz Kurt
30 декабря 2021 в 01:17
1

Вы имеете в виду получение указателя на внутреннюю функцию из модуля

import foo
method = foo.bar
executed = method(parameter)

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

avatar
JeffUK
30 ноября 2021 в 15:56
1

Во многих случаях ответ - «Не надо!»

Вместо этого сделайте что-нибудь вроде:

safe_functions = {'baz':foo.baz, 'bar':foo.bar}
safe_functions['bar']()
safe_functions['delete_all_the_things']() 
avatar
U12-Forward
20 сентября 2021 в 06:13
2

Никто еще не упомянул operator.attrgetter:

>>> from operator import attrgetter
>>> l = [1, 2, 3]
>>> attrgetter('reverse')(l)()
>>> l
[3, 2, 1]
>>> 
avatar
Aliakbar Ahmadi
7 июля 2021 в 09:30
0

В python3 вы можете использовать метод __getattribute__. См. Следующий пример со строкой имени метода списка:

func_name = 'reverse'

l = [1, 2, 3, 4]
print(l)
>> [1, 2, 3, 4]

l.__getattribute__(func_name)()
print(l)
>> [4, 3, 2, 1]
avatar
Bowen 404
15 июня 2021 в 08:14
0

Раньше я сталкивался с подобной проблемой: преобразовать строку в функцию. , но я не могу использовать eval() или ast.literal_eval(), потому что я не хочу выполнять этот код немедленно.

например У меня есть строка "foo.bar", и я хочу назначить ее x в качестве имени функции вместо строки, что означает, что я могу вызвать функцию по x() ПО ТРЕБОВАНИЮ .

.

вот мой код:

str_to_convert = "foo.bar"
exec(f"x = {str_to_convert}")
x()

что касается вашего вопроса, вам нужно только добавить имя вашего модуля foo и . перед {} следующим образом:

str_to_convert = "bar"
exec(f"x = foo.{str_to_convert}")
x()

ВНИМАНИЕ !!! либо eval(), либо exec() - опасный метод, вы должны подтвердить безопасность. ВНИМАНИЕ !!! либо eval(), либо exec() - опасный метод, вы должны подтвердить безопасность. ВНИМАНИЕ !!! либо eval(), либо exec() - опасный метод, вы должны подтвердить безопасность.

avatar
Lukas
16 июля 2020 в 15:20
8

Хотя getattr () является элегантным (и примерно в 7 раз быстрее) методом, вы можете получить возвращаемое значение из функции (локальный, метод класса, модуль) с eval, столь же элегантный, как x = eval('foo.bar')(). И когда вы реализуете некоторую обработку ошибок, то достаточно безопасно (тот же принцип можно использовать для getattr). Пример с импортом модуля и классом:

# import module, call module function, pass parameters and print retured value with eval():
import random
bar = 'random.randint'
randint = eval(bar)(0,100)
print(randint) # will print random int from <0;100)

# also class method returning (or not) value(s) can be used with eval: 
class Say:
    def say(something='nothing'):
        return something

bar = 'Say.say'
print(eval(bar)('nice to meet you too')) # will print 'nice to meet you' 

Когда модуль или класс не существует (опечатка или что-то еще), возникает ошибка NameError. Когда функция не существует, возникает AttributeError. Это можно использовать для обработки ошибок:

# try/except block can be used to catch both errors
try:
    eval('Say.talk')() # raises AttributeError because function does not exist
    eval('Says.say')() # raises NameError because the class does not exist
    # or the same with getattr:
    getattr(Say, 'talk')() # raises AttributeError
    getattr(Says, 'say')() # raises NameError
except AttributeError:
    # do domething or just...
    print('Function does not exist')
except NameError:
    # do domething or just...
    print('Module does not exist')
avatar
정도유
1 июля 2020 в 08:09
4

getattr вызывает метод по имени из объекта. Но этот объект должен быть родительским для вызывающего класса. Родительский класс можно получить по super(self.__class__, self)

class Base:
    def call_base(func):
        """This does not work"""
        def new_func(self, *args, **kwargs):
            name = func.__name__
            getattr(super(self.__class__, self), name)(*args, **kwargs)
        return new_func

    def f(self, *args):
        print(f"BASE method invoked.")

    def g(self, *args):
        print(f"BASE method invoked.")

class Inherit(Base):
    @Base.call_base
    def f(self, *args):
        """function body will be ignored by the decorator."""
        pass

    @Base.call_base
    def g(self, *args):
        """function body will be ignored by the decorator."""
        pass

Inherit().f() # The goal is to print "BASE method invoked."
avatar
Number File
28 августа 2019 в 16:46
-11

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

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Tuncay Göncüoğlu
14 ноября 2019 в 07:30
5

Это не то, о чем спрашивает op.

Jean-François Fabre♦
17 июля 2020 в 05:00
9

этот фрагмент кода содержит 2 наихудших недостатка безопасности, вложенных друг в друга. Какая-то запись.

avatar
Serjik
26 марта 2019 в 18:15
12

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

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

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

avatar
tvt173
7 декабря 2016 в 18:29
31

Попробуйте это. Хотя он все еще использует 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)
iankit
30 декабря 2016 в 18:13
2

Это было бы рискованно. Строка может иметь что угодно, и eval в конечном итоге вычислит ее безо всяких соображений.

tvt173
6 января 2017 в 18:48
5

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

red777
14 августа 2018 в 11:01
3

Функция не должна отвечать за проверку своих параметров - это задача другой функции. Сказать, что использовать eval со строкой рискованно, значит, что использование каждой функции рискованно.

moi
14 января 2019 в 12:05
2

Вы никогда не должны использовать eval без крайней необходимости. getattr(__module__, method_name) - гораздо лучший выбор в этом контексте.

avatar
user3946687
24 октября 2016 в 13:20
73

Лучшим ответом согласно FAQ по программированию на Python будет:

functions = {'myfoo': foo.bar}

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

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

avatar
00500005
9 апреля 2014 в 10:17
59

Ответ (надеюсь) никому и никогда не нужен

поведение, подобное оценке

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")()
wilks
21 мая 2020 в 00:43
14

это можно улучшить, рекурсивно просканируя дерево каталогов и автоматически монтируя USB-накопители.

SaladHead
27 февраля 2021 в 20:13
0

Это определенно тот ответ, который я хотел. Идеальный.

avatar
ferrouswheel
16 октября 2013 в 00:24
129

Учитывая строку с полным путем 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()
Pankaj Bhambhani
16 декабря 2015 в 13:19
3

Это мне помогло. Это облегченная версия функции __import__.

SdSaati
22 января 2020 в 09:12
2

Думаю, это был лучший ответ.

avatar
Natdrip
28 декабря 2012 в 16:56
16

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

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

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

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

V13
29 июля 2016 в 12:26
21

В чем это лучше, чем getattr ()?

ioaniatr
6 августа 2018 в 18:49
1

Именно то, что я хотел. Работает как шарм! Идеальный!! self.__getattribute__('title') равно self.title

ioaniatr
16 августа 2018 в 16:52
0

self.__getattribute__('title') не работает ни в каких случаях (не знаю почему), но func = getattr(self, 'title'); func(); работает. Так что, возможно, лучше использовать вместо этого getattr()

Aran-Fey
23 октября 2018 в 05:19
16

Могут ли люди, которые не знают python, перестать голосовать за этот хлам? Используйте вместо него getattr.

avatar
Sourcegeek
19 августа 2012 в 09:40
136

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

# 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')
avatar
trubliphone
14 февраля 2012 в 05:55
34

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

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
lony
6 октября 2017 в 12:33
0

Немного более общий: handler = getattr(sys.modules[__name__], myFnName)

avatar
sastanin
7 мая 2009 в 12:45
638
locals()["myfunction"]()

или

globals()["myfunction"]()

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

Joelmob
9 октября 2014 в 21:36
73

Этот метод с globals / locals хорош, если метод, который вам нужно вызвать, определен в том же модуле, из которого вы вызываете.

Nick T
26 января 2015 в 20:51
0

@Joelmob есть ли другой способ получить объект по строке из корневого пространства имен?

Joelmob
27 января 2015 в 12:34
0

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

David Stein
30 января 2017 в 15:18
1

У меня для вас есть причина (на самом деле то, что привело меня сюда): в модуле A есть функция F, которая должна вызывать функцию по имени. Модуль B импортирует модуль A и вызывает функцию F с запросом на вызов функции G, которая определена в модуле B. Этот вызов завершается неудачно, потому что, по-видимому, функция F работает только с глобальными объектами, которые определены в модуле F, поэтому globals () ['G'] = Нет.

avatar
HS.
7 августа 2008 в 11:35
391

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

module = __import__('foo')
func = getattr(module, 'bar')
func()
hoffmaje
5 мая 2012 в 09:33
98

Я не понимаю этого последнего комментария. __import__ имеет собственное право, и следующее предложение в упомянутых документах говорит: «Прямое использование __import __ () редко, за исключением случаев, когда вы хотите импортировать модуль, имя которого известно только во время выполнения». Итак: +1 за данный ответ.

glarrain
5 августа 2013 в 22:07
55

Используйте importlib.import_module. Официальные документы говорят о __import__: «Это расширенная функция, которая не нужна в повседневном программировании на Python, в отличие от importlib.import_module ()». docs.python.org/2/library/functions.html#__import__

Xiong Chiamiov
14 сентября 2013 в 16:54
9

@glarrain Если у вас все в порядке, только с поддержкой 2.7 и выше.

cowlinator
5 октября 2017 в 19:28
2

@Xiong Chaimiov, importlib.import_module поддерживается в версии 3.6. См. docs.python.org/3.6/library/…

Xiong Chiamiov
5 октября 2017 в 23:55
8

@cowlinator Да, 3.6 является частью "2.7 и выше", как в строгой семантике управления версиями, так и в датах выпуска (это произошло примерно шесть лет спустя). Его также не существовало в течение трех лет после моего комментария. ;) В ветке 3.x модуль существует с 3.1. 2.7 и 3.1 теперь довольно древние; вы по-прежнему найдете серверы, поддерживающие только 2.6, но, вероятно, в настоящее время стандартным советом будет importlib.