2011-11-18 12 views
58

Załóżmy, że mamy klasę z konstruktorem (lub inną funkcją), która pobiera zmienną liczbę argumentów, a następnie warunkowo ustawia je jako atrybuty klas.Jak ustawić atrybuty klas z argumentów zmiennych (kwargs) w pythonie

Mogę ustawić je ręcznie, ale wydaje się, że parametry zmienne są dość powszechne w Pythonie, że powinien to być wspólny idiom do tego. Ale nie jestem pewien, jak to zrobić dynamicznie.

Mam przykład przy użyciu eval, ale to prawie nie bezpieczne. Chcę wiedzieć, jak to zrobić - może z lambda?

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

Wygląda na te pytania są podobne: http://stackoverflow.com/questions/3884612/automatically-setting-class-member- variables-in-python http://stackoverflow.com/questions/356718/how-to-handle-constructors-or-methods-with-a-different-set-or-type-of-argument http: // stackoverflow. com/questions/1446555/python-decorator-to-ensure-that-kwargs-is-correct, więc wygląda na to, co chcę, to może to - ja .__ dict __ [key] = kwargs [key] – fijiaaron

+0

Nie bardzo pasuje do twojego pytanie, ale możesz chcieć sprawdzić [PEP8] (http://www.python.org/dev/peps/pep-0008/) kilka wskazówek na temat konwencjonalnej stylizacji Pythona. –

+0

Istnieje fantastyczna biblioteka do tego zwana attrs. po prostu 'pip install attrs', udekoruj swoją klasę' @ attr.s' i ustaw args jako 'a = attr.ib(); b = attr.ib() 'itp. Przeczytaj więcej [tutaj] (https://glyph.twistedmatrix.com/2016/08/attrs.html). –

Odpowiedz

105

Można użyć metody setattr:

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

Jest analogiczna getattr metoda pobierania atrybutów.

+0

@larsks dzięki, ale jakikolwiek pomysł, jak rozpakować tylko klucz słownika? http://stackoverflow.com/questions/41792761/calling-and-using-an-attribute-stored-in-variable-using-beautifulsoup-4 – JinSnow

0

Ich może być lepszym rozwiązaniem, ale to, co przychodzi na myśl jest dla mnie:

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} 
+0

Czego szukam to warunkowe ustawienie atrybutów na podstawie sprawdzania poprawności. Zdałem sobie sprawę, że problem z używaniem kwargs polega na tym, że nie sprawdza (ani nie dokumentuje), jakie atrybuty są dopuszczalne. – fijiaaron

+0

Tak, wiem, że odpowiedź @larsks jest lepsza. Naucz się czegoś nowego codziennie w SO! – Tom

-1

Podejrzewam, może lepiej w większości przypadków używać nazwanych args (na lepsze kodu własnym dokumentującego), więc może to wyglądać coś takiego:

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

Ta iteracja nie działa: 'dla klucza, wartość w (a, b, c)' – rerx

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 

Zadzwoniłem klasę SymbolDict ponieważ zasadniczo jest słownikiem, który działa za pomocą symboli zamiast strun. Innymi słowy, robisz x.foo zamiast x['foo'], ale pod okładkami to naprawdę to samo dzieje się.

65

Czy to nie jest jeszcze łatwiejsze?

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

następnie można:

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

iz czymś takim:

allowed_keys = ['a', 'b', 'c'] 
self.__dict__.update((k, v) for k, v in kwargs.iteritems() if k in allowed_keys) 

można filtrować klawiszy wcześniej.

+1

Jeszcze lepiej ustawić dla allow_keys zestaw :) – PascalVKooten

4

proponuję odmianę fqxp jest odpowiedź, która, oprócz dozwolonym przypisuje, pozwala ustawić domyślne wartości dla atrybutów:

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) 

To Python 3.x kod, dla Pythona 2.x potrzebujesz co najmniej jednej korekty, iteritems() zamiast items().

1

ten jest najprostszym poprzez larsks

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

moim przykładzie:

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 
+0

może wytłumaczyć czym jest kwargs.items()? –

+0

'kwargs' to słownik argumentów słów kluczowych, a' items() 'to metoda, która zwraca kopię listy słowników z parami' (klucz, wartość) '. – harryscholes

4

Większość odpowiedzi tutaj nie obejmują dobry sposób, aby zainicjować wszystkie atrybuty dozwolone tylko jedna wartość domyślna. Tak więc, aby dodać do odpowiedzi udzielonych przez @fqxp i @mmj:

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) 
+0

Myślę, że jest to najbardziej kompletna odpowiedź ze względu na inizjalizację "Fałsz". Słuszna uwaga! – Kyrol

Powiązane problemy