如何从字典生成器中合并多个字典以创建单一的汇总Python字典

0 投票
2 回答
884 浏览
提问于 2025-04-18 18:37

我在寻求帮助,希望能用更Pythonic的方式来做这件事。

这是我目前的代码。
我创建了一个生成器,用来生成一个字典,字典的键是一个元组,值是我在项目中想要使用的对象。
我相信有更好的方法来实现这个目标。我尝试用字典和列表推导式,但都没有成功。

app = models.get_app('djangoapp')
appmodels = models.get_models(app)

gen = mapEntGen(appmodels)  
d = {}  
for x in gen:  
    d.update(x)   

为了让大家更清楚我的对象是什么样的。

for x in gen:
    print(x)

{('in', 1): <class 'djangoapp.models.entrance01IN'>}
{('out', 1): <class 'djangoapp.models.entrance01OUT'>}
{('in', 2): <class 'djangoapp.models.entrance02IN'>}
{('out', 2): <class 'djangoapp.models.entrance02OUT'>}
...

这是我尝试使用推导式的失败例子。

{d: aDict for aDict in mapEntGen(appmodels)}  
d = {key: value for (key, value) in gen}  
d = {key: value for (key, value) in mapEntGen(appmodels[1:])}  

我认为问题很可能是因为我已经在使用一个字典,而不是其他两种类型。字典可以用作键值对。

----> 1 for k, v in gen:
      2     d[k] = v
      3 

ValueError: need more than 1 value to unpack

这是我用来生成对象映射和查找键的另一个代码。

def mapEntGen(EntranceObj = []):    
    for x in EntranceObj:
        thisEnt = (x._meta.verbose_name[10:],
                   int(x._meta.verbose_name[8:10]))
        aDict = {thisEnt : x}
        yield aDict

这可能看起来像是重复的问题,但我尝试的所有例子都没有成功。

2 个回答

1

解决你问题的最简单方法就是不使用mapEntGen,直接这样做:

d = {((x._meta.verbose_name[10:],
       int(x._meta.verbose_name[8:10])):x 
           for x in EntranceObj}

这样就不需要创建不同的字典再把它们合在一起了。


如果你想用生成器的话,为什么要让它返回一个字典呢?而这个字典里只有一个键值对?不如让它返回一个元组:

def mapEntGen(EntranceObj = []):    
    for x in EntranceObj:
        thisEnt = (x._meta.verbose_name[10:],
                   int(x._meta.verbose_name[8:10]))
        yield (thisEnt, x)

这样获取d就简单多了:

d = dict(mapEntGen(appmodels))

如果你想用你自己的生成器:

mapEntGen(appmodels)会生成字典,直接把它当成字典来用就行。当你想遍历它们的内容时,需要调用iteritems,然后进行遍历。这样应该可以:

d = {key:value for subdct in gen for key, value in subdct.iteritems()}  

为了确保你理解这个表达式,它其实等同于这样做:

d = {}
for subdct in gen:
    for key, value in subdct.iteritems():
        d[key] = value

(当然gen可以用mapEntGen(appmodels)替代)

1

为了让你的代码更清晰易懂,我想给你一个完全不同的方向,可能会更好用,具体要看你其他代码的情况:

我们不再使用mapGenEnt,而是从appModels(不管那是什么)创建两个独立的生成器:

  1. 一个是代码的列表(你之后会把它当作键,比如 ('out', 1))。
  2. 另一个是实际的对象或模型的列表(你之后会把它当作值,比如 <class 'djangoapp.models.entrance01IN'>)。

假设你有这两个生成器:

def code_names(EntranceObj):
   return ((entrance._meta.verbose_name[10:], int(entrance._meta.verbose_name[8:10]) for entrance in EntranceObj)

def entries(EntranceObj):
   return EntranceObj    # Shorter than: (entrance for entrance in EntranceObj)

现在我们有两个函数,各自做自己的事情,我们还需要创建 d。这很简单,可以使用 zipitertools.izip

d = dict(zip(code_names(appmodels), entries(appmodels)))

为了让代码更易读,我个人建议给这个函数起个名字,比如 generate_model_entries(appmodels)

def generate_model_entries(models):
    return dict(zip(code_names(models), entries(models)))

d = generate_model_entries(appmodels)

PS:如果你能给循环变量起个比 x 更准确的名字,请务必使用。

撰写回答