Establecer atributos del diccionario en Python

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)
Ian Clelland
fuente
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.

Mike Graham
fuente
¿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
Dave Kirby
fuente
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)

RufusVS
fuente
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.

camioneta
fuente
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)
islam ezzat
fuente
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'
Alex Spencer
fuente