Bir işlev çağrısında yıldız operatörü ne anlama geliyor?


622

*Python'da operatör ne anlama geliyor, örneğin zip(*x)veya gibi bir kodda f(**k)?

  1. Tercümanda dahili olarak nasıl ele alınır?
  2. Performansı hiç etkiliyor mu? Hızlı mı yoksa yavaş mı?
  3. Ne zaman yararlıdır ve ne zaman değildir?
  4. Bir işlev bildiriminde mi yoksa bir çağrıda mı kullanılmalıdır?

4
Bunun "* işlev çağrısı sözdizimi" olarak ifade edilmesi gerektiğini düşünüyorum. Orada gibi kafa karıştırıcı alacak olsa da onlar, operatörler değil ise bir *ve **bu sözdizimi ile ilgisi olduğunu operatörü.
Ian Bicking

1
@ Ian Bicking: haklısınız, * ve ** argüman listesinde saf sözdizimi (belirteçler).
P. Ortiz

25
f(**k)bana sorarsan yanlış çıkıyor :)
Jean-François Fabre

1
Not: PEP 448 için: Ek Paket Açma Genellemelerine özgü şeyler (örneğin [*a, b, *c]veya {**d1, **d2}) için, işlev çağrıları ve işlev tanımları dışındaki kullanıma özgü, demet, liste ve tanımları belirleme, dikte tanımında çift yıldız işareti okumak isteyeceksiniz. . Önceki PEP 3132 için , sıra uzunluğunu bilmediğinizde Python'da Çoklu Paket Açma Ataması'na bakın .
ShadowRanger

1
VTR - Bu, parametreler için ** (çift yıldız / yıldız) ve * (yıldız / yıldız) ne yapar ifadelerinin bir kopyası değildir ? Bu soru sadece parametrelerle ilgili olduğundan, cevaplar fonksiyon çağrılarını da kapsasa da. İşlev çağrısındaki yıldız işareti, daha az popüler olduğundan ve en üstteki yanıt daha az eksiksiz olduğundan bu sorunun bir kopyası olarak işaretlenmelidir.
wjandrea

Yanıtlar:


955

Tek yıldız *, diziyi / koleksiyonu konumsal argümanlara ayırır, böylece bunu yapabilirsiniz:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

Bu, tuple'ı şu şekilde yürütecek şekilde açacaktır:

s = sum(1, 2)

Çift yıldız **da aynı şeyi yapar, sadece bir sözlük ve dolayısıyla adlandırılmış argümanlar kullanarak:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

Ayrıca şunları da birleştirebilirsiniz:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

şu şekilde yürütülecek:

s = sum(1, 2, c=10, d=15)

Ayrıca Python belgelerinin 4.7.4 - Bağımsız Değişken Listelerini Açma bölümüne bakın .


Ek olarak, alacak işlevleri *xve **ybağımsız değişkenleri tanımlayabilirsiniz , bu, bir işlevin bildirimde özel olarak adlandırılmayan herhangi bir sayıda konumsal ve / veya adlandırılmış bağımsız değişkeni kabul etmesine izin verir.

Misal:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

veya ile **:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

bu, onları bildirmek zorunda kalmadan çok sayıda isteğe bağlı parametre belirlemenize izin verebilir.

Ve yine şunları birleştirebilirsiniz:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

4
neden buna ihtiyacınız var, işlev, sağlanan liste üzerinde genişletilmeden sadece yineleyemez mi?
Martin Beckett

29
: Elbette, ama sonra onu aramak zorunda kalacak s = sum((1, 2, 3, 4, 5))ya s = sum([1, 2, 3, 4, 5]), *valuesopsiyon böyle çağrı göz argümanları bir dizi alır, ama onlar fonksiyon kodu için bir koleksiyon içine paketlenmiş konum yapar.
Lasse V. Karlsen

11
İşte gerçek fayda: Değişken sayıda argümana ihtiyacınız olduğunda başka türlü mümkün olmayacak işlevler yazabilirsiniz. Örneğin, 1 + n argümanı olan C'nin printf işlevi, yeni başlayan herhangi bir programcı için bir alıştırma olarak yazmak zordur. Python'da yeni başlayanlar def printf (string_template, * args) yazıp devam edebilir.
IceArdor

1
Bir sözlüğü iki yerine yalnızca bir * ile açarsanız (yanlışlıkla belki: p) ne olur? Bir şey yapıyor gibi görünüyor, bir tuple çıkıyor gibi görünüyor, ama ne olduğu o kadar açık değil. (düzenleme: tamam) Sanırım cevap sadece anahtarları açıyor, değerler atılıyor)
Ben Farmer

1
Son örnek, * ve ** 'nin yalnızca ambalajı açmakla kalmayıp aynı zamanda paketlemeyi de yaptığını göstermektedir! Bu mükemmel sayfa kodlamaadı.com
playgrounds/

46

Küçük bir nokta: bunlar operatör değil. İşleçler, mevcut değerlerden yeni değerler oluşturmak için ifadelerde kullanılır (örneğin 1 + 2, 3 olur. Buradaki * ve **, işlev bildirimleri ve çağrılarının sözdiziminin bir parçasıdır.


6
Python belgelerinin bu bağlamda bir operatör çağırdığına dikkat edin; Katılıyorum, bu biraz yanıltıcı.
Christophe

Teşekkürler. Python referans belgelerinde bunun net bir açıklamasını arıyordum ve hala göremiyorum. Yani işlev çağrıları için kural, temelde bir işlev çağrısı içindeki bir ifadenin başında bulunan bir "*" veya "**" nin bu tür bir genişlemeye neden olmasıdır?
nealmcb

20

Bunu özellikle bir işlev çağrısını 'saklamak' istediğinizde yararlı buluyorum.

Örneğin, bir 'ekle' işlevi için bazı birim testlerim olduğunu varsayalım:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

Çirkin olan add (test [0], test [1]) gibi bir şeyi elle yapmaktan başka add çağırmanın başka bir yolu yoktur. Ayrıca, değişken sayıda değişken varsa, ihtiyacınız olan tüm if ifadeleriyle kod oldukça çirkinleşebilir.

Bunun yararlı olduğu başka bir yer de Factory nesnelerini (sizin için nesneler oluşturan nesneler) tanımlamak içindir. Farz edelim ki, Araba nesneleri yapan ve onları geri döndüren bir sınıf Fabrikanız var. MyFactory.make_car ('kırmızı', 'bmw', '335ix') Araba ('kırmızı', 'bmw', '335ix') oluşturacak ve sonra onu döndürecek şekilde yapabilirsiniz.

def make_car(*args):
   return Car(*args)

Bu aynı zamanda bir üst sınıf kurucusunu çağırmak istediğinizde de kullanışlıdır.


4
Örneklerinizi beğendim. Ama bence -1 + 3 == 2.
eksortso

5
Oraya kasıtlı olarak başarısız olacak bir şey koydum :)
Donald Miner

19

Genişletilmiş arama sözdizimi olarak adlandırılır. Gönderen belgeler :

Sözdizimi * ifadesi işlev çağrısında görünürse, ifade bir sıra olarak değerlendirilmelidir. Bu dizideki öğeler, ek konumsal bağımsız değişkenlermiş gibi ele alınır; konumsal bağımsız değişkenler x1, ..., xN varsa ve ifade bir y1, ..., yM dizisi olarak değerlendirilirse, bu, M + N konumsal bağımsız değişken x1, ..., xN, y1, içeren bir çağrıya eşdeğerdir. .., yM.

ve:

Sözdizimi ** ifadesi işlev çağrısında görünürse, ifade, içeriği ek anahtar sözcük bağımsız değişkenleri olarak değerlendirilen bir eşleme olarak değerlendirilmelidir. Hem ifadede hem de açık anahtar kelime argümanı olarak görünen bir anahtar kelimenin olması durumunda, bir TypeError istisnası ortaya çıkar.


3
Sadece ders kitabı cevap dipnot ekleyerek - sözdizimsel destek gelmeden önce, aynı işlevselliği yerleşik içinde elde edilmiş apply()fonksiyonu
Jeremy Brown

18

Bir işlev çağrısında, tek yıldız bir listeyi ayrı bağımsız değişkenlere dönüştürür (örneğin zip(*x), zip(x1,x2,x3)eğer ile aynıdır x=[x1,x2,x3]) ve çift yıldız bir sözlüğü ayrı anahtar kelime argümanlarına dönüştürür (örneğin sanki f(**k)aynıdır .f(x=my_x, y=my_y)k = {'x':my_x, 'y':my_y}

Bir işlev tanımında durum tam tersidir: tek yıldız, rastgele sayıda bağımsız değişkeni bir listeye dönüştürür ve çift başlangıç, keyfi sayıda anahtar sözcük bağımsız değişkenini bir sözlüğe dönüştürür. Örneğin def foo(*x), "foo rastgele sayıda bağımsız değişken alır ve bunlara x listesinden erişilebilir (yani, kullanıcı ararsa foo(1,2,3), xolur [1,2,3])" def bar(**k)anlamına gelir ve "bar, keyfi sayıda anahtar kelime bağımsız değişkeni alır ve bunlara sözlük aracılığıyla erişilebilir k (yani kullanıcı ararsa bar(x=42, y=23), kolacaktır {'x': 42, 'y': 23}) ".


3
(çok) geç bir yorum, ama bence def foo(*x)* x bir liste değil, bir demet veriyor.
jeremycg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.