Встановити атрибути зі словника в python


102

Чи можливо створити об’єкт зі словника в python таким чином, щоб кожен ключ був атрибутом цього об’єкта?

Щось на зразок цього:

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

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

Я думаю, що це було б досить зворотним питанням: словник Python з полів об'єкта

Відповіді:


172

Звичайно, щось подібне:

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

Оновлення

Як пропонує Brent Nash, ви можете зробити це більш гнучким, дозволяючи також аргументи ключових слів:

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)отримуючи додаткову перевагу від використання методу init, який також може робити аргументи ключових слів (наприклад, "e = Співробітник (ім'я =" Оскар ")" або просто взяти в словник (наприклад, "e = Співробітник ( ** dict) ").
Brent пише Code

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

46

Встановлення атрибутів таким чином майже не є найкращим способом вирішення проблеми. Або:

  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. Ви не знаєте, які всі поля повинні бути випереджені. У цьому випадку слід зберігати дані як дикт, а не забруднювати простір імен об'єктів. Атрибути призначені для статичного доступу. Цей вигляд виглядав би так

    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, - використання a 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)


7

Я думаю, що відповідь на settattrце - шлях, якщо вам справді потрібно підтримати dict.

Але якщо Employeeоб’єкт - це лише структура, до якої ви можете отримати доступ із синтаксисом крапки ( .name) замість синтаксису диктату ( ['name']), ви можете використовувати найменування типуtuple, як це:

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")
simno

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'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.