Izz*_*sin 273 python syntax dictionary
我发现访问dict密钥更加方便,obj.foo
而不是obj['foo']
,所以我写了这个代码片段:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
Run Code Online (Sandbox Code Playgroud)
但是,我认为必须有一些原因,Python不提供开箱即用的功能.以这种方式访问dict密钥有什么警告和陷阱?
Kim*_*ais 283
最好的方法是:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
Run Code Online (Sandbox Code Playgroud)
一些优点:
.keys()
工作得很好)AttributeError
而不是KeyError
缺点:
.keys()
将无法正常工作E1123(unexpected-keyword-arg)
和去香蕉E1103(maybe-no-member)
__dict__
.__dict__
需要"只是一个简单的字典",所以我们可以将任何子类分配给dict()
内部字典.AttrDict()
我们实例化的实例(就像我们一样__init__
).super()
的__init__()
方法,我们确信它(已经)的行为完全像一本字典,因为该函数调用的所有字典实例代码.如"cons"列表中所述,这将存储键的命名空间(可能来自任意和/或不受信任的数据!)与内置dict方法属性的命名空间相结合.例如:
d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items(): # TypeError: 'list' object is not callable
print "Never reached!"
Run Code Online (Sandbox Code Playgroud)
Her*_*ery 125
如果使用数组表示法,则可以将所有合法字符串字符作为键的一部分.例如,obj['!#$%^&*()_']
sla*_*acy 78
从另一个问题来看,有一个很好的实现示例可以简化现有代码.怎么样:
class AttributeDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
Run Code Online (Sandbox Code Playgroud)
更简洁,不留任何余地额外的克鲁夫特进入你__getattr__
,并__setattr__
在未来的功能.
Dou*_* R. 71
我怀疑它与Python的禅宗有关:"应该有一个 - 最好只有一个 - 显而易见的方法." 这将创建两种从字典访问值的明显方法:obj['key']
和obj.key
.
这些可能包括代码中可能缺乏清晰度和混淆.也就是说,如果您暂时不再使用它,以下内容可能会让以后维护您的代码的其他人感到困惑,甚至可能会让您感到困惑.再次,来自Zen:"可读性很重要!"
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Run Code Online (Sandbox Code Playgroud)
如果d
被实例化或被 KEY
定义或被 d[KEY]
分配远离d.spam
正在使用的地方,则很容易导致对正在做什么的混淆,因为这不是常用的习语.我知道它有可能让我困惑.
另外,如果你改变KEY
如下的值(但是错过了改变d.spam
),你现在得到:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
Run Code Online (Sandbox Code Playgroud)
IMO,不值得努力.
正如其他人所说,你可以使用任何可散列对象(不仅仅是一个字符串)作为dict键.例如,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
Run Code Online (Sandbox Code Playgroud)
是合法的,但是
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
Run Code Online (Sandbox Code Playgroud)
不是.这使您可以访问字典键的所有可打印字符或其他可清除对象,这是访问对象属性时没有的.这使得像缓存对象元类一样神奇,就像Python Cookbook(第9章)中的食谱一样.
我更喜欢的美学spam.eggs
过spam['eggs']
(我认为它看起来更清洁),我真的开始渴望这个功能时,我遇到了namedtuple
.但是能够做到以下几点的便利胜过它.
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
Run Code Online (Sandbox Code Playgroud)
这是一个简单的例子,但我经常发现自己在不同情况下使用dicts而不是使用obj.key
符号(即,当我需要从XML文件中读取prefs时).在其他情况下,我试图实例化一个动态类并为了美观原因对其进行一些属性,我继续使用dict来保持一致性以增强可读性.
我确信OP早已解决了这个让他满意的问题,但如果他仍然想要这个功能,那么我建议他从pypi下载一个提供它的软件包:
dict
,所以你有所有的功能.但是,为了提高他的代码的可读性,我强烈建议他不要混合他的符号样式.如果他更喜欢这种表示法,那么他应该简单地实例化一个动态对象,将它所需的属性添加到它,然后每天调用它:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
Run Code Online (Sandbox Code Playgroud)
在评论(下面)中,Elmo问道:
如果你想更深入一下怎么办?(指类型(...))
虽然我从未使用过这个用例(同样,我倾向于使用嵌套dict
,为了保持一致性),以下代码可以工作:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
Run Code Online (Sandbox Code Playgroud)
The*_*uck 18
如果你想要一个方法的密钥怎么办,比如__eq__
或__getattr__
?
并且您将无法拥有一个不以字母开头的条目,因此将0343853
密钥用作密钥.
如果你不想使用字符串怎么办?
lin*_*urn 16
您可以从标准库中提取一个方便的容器类:
from argparse import Namespace
Run Code Online (Sandbox Code Playgroud)
避免必须复制代码位.没有标准的字典访问,但如果你真的想要它,很容易得到一个.argparse中的代码很简单,
class Namespace(_AttributeHolder):
"""Simple object for storing attributes.
Implements equality by attribute names and values, and provides a simple
string representation.
"""
def __init__(self, **kwargs):
for name in kwargs:
setattr(self, name, kwargs[name])
__hash__ = None
def __eq__(self, other):
return vars(self) == vars(other)
def __ne__(self, other):
return not (self == other)
def __contains__(self, key):
return key in self.__dict__
Run Code Online (Sandbox Code Playgroud)
Deu*_*ina 16
我发现自己想知道 python 生态系统中“dict keys as attr”的当前状态。正如几位评论者指出的那样,这可能不是您想从头开始制作自己的东西,因为有几个陷阱和脚步,其中一些非常微妙。另外,我不建议将其Namespace
用作基类,我一直在这条路上,它并不漂亮。
幸运的是,有几个开源包提供了这个功能,准备 pip 安装!不幸的是,有几个包。这是截至 2019 年 12 月的概要。
竞争者(最近提交给 master|#commits|#contribs|coverage%):
不再维护或维护不足:
我目前推荐吃或上瘾。他们拥有最多的提交、贡献者和发布,为每个人提供了一个健康的开源代码库。他们有最干净的 readme.md、100% 的覆盖率和漂亮的测试集。
我在这场比赛中没有狗(现在!),除了滚动我自己的 dict/attr 代码并浪费了大量时间,因为我不知道所有这些选项:)。我可能会在未来为上瘾/咀嚼做出贡献,因为我宁愿看到一个坚固的包装而不是一堆零散的包装。如果你喜欢他们,贡献!特别是,看起来 munch 可以使用 codecov 徽章,而上瘾者可以使用 python 版本徽章。
瘾君子的优点:
上瘾的缺点:
typing.Dict
如果你from addict import Dict
蒙克优点:
咀嚼缺点:
foo.a.b.c = 'bar'
,你必须设置foo.a
,然后foo.a.b
等等。许多个月前,当我使用文本编辑器编写 python 时,在只有我自己或其他开发人员的项目中,我喜欢 dict-attrs 的风格,只需声明foo.bar.spam = eggs
. 现在我在团队中工作,所有事情都使用 IDE,并且我已经远离这些类型的数据结构和动态类型,转而支持静态分析、功能技术和类型提示。我已经开始试验这种技术,用我自己设计的对象子类化 Pstruct:
class BasePstruct(dict):
def __getattr__(self, name):
if name in self.__slots__:
return self[name]
return self.__getattribute__(name)
def __setattr__(self, key, value):
if key in self.__slots__:
self[key] = value
return
if key in type(self).__dict__:
self[key] = value
return
raise AttributeError(
"type object '{}' has no attribute '{}'".format(type(self).__name__, key))
class FooPstruct(BasePstruct):
__slots__ = ['foo', 'bar']
Run Code Online (Sandbox Code Playgroud)
这为您提供了一个仍然表现得像 dict 的对象,但也允许您以更加严格的方式访问属性等键。这里的优势是我(或您的代码的不幸消费者)确切地知道哪些字段可以和不存在,并且 IDE 可以自动完成字段。子类化 vanilladict
意味着 json 序列化很容易。我认为这个想法的下一个演变将是一个定制的 protobuf 生成器,它发出这些接口,一个很好的连锁反应是你几乎免费地通过 gRPC 获得跨语言数据结构和 IPC。
如果您决定使用 attr-dicts,为了您自己(和您的队友)的理智,必须记录哪些字段是预期的。
随意编辑/更新这篇文章以使其保持最新状态!
Sen*_*ran 11
元组可以用dict键.你如何访问你的构造中的元组?
此外,namedtuple是一种方便的结构,可以通过属性访问提供值.
这并没有解决最初的问题,但对于像我这样在寻找提供此功能的库时最终来到这里的人来说应该很有用。
上瘾者这是一个很好的库:https : //github.com/mewwts/accine它解决了之前答案中提到的许多问题。
文档中的一个示例:
body = {
'query': {
'filtered': {
'query': {
'match': {'description': 'addictive'}
},
'filter': {
'term': {'created_by': 'Mats'}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
与瘾君子:
from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'
Run Code Online (Sandbox Code Playgroud)
只是为了给答案添加一些多样性,sci-kit learn将其实现为Bunch
:
class Bunch(dict):
""" Scikit Learn's container object
Dictionary-like object that exposes its keys as attributes.
>>> b = Bunch(a=1, b=2)
>>> b['b']
2
>>> b.b
2
>>> b.c = 6
>>> b['c']
6
"""
def __init__(self, **kwargs):
super(Bunch, self).__init__(kwargs)
def __setattr__(self, key, value):
self[key] = value
def __dir__(self):
return self.keys()
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setstate__(self, state):
pass
Run Code Online (Sandbox Code Playgroud)
您所需要的只是获取setattr
和getattr
方法 -getattr
检查 dict 键并继续检查实际属性。这setstaet
是对酸洗/解酸“束”的修复 - 如果有兴趣检查https://github.com/scikit-learn/scikit-learn/issues/6196
小智 7
由于以下原因对现有选项不满意后,我开发了MetaDict。它的行为与其他解决方案完全相同,dict
但支持点表示法和 IDE 自动完成,没有其他解决方案的缺点和潜在的命名空间冲突。所有功能和使用示例都可以在 GitHub 上找到(请参阅上面的链接)。
完全披露:我是MetaDict的作者。
我在尝试其他解决方案时遇到的缺点/限制:
dict
对象不会转换为支持属性样式键访问Dict
dataclass
)dict
嵌入list
或其他内置可迭代对象时,不会进行对象的递归转换list
对象转换到tuple
幕后items()
、update()
等可以被覆盖obj.items = [1, 2, 3]
dict
嵌入list
或其他内置可迭代对象时,不会进行对象的递归转换dict
接受所有可哈希对象作为键items()
、update()
等可以被覆盖obj.items = [1, 2, 3]
obj.pop('unknown_key', None)
引发AttributeError
怎么样Prodict,我写的用来统治它们的小Python类:)
另外,您将获得自动代码完成,递归对象实例化和自动类型转换!
您可以按照自己的要求做:
p = Prodict()
p.foo = 1
p.bar = "baz"
Run Code Online (Sandbox Code Playgroud)
class Country(Prodict):
name: str
population: int
turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871
Run Code Online (Sandbox Code Playgroud)
germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])
print(germany.population) # 82175700
print(type(germany.population)) # <class 'int'>
print(germany.flag_colors) # ['black', 'red', 'yellow']
print(type(germany.flag_colors)) # <class 'list'>
Run Code Online (Sandbox Code Playgroud)
from types import SimpleNamespace
obj = SimpleNamespace(color="blue", year=2050)
print(obj.color) #> "blue"
print(obj.year) #> 2050
Run Code Online (Sandbox Code Playgroud)
编辑/更新:从字典开始,更接近OP问题的答案:
from types import SimpleNamespace
params = {"color":"blue", "year":2020}
obj = SimpleNamespace(**params)
print(obj.color) #> "blue"
print(obj.year) #> 2050
Run Code Online (Sandbox Code Playgroud)
这是使用内置的不可变记录的简短示例collections.namedtuple
:
def record(name, d):
return namedtuple(name, d.keys())(**d)
Run Code Online (Sandbox Code Playgroud)
和用法示例:
rec = record('Model', {
'train_op': train_op,
'loss': loss,
})
print rec.loss(..)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
158730 次 |
最近记录: |