เรียกใช้ฟังก์ชันที่มีรายการอาร์กิวเมนต์ใน python


150

ฉันพยายามเรียกใช้ฟังก์ชันภายในฟังก์ชันอื่นในไพ ธ อน แต่ไม่พบไวยากรณ์ที่ถูกต้อง สิ่งที่ฉันต้องการทำคืออะไรเช่นนี้:

def wrapper(func, args):
    func(args)

def func1(x):
    print(x)

def func2(x, y, z):
    return x+y+z

wrapper(func1, [x])
wrapper(func2, [x, y, z])

ในกรณีนี้การโทรครั้งแรกจะใช้งานได้และสายที่สองจะไม่ทำงาน สิ่งที่ฉันต้องการแก้ไขคือฟังก์ชั่น wrapper ไม่ใช่ฟังก์ชั่นที่เรียกว่า

คำตอบ:


268

หากต้องการขยายคำตอบอื่น ๆ เล็กน้อย:

ในบรรทัด:

def wrapper(func, *args):

เครื่องหมาย * ถัดจากargsหมายถึง "ใช้พารามิเตอร์ที่เหลือและกำหนดไว้ในรายการที่เรียกว่าargs"

ในบรรทัด:

    func(*args)

* ถัดจากargsที่นี่หมายถึง "นำรายการนี้เรียกว่า args และ 'แกะ' ลงในพารามิเตอร์ที่เหลือ

ดังนั้นคุณสามารถทำสิ่งต่อไปนี้:

def wrapper1(func, *args): # with star
    func(*args)

def wrapper2(func, args): # without star
    func(*args)

def func2(x, y, z):
    print x+y+z

wrapper1(func2, 1, 2, 3)
wrapper2(func2, [1, 2, 3])

ในwrapper2รายการนี้จะถูกส่งผ่านไปอย่างชัดเจน แต่ในทั้งสองห่อมีรายการargs[1,2,3]


26
สิ่งหนึ่งที่ฉันไม่ได้กล่าวถึงบ่อย ๆ คือวิธีเรียกใช้ฟังก์ชันที่มี * args หากคุณมีรายการหรือสิ่งอันดับที่คุณต้องการส่งผ่าน เพื่อที่คุณจะต้องเรียกมันว่า: wrapper1 (func2, * mylist)
Ali

* args in def wrapper(func, *args)คืออะไรmethod(params object[] args)ใน C #
Jim Aho

1
โปรดทราบว่า*argsจะต้องเป็นอาร์กิวเมนต์สุดท้ายในการกำหนดฟังก์ชั่น
Jim Aho

2
The * next to args means "take the rest of the parameters given and put them in a list called args".มันทำให้พวกเขาอยู่ในสิ่งอันดับไม่ใช่รายการ
Tomasz Nocoń

20

วิธีที่ง่ายที่สุดในการห่อฟังก์ชั่น

    func(*args, **kwargs)

... คือการเขียน wrapper ที่จะเรียกfunc ()ภายในตัวมันเอง:

    def wrapper(*args, **kwargs):
        # do something before
        try:
            return func(*a, **kwargs)
        finally:
            # do something after

ในฟังก์ชั่น Python เป็นวัตถุดังนั้นคุณสามารถส่งมันชื่อเป็นอาร์กิวเมนต์ของฟังก์ชันอื่นและส่งคืน คุณสามารถเขียนตัวสร้าง wrapper สำหรับฟังก์ชั่นใด ๆanyFunc () :

    def wrapperGenerator(anyFunc, *args, **kwargs):
        def wrapper(*args, **kwargs):
            try:
                # do something before
                return anyFunc(*args, **kwargs)
            finally:
                #do something after
        return wrapper

โปรดทราบว่าใน Python เมื่อคุณไม่ทราบหรือไม่ต้องการตั้งชื่ออาร์กิวเมนต์ทั้งหมดของฟังก์ชันคุณสามารถอ้างถึง tuple of arguments ซึ่งแทนด้วยชื่อของมันนำหน้าด้วยเครื่องหมายดอกจันในวงเล็บหลัง ชื่อฟังก์ชั่น:

    *args

ตัวอย่างเช่นคุณสามารถกำหนดฟังก์ชั่นที่จะรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้:

    def testFunc(*args):
        print args    # prints the tuple of arguments

Python ให้การจัดการกับอาร์กิวเมนต์ของฟังก์ชันมากยิ่งขึ้น คุณสามารถอนุญาตให้ฟังก์ชันรับอาร์กิวเมนต์คำหลักได้ ภายในส่วนของฟังก์ชั่นอาร์กิวเมนต์คำหลักจะถูกเก็บไว้ในพจนานุกรม ในวงเล็บหลังชื่อฟังก์ชันพจนานุกรมนี้ถูกแสดงด้วยเครื่องหมายดอกจันสองตัวตามด้วยชื่อของพจนานุกรม:

    **kwargs

ตัวอย่างที่คล้ายกันที่พิมพ์พจนานุกรมอาร์กิวเมนต์คำหลัก:

    def testFunc(**kwargs):
        print kwargs    # prints the dictionary of keyword arguments

11

คุณสามารถใช้ไวยากรณ์ * args และ ** kwargs สำหรับอาร์กิวเมนต์ความยาวตัวแปร

* args และ ** kwargs หมายถึงอะไร

และจากการสอนของ python อย่างเป็นทางการ

http://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions


ดังนั้นฉันต้องการมันเพื่อรับทั้ง * args และ ** kwargs แล้วโทรหาพวกมันด้วยเหรอ?
SurDin

1
ไม่คุณสามารถใช้อย่างใดอย่างหนึ่ง / หรือ แต่พวกเขามักจะจับคู่กัน ในกรณีของคุณคุณต้องการเพียง * args
JimB

ตกลงใช้งานได้ แต่ก็ยังไม่ยอมให้ฉันผ่านรายการของการโต้แย้งและฉันต้องผ่านพวกเขาแยกกัน มันไม่ได้รบกวนฉันมากนักในสถานการณ์ปัจจุบันของฉัน แต่มันก็ยังดีที่จะรู้ว่าต้องทำอย่างไร (ฉันต้องทำ wrapper (func2, x, y, z) และไม่ต้อง wrapper (func2, [x, y, z])))
SurDin

หากหลังเป็นสิ่งที่คุณต้องการให้ใช้แบบฟอร์ม * args เมื่อ wrapper เรียก func2 แต่ไม่ใช่ใน 'def wrapper'
Alex Martelli

10

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

func(args)

อ่าน

func(*args)

สิ่งนี้บอกให้ Python รับรายการที่กำหนด (ในกรณีนี้args) และส่งเนื้อหาไปยังฟังก์ชันเป็นอาร์กิวเมนต์ตำแหน่ง

เคล็ดลับนี้ใช้ได้กับ "ทั้งสองด้าน" ของการเรียกใช้ฟังก์ชันดังนั้นฟังก์ชันที่กำหนดเช่นนี้:

def func2(*args):
    return sum(args)

argsจะสามารถที่จะยอมรับเป็นข้อโต้แย้งตำแหน่งมากที่สุดเท่าที่คุณโยนมันและวางพวกเขาทั้งหมดลงในรายการที่เรียกว่า

ฉันหวังว่านี่จะช่วยชี้แจงสิ่งต่าง ๆ เล็กน้อย หมายเหตุว่านี่เป็นสิ่งที่เป็นไปได้กับ dicts / ข้อโต้แย้งคำหลักเช่นกันโดยใช้แทน***


8

คุณต้องใช้อาร์กิวเมนต์เปิดออก ..

def wrapper(func, *args):
    func(*args)

def func1(x):
    print(x)

def func2(x, y, z):
    print x+y+z

wrapper(func1, 1)
wrapper(func2, 1, 2, 3)

0

นอกเหนือจากคำตอบก่อนหน้าเล็กน้อยเนื่องจากฉันไม่สามารถหาวิธีแก้ปัญหาได้ซึ่งไม่คุ้มค่าที่จะเปิดคำถามใหม่ แต่นำฉันมาที่นี่

นี่คือข้อมูลโค้ดขนาดเล็กซึ่งรวมlists, zip()และ*argsเพื่อให้เสื้อคลุมที่สามารถจัดการกับไม่ทราบจำนวนของฟังก์ชั่นที่มีจำนวนไม่รู้จักของข้อโต้แย้ง

def f1(var1, var2, var3):
    print(var1+var2+var3)

def f2(var1, var2):
    print(var1*var2)

def f3():
    print('f3, empty')

def wrapper(a,b, func_list, arg_list):
    print(a)
    for f,var in zip(func_list,arg_list):
        f(*var)
    print(b)

f_list = [f1, f2, f3]
a_list = [[1,2,3], [4,5], []]

wrapper('begin', 'end', f_list, a_list)

โปรดจำไว้ว่าzip()ไม่มีการตรวจสอบความปลอดภัยสำหรับรายการที่มีความยาวไม่เท่ากันดูที่ตัววนซ้ำ zip เพื่อยืนยันความยาวเท่ากันในไพธ อน

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