在python中从字典设置属性


Answers:


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 = Employee(name ='Oscar')”)或仅接受字典(例如“ e = Employee( **字典)“)。
布伦特编写代码

2
同时提供Employee(some_dict)Employee(**some_dict)API是不一致的。应该提供哪个更好。
Mike Graham

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中。我正在使用它来处理python GeoIP2 API抛出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. 您不知道所有字段都应该提前。在这种情况下,您应该将数据存储为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。有关如何实现的信息,请参见van的答案。


如果情况介于您的两个极端之间,该怎么办?这恰好是这种情况的用例,目前AFAICT尚无办法以DRY和pythonic的方式做到这一点。
DylanYoung

14

您可以使用访问对象的属性__dict__,并对其调用update方法:

>>> 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对“实现工件”是什么意思?我们不应该意识到的是什么?还是它可能不存在于不同的平台中?(例如Windows中的Python与Linux中的Python)可以接受的答案是什么?
OscarRyz 2010年

12
__dict__是语言的文档部分,而不是实现工件。
戴夫·柯比

3
使用setattr优于__dict__直接访问。您必须牢记很多事情,这些事情可能会导致__dict__您在使用时不在那里或不做自己想要做的事情__dict__,但setattr实际上与实际要做的事情相同foo.bar = baz
Mike Graham 2010年

1
@DaveKirby:__dict__建议不要使用:docs.python.org/tutorial/classes.html#id2
equaeghe 2014年

10

为什么不只使用属性名称作为字典的键?

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

但是,如果Employeeobject只是可以使用点语法(.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")
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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.