Definir atributos do dicionário em python

Respostas:

172

Claro, algo assim:

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

Atualizar

Como sugere Brent Nash, você pode tornar isso mais flexível permitindo também argumentos de palavras-chave:

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

Então você pode chamá-lo assim:

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

ou assim:

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

ou mesmo assim:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)
Ian Clelland
fonte
4
Se você passar os dados iniciais conforme def __init__(self,**initial_data)obtém o benefício adicional de ter um método init que também pode fazer argumentos de palavras-chave (por exemplo, "e = Employee (name = 'Oscar')" ou apenas obter um dicionário (por exemplo, "e = Employee ( ** dict) ").
Brent escreve o código de
2
Oferecer APIs Employee(some_dict)e Employee(**some_dict)APIs é inconsistente. O que for melhor deve ser fornecido.
Mike Graham
4
Se você definir padrão do seu arg ao ()invés de None, você poderia fazê-lo assim: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson
4
Sei que é uma pergunta antiga, mas só quero acrescentar que pode ser feito em duas linhas com compreensão de lista, por exemplo:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ
2
Eu adoraria saber por que isso não está integrado ao python de uma maneira simples. Estou usando isso para lidar com os lançamentos da API Python GeoIP2 AddressNotFoundError(para retornar dados corrigidos nesse caso, em vez de explodir) - achei uma loucura que algo tão fácil em PHP ( (object) ['x' => 'y']) exigisse tanto lixo em Python
Someguy123
46

Definir os atributos dessa maneira quase certamente não é a melhor maneira de resolver um problema. Ou:

  1. Você sabe o que todos os campos devem ser antes do tempo. Nesse caso, você pode definir todos os atributos explicitamente. Isso pareceria

    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 
    

    ou

  2. Você não sabe o que todos os campos devem estar antes do tempo. Nesse caso, você deve armazenar os dados como um dicionário em vez de poluir o namespace de um objeto. Os atributos são para acesso estático. Este caso pareceria

    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 
    

Outra solução que é basicamente equivalente ao caso 1 é usar a collections.namedtuple. Veja a resposta da van para saber como implementar isso.

Mike Graham
fonte
E se o cenário estiver em algum lugar entre seus dois extremos? Esse é exatamente o caso de uso para isso, e atualmente AFAICT não há como fazer isso de uma forma SECA e pítônica.
DylanYoung
15

Você pode acessar os atributos de um objeto com __dict__e chamar o método de atualização nele:

>>> 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
fonte
3
__dict__é um artefato de implementação e não deve ser usado. Além disso, isso ignora a existência de descritores na classe.
Ignacio Vazquez-Abrams
1
@Ignacio o que você quer dizer com "artefato de implementação"? O que não deveríamos não estar cientes disso? Ou que pode não estar presente em diferentes plataformas? (por exemplo, Python no Windows vs. Python no Linux) Qual seria uma resposta aceitável?
OscarRyz
12
__dict__é uma parte documentada da linguagem, não um artefato de implementação.
Dave Kirby
3
Usar setattré preferível a acessar __dict__diretamente. Você tem que ter em mente muitas coisas que podem __dict__fazer com que você não esteja lá ou não faça o que você quer ao usar __dict__, mas setattré virtualmente idêntico a realmente fazer foo.bar = baz.
Mike Graham
1
@DaveKirby: Parece que o uso geral de __dict__é desaconselhado: docs.python.org/tutorial/classes.html#id2
equaeghe
11

Por que não usar nomes de atributos apenas como chaves para um dicionário?

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

Você pode inicializar com argumentos nomeados, uma lista de tuplas ou um dicionário ou atribuições de atributos individuais, por exemplo:

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, em vez de aumentar o erro de atributo, você pode retornar Nenhum para valores desconhecidos. (Um truque usado na classe de armazenamento web2py)

RufusVS
fonte
7

Acho que usar essa resposta settattré o caminho a percorrer se você realmente precisa de suporte dict.

Mas se Employeeobjeto for apenas uma estrutura que você pode acessar com sintaxe de ponto ( .name) em vez de sintaxe de dict ( ['name']), você pode usar namedtuple como este:

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'}

Você tem um _asdict()método para acessar todas as propriedades como dicionário, mas não pode adicionar atributos adicionais posteriormente, apenas durante a construção.

furgão
fonte
6

diga por exemplo

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

se você quiser definir os atributos de uma vez

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)
islam ezzat
fonte
1
você pode usar chaves / valores por conveniência:a.__dict__.update(x=100, y=300, z="blah")
simno
1

semelhante a usar um dicionário, você pode apenas usar kwargs assim:

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
fonte