Виклик функції модуля за допомогою його імені (рядок)


1734

Назвіть найкращий спосіб викликати функцію, задану рядок із назвою функції в програмі Python. Наприклад, скажімо, що у мене є модуль foo, і у мене є рядок, вміст якого є "bar". Який найкращий спосіб зателефонувати foo.bar()?

Мені потрібно отримати повернене значення функції, саме тому я не просто використовую eval. Я зрозумів, як це зробити, використовуючи evalдля визначення функції temp, яка повертає результат цього виклику функції, але я сподіваюся, що є більш елегантний спосіб зробити це.

Відповіді:


2078

Припустимо модуль 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). Прошу вибачення за форматування; додаток stackexchange для Android є, мабуть, жахливим.
geekofalltrades

3
Дивіться також відповідь, яку надає @sastanin, якщо вас цікавить лише, наприклад, про функції вашого локального / поточного модуля.
NuSkooler

6
@akki Так, якщо ви в в fooмодулі ви можете використовувати , globals()щоб зробити це:methodToCall = globals()['bar']
Бен Хойт

543
locals()["myfunction"]()

або

globals()["myfunction"]()

місцеві жителі повертають словник з поточною таблицею місцевих символів. globals повертає словник із таблицею глобальних символів.


53
Цей метод з глобальними / локальними послугами хороший, якщо метод, з якого потрібно зателефонувати, визначений у тому ж модулі, з якого ви телефонуєте.
Joelmob

@Joelmob Чи є інший спосіб отримати об'єкт за допомогою рядка з кореневого простору імен?
Нік Т

@NickT Я знаю лише ці методи, я не думаю, що є інші, які виконують таку ж функцію, як ці, принаймні я не можу придумати причину, чому повинно бути більше.
Йоельмоб

У мене є причина для вас (насправді, що мене привело сюди): Модуль A має функцію F, якій потрібно викликати функцію по імені. Модуль B імпортує модуль A і викликає функцію F із запитом викликати функцію G, яка визначена в модулі B. Цей виклик не вдається, оскільки, мабуть, функція F працює лише з глобалами, визначеними в модулі F - так глобалами () ['G'] = Немає.
Девід Штейн

337

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

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

93
Я не розумію того останнього коментаря. __import__ має власне право, і наступне речення у згаданих документах говорить: "Пряме використання __import __ () зустрічається рідко, за винятком випадків, коли потрібно імпортувати модуль, ім'я якого відомо лише під час виконання". Отже: +1 за дану відповідь.
hoffmaje

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 був стандартною порадою в наш час.
Xiong Chiamiov

113

Просто простий внесок. Якщо клас, який нам потрібно застосувати, знаходиться в одному файлі, ми можемо використовувати щось подібне:

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

100

З огляду на рядок із повним пітоновим шляхом до функції, ось як я пішов про отримання результату зазначеної функції:

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

1
Це мені допомогло. Його легка версія __import__функції.
Панкай Бхаммбані

Я думаю, що це була найкраща відповідь.
Саїд

55

Найкращою відповіддю відповідно до FAQ-питань програмування Python буде:

functions = {'myfoo': foo.bar}

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

Основна перевага цієї методики полягає в тому, що рядки не повинні відповідати назвам функцій. Це також основний прийом, який використовується для імітації конструктивної справи


43

Відповіді (сподіваюся) ніхто ніколи не хотів

Еваль, як поведінка

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-накопичувачів
wilks

27

Для чого це варто, якщо вам потрібно було передати ім’я функції (або класу) і ім'я програми як рядок, то ви можете зробити це:

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

Лише трохи більш загальнеhandler = getattr(sys.modules[__name__], myFnName)
одинокий

21

Спробуйте це. Хоча це все ще використовує 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)

1
Це було б ризиковано. рядок може мати все, що завгодно, і eval закінчиться овально, без будь-якого розгляду.
iankit

4
Звичайно, ви повинні пам’ятати про контекст, в якому ви його використовуєте, чи буде це підходящим чи ні, враховуючи ці ризики.
tvt173

1
Функція не повинна відповідати за перевірку параметрів - це завдання іншої функції. Говорячи, що використовувати eval зі строкою ризиковано, це означає, що використання кожної функції є ризикованим.
red777

Ніколи не слід вживати, evalякщо це не потрібно. getattr(__module__, method_name)є набагато кращим вибором у цьому контексті.
Moi

14

нічого із запропонованого мені не допомогло. Я все-таки це виявив.

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

Я використовую python 2.66

Сподіваюсь, це допомагає


13
У якому аспекті це краще, ніж getattr ()?
V13

1
Саме те, що я хотів. Працює як шарм! Ідеально !! self.__getattribute__('title')дорівнюєself.title
ioaniatr

self.__getattribute__('title')не працює ні в якому разі (не знаю, чому) післязавжди, але так func = getattr(self, 'title'); func();і є. Тож, можливо, краще використовувати getattr()натомість
ioaniatr

10
Чи можуть люди, які не знають пітона, перестати рекламувати цей мотлох? Використовуйте getattrзамість цього.
Аран-Фей

6

Оскільки це запитання, як динамічно викликати методи у класі, використовуючи присвоєння імені методу змінній [дублікат], позначеній як дублікат, як ця, я публікую тут відповідну відповідь:

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

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.