问题描述
我已经开始使用如下构造:
class DictObj(object):
def __init__(self):
self.d = {}
def __getattr__(self, m):
return self.d.get(m, None)
def __setattr__(self, m, v):
super.__setattr__(self, m, v)
更新:基于此线程,我已将DictObj实现修改为:
class dotdict(dict):
def __getattr__(self, attr):
return self.get(attr, None)
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
class AutoEnum(object):
def __init__(self):
self.counter = 0
self.d = {}
def __getattr__(self, c):
if c not in self.d:
self.d[c] = self.counter
self.counter += 1
return self.d[c]
其中DictObj是可以通过点表示法访问的字典:
d = DictObj()
d.something = 'one'
我发现它比d['something']
更美观。
请注意,访问未定义的键将返回None而不是引发异常,这也很好。
更新:Smashery很好,mhawke对其进行了扩展以提供更简单的解决方案。 我想知道使用dict而不是定义新字典是否有任何不良后果; 如果没有,我非常喜欢mhawke的解决方案。
AutoEnum是一个自动递增的Enum,用法如下:
CMD = AutoEnum()
cmds = {
"peek": CMD.PEEK,
"look": CMD.PEEK,
"help": CMD.HELP,
"poke": CMD.POKE,
"modify": CMD.POKE,
}
两者都对我来说运作良好,但是我对它们感到python昧。
这些实际上是不好的构造吗?
1楼
您的DictObj示例实际上很常见。 如果您要处理“类似于对象的事物”,则对象样式的点符号访问可能是一个胜利。 它们具有固定的属性名称,仅包含在Python标识符中有效的字符。 诸如数据库行或表单提交之类的东西可以有效地存储在此类对象中,从而使代码更具可读性,而无需过多的['item access']。
实现有点受限-您无法获得dict,len(),比较,'in',迭代或不错的reprs的出色构造函数语法。 您当然可以自己实现这些东西,但是在新式类世界中,只需将dict子类化即可免费获得它们:
class AttrDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
要获得默认为无的行为,只需将Python 2.5的collections.defaultdict类而不是dict子类化即可。
2楼
关于DictObj
,以下内容对您DictObj
吗?
空白类将允许您随意添加或替换容器对象中的内容。
class Container(object):
pass
>>> myContainer = Container()
>>> myContainer.spam = "in a can"
>>> myContainer.eggs = "in a shell"
如果您不希望在没有属性时不抛出AttributeError,那么您如何看待以下内容? 就个人而言,我更愿意使用dict来表示清楚,或使用try / except子句。
class QuietContainer(object):
def __getattr__(self, attribute):
try:
return object.__getattr__(self,attribute)
except AttributeError:
return None
>>> cont = QuietContainer()
>>> print cont.me
None
对?
3楼
这是DictObj类的简单版本:
class DictObj(object):
def __getattr__(self, attr):
return self.__dict__.get(attr)
>>> d = DictObj()
>>> d.something = 'one'
>>> print d.something
one
>>> print d.somethingelse
None
>>>
4楼
据我所知,Python类无论如何都使用字典来存储其属性(对程序员而言是隐藏的),因此在我看来,您在那里所做的工作实际上是在模仿Python类……使用python类。
5楼
这样做不是“错”的,如果您的字典在某个时候很有可能变成对象,那会更好,但是要当心要首先使用括号的原因:
- 点访问不能使用关键字作为键。
- 点访问必须在键中使用Python标识符有效的字符。
- 字典可以包含任何可哈希元素-不仅仅是字符串。
另外请记住,如果以后决定切换到对象,则始终可以像字典一样访问对象。
对于这种情况,我会默认使用“可读性计数”的口头禅:大概其他Python程序员将在阅读您的代码,并且他们可能不会期望到处都有字典/对象混合。 如果在特定情况下这是一个不错的设计决策,请使用它,但我不会在没有必要的情况下使用它。
6楼
和答案之间是对称的:
class dotdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
相同的接口,只是反过来实现了...
class container(object):
__getitem__ = object.__getattribute__
__setitem__ = object.__setattr__
__delitem__ = object.__delattr__
7楼
使用类似DictObj之类的一个主要缺点是您要么必须限制允许的键,要么就不能在DictObj上使用诸如.keys()
.values()
.items()
等方法。
8楼
不要忽视 。
它是Dictionary的子级,可以导入YAML或JSON,也可以将任何现有字典转换为Bunch,反之亦然。 一旦“打包”,字典将获得点符号,而不会丢失任何其他字典方法。
9楼
我个人喜欢点符号比字典字段好得多。 原因是它使自动完成工作更好。
10楼
如果达到您的目的还不错。 “实用胜过纯洁”。
我在其他地方(例如 )看到了这种方法,因此可以将其视为常见需求 (或期望)。
11楼
因为您要求不良的副作用:
缺点是,在类似eclipse + pyDev的可视编辑器中,您会在使用点表示法的行上看到许多未定义的变量错误。 Pydef将无法找到此类运行时“对象”定义。 而对于普通字典,它知道您只是在获取字典条目。
您将需要:1)忽略那些错误并使用红叉标记; 2)使用#@ UndefinedVariable逐行抑制这些警告,或3)完全禁用未定义??变量错误,导致您错过实际的未定义变量定义。
12楼
如果您正在寻找可处理嵌套字典的替代方法:
将字典递归地转换为所需类的实例
import json
from collections import namedtuple
class DictTransformer():
@classmethod
def constantize(self, d):
return self.transform(d, klass=namedtuple, klassname='namedtuple')
@classmethod
def transform(self, d, klass, klassname):
return self._from_json(self._to_json(d), klass=klass, klassname=klassname)
@classmethod
def _to_json(self, d, access_method='__dict__'):
return json.dumps(d, default=lambda o: getattr(o, access_method, str(o)))
@classmethod
def _from_json(self, jsonstr, klass, klassname):
return json.loads(jsonstr, object_hook=lambda d: klass(klassname, d.keys())(*d.values()))
例如:
constants = {
'A': {
'B': {
'C': 'D'
}
}
}
CONSTANTS = DictTransformer.transform(d, klass=namedtuple, klassname='namedtuple')
CONSTANTS.A.B.C == 'D'
优点:
- 处理嵌套的字典
- 可能会产生其他类
- namedtuple提供常量的不变性
缺点:
-
如果您的klass中未提供
.keys
和.values
则它们可能不会响应。(尽管有时您可以模仿._fields
和list(ABC)
)
有什么想法吗?
h / t到@hlzr以获取原始的课堂想法