2011-11-18 8 views
58

Supongamos que tenemos una clase con un constructor (u otra función) que toma un número variable de argumentos y los establece como atributos de clase condicionalmente.Cómo se pueden establecer atributos de clase a partir de argumentos variables (kwargs) en python

Podría establecerlos manualmente, pero parece que los parámetros variables son lo suficientemente comunes en python como para que haya una expresión común para hacer esto. Pero no estoy seguro de cómo hacer esto de forma dinámica.

Tengo un ejemplo que usa eval, pero eso no es seguro. Quiero saber la forma correcta de hacer esto, ¿tal vez con 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

Parece que estas preguntas son similares: 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-are-correct así que parece que lo que quiero es quizás esto-- self .__ dict __ [key] = kwargs [key] – fijiaaron

+0

No es realmente relevante para su pregunta, pero es posible que desee comprobar [PEP8] (http://www.python.org/dev/peps/pep-0008/) para obtener algunos consejos sobre el estilo convencional de Python. –

+0

Hay una biblioteca fantástica para este llamado atributos. simplemente 'pip install attrs', decora tu clase con' @ attr.s', y establece los argumentos como 'a = attr.ib(); b = attr.ib() 'etc. Lea más [aquí] (https://glyph.twistedmatrix.com/2016/08/attrs.html). –

Respuesta

105

Se puede utilizar el método de setattr:

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

Existe un método análogo getattr para recuperar los atributos.

+0

@larsks gracias, pero ¿alguna idea de cómo podríamos desempaquetar solo una clave de diccionario? http://stackoverflow.com/questions/41792761/calling-and-using-an-attribute-stored-in-variable-using-beautifulsoup-4 – JinSnow

0

Su podría ser una solución mejor, pero lo que viene a mi mente es:

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

Lo que estoy buscando es configurar condicionalmente los atributos en función de la validación. Me di cuenta de que el problema con el uso de kwargs es que no valida (o documenta) qué atributos son aceptables. – fijiaaron

+0

Sí, me doy cuenta de que la respuesta de @larsks es mejor. ¡Aprende algo nuevo todos los días en SO! – Tom

-1

sospecho que podría ser mejor en la mayoría de los casos a utilizar argumentos con nombre (para un mejor código autodocumentado) por lo que puede tener un aspecto algo como esto:

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

Esta iteración no funciona: 'para la clave, valor en (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 

me llama la clase SymbolDict, ya que en esencia es un diccionario que funciona con símbolos en lugar de cadenas. En otras palabras, usted hace x.foo en lugar de x['foo'] pero bajo las sábanas es realmente lo mismo que sucede.

65

¿No es esto aún más fácil?

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

continuación, se puede:

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

y con algo como:

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

puede filtrar las teclas de antemano.

+1

Mejor aún hacer que allowed_keys sea un conjunto :) – PascalVKooten

4

propongo una variación de la respuesta fqxp 's, que, además de permitido atributos, le permite definir valores por defecto de atributos:

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) 

Esta es Python Código 3.x, para Python 2.x necesita al menos un ajuste, iteritems() en lugar de items().

1

éste es el más fácil a través de larsks

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

mi ejemplo:

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

podría explicar qué es kwargs.items()? –

+0

'kwargs' es un diccionario de argumentos de palabra clave, y' items() 'es un método que devuelve una copia de la lista del diccionario de pares' (clave, valor) '. – harryscholes

4

La mayoría de respuestas aquí no cubren una buena manera de inicializar todos los atributos permitido a sólo un valor predeterminado. Así, para añadir a las respuestas dadas por @fqxp y @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

Creo que esta es la respuesta más completa debido a la inizialización de 'False'. ¡Buen punto! – Kyrol

Cuestiones relacionadas