Установить атрибуты из словаря в Python

102

Можно ли создать объект из словаря в Python таким образом, чтобы каждый ключ был атрибутом этого объекта?

Что-то вроде этого:

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

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

Я думаю, что это было бы в значительной степени обратным этому вопросу: словарь Python из полей объекта

OscarRyz
источник

Ответы:

172

Конечно, примерно так:

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

Обновить

Как предлагает Брент Нэш, вы можете сделать это более гибким, разрешив также аргументы ключевых слов:

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

Тогда вы можете назвать это так:

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

или вот так:

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

или даже так:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)
Ян Клелланд
источник
4
Если вы передаете начальные данные, def __init__(self,**initial_data)вы получаете дополнительное преимущество наличия метода инициализации, который также может выполнять аргументы ключевого слова (например, «e = Employee (name = 'Oscar')» или просто использовать словарь (например, «e = Employee ( ** dict) ").
Брент пишет код
2
Предложение API Employee(some_dict)и Employee(**some_dict)API несовместимо. Следует поставить то, что лучше.
Майк Грэм
4
Если установить значение по умолчанию вашего ARG, чтобы ()вместо того None, вы можете сделать это следующим образом: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Мэтт Андерсон
4
Я знаю, что это старый вопрос, но я просто хочу добавить, что это можно сделать в две строки с пониманием списка, например:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ
2
Я хотел бы знать, почему это не встроено в Python каким-либо простым способом. Я использую это, чтобы справиться с метанием API-интерфейса Python GeoIP2 AddressNotFoundError(чтобы в этом случае возвращать исправленные данные, а не взрывать) - мне показалось безумием, что что-то такое простое в PHP ( (object) ['x' => 'y']) требует так много беспорядка в Python
Someguy123
47

Такая установка атрибутов почти наверняка не лучший способ решить проблему. Либо:

  1. Вы знаете, какие поля должны быть заранее. В этом случае вы можете явно установить все атрибуты. Это будет выглядеть как

    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 

    или

  2. Вы не знаете, какие поля должны быть заранее. В этом случае вы должны хранить данные как dict вместо того, чтобы загрязнять пространство имен объектов. Атрибуты предназначены для статического доступа. Этот случай будет выглядеть как

    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 

Другое решение, которое в основном эквивалентно случаю 1, - использовать файл collections.namedtuple. См. Ответ Ван, чтобы узнать, как это реализовать.

Майк Грэм
источник
А что, если сценарий находится где-то между двумя вашими крайностями? Это именно тот вариант использования, и в настоящее время, AFAICT, нет способа сделать это СУХОЙ и питоническим способом.
DylanYoung,
15

Вы можете получить доступ к атрибутам объекта с помощью __dict__и вызвать для него метод обновления:

>>> 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__является артефактом реализации и не должен использоваться. Кроме того, это игнорирует наличие дескрипторов в классе.
Игнасио Васкес-Абрамс,
1
@Ignacio, что вы имеете в виду под "артефактом реализации"? Чего не следует об этом знать? Или что его может не быть на разных платформах? (например, Python в Windows против Python в Linux) Каков будет приемлемый ответ?
OscarRyz
12
__dict__является документированной частью языка, а не артефактом реализации.
Дэйв Кирби
3
Использование setattrпредпочтительнее __dict__прямого доступа . Вы должны помнить о множестве вещей, которые могут привести к __dict__тому, что вы не будете присутствовать или не будете делать то, что вы хотите, когда вы используете __dict__, но setattrпрактически идентичны тому, что вы делаете на самом деле foo.bar = baz.
Майк Грэм
1
@DaveKirby: Похоже, __dict__не рекомендуется использовать в целом : docs.python.org/tutorial/classes.html#id2
equaeghe
11

Почему бы просто не использовать имена атрибутов как ключи к словарю?

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

Вы можете инициализировать именованные аргументы, список кортежей, словарь или отдельные атрибуты, например:

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)

В качестве альтернативы, вместо того, чтобы вызывать ошибку атрибута, вы можете вернуть None для неизвестных значений. (Уловка, используемая в классе хранилища web2py)

RufusVS
источник
7

Я думаю, что использование ответа settattr- лучший вариант, если вам действительно нужна поддержка dict.

Но если Employeeобъект - это просто структура, к которой вы можете получить доступ с помощью точечного синтаксиса ( .name) вместо синтаксиса dict ( ['name']), вы можете использовать namedtuple следующим образом:

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

У вас есть _asdict()способ доступа ко всем свойствам в виде словаря, но вы не можете добавить дополнительные атрибуты позже, только во время построения.

фургон
источник
6

скажем например

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

если вы хотите установить атрибуты сразу

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)
Ислам Эззат
источник
1
для удобства можно использовать a.__dict__.update(x=100, y=300, z="blah")
пары "
1

аналогично использованию dict, вы можете просто использовать kwargs следующим образом:

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'
Алекс Спенсер
источник