2010-03-17 9 views
61

¿Es posible crear un objeto de un diccionario en python de tal forma que cada clave sea un atributo de ese objeto?Establecer atributos del diccionario en python

Algo como esto:

d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 } 

e = Employee(d) 
print e.name # Oscar 
print e.age + 10 # 42 

Creo que sería más o menos la inversa de esta pregunta: ¿Python dictionary from an object's fields

+24

nota al margen: 'dict' no es un gran nombre para la variable, se esconde el tipo' dict' – van

Respuesta

107

Claro, algo como esto:

class Employee(object): 
    def __init__(self, initial_data): 
     for key in initial_data: 
      setattr(self, key, initial_data[key]) 

actualización

Como sugiere Brent Nash, puede que esto sea más flexible, permitiendo que los argumentos de palabras clave, así:

class Employee(object): 
    def __init__(self, *initial_data, **kwargs): 
     for dictionary in initial_data: 
      for key in dictionary: 
       setattr(self, key, dictionary[key]) 
     for key in kwargs: 
      setattr(self, key, kwargs[key]) 

Entonces se le puede llamar así:

e = Employee({"name": "abc", "age": 32}) 

o como esto:

e = Employee(name="abc", age=32) 

o incluso así:

employee_template = {"role": "minion"} 
e = Employee(employee_template, name="abc", age=32) 
+3

Si pasa los datos iniciales como 'def __init __ (self, ** initial_data)' usted obtiene el beneficio adicional de tener un método init que también puede hacer argumentos clave (ej. "e = Empleado (nombre = 'Oscar')" o simplemente incluir en un diccionario (por ejemplo, "e = Empleado (** dict)"). –

+2

Ofrecer las API 'Employee (some_dict)' y 'Employee (** some_dict)' es incoherente. Cualquiera que sea mejor, debe ser provisto. –

+0

(También, quiere decir 'si initial_data no es None'; en una circunstancia bastante extraña, esto podría introducir código que no funciona como estaba previsto. Además, no puede usar una clave' 'initial_data'' ahora usando una de las API.) –

9

Puede acceder a los atributos de un objeto con __dict__, y llamar al método de actualización en él:

>>> class Employee(object): 
...  def __init__(self, _dict): 
...   self.__dict__.update(_dict) 
... 


>>> dict = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 } 

>>> e = Employee(dict) 

>>> e.name 
'Oscar' 

>>> e.age 
32 
+3

'__dict__' es un artefacto de implementación y no debe utilizarse.Además, esto ignora la existencia de descriptores en la clase. –

+1

@Ignacio ¿Qué quiere decir con "artefacto de implementación"? Lo que no deberíamos saber? ¿O que puede no estar presente en diferentes plataformas? (por ejemplo, Python en Windows vs. Python en Linux) ¿Cuál sería una respuesta aceptable? – OscarRyz

+0

I * iba a decir que no hay garantía de que exista en una implementación de Python determinada, pero se hace referencia a ella varias veces en la langref. La respuesta de Ian cubre la otra preocupación, es decir, pasarla a un descriptor en lugar de darle una paliza. –

5

creo que la respuesta utilizando settattr son el camino a seguir si realmente se necesita para apoyar dict.

Pero si Employee objeto es sólo una estructura que se puede acceder con la sintaxis con punto (.name) en lugar de la sintaxis dict (['name']), puede utilizar namedtuple así:

from collections import namedtuple 

Employee = namedtuple('Employee', 'name age') 
e = Employee('noname01', 6) 
print e 
#>> Employee(name='noname01', age=6) 

# create Employee from dictionary 
d = {'name': 'noname02', 'age': 7} 
e = Employee(**d) 
print e 
#>> Employee(name='noname02', age=7) 
print e._asdict() 
#>> {'age': 7, 'name': 'noname02'} 

Usted tiene _asdict() método para acceda a todas las propiedades como diccionario, pero no puede agregar atributos adicionales más adelante, solo durante la construcción.

+0

Lo mejor es hacer referencia a los atributos como "atributos" en lugar de " propiedades ", ya que este último término se puede confundir con lo que obtienes cuando usas' propiedad'. –

+0

buen punto - fijo – van

29

Establecer los atributos de esta manera es casi seguro que no es la mejor manera de resolver un problema. O bien:

  1. Usted sabe lo que todos los campos deben ser antes de tiempo. En ese caso, puede establecer todos los atributos de forma explícita. Esto se vería

    class Employee(object): 
        def __init__(self, name, last_name, age): 
         self.name = name 
         self.last_name = last_name 
         self.age = age 
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 } 
    e = Employee(**d) 
    
    print e.name # Oscar 
    print e.age + 10 # 42 
    

    o

  2. Usted no sabe lo que todos los campos deben estar antes de tiempo. En este caso, debe almacenar los datos como un dict en lugar de contaminar un espacio de nombres de objetos. Los atributos son para acceso estático.Este caso se vería

    class Employee(object): 
        def __init__(self, data): 
         self.data = data 
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 } 
    e = Employee(d) 
    
    print e.data['name'] # Oscar 
    print e.data['age'] + 10 # 42 
    

Otra solución que es básicamente equivalente a 1 caso es utilizar un collections.namedtuple. Vea la respuesta de van sobre cómo implementar eso.

6

¿Por qué no usar simplemente los nombres de atributos como claves para un diccionario?

class StructMyDict(dict): 

    def __getattr__(self, name): 
     try: 
      return self[name] 
     except KeyError as e: 
      raise AttributeError(e) 

    def __setattr__(self, name, value): 
     self[name] = value 

Puede inicializar con argumentos con nombre, una lista de tuplas, o un diccionario, o asignaciones de atributos individuales, por ejemplo:

nautical = StructMyDict(left = "Port", right = "Starboard") # named args 

nautical2 = StructMyDict({"left":"Port","right":"Starboard"}) # dictionary 

nautical3 = StructMyDict([("left","Port"),("right","Starboard")]) # tuples list 

nautical4 = StructMyDict() # fields TBD 
nautical4.left = "Port" 
nautical4.right = "Starboard" 

for x in [nautical, nautical2, nautical3, nautical4]: 
    print "%s <--> %s" % (x.left,x.right) 

Como alternativa, en lugar de elevar el error de atributo, puede volver Ninguno para valores desconocidos (Un truco utilizado en la clase de almacenamiento web2py)

1

similar al uso de un diccionario, es posible que utilices kwargs así:

class Person: 
    def __init__(self, **kwargs): 
     self.properties = kwargs 

    def get_property(self, key): 
     return self.properties.get(key, None) 

    def main(): 
     timmy = Person(color = 'red') 
     print(timmy.get_property('color')) #prints 'red' 
4

decir, por ejemplo

class A(): 
    def __init__(self): 
     self.x=7 
     self.y=8 
     self.z="name" 

si desea establecer los atributos a la vez

d = {'x':100,'y':300,'z':"blah"} 
a = A() 
a.__dict__.update(d) 
+1

puede usar claves/valores para mayor comodidad: 'a .__ dict __. Actualización (x = 100, y = 300, z =" blah ")' – simno

Cuestiones relacionadas