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






Предполагая модуль foo с методом bar:
import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()
Вы можете сократить строки 2 и 3 до:
result = getattr(foo, 'bar')()
если это имеет больше смысла для вашего варианта использования.
Вы можете использовать getattr таким образом в методах, связанных с экземпляром класса, методах уровня модуля, методах класса ... список можно продолжить.
Это работает, если вы уже знаете имя модуля. Однако, если вы хотите, чтобы пользователь указал имя модуля в виде строки, это не сработает.
Если вам нужно избежать исключения NoneType is not callable, вы также можете использовать форму с тремя аргументами getattr: getattr (foo, 'bar', lambda: None). Прошу прощения за форматирование; приложение для Android stackexchange, по-видимому, ужасно.
См. Также ответ @sastanin, если вас интересуют только, например, функции вашего локального / текущего модуля.
Это не работает с функциями, использующими декоратор. getattr возвращает внешнюю функцию
Примечание: круто +1, это заставило меня еще раз понять, что в Python все является объектом. Следовательно, он работает также с переменными, вы можете получить доступ к переменным модуля как к любым другим переменным объекта.
Если у вас есть глубокие структуры, может быть полезен следующий синтаксис: from functools import reduce reduce (getattr, "a.b.c.d.e.f.g" .split ('.'), DeepStructure)
Это очень полезный ответ, покупайте, я не мог его решить, когда модуль foo сам является текущим модулем. Кто-нибудь знает, как это сделать?
@akki Да, если вы в модуль foo, вы можете использовать globals() для этого: methodToCall = globals()['bar']
Вы МОЖЕТЕ это сделать, но ДОЛЖНЫ ли вы? Как вы найдете все экземпляры вызова функции, когда ваша база кода станет большой? Нечего искать, и никакая IDE не найдет эту ссылку. Это делает такой динамический вызов действительно неприятным. В любом случае, не лучше ли говорить откровенно вашим коллегам? Меньше кода не всегда означает более читабельный.
@KeatsKelleher, поскольку мы не знаем варианта использования, только вопрос, все, что я могу с уверенностью сказать, это то, что существует множество вариантов использования, в которых ваша точка зрения применима, а некоторые - нет. Обычно и разумно делать такие вещи в механизмах правил, DSL, сценариях внутри приложений и т. д., В которых код, который вы пишете, существует для запуска другого кода. Вероятно, существует множество других «хороших» моментов для этого, о которых я сейчас не думаю. Итак, должен мы это делаем? Ответ, как и в случае с большинством языковых функций в большинстве языков, - «это зависит от обстоятельств».
@PatrickJohnmeyer Я никогда полностью не исключаю паттерны / парадигмы ... Вот почему я задал вопрос вместо того, чтобы делать общее заявление. Существует множество негативных последствий чрезмерного использования этого шаблона ... что (по крайней мере, по моему опыту) является обычным явлением.
В случае, если сам модуль является строкой, и нужно преобразовать из str в класс, в этом случае «foo» - это строка, но ее нужно вызвать, тогда проверьте эту ссылку blender.stackexchange.com/questions/36781/… PS: Я знаю, что она не принадлежит В контексте заданного вопроса я подумал, что кому-то может быть полезен этот комментарий.
мы можем получить это foo также из строки?
@conol вы можете использовать что-то вроде m = __import__('foo'), где m теперь является именем, используемым для модуля «foo». Затем вы сделали бы method_to_call = getattr(m, 'bar'). Прямой вызов __import__ редко бывает правильным ответом, но возможен. stackoverflow.com/a/4605/363
Решение Патрика, наверное, самое чистое. Если вам нужно также динамически подобрать модуль, вы можете импортировать его следующим образом:
module = __import__('foo')
func = getattr(module, 'bar')
func()
Я не понимаю этого последнего комментария. __import__ имеет собственное право, и следующее предложение в упомянутых документах говорит: «Прямое использование __import __ () редко, за исключением случаев, когда вы хотите импортировать модуль, имя которого известно только во время выполнения». Итак: +1 за данный ответ.
Используйте importlib.import_module. Официальные документы говорят о __import__: «Это расширенная функция, которая не нужна в повседневном программировании на Python, в отличие от importlib.import_module ()». docs.python.org/2/library/functions.html#__import__
@glarrain Если у вас все в порядке, только с поддержкой 2.7 и выше.
@Xiong Chaimiov, importlib.import_module поддерживается в версии 3.6. См. docs.python.org/3.6/library/…
@cowlinator Да, 3.6 является частью "2.7 и выше", как в строгой семантике управления версиями, так и в датах выпуска (это произошло примерно шесть лет спустя). Его также не существовало в течение трех лет после моего комментария. ;) В ветке 3.x модуль существует с 3.1. 2.7 и 3.1 теперь довольно древние; вы по-прежнему найдете серверы, поддерживающие только 2.6, но, вероятно, в настоящее время стандартным советом будет importlib.
locals()["myfunction"]()
или же
globals()["myfunction"]()
местные жители возвращает словарь с текущей локальной таблицей символов. глобалы возвращает словарь с глобальной таблицей символов.
Этот метод с globals / locals хорош, если метод, который вам нужно вызвать, определен в том же модуле, из которого вы вызываете.
@Joelmob есть ли другой способ получить объект по строке из корневого пространства имен?
@NickT Мне известны только эти методы, я не думаю, что есть другие, которые выполняют ту же функцию, что и эти, по крайней мере, я не могу придумать причину, по которой их должно быть больше.
У меня для вас есть причина (на самом деле то, что привело меня сюда): в модуле A есть функция F, которая должна вызывать функцию по имени. Модуль B импортирует модуль A и вызывает функцию F с запросом на вызов функции G, которая определена в модуле B. Этот вызов завершается неудачно, потому что, по-видимому, функция F работает только с глобальными объектами, которые определены в модуле F, поэтому globals () ['G'] = Нет.
Как бы то ни было, если вам нужно передать имя функции (или класса) и имя приложения в виде строки, вы можете сделать это:
myFnName = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn = getattr(app,myFnName)
Чуть более общий - handler = getattr(sys.modules[__name__], myFnName).
Просто простой вклад. Если класс, который нам нужно создать, находится в том же файле, мы можем использовать что-то вроде этого:
# 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')
ничего из предложенного мне не помогло. Я все же обнаружил это.
<object>.__getattribute__(<string name>)(<params>)
Я использую Python 2.66
Надеюсь это поможет
В чем это лучше, чем getattr ()?
Именно то, что я хотел. Работает как шарм! Идеально!! self.__getattribute__('title') невероятно похож на self.title
self.__getattribute__('title') не работает ни в одном случае (не знаю почему), а вот func = getattr(self, 'title'); func(); работает. Так что, может быть, лучше использовать getattr() вместо
Учитывая строку с полным путем 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()
Это мне помогло. Это облегченная версия функции __import__.
Думаю, это был лучший ответ.
Ответ (надеюсь) никто никогда не хотел
Eval like поведение
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")()
это можно улучшить, рекурсивно просканируя дерево каталогов и автоматически монтируя USB-накопители.
Это определенно тот ответ, который я хотел. Идеально.
Лучшим ответом согласно FAQ по программированию на Python будет:
functions = {'myfoo': foo.bar}
mystring = 'myfoo'
if mystring in functions:
functions[mystring]()
The primary advantage of this technique is that the strings do not need to match the names of the functions. This is also the primary technique used to emulate a case construct
Попробуй это. Хотя он все еще использует 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)
Это было бы рискованно. Строка может иметь что угодно, и eval в конечном итоге вычислит ее безо всяких соображений.
Конечно, вы должны помнить о контексте, в котором вы его используете, независимо от того, будет ли это уместно с учетом этих рисков.
Функция не должна отвечать за проверку своих параметров - это задача другой функции. Сказать, что использовать eval со строкой рискованно, означает, что использование каждой функции рискованно.
Никогда не используйте eval без крайней необходимости. getattr(__module__, method_name) - лучший выбор в этом контексте.
Поскольку этот вопрос Как динамически вызывать методы внутри класса, используя присвоение имени метода переменной [дубликат] отмечен как дубликат, я отправляю связанный ответ здесь:
Сценарий заключается в том, что метод в классе хочет динамически вызывать другой метод в том же классе, я добавил некоторые детали в исходный пример, который предлагает более широкий сценарий и ясность:
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()
Output (Python 3.7.x)
function1: 12
function2: 12
Это простой ответ, это позволит вам, например, очистить экран. Ниже приведены два примера с eval и exec, которые будут печатать 0 вверху после очистки (если вы используете Windows, измените clear на cls, пользователи Linux и Mac оставьте, например, как есть) или просто выполните его, соответственно.
eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Это не то, о чем спрашивает op.
этот фрагмент кода содержит 2 наихудших недостатка безопасности, вложенных друг в друга. Какая-то запись.
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."
Хотя 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')
hasattr или getattr могут использоваться, чтобы определить, определена ли функция. У меня было отображение базы данных (eventType и обработка functionName), и я хотел убедиться, что никогда не «забывал» определить обработчик событий в моем питоне.