Python'da ** kwarg'ları kullanmanın uygun yolu


454

**kwargsVarsayılan değerler söz konusu olduğunda Python'da kullanmanın doğru yolu nedir ?

kwargssözlük döndürür, ancak varsayılan değerleri ayarlamanın en iyi yolu nedir, yoksa bir tane var mı? Sadece sözlük olarak mı erişmeliyim? Get işlevini kullan?

class ExampleClass:
    def __init__(self, **kwargs):
        self.val = kwargs['val']
        self.val2 = kwargs.get('val2')

Basit bir soru, ama iyi kaynaklar bulamadığım bir soru. İnsanlar gördüğüm kodda farklı yollar yapıyorlar ve ne kullanacaklarını bilmek zor.

Yanıtlar:


468

get()Sözlükte olmayan tuşlar için varsayılan bir değer iletebilirsiniz :

self.val2 = kwargs.get('val2',"default value")

Ancak, belirli bir varsayılan değeri olan belirli bir bağımsız değişkeni kullanmayı planlıyorsanız, neden adlandırılmış bağımsız değişkenleri ilk etapta kullanmıyorsunuz?

def __init__(self, val2="default value", **kwargs):

16
Konumsal bağımsız değişkenleri yalnızca gerekli bağımsız değişkenler için ve belirtilebilir veya belirtilemeyen bağımsız değişkenler için kwargs kullanmak istiyorum, ancak varsayılan bir değere sahip olmak yardımcı olur. kwargs güzel çünkü argümanlarınızı istediğiniz sırayla gönderebilirsiniz. Konumsal argümanlar size bu özgürlüğü vermez.
Kekoa

95
Adlandırılmış bağımsız değişkenleri istediğiniz sırayla iletebilirsiniz. Pozisyonlara sadece isimleri kullanmazsanız uymanız gerekir - ki bu da kwargs durumunda yapmanız gerekir. Bunun yerine, kwarg'ların aksine adlandırılmış argümanların kullanılması, adları kullanmamanız için ek özgürlük sağlar - ancak, siparişi korumanız gerekir.
balpha

13
@Kekoa: Adlandırılmış bağımsız değişkenleri istediğiniz zaman istediğiniz sırada gönderebilirsiniz. Bu esnekliği elde etmek için ** kwargs kullanmanız gerekmez.
S.Lott

13
pylint, kwarg'ları kullanmak için kötü form olarak işaretler __init__(). Birisi bunun neden tiftikli bir tecavüz olduğunu açıklayabilir mi?
hughdbrown


271

Çoğu cevap bunu söylerken, örneğin,

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...etc...

aynıdır"

def f(foo=None, bar=None, **kwargs):
    ...etc...

Bu doğru değil. İkinci durumda, folarak adlandırılabilir f(23, 42)eski vaka argümanlar adlı kabul eder iken, yalnızca hiçbir konumsal aramaları -. Genellikle arayanın maksimum esnekliğine izin vermek istersiniz ve bu nedenle çoğu cevap iddia edildiği gibi ikinci form tercih edilir: ancak bu her zaman böyle değildir. Tipik olarak sadece birkaçı geçirilen birçok isteğe bağlı parametreyi kabul ettiğinizde, adlandırılmış argümanların kullanımını zorlamak mükemmel bir fikir olabilir (çağrı sitelerinizdeki kazalardan ve okunamayan kodlardan kaçınmak!) - threading.Threadbir örnektir. İlk form Python 2'de bunu nasıl uyguladığınızdır.

Tek bir sonraki her argüman: deyim Python 3'te şimdi özel destek sözdizimi olduğunu çok önemlidir *içinde defimza anahtar kelime sadece olduğunu, bir konumsal argüman olarak geçilemeyen, ancak yalnızca adlandırılmış biri gibidir. Python 3'te yukarıdakileri şu şekilde kodlayabilirsiniz:

def f(*, foo=None, bar=None, **kwargs):
    ...etc...

Nitekim, Python 3'te bile argümanları yalnızca anahtar kelime olabilir değil (varsayılan değeri olmayan olanlar) isteğe bağlı.

Ancak, Python 2 hala önde üretken yaşamın uzun yıllar var, bu daha iyidir, böylece değil Python 2 doğrudan Python 3 dilde desteklenmektedir önemli tasarım fikirleri uygulamak izin teknikleri ve deyimleri unutun!


10
@Alex Martelli: Kwargların, üstün olmakla birlikte, isimlendirilmiş argümanlarla aynı olduğunu iddia eden tek bir cevap bulamadım. Ama Py3k için iyi bir söylem değişti, bu yüzden +1
balpha

1
@Alex Martelli: Cevabınız için çok teşekkürler, python 3'ün zorunlu anahtar kelime argümanlarına izin verdiğini öğrendi, bu da eksikliğinin genellikle kodum ve işlevlerimde tatmin edici olmayan mimariye neden oldu.
FloW

78

Böyle bir şey öneririm

def testFunc( **kwargs ):
    options = {
            'option1' : 'default_value1',
            'option2' : 'default_value2',
            'option3' : 'default_value3', }

    options.update(kwargs)
    print options

testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}

testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}

Ve sonra değerleri istediğiniz şekilde kullanın

dictionaryA.update(dictionaryB)içeriğini ekler dictionaryBiçin dictionaryAherhangi yinelenen anahtarlar üzerine yazarak.


2
Teşekkürler @AbhinavGupta! Tam aradığım şey. Sadece ekledim for key in options: self.__setattr__(key, options[key])ve gitmekte iyiyim. :)
propjk007

53

Yapardın

self.attribute = kwargs.pop('name', default_value)

veya

self.attribute = kwargs.get('name', default_value)

Eğer kullanırsanız pop, o zaman gönderilen sahte değerler olup olmadığını kontrol edin ve uygun eylemi (varsa) alabilir.


2
.pop“Gönderilen sahte değerlerin olup olmadığını kontrol etmenize” yardımcı olacağını önererek ne demek istediğinizi açıklayabilir misiniz ?
Alan H.

13
@Alan H .: tüm haşhaş yapıldıktan sonra kwarg'larda kalan bir şey varsa, o zaman sahte değerleriniz var.
Vinay Sajip

@VinaySajip: Tamam, bu .pop "vs" .get üzerinde harika bir nokta, ama hala arayanı konumsal parametreler kullanmamaya zorlamanın yanı sıra, adlandırılmış argümanlar üzerinde pop'un neden tercih edildiğini göremiyorum.
MestreLion

1
@MestreLion: API'nızın kaç anahtar kelime bağımsız değişkenine izin verdiğine bağlıdır. Önerimin adlandırılmış argümanlardan daha iyi olduğunu iddia etmiyorum , ancak Python kwargsbir nedenle isimsiz argümanları yakalamanızı sağlıyor .
Vinay Sajip

Yani, sadece kontrol ediyorum. Pop, anahtar varsa ve geçmezse sözlük değerini döndürür default_valuemü? Ve daha sonra bu anahtarı kaldırır mı?
Daniel Möller

43

** kwargs ve varsayılan değerleri kullanmak kolaydır. Ancak bazen ilk etapta ** kwargs kullanmamalısınız.

Bu durumda, ** kwarg'ları en iyi şekilde kullanmıyoruz.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = kwargs.get('val',"default1")
        self.val2 = kwargs.get('val2',"default2")

Yukarıda "neden rahatsız?" beyanı. Aynı

class ExampleClass( object ):
    def __init__(self, val="default1", val2="default2"):
        self.val = val
        self.val2 = val2

** kwargs kullanırken, bir anahtar kelimenin yalnızca isteğe bağlı değil, koşullu olduğunu da belirtirsiniz. Basit varsayılan değerlerden daha karmaşık kurallar vardır.

** kwargs kullanırken, genellikle basit varsayılanların geçerli olmadığı aşağıdakilere benzer bir şey ifade edersiniz.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = "default1"
        self.val2 = "default2"
        if "val" in kwargs:
            self.val = kwargs["val"]
            self.val2 = 2*self.val
        elif "val2" in kwargs:
            self.val2 = kwargs["val2"]
            self.val = self.val2 / 2
        else:
            raise TypeError( "must provide val= or val2= parameter values" )

O küçük beyin jimnastiği hoşuma gitti! Düşünmeye devam ettim, "Ama sadece get veya pop'u kullanabilirsiniz - ah, onlar birbirlerine bağımlılar ..."
trojjer

28

Yana **kwargstartışmaların numarası bilinmediği zaman kullanılır, neden yapmıyorum?

class Exampleclass(object):
  def __init__(self, **kwargs):
    for k in kwargs.keys():
       if k in [acceptable_keys_list]:
          self.__setattr__(k, kwargs[k])

Evet, bu zarif ve güçlü ... kabul edilebilir_anahtarlar_listesi etrafındaki köşeli parantezlerden pek emin değilim: Bunu bir demet ya da bir liste haline getireceğim ve sonra bu parantezleri "if" deyiminde bırakacağım
mike rodent

Tüm tuşların beklendiği durumlar için bunu biraz değiştirdim: stackoverflow.com/questions/1098549/…
rebelliard

14

İşte başka bir yaklaşım:

def my_func(arg1, arg2, arg3):
    ... so something ...

kwargs = {'arg1': 'Value One', 'arg2': 'Value Two', 'arg3': 'Value Three'}
# Now you can call the function with kwargs like this:

my_func(**kwargs)

Django CBV'lerinde çok kullanılır (örn. get_form_kwargs()). ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/…
trojjer

get_form()(Yöntem Şekil ne kadar kapsamlı bir yönteme erteleyerek kelime bağımsız değişkenler elde etmek için get_form_kwargsyukarıda belirtildiği gibi). Bu, aşağıdaki gibi formu başlatır: form_class(**self.get_form_kwargs()).
trojjer

Daha sonra get_form_kwargs()bir alt sınıf görünümünde geçersiz kılmak ve belirli mantığa göre kwarg eklemek / kaldırmak kolaydır. Ama bu bir Django eğitimi için.
trojjer

12

**kwargsVarsayılan değerlere gelince Python'da kullanmanın doğru yolunun sözlük yöntemini setdefaultaşağıda belirtildiği gibi kullanmak olduğunu düşünüyorum :

class ExampleClass:
    def __init__(self, **kwargs):
        kwargs.setdefault('val', value1)
        kwargs.setdefault('val2', value2)

Bu şekilde, kullanıcı anahtar kelimeye 'val' veya 'val2' iletirse argskullanılır; aksi takdirde, ayarlanan varsayılan değerler kullanılır.


11

Böyle bir şey yapabilirsin

class ExampleClass:
    def __init__(self, **kwargs):
        arguments = {'val':1, 'val2':2}
        arguments.update(kwargs)
        self.val = arguments['val']
        self.val2 = arguments['val2']

11

Takip etmek @srhegde kullanarak önerisi setattr :

class ExampleClass(object):
    __acceptable_keys_list = ['foo', 'bar']

    def __init__(self, **kwargs):
        [self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]

Bu varyant, sınıfın acceptablelistemizdeki tüm öğelere sahip olması bekleniyorsa yararlıdır .


1
Bu liste kavraması için bir kullanım durumu değildir, init yönteminizde bir for döngüsü kullanmalısınız.
ettanany

5

Eğer bunu * args ile birleştirmek istiyorsanız, tanımın sonunda * args ve ** kwargs tutmak zorundasınız.

Yani:

def method(foo, bar=None, *args, **kwargs):
    do_something_with(foo, bar)
    some_other_function(*args, **kwargs)

1

@AbhinavGupta ve @Steef update(), büyük argüman listelerini işlemek için çok yararlı bulduğumu önerdi :

args.update(kwargs)

Kullanıcının herhangi bir sahte / desteklenmeyen bağımsız değişken geçip geçmediğini kontrol etmek istersek ne olur? @VinaySajip dikkat çektipop() , argüman listesini tekrarlı olarak işlemek için kullanılabileceğine . Ardından, kalan argümanlar sahte olur. Güzel.

İşte bunu yapmanın basit bir sözdizimini tutan başka bir olası yolu update():

# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments

# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
    raise TypeError('Unknown arguments: {}'.format(unknown_args))

# Update args to contain user-supplied arguments
args.update(kwargs)

unknown_args, setvarsayılanlarda gerçekleşmeyen bağımsız değişkenlerin adlarını içerir.


0

Bilinmeyen veya birden fazla argümanı işlemek için başka bir basit çözüm şunlar olabilir:

class ExampleClass(object):

    def __init__(self, x, y, **kwargs):
      self.x = x
      self.y = y
      self.attributes = kwargs

    def SomeFunction(self):
      if 'something' in self.attributes:
        dosomething()

0

** kwargs, istediğiniz sayıda anahtar kelime argümanı ekleme özgürlüğü verir. Varsayılan değerlerini ayarlayabileceği bir anahtar listesi olabilir. Ancak, belirsiz sayıda anahtar için varsayılan değerlerin ayarlanması gereksiz görünüyor. Son olarak, anahtarların örnek nitelikleri olması önemli olabilir. Yani, bunu şöyle yaparım:

class Person(object):
listed_keys = ['name', 'age']

def __init__(self, **kwargs):
    _dict = {}
    # Set default values for listed keys
    for item in self.listed_keys: 
        _dict[item] = 'default'
    # Update the dictionary with all kwargs
    _dict.update(kwargs)

    # Have the keys of kwargs as instance attributes
    self.__dict__.update(_dict)
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.