您遇到的基本问题是您的字典不知道自己的名称。这是正常的;通常,可以将任意数量的名称绑定到 Python 对象,并且没有任何一个名称以任何方式优于其他任何名称。换句话说, in a = {}; b = a
、a
和b
都是同一个字典的名称,并且a
不是字典的“真实”名称,因为它是首先分配的。事实上,字典甚至无法知道变量左侧的名称是什么。
因此,一种替代方法是让字典包含其自己的名称作为键。例如:
fruit = {"_name": "fruit"}
fruit["red"] = "cherry"
food[fruit["_name"]] = fruit
好吧,这并没有多大帮助,不是吗?它在某种程度上做到了,因为fruit
现在不再是food
字典附件中的字符串,所以如果你输入错误,至少 Python 会给你一个错误消息。但实际上,您输入“fruit”的次数比以前更多:您现在必须在创建词典时输入它,以及将它附加到另一个词典时输入它。一般来说,打字要多一些。
此外,将名称作为字典中的项目有点不方便。当您遍历字典时,您必须编写代码来跳过它。
你可以编写一个函数来为你做附件:
def attach(main, other):
main[other["_name"]] = other
然后,当您将子词典附加到主词典时,您不必重复自己:
fruit = {"_name": "fruit"}
fruit["red"] = "cherry"
attach(food, fruit)
当然,现在您实际上可以创建一个知道自己名称的字典子类,并且可以附加一个命名的子字典。作为奖励,我们可以将名称作为字典的属性,而不是将其存储在字典中,这将使实际字典更整洁。
class NamedDict(dict):
def __init__(self, name="", seq=(), **kwargs):
dict.__init__(self, seq, **kwargs)
self.__name__ = name
def attach(self, other):
self[other.__name__] = other
food = NamedDict("food")
fruit = NamedDict("fruit")
fruit["red"] = "cherry"
food.attach(fruit)
但是我们仍然有一个重复,当NamedDict
最初定义时:food = NamedDict("food")
例如。我们如何免除它?
这是可能的,虽然很笨拙,而且可能不值得麻烦。Python 有两种具有“固有”名称的对象:类和函数。换句话说:
class Foo:
pass
上面不仅创建了一个Foo
在当前命名空间中命名的变量,类名也方便的存储在类的__name__
属性中。(函数做类似的事情。)通过滥用类和元类,我们可以利用底层机制来完全避免重复自己——有一个小缺点,就是必须把我们的字典当作类来编写!
class NamedDict(dict):
class __metaclass__(type):
def __new__(meta, name, bases, attrs):
if "NamedDict" not in globals(): # we're defining the base class
return type.__new__(meta, name, bases, attrs)
else:
attrs.pop("__module__", None) # Python adds this; do not want!
return meta.NamedDict(name, **attrs)
class NamedDict(dict):
def __init__(self, name, seq=(), **kwargs):
dict.__init__(self, seq, **kwargs)
self.__name__ = name
def attach(self, other):
self[other.__name__] = other
__call__ = NamedDict
现在,我们不再以通常的方式定义我们的字典,而是将它们声明为NamedDict
. 由于元类,子类化外部NamedDict
类实际上创建了内部类的实例NamedDict
(与以前相同)。我们定义的子类的属性(如果有的话)成为字典中的项目,例如dict()
.
class food(NamedDict): pass
class fruit(NamedDict): red = "cherry"
# or, defining each item separately:
class fruit(NamedDict): pass
fruit["red"] = "cherry"
food.attach(fruit)
作为奖励,您仍然可以NamedDict
通过将其实例化为类来定义“常规”方式:
fruit = NamedDict("fruit", red="cherry")
但是请注意:“真正是字典的类”对于 Python 来说是一个非常不标准的习语,我建议你不要真正这样做,因为其他程序员根本不会发现它很清楚。尽管如此,这仍然是在 Python 中完成的方式。