การเรียกใช้ฟังก์ชันของโมดูลโดยใช้ชื่อ (สตริง)


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) และฉันต้องการให้แน่ใจว่าฉันไม่เคย "ลืม" เพื่อกำหนดตัวจัดการเหตุการณ์ในไพ ธ
อน

9
ใช้งานได้ถ้าคุณรู้ชื่อโมดูลแล้ว อย่างไรก็ตามหากคุณต้องการให้ผู้ใช้ระบุชื่อโมดูลเป็นสตริงสิ่งนี้จะไม่ทำงาน
Blairg23

7
ถ้าคุณต้องการหลีกเลี่ยง NoneType ไม่ใช่ข้อยกเว้น callable คุณสามารถใช้รูปแบบสามอาร์กิวเมนต์ของ getattr: getattr (foo, 'bar', lambda: None) ฉันขอโทษสำหรับการจัดรูปแบบ; แอพพลิเคชั่น android stackexchange นั้นแย่มาก
geekofalltrades

3
ดูคำตอบที่ @sastanin จัดหาให้หากคุณสนใจเพียงตัวอย่างเกี่ยวกับฟังก์ชั่นการทำงานของโมดูลโลคัล / ปัจจุบันของคุณ
NuSkooler

6
@akki ใช่ถ้าคุณอยู่ในfooโมดูลคุณสามารถใช้globals()การทำเช่นนี้:methodToCall = globals()['bar']
เบนฮอยต์

543
locals()["myfunction"]()

หรือ

globals()["myfunction"]()

ชาวบ้านส่งกลับพจนานุกรมพร้อมตารางสัญลักษณ์ท้องถิ่นปัจจุบัน globalsส่งคืนพจนานุกรมพร้อมตารางสัญลักษณ์ทั่วโลก


53
วิธีนี้ที่มี globals / locals นั้นดีถ้าวิธีที่คุณต้องการโทรนั้นถูกกำหนดในโมดูลเดียวกันกับที่คุณกำลังเรียก
Joelmob

@Joelmob มีวิธีอื่นในการรับวัตถุโดยสตริงจากรูทเนมสเปซหรือไม่?
Nick T

@NickT ฉันรู้แค่วิธีการเหล่านี้ฉันไม่คิดว่าจะมีคนอื่นที่เติมฟังก์ชั่นแบบเดียวกันอย่างน้อยที่สุดฉันก็ไม่สามารถคิดถึงเหตุผลที่ทำไมควรมีมากกว่านี้
Joelmob

ฉันมีเหตุผลสำหรับคุณ (อันที่จริงสิ่งที่นำฉันมาที่นี่): โมดูล A มีฟังก์ชั่น F ที่ต้องเรียกใช้ฟังก์ชันตามชื่อ โมดูล B นำเข้าโมดูล A และเรียกใช้ฟังก์ชัน F พร้อมการร้องขอเพื่อเรียกใช้ฟังก์ชัน G ซึ่งกำหนดไว้ในโมดูล B การเรียกนี้ล้มเหลวเนื่องจากเห็นได้ชัดว่าฟังก์ชั่น F ทำงานเฉพาะกับ globals ที่กำหนดไว้ในโมดูล F - ดังนั้น globals () ['G'] = ไม่มี
David Stein

337

วิธีแก้ปัญหาของ Patrick น่าจะสะอาดที่สุด หากคุณต้องการรับโมดูลด้วยตนเองคุณสามารถนำเข้าสิ่งต่อไปนี้:

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 เท่านั้น แต่น่าจะมีการนำเข้าสินค้าเป็นคำแนะนำมาตรฐานในปัจจุบัน
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')

101

รับสตริงกับเส้นทางหลามที่สมบูรณ์ไปยังฟังก์ชั่นนี่คือวิธีที่ฉันไปเกี่ยวกับการรับผลลัพธ์ของฟังก์ชันดังกล่าว:

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__ฟังก์ชั่นที่มีน้ำหนักเบารุ่น
Pankaj Bhambhani

ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุด
Saeid

55

คำตอบที่ดีที่สุดตามคำถามที่พบบ่อยของโปรแกรม Pythonคือ:

functions = {'myfoo': foo.bar}

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

ข้อได้เปรียบหลักของเทคนิคนี้คือสตริงไม่จำเป็นต้องตรงกับชื่อของฟังก์ชัน นี่เป็นเทคนิคหลักที่ใช้จำลองการสร้างเคส


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

นี้อาจจะดีขึ้นโดยการสแกนซ้ำต้นไม้ไดเรกทอรีและรถยนต์ติดตั้งไดรฟ์ USB
Wilks

27

สำหรับสิ่งที่คุ้มค่าถ้าคุณต้องการส่งชื่อฟังก์ชั่น (หรือคลาส) และชื่อแอปเป็นสตริงคุณสามารถทำสิ่งนี้ได้:

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

แค่อีกนิดหน่อยก็คือhandler = getattr(sys.modules[__name__], myFnName)
lony

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)

1
สิ่งนี้จะมีความเสี่ยง สตริงสามารถมีอะไรและ eval จะท้าย eval-ling โดยไม่พิจารณาใด ๆ
iankit

4
แน่นอนว่าคุณต้องคำนึงถึงบริบทที่คุณใช้อยู่ไม่ว่าจะเหมาะสมหรือไม่ก็ตามหากมีความเสี่ยงเหล่านั้น
tvt173

1
ฟังก์ชั่นไม่ควรรับผิดชอบในการตรวจสอบความถูกต้องของพารามิเตอร์ซึ่งเป็นหน้าที่ของฟังก์ชั่นที่แตกต่างกัน การบอกว่ามันเสี่ยงที่จะใช้ eval กับสตริงว่าการใช้ฟังก์ชั่นทุกอย่างนั้นมีความเสี่ยง
red777

คุณไม่ควรใช้evalเว้นแต่มีความจำเป็นอย่างเคร่งครัด getattr(__module__, method_name)เป็นตัวเลือกที่ดีกว่ามากในบริบทนี้
moi

14

ไม่มีสิ่งใดแนะนำให้ช่วยฉันได้ ฉันค้นพบสิ่งนี้แม้ว่า

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

ฉันใช้ไพ ธ อน 2.66

หวังว่านี่จะช่วยได้


13
นี่เป็นสิ่งที่ดีกว่า getattr () ไหม
V13

1
สิ่งที่ฉันต้องการ ทำงานเหมือนจับใจ! ที่สมบูรณ์แบบ !! self.__getattribute__('title')เท่ากับself.title
ioaniatr

self.__getattribute__('title')ไม่ทำงานในกรณีใด ๆ (ไม่ทราบสาเหตุ) หลังจากนั้นทั้งหมด แต่func = getattr(self, 'title'); func();ทำ ดังนั้นอาจใช้ดีกว่าgetattr()แทน
ioaniatr

10
ผู้ที่ไม่รู้จักงูหลามได้โปรดหยุดการอัปโหลดขยะนี้หรือไม่ ใช้getattrแทน
Aran-Fey

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)

function1: 12

function2: 12


-7

นี่เป็นคำตอบง่าย ๆ ซึ่งจะช่วยให้คุณสามารถล้างหน้าจอได้ มีตัวอย่างที่สองอยู่ด้านล่างด้วย EVAL และ exec ที่จะพิมพ์ 0 ที่ด้านบนหลังจากการทำความสะอาด (ถ้าคุณใช้ Windows เปลี่ยนแปลงclearไปcls, Linux และ Mac ผู้ใช้ออกเป็นตัวอย่าง) หรือเพียงแค่ดำเนินการได้ตามลำดับ

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")

3
นี่ไม่ใช่สิ่งที่ op ถาม
Tuncay Göncüoğlu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.