我认为最好的方法是不这样做 -- '*' 是字典中完全有效的键,因此 myiter['*'] 具有完全明确定义的含义和有用性,和颠覆肯定会导致问题。如何对 非 字符串的键(包括列表而非映射的元素中的唯一整数“键”(索引))进行“glob”也是一个相当大的设计问题。
如果您仍然必须这样做,我建议通过子类化abstract base class collections.MutableMapping 来完全控制,并实现所需的方法(__len__, __iter__、__getitem__、__setitem__、__delitem__,并且,为了获得更好的性能,还覆盖了诸如 __contains__ 之类的其他函数,ABC 确实在其他函数的基础上实施,但速度较慢)包含dict。相反,根据其他建议,子类化 dict 将需要您覆盖大量方法,以避免在您覆盖的方法和不覆盖的方法中使用“包含通配符的键”之间的不一致行为。
无论你是collections.MutableMapping 还是dict 的子类,要创建你的Globbable 类,你都必须做出一个核心设计决策:yourthing[somekey] 是什么return 当yourthing 是Globbable?
当somekey 是一个包含通配符的字符串时,它可能必须返回不同的类型,而不是其他任何类型。在后一种情况下,人们会想象,那个入口处的实际情况是什么;但在前者中,它不能只返回另一个Globbable——否则,yourthing[somekey] = 'bah' 在一般情况下会做什么?对于您的单个“流畅语法”示例,您希望它在 yourthing 的每个 items 中设置一个 somekey 条目(与行为的 HUGE 语义中断宇宙中所有其他映射;-) -- 但是,您如何在yourthing 本身中设置条目?!
让我们看看 Python 的禅宗对这种你渴望的“精巧语法”有什么要说的……:
>>> import this
...
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
考虑一下放弃“流畅语法”(以及它必然暗示的所有 巨大 语义头痛)的替代方案,以支持清晰和简单(此处使用 Python 2.7 及更好的语法) ,仅用于 dict 理解——如果您坚持使用 2.6 或更早版本,请使用显式的 dict(...) 调用),例如:
def match(s, pat):
try: return fnmatch.fnmatch(s, pat)
except TypeError: return False
def sel(ds, pat):
return [d[k] for d in ds for k in d if match(k, pat)]
def set(ds, k, v):
for d in ds: d[k] = v
所以你的任务可能会变成
set(sel(sel([myiter], '*')), '*.txt'), 'name', 'Woot')
(如果全部选择 '*' 是多余的,我只是省略了它)。这是否如此可怕到值得我上面提到的问题的泥潭来代替使用
myiter['*']['*.txt']['name'] = 'Woot'
...?到目前为止,最清晰、性能最好的方法当然仍然是更简单的方法
def match(k, v, pat):
try:
if fnmatch.fnmatch(k, pat):
return isinstance(v, dict)
except TypeError:
return False
for k, v in myiter.items():
if match(k, v, '*'):
for sk, sv in v.items():
if match(sk, sv, '*.txt'):
sv['name'] = 'Woot'
但如果你绝对渴望简洁和紧凑,鄙视 Python 公案“稀疏胜于密集”的禅宗,你可以至少在没有我提到的各种噩梦的情况下获得它们,以实现你的理想的“语法糖”。