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

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

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

19 ответов

Решение

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

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

Что касается этого, строки 2 и 3 могут быть сжаты до:

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

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

locals()["myfunction"]()

или же

globals()["myfunction"]()

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

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

module = __import__('foo')
func = getattr(module, 'bar')
func()

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

# 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')

Получив строку с полным путем к 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()

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

functions = {'myfoo': foo.bar}

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

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

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

Эваль как поведение

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")()

Попробуй это. Хотя он по-прежнему использует 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)

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

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)

Как этот вопрос Как динамически вызывать методы в классе, используя присвоение имени метода переменной [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

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

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

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

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

Хотя 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')

В 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]

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

      >>> from operator import attrgetter
>>> l = [1, 2, 3]
>>> attrgetter('reverse')(l)()
>>> l
[3, 2, 1]
>>> 

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."

Раньше я сталкивался с аналогичной проблемой, которая заключается в преобразовании строки в функцию. но я не могу использовать или 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() опасный метод, вы должны подтвердить безопасность.

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

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

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

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

Вместо этого сделайте что-то вроде:

      safe_functions = {'baz':foo.baz, 'bar':foo.bar}
safe_functions['bar']()
safe_functions['delete_all_the_things']() 

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

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Другие вопросы по тегам