0

好的,我有一本字典,叫做食物。食物有两个元素,它们本身就是字典,蔬菜和奶制品。蔬菜有两个元素根:萝卜,茎:芦笋。乳制品有奶酪:切达干酪和酸奶:草莓。我还有一本新的字典水果,有红色:樱桃和黄色:香蕉。

food['veg']['root'] == 'Turnip'
food['dairy']['cheese'] == 'Cheddar' 

等和

fruit['red'] == 'Cherry'

现在我想将“fruit”字典完整地添加到“food”字典中,这样我将拥有:

food['fruit']['red'] == 'Cherry'

我知道我可以做这样的事情:

food['fruit'] = fruit

但这似乎很笨拙。我想做类似的事情

food.Append(fruit)

但这并不能满足我的需要。

(编辑删除了变量名的首字母大写,因为这似乎引起了分心。)

4

6 回答 6

7

Food['Fruit'] = Fruit是正确的方式(除了大写的名字)

正如@kindall 在评论中明智地指出的那样,可以有多个名称引用同一个字典,这就是为什么不能构建一个将对象映射到其名称并将其用作food字典中的新键的函数的原因。

于 2012-06-05T19:34:54.347 回答
4

我知道你想做什么。您正试图避免在 python 中使用DRY 。可悲的是,python 和许多其他语言在这方面非常糟糕。

但主要问题是您将变量的名称与程序中的值混合在一起。这是许多编程语言中常见的错误。

如果你真的想这样做,你绑定到fruit=...变量的东西需要知道它的名字。

  • 你想说的是,在 python 中“将这个 dict 移植到另一个 dict 上”。
  • Python 要求你说它就像“将这个 dict 移植到另一个 dict 上,将它附加在"Fruit"
  • 解决这个问题的唯一方法是“水果”这个名字已经存在于某个地方。但它没有:它只存在于一个变量名中,而不是在你的程序中。

有两种解决方案:

  • 您可以避免创建fruit=...变量,而是直接只移植"Fruit"名称。因此,您只需键入一次“Fruit”,而不是不键入somedict["Fruit"]您想要的,我们避免键入变量名称。这是通过以匿名方式编程来完成的:

    somedict["Fruit"] = {'red':'apple', 'yellow':'banana'}
    

可悲的是,如果您的构造需要语句,python 不会让您这样做;如果你只有一个文字,你只能逃脱这个。另一个解决方案是:

  • 您可以创建一个为您执行此操作的函数:

    graft(somedict, "Fruit", {'red':'apple', 'yellow':'banana'})
    

遗憾的是,如果您的构造需要任何类型的声明,这也将不起作用。您可以创建一个变量x={'red':...}或其他东西,但这违背了整个目的。第三种方式,您不应该使用,是locals(),但这仍然需要您参考名称。

总之,如果您需要大量的 for 循环和函数以及 if 语句,那么您尝试做的事情在 python 中是不可能的,除非您更改构建fruit=...字典的整个方式。多行 lambda、字典推导 ( dict((k,v) for k,v in ...))、内联IFTRUE if EXPR else IFFALSE等的组合是可能的。这种风格的危险在于它很快就会变得难以阅读。

如果您能够将您的字典表示为文字字典、字典理解或您已经编写的函数的输出,则这是可能的。事实上,这很容易(见其他答案)。不幸的是,最初的问题并没有说明您是如何构建这些词典的。

假设这些答案不能回答您的问题(也就是说,您正在以非常复杂的方式构建这些),您可以编写“元”代码:为您制作字典的代码,并滥用反射。然而,python 中最好的解决方案是尝试通过迭代来制作你的字典。例如:

foods = {
    "Fruit": {...},
    "Meats": makeMeats(),
}
for name,data in ...:
    foods[name] = someProcessing(data)
foods.update(dataFromSomeFile)  #almost same as last two lines
于 2012-06-05T19:53:54.327 回答
2

你有什么理由使用方括号吗?因为你可以用嵌套的文字字典来表示这个数据结构:

food = {
    'veg': {
        'red': 'tomato',
        'green': 'lettuce',
    },

    'fruit': {
        'red': 'cherry',
        'green': 'grape',
    },
}
于 2012-06-05T19:39:52.113 回答
1

您不能追加,因为字典不是列表:它没有顺序。您要做的是使用新的键/值对更新字典。你需要使用:

Food['Fruit'] = Fruit

或者,或者:

Food.update({'Fruit': Fruit})

不相关的说明:用大写字母编写变量不是 Python 编码风格。Fruit将被写为fruit

于 2012-06-05T19:35:57.503 回答
1

您遇到的基本问题是您的字典不知道自己的名称。这是正常的;通常,可以将任意数量的名称绑定到 Python 对象,并且没有任何一个名称以任何方式优于其他任何名称。换句话说, in a = {}; b = aab都是同一个字典的名称,并且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 中完成的方式。

于 2012-06-05T21:35:13.683 回答
0

您可能正在寻找 defaultdict。每当您要求新的第一级类型时,它都会通过添加一个新的空字典来工作。示例如下所示:

from collections import defaultdict

basic_foods = {'Veg' : {'Root' : 'Turnip'}, 'Dairy' : {'Cheese' : 'Cheddar'}}

foods = defaultdict(dict, basic_foods)
foods['Fruit']['Red'] = "Cherry"
print foods['Fruit']
于 2012-06-05T19:40:43.187 回答