Python'da değişken bağımsız değişkenlerden (kwargs) sınıf niteliklerini nasıl ayarlayabilirsiniz?


119

Bir yapıcıya (veya başka bir işleve) sahip, değişken sayıda bağımsız değişken alan ve ardından bunları koşullu olarak sınıf öznitelikleri olarak ayarlayan bir sınıfım olduğunu varsayalım.

Bunları manuel olarak ayarlayabilirim, ancak değişken parametrelerin python'da yeterince yaygın olduğu ve bunu yapmak için ortak bir deyim olması gerektiği görülüyor. Ancak bunu dinamik olarak nasıl yapacağımdan emin değilim.

Eval kullanan bir örneğim var ama bu pek güvenli değil. Bunu yapmanın doğru yolunu bilmek istiyorum - belki lambda ile?

class Foo:
    def setAllManually(self, a=None, b=None, c=None):
        if a!=None: 
            self.a = a
        if b!=None:
            self.b = b
        if c!=None:
            self.c = c
    def setAllWithEval(self, **kwargs):
        for key in **kwargs:
            if kwargs[param] != None
                eval("self." + key + "=" + kwargs[param])

Görünüşe göre şu sorular benzer: stackoverflow.com/questions/3884612/… stackoverflow.com/questions/356718/… stackoverflow.com/questions/1446555/… bu yüzden istediğim şey belki bu-- self .__ dict__ [anahtar] = kwargs [anahtar]
fijiaaron

Sorunuzla pek alakalı değil, ancak geleneksel Python stiliyle ilgili birkaç ipucu için PEP8'i kontrol etmek isteyebilirsiniz .
Thomas Orozco

Bunun için attrs adlı harika bir kütüphane var. basitçe pip install attrs, sınıfınızı ile dekore edin @attr.sve bağımsız değişkenleri a = attr.ib(); b = attr.ib()vb. ayarlayın . Buradan daha fazla bilgi edinin .
Adam Barnes

Burada bir şey mi kaçırıyorum? Yine de self.x = kwargs.get'x '] yapmanız gerekiyor] Arayanın yazım hatalarını açıyorsunuz Ekstra karakterli istemci yaratmalısınız instance = Class (** {}) self.x = kwargs.get'x '] sıradanlık, sizi daha sonra ısırmayacak mı? yani, self.x yerine, self .__ dict __ ['x'] ile sonuçlanacaksınız, doğru mu? Veya getattr () Ya kendinden daha fazla yazarak.
JGFMK

Yanıtlar:


148

Sen güncelleme olabilir __dict__anahtar kelime argümanlarla (bir sözlüğe şeklinde sınıfı özelliklerini gösterir) niteliğini:

class Bar(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

o zaman yapabilirsin:

>>> bar = Bar(a=1, b=2)
>>> bar.a
1

ve şunun gibi bir şeyle:

allowed_keys = {'a', 'b', 'c'}
self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)

anahtarları önceden filtreleyebilirsiniz ( hala Python 2.x kullanıyorsanız iteritemsbunun yerine kullanın items).


2
Konumsal self.__dict__.update(locals())argümanları da kopyalamak için kullanırsanız daha da iyi .
Giancarlo Sportelli

2
Sanırım bugünlerde buna ihtiyacınız olacak .. kwargs.iteritems () yerine kwargs [0] .items () - (Yazarken Python 3.6.5 kullanıyorum)
JGFMK

@JGFMK Neden kwargs[0]sadece yerine kwargs? Can kwargshatta bir tamsayı anahtarı var mı? Dizge olmaları gerektiğinden oldukça eminim.
wjandrea

146

setattr()Yöntemi kullanabilirsiniz :

class Foo:
  def setAllWithKwArgs(self, **kwargs):
    for key, value in kwargs.items():
      setattr(self, key, value)

getattr()Öznitelikleri almak için benzer bir yöntem vardır .


@larsks teşekkürler ama sadece bir sözlük anahtarını nasıl açabileceğimiz hakkında bir fikrin var mı? stackoverflow.com/questions/41792761/…
JinSnow

Kullanman gerekiyor .getattr()mu Veya özniteliklere ile erişebilir misiniz Foo.key?
Stevoisiak

@StevenVascellaro elbette sadece kullanabilirsiniz Foo.attrname. Sanırım sadece getattryöntemin var olduğu gerçeğine işaret ediyordum . Ayrıca, adlandırılmış özniteliğin mevcut olmadığı durumlar için varsayılan bir değer sağlamak istiyorsanız da kullanışlıdır.
larsks

3
Kabul edilen cevapla farkı nedir ? . Artıları ve eksileri neler?
Eduardo Pignatelli

15

Buradaki yanıtların çoğu, izin verilen tüm öznitelikleri tek bir varsayılan değerle başlatmanın iyi bir yolunu kapsamaz. Öyleyse, @fqxp ve @mmj tarafından verilen cevaplara eklemek için :

class Myclass:

    def __init__(self, **kwargs):
        # all those keys will be initialized as class attributes
        allowed_keys = set(['attr1','attr2','attr3'])
        # initialize all allowed keys to false
        self.__dict__.update((key, False) for key in allowed_keys)
        # and update the given keys by their given values
        self.__dict__.update((key, value) for key, value in kwargs.items() if key in allowed_keys)

Bence bu, başlatılmadan dolayı en eksiksiz cevap False. İyi bir nokta!
Kyrol

9

İzin verilen özniteliklere ek olarak öznitelikler için varsayılan değerleri ayarlamanıza izin veren fqxp yanıtının bir varyasyonunu öneriyorum :

class Foo():
    def __init__(self, **kwargs):
        # define default attributes
        default_attr = dict(a=0, b=None, c=True)
        # define (additional) allowed attributes with no default value
        more_allowed_attr = ['d','e','f']
        allowed_attr = list(default_attr.keys()) + more_allowed_attr
        default_attr.update(kwargs)
        self.__dict__.update((k,v) for k,v in default_attr.items() if k in allowed_attr)

Bu Python Eğer, en az bir düzenleme gereken 2.x için, Python 3.x kodu iteritems()yerine items().


1
Bu başlıktaki diğer yaklaşımları özetleyen en esnek cevap budur. Öznitelikleri ayarlar, varsayılan değerlere izin verir ve yalnızca izin verilen öznitelik adlarını ekler. Burada gösterildiği gibi python 3.x ile iyi çalışır.
squarespiral

7

Yine, mmj ve fqxp'nin mükemmel yanıtlarına dayanan başka bir varyant . Ya istersek

  1. İzin verilen özniteliklerin listesini kodlamaktan kaçının
  2. Yapıcıdaki her öznitelik için doğrudan ve açıkça varsayılan değerleri ayarlayın
  3. Kwarg'ları önceden tanımlanmış özniteliklerle sınırlayın.
    • geçersiz argümanları sessizce reddetmek veya alternatif olarak,
    • bir hata ortaya çıkarmak.

"Doğrudan" derken, gereksiz bir default_attributessözlükten kaçınmayı kastediyorum .

class Bar(object):
    def __init__(self, **kwargs):

        # Predefine attributes with default values
        self.a = 0
        self.b = 0
        self.A = True
        self.B = True

        # get a list of all predefined values directly from __dict__
        allowed_keys = list(self.__dict__.keys())

        # Update __dict__ but only for keys that have been predefined 
        # (silently ignore others)
        self.__dict__.update((key, value) for key, value in kwargs.items() 
                             if key in allowed_keys)

        # To NOT silently ignore rejected keys
        rejected_keys = set(kwargs.keys()) - set(allowed_keys)
        if rejected_keys:
            raise ValueError("Invalid arguments in constructor:{}".format(rejected_keys))

Büyük bir ilerleme değil, ama belki biri için yararlıdır ...

DÜZENLEME: Sınıfımız @propertyalıcılar ve ayarlayıcılarla "korumalı" öznitelikleri kapsüllemek için dekoratörler kullanıyorsa ve bu özellikleri kurucumuzla ayarlayabilmek istiyorsak, allowed_keyslisteyi dir(self)aşağıdaki gibi değerlerle genişletmek isteyebiliriz :

allowed_keys = [i for i in dir(self) if "__" not in i and any([j.endswith(i) for j in self.__dict__.keys()])]

Yukarıdaki kod hariçtir

  • dan herhangi bir gizli değişken dir()("__" varlığına bağlı olarak hariç tutma) ve
  • den dir()bir öznitelik adının sonunda (korumalı veya başka bir şekilde) adı bulunmayan herhangi bir yöntem __dict__.keys(), bu nedenle muhtemelen yalnızca @ özellik dekore edilmiş yöntemler tutulur.

Bu düzenleme muhtemelen sadece Python 3 ve üstü için geçerlidir.


2
class SymbolDict(object):
  def __init__(self, **kwargs):
    for key in kwargs:
      setattr(self, key, kwargs[key])

x = SymbolDict(foo=1, bar='3')
assert x.foo == 1

Sınıfı aradım SymbolDictçünkü esasen dizeler yerine semboller kullanarak çalışan bir sözlük. Başka bir deyişle, x.fooyerine yaparsınız x['foo']ama örtülerin altında gerçekten aynı şey oluyor.


2

Aşağıdaki çözümler vars(self).update(kwargs)veya self.__dict__.update(**kwargs)sağlam değildir, çünkü kullanıcı herhangi bir sözlüğe hata mesajı vermeden girebilir. Kullanıcının aşağıdaki imzayı ('a1', 'a2', 'a3', 'a4', 'a5') ekleyip eklemediğini kontrol etmem gerekirse, çözüm çalışmıyor. Ayrıca kullanıcı, "konumsal parametreler" veya "kay-değer çiftleri parametreleri" ni geçerek nesneyi kullanabilmelidir.

Bu yüzden bir metasınıf kullanarak aşağıdaki çözümü öneriyorum.

from inspect import Parameter, Signature

class StructMeta(type):
    def __new__(cls, name, bases, dict):
        clsobj = super().__new__(cls, name, bases, dict)
        sig = cls.make_signature(clsobj._fields)
        setattr(clsobj, '__signature__', sig)
        return clsobj

def make_signature(names):
    return Signature(
        Parameter(v, Parameter.POSITIONAL_OR_KEYWORD) for v in names
    )

class Structure(metaclass = StructMeta):
    _fields = []
    def __init__(self, *args, **kwargs):
        bond = self.__signature__.bind(*args, **kwargs)
        for name, val in bond.arguments.items():
            setattr(self, name, val)

if __name__ == 'main':

   class A(Structure):
      _fields = ['a1', 'a2']

   if __name__ == '__main__':
      a = A(a1 = 1, a2 = 2)
      print(vars(a))

      a = A(**{a1: 1, a2: 2})
      print(vars(a))

1

Daha iyi bir çözüm olabilir ama benim için aklıma gelen şudur:

class Test:
    def __init__(self, *args, **kwargs):
        self.args=dict(**kwargs)

    def getkwargs(self):
        print(self.args)

t=Test(a=1, b=2, c="cats")
t.getkwargs()


python Test.py 
{'a': 1, 'c': 'cats', 'b': 2}

Aradığım şey, doğrulamaya dayalı olarak öznitelikleri koşullu olarak ayarlamaktır. Kwarg kullanımındaki sorunun, hangi özelliklerin kabul edilebilir olduğunu doğrulamaması (veya belgelememesi) olduğunu fark ettim
fijiaaron

Evet, @ larsks cevabının daha iyi olduğunun farkındayım. SO'da her gün yeni bir şey öğrenin!
Tom

1

bu, larsks aracılığıyla en kolay olanıdır

class Foo:
    def setAllWithKwArgs(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

benim örneğim:

class Foo:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

door = Foo(size='180x70', color='red chestnut', material='oak')
print(door.size) #180x70

smb kwargs.items () ne olduğunu açıklayabilir mi?
Oleg_Kornilov

kwargsanahtar kelime argümanları items()sözlüğüdür ve sözlüğün (key, value)çiftler listesinin bir kopyasını döndüren bir yöntemdir .
harryscholes

-1

Çoğu durumda adlandırılmış bağımsız değişkenleri kullanmanın (kendi kendini daha iyi belgeleyen kod için) daha iyi olabileceğinden şüpheleniyorum, bu nedenle şuna benzer bir şey olabilir:

class Foo:
    def setAll(a=None, b=None, c=None):
        for key, value in (a, b, c):
            if (value != None):
                settattr(self, key, value)

Bu yineleme çalışmıyor:for key, value in (a, b, c)
47'de
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.