是否可以通过python中的字典创建对象,使得每个键都是该对象的属性?
像这样:
d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }
e = Employee(d)
print e.name # Oscar
print e.age + 10 # 42
我认为这与该问题几乎完全相反:来自对象字段的Python字典
当然,是这样的:
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)
如果您以def __init__(self,**initial_data)的形式传递初始数据,您将获得一个额外的好处,即拥有一个也可以执行关键字参数的init方法(例如" e = Employee(name = Oscar)")或仅接受字典(例如" e = Employee (**字典)")。
同时提供Employee(some_dict)和Employee(**some_dict) API是不一致的。应该提供哪个更好。
(此外,您的意思是if initial_data is not None;在非常奇怪的情况下,这可能会引入无法按预期工作的代码。此外,您现在不能使用其中一个API来使用键initial_data。)
如果将args默认设置为()而不是None,则可以这样做:def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs)。
@Matt Anderson,这段代码对我来说似乎有点聪明。似乎更易读的解决方案是Ian使用的逻辑,或者更好的是选择一个一致的API。
@迈克·格雷厄姆:双方都同意。虽然提供这两个接口正是python dict构造函数所做的。香港专业教育学院更新了该示例以采用任意数量的映射对象,而无需保留任何名称(可能,除了自身)
@伊恩·克莱尔兰德(Ian Clelland),如果你问我的话,那就太丢脸了! ;)好点,但是。
对于键,initial_data.items()中的值:看起来更pythonic
我知道这是一个老问题,但我只想补充一点,可以用列表理解功能在两行中完成它,例如:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
我很想知道为什么这不是以某种简单的方式内置到python中的。我用它来处理python GeoIP2 API抛出AddressNotFoundError的问题(在这种情况下,返回正确的数据,而不是炸毁)-我发疯了,在PHP((object) [x => y])中这么容易的事情需要花费很多精力Python
我试图找到一种方法来做到这一点,但是我很好奇如何将这些属性私有化。即使有巧妙的方法将下划线插入键名,您将如何为这些未知属性添加修饰符?
以这种方式设置属性几乎肯定不是解决问题的最佳方法。或者:
您知道所有字段都应该提前。在这种情况下,您可以显式设置所有属性。这看起来像
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
要么
您不知道所有字段都应该提前。在这种情况下,您应该将数据存储为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的答案。
您可以使用__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
__dict__是一个实现工件,不应使用。同样,这会忽略类中描述符的存在。
@Ignacio对"实现工件"是什么意思?我们不应该意识到的是什么?还是它可能不存在于不同的平台中? (例如Windows中的Python与Linux中的Python)可以接受的答案是什么?
我要说的是,不能保证给定的Python实现中存在它,但是在langref中多次引用了它。 Ians的答案涵盖了另一个问题,即将其传递给描述符而不是破坏它。
__dict__是该语言的文档部分,不是实现工件。
@Dave是这个吗? docs.python.org/library/stdtypes.html#special-attributes
如果您知道该类没有任何描述符,则__dict__.update是完全有效的-它是在标准库中的很多地方完成的。
使用setattr比直接访问__dict__更可取。您必须牢记很多事情,这些事情可能导致__dict__不在或使用__dict__时未做您想要的事情,但是setattr实际上与实际执行foo.bar = baz相同。
@DaveKirby:似乎建议不要使用__dict__:docs.python.org/tutorial/classes.html#id2
为什么不只使用属性名称作为字典的键?
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存储类中使用的一个技巧)
例如说
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)
为了方便起见,您可以使用键/值:a.__dict__.update(x=100, y=300, z="blah")
我认为,如果确实需要支持dict,使用settattr的答案是可行的方法。
但是,如果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()方法可以以字典的形式访问所有属性,但是以后只能在构造过程中才能添加其他属性。
与使用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'