Panggil fungsi dengan daftar argumen dengan python

150

Saya mencoba memanggil fungsi di dalam fungsi lain dengan python, tetapi tidak dapat menemukan sintaks yang tepat. Yang ingin saya lakukan adalah sesuatu seperti ini:

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

Dalam hal ini panggilan pertama akan berfungsi, dan yang kedua tidak. Yang ingin saya modifikasi adalah fungsi wrapper dan bukan fungsi yang dipanggil.

SurDin
sumber

Jawaban:

268

Untuk sedikit memperluas jawaban lain:

Dalam barisan:

def wrapper(func, *args):

Tanda * di sebelah argsberarti "ambil sisa parameter yang diberikan dan masukkan ke dalam daftar yang disebut args".

Dalam barisan:

    func(*args)

Tanda * di sebelah argssini berarti "ambil daftar ini yang disebut args dan 'buka' ke dalam parameter lainnya.

Jadi, Anda dapat melakukan hal berikut:

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

Di wrapper2, daftar diteruskan secara eksplisit, tetapi di kedua pembungkus argsberisi daftar [1,2,3].

itsadok
sumber
26
Satu hal yang saya tidak menemukan disebutkan sangat sering adalah bagaimana memanggil fungsi dengan * args, jika Anda memiliki daftar atau tuple yang ingin Anda sampaikan. Untuk itu Anda perlu menyebutnya seperti ini: wrapper1 (func2, * mylist)
Ali
* args in def wrapper(func, *args)adalah apa yang method(params object[] args)ada di C #.
Jim Aho
1
Catatan yang *argsharus menjadi argumen terakhir dalam definisi fungsi.
Jim Aho
2
The * next to args means "take the rest of the parameters given and put them in a list called args".Itu menempatkan mereka dalam sebuah tuple, bukan daftar.
Tomasz Nocoń
20

Cara paling sederhana untuk membungkus suatu fungsi

    func(*args, **kwargs)

... adalah menulis secara manual pembungkus yang akan memanggil func () di dalamnya:

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

Dalam fungsi Python adalah sebuah objek, sehingga Anda dapat meneruskan namanya sebagai argumen dari fungsi lain dan mengembalikannya. Anda juga dapat menulis generator pembungkus untuk fungsi anyFunc () :

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

Harap perhatikan juga bahwa dalam Python ketika Anda tidak tahu atau tidak ingin menyebutkan semua argumen dari suatu fungsi, Anda bisa merujuk ke beberapa argumen, yang dilambangkan dengan namanya, didahului dengan tanda bintang di tanda kurung setelah nama fungsi:

    *args

Misalnya, Anda dapat mendefinisikan fungsi yang akan mengambil sejumlah argumen:

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

Python menyediakan manipulasi lebih jauh pada argumen fungsi. Anda dapat mengizinkan suatu fungsi untuk mengambil argumen kata kunci. Dalam badan fungsi argumen kata kunci disimpan dalam kamus. Dalam tanda kurung setelah nama fungsi kamus ini dilambangkan dengan dua tanda bintang diikuti oleh nama kamus:

    **kwargs

Contoh serupa yang mencetak kamus argumen kata kunci:

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

Anda bisa menggunakan sintaks * args dan ** kwargs untuk argumen panjang variabel.

Apa yang dimaksud * args dan ** kwargs?

Dan dari tutorial python resmi

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

JimB
sumber
Jadi saya membutuhkannya untuk mendapatkan * args dan ** kwargs dan menyebutnya dengan mereka?
SurDin
1
Tidak, Anda bisa menggunakan salah satu dari ini / atau, tetapi mereka sering dipasangkan bersama. Dalam kasus Anda, Anda hanya perlu * args.
JimB
Ok, itu berhasil, tetapi masih tidak membiarkan saya melewati daftar argumen, dan saya harus melewati mereka secara terpisah. Tidak banyak mengganggu saya, dalam situasi saya saat ini, tetapi masih baik untuk mengetahui bagaimana melakukan ini. (Saya perlu melakukan wrapper (func2, x, y, z) dan bukan wrapper (func2, [x, y, z])))
SurDin
Jika yang terakhir adalah yang Anda inginkan, gunakan formulir * args ketika wrapper memanggil func2, tetapi tidak dalam 'def wrapper'.
Alex Martelli
10

Jawaban literal untuk pertanyaan Anda (untuk melakukan persis seperti yang Anda minta, mengubah hanya pembungkus, bukan fungsi atau fungsi panggilan) hanya untuk mengubah baris

func(args)

untuk membaca

func(*args)

Ini memberitahu Python untuk mengambil daftar yang diberikan (dalam hal ini, args) dan meneruskan isinya ke fungsi sebagai argumen posisional.

Trik ini berfungsi di kedua "sisi" panggilan fungsi, jadi fungsi yang didefinisikan seperti ini:

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

akan dapat menerima argumen posisi sebanyak yang Anda melemparkannya, dan menempatkan semuanya ke dalam daftar yang disebut args.

Saya harap ini membantu sedikit memperjelas hal-hal. Perhatikan bahwa ini semua dimungkinkan dengan argumen dikts / kata kunci juga, menggunakan **bukan *.

Alan Rowarth
sumber
8

Anda perlu menggunakan argumen membongkar ..

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)
Joril
sumber
0

Tambahan kecil untuk jawaban sebelumnya, karena saya tidak dapat menemukan solusi untuk masalah, yang tidak layak membuka pertanyaan baru, tetapi membawa saya ke sini.

Berikut ini adalah cuplikan kode kecil, yang menggabungkan lists, zip()dan *args, untuk menyediakan pembungkus yang dapat menangani sejumlah fungsi yang tidak diketahui dengan jumlah argumen yang tidak diketahui.

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)

Perlu diingat, itu zip()tidak memberikan pemeriksaan keamanan untuk daftar panjang yang tidak sama, lihat zip iterators yang menyatakan panjangnya sama dengan python .

P. Siehr
sumber