Establecer atributos del diccionario en Python


102

¿Es posible crear un objeto a partir de un diccionario en Python de tal manera que cada clave sea un atributo de ese objeto?

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 prácticamente lo contrario de esta pregunta: diccionario de Python de los campos de un objeto

Respuestas:


172

Claro, algo como esto:

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

Actualizar

Como sugiere Brent Nash, puede hacer esto más flexible permitiendo también argumentos de palabras clave:

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 puedes llamarlo así:

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

o así:

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

o incluso así:

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

4
Si pasa los datos iniciales def __init__(self,**initial_data), obtiene el beneficio adicional de tener un método de inicio que también puede utilizar argumentos de palabras clave (por ejemplo, "e = Empleado (nombre = 'Oscar')" o simplemente tomar un diccionario (por ejemplo, "e = Empleado ( ** dict) ").
Brent escribe código

2
Ofrecer tanto la API Employee(some_dict)como la Employee(**some_dict)API no es coherente. Se debe suministrar el que sea mejor.
Mike Graham

4
Si se establece por defecto de su arg al ()lugar de None, usted podría hacerlo de esta manera: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson

4
Sé que es una pregunta antigua, pero solo quiero agregar que se puede hacer en dos líneas con comprensión de lista, por ejemplo:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ

2
Me encantaría saber por qué esto no está integrado en Python de una manera simple. Estoy usando esto para lidiar con los lanzamientos de la API Python GeoIP2 AddressNotFoundError(para devolver datos corregidos en ese caso, en lugar de explotar) - Me pareció una locura que algo tan fácil en PHP ( (object) ['x' => 'y']) requiriera tanto cruft en Python
Someguy123

46

Definir atributos de esta manera casi con seguridad no es la mejor manera de resolver un problema. Ya sea:

  1. Sabes cuáles deberían ser todos los campos de antemano. En ese caso, puede establecer todos los atributos explícitamente. Esto se vería como

    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. No sabe cuáles deberían ser todos los campos antes de tiempo. En este caso, debe almacenar los datos como un dictado en lugar de contaminar un espacio de nombres de objetos. Los atributos son para acceso estático. Este caso se vería así

    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 al caso 1 es usar un collections.namedtuple . Vea la respuesta de van sobre cómo implementar eso.


¿Y si el escenario se encuentra en algún lugar entre sus dos extremos? Ese es precisamente el caso de uso para esto, y actualmente AFAICT no hay forma de hacer esto de una manera SECA y pitónica.
DylanYoung

15

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.
Ignacio Vazquez-Abrams

1
@Ignacio, ¿a qué te refieres con "artefacto de implementación"? ¿Qué no deberíamos tener en cuenta? ¿O que puede que no esté presente en distintas plataformas? (por ejemplo, Python en Windows frente a Python en Linux) ¿Cuál sería una respuesta aceptable?
OscarRyz

12
__dict__es una parte documentada del lenguaje, no un artefacto de implementación.
Dave Kirby

3
Usando setattres preferible acceder __dict__directamente. Tienes que tener en cuenta muchas cosas que podrían llevar a __dict__no estar ahí o no hacer lo que quieres cuando lo uses __dict__, pero setattres prácticamente idéntico a hacerlo realmente foo.bar = baz.
Mike Graham

1
@DaveKirby: Parece que __dict__se desaconseja el uso general de : docs.python.org/tutorial/classes.html#id2
equaeghe

11

¿Por qué no usar 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)

Alternativamente, en lugar de generar el error de atributo, puede devolver Ninguno para valores desconocidos. (Un truco utilizado en la clase de almacenamiento web2py)


7

Creo que el uso de respuestas settattres el camino a seguir si realmente necesitas apoyo dict.

Pero si el Employeeobjeto es solo una estructura a la que puede acceder con la sintaxis de puntos ( .name) en lugar de la sintaxis dict ( ['name']), puede usar 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'}

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


6

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 utilizar claves / valores para su comodidad:a.__dict__.update(x=100, y=300, z="blah")
simno

1

similar a usar un dict, podría usar 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'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.