关于 Python 生成器,请教各位大佬一个问题

2018-05-18 09:40:47 +08:00
 xuegj1010

写了两个生成器 agentman,agreement,生成器里面的元素是一个个的 python 字典,每个字典里面都有一个 key 相同,key 对应的 value 也相同,我的需求就是分别遍历两个生成器找出拥有相同 key-value 的字典,然后将这两个字典合并成一个字典。(有没有更好的实现方法,两个 for 循环看起来实在是太丑了)

for i in agreement:
    for j in agentman:
        if i['agent_name'] == j['agent_name']:
            print(dict(i, **j))

结果我发现,当 agreement 取了第一个值的时候,agentman 可以遍历一遍,但是,当 agreement 取第二个值一直到最后一个值的时候,无法进入 agentman 的循环了,j 一直等于 agentman 的最后一个值,感觉就像是 agentman 为空一样,最后的结果就是有且只有第一次的循环得到的一个合并之后的字典。

我本以为是因为生成器只能迭代一次。可当我又写了个小例子的时候,发现可以正常输出我想要的结果。

a = [{'a': 1, 'b': 2, 'c': 3}, {'a': 4, 'b': 5, 'c': 6}, {'a': 7, 'b': 8, 'c': 9}]
b = [{'a': 1, 'd': 14, 'e': 45}, {'a': 4, 'd': 24, 'e': 5}, {'a': 7, 'd': 34, 'e': 55}]

def ag():
    for i in a:
        yield i

def bg():
    for j in b:
        yield j

for i in ag():
    for j in bg():
        if i['a'] == j['a']:
            print(dict(i, **j))

结果:

	{'a': 1, 'b': 2, 'c': 3, 'd': 14, 'e': 45}
	{'a': 4, 'b': 5, 'c': 6, 'd': 24, 'e': 5}
	{'a': 7, 'b': 8, 'c': 9, 'd': 34, 'e': 55}

所以我选择就不明白到底是什么问题??

我的描述可以看的明白吧?

3093 次点击
所在节点    Python
19 条回复
xuegj1010
2018-05-18 09:41:19 +08:00
好像没法编辑代码的格式啊
ipwx
2018-05-18 10:04:52 +08:00
你的 agentman 怕不是一个迭代器。迭代器遍历完了就消耗完了,不会自动重启的。

你下面的小例子,bg() 每次都产生了一个新的迭代器。所以可以重复遍历。
guyskk0x0
2018-05-18 10:24:49 +08:00
理解 生成器函数,生成器,迭代器,列表 的区别和关联
xuegj1010
2018-05-18 10:25:50 +08:00
@ipwx 肯定是生成器的,以为我对调了 agentman 和 agreement 的循环顺序,结果还是一样的
yonoho
2018-05-18 10:27:34 +08:00
楼上解释了部分问题。对于两个 for 太丑的问题:当你使用两个序列类型配对的时候,因为你只能进行顺序查找,所以复杂度会是 O(n2)。那么你可以把一个较大的对象换成字典来实现 O(n) 配对。字典可以 ( key,value ) 元组为键,元素为值。
ipwx
2018-05-18 10:28:38 +08:00
@xuegj1010 所以你能不能把整个代码发上来,包括怎么产生这两个变量的。。。
whoami9894
2018-05-18 11:23:26 +08:00
迭代器每一次执行__next()__方法后会记录当前变量值,所以一次循环结束会指向最后一个元素

可以在一次循环结束调用生成器的 send 函数,手动指向第一个元素
princelai
2018-05-18 12:10:40 +08:00
j = bg()


id(bg())
Out[125]: 139947918612704


id(bg())
Out[126]: 139947918289296


id(j)
Out[127]: 139947918612176


id(j)
Out[128]: 139947918612176
xuegj1010
2018-05-18 13:00:35 +08:00
@ipwx
```python
class AgentmanReader(ReadExcel):
def __init__(self, path):
super(AgentmanReader, self).__init__(path)
assert self.ncols == 27, 'columns must be 27'

def parse_data(self):
for i in range(1, self.nrows):
agentman_dict = dict(
# 公司名称(代理人名称)*
agent_name=self.sheet.cell_value(i, 0),
# 归属机构代码
org_code=str(self.sheet.cell_value(i, 1)),
# 地址*
address=self.sheet.cell_value(i, 2),
# 邮编*
postcode=self.sheet.cell_value(i, 3),
# 业务渠道*
trade_channel=str(TRADE_CHANNEL[self.sheet.cell_value(i, 4)]),
# 代理人类型*
agentman_type=str(AGENT_TYPE[self.sheet.cell_value(i, 5)]),
# 许可证号*
license_num=self.sheet.cell_value(i, 6),
# 组织机构代码*
social_code=self.sheet.cell_value(i, 7),
# 负责人*
principal=self.sheet.cell_value(i, 8),
# 电话*
phone=self.sheet.cell_value(i, 9),
# 手机
mobile=self.sheet.cell_value(i, 10),
# MAC 地址
mac_addr=self.sheet.cell_value(i, 11),
# 资格证有效期
Validity=self.sheet.cell_value(i, 12),
# 数字证书编码
digital_code=self.sheet.cell_value(i, 13),
# 开户银行
opening_bank=self.sheet.cell_value(i, 14),
# 户名
account_name=self.sheet.cell_value(i, 15),
# 银行类别
bank_type=BANK_TYPE.get(self.sheet.cell_value(i, 16), '0'),
# 省份
province=self.sheet.cell_value(i, 17),
# 城市
city=self.sheet.cell_value(i, 18),
# 银行帐号
bank_account=self.sheet.cell_value(i, 19),
# 是否发送短信息
is_send=self.sheet.cell_value(i, 20),
# 纳税人身份
taxpayer=self.sheet.cell_value(i, 21),
# 纳税人识别号
taxpayer_num=self.sheet.cell_value(i, 22),
# 纳税人地址
taxpayer_addr=self.sheet.cell_value(i, 23),
# 纳税人电话
taxpayer_ph=self.sheet.cell_value(i, 24),
# 纳税人开户行名称
taxpayer_bank=self.sheet.cell_value(i, 25),
# 纳税人银行账号
taxpayer_account=self.sheet.cell_value(i, 26)
)
yield agentman_dict

agentman_data = AgentmanReader(AGENTMAN_PATH).parse_data()
```
excel 里面有几千条数据,读出来转换成一个生成器。
xuegj1010
2018-05-18 14:27:01 +08:00
@yonoho 具体怎么做,能不能举个列子
luhuisicnu
2018-05-18 14:27:22 +08:00
一个生成器只能遍历一次。
yonoho
2018-05-18 15:25:42 +08:00
princelai
2018-05-18 16:13:33 +08:00
读 excel 为什么不用 pandas
ipwx
2018-05-18 19:08:16 +08:00
@xuegj1010 agentman_data = AgentmanReader(AGENTMAN_PATH).parse_data()

这一条就是根本原因。agentman_data 现在只是一个迭代器,只能用一次。
xuegj1010
2018-05-21 21:19:19 +08:00
@ipwx 那应该怎么改呢?
xuegj1010
2018-05-21 21:19:40 +08:00
@princelai 没用过 pandas,有空学习下
xuegj1010
2018-05-21 21:20:37 +08:00
@yonoho 居然打不开
ipwx
2018-05-21 21:32:09 +08:00
@xuegj1010 别存它,直接 for 函数调用
yonoho
2018-05-22 10:10:58 +08:00
@xuegj1010

mans_agreements = {} # 假设 agreement 比较大
for a in agreement:
mans_agreements.setdefault(a['agent_name'], [])
mans_agreements[a['agent_name']].append(a)

for man in agentman:
his_agreements = mans_agreements.get(man['agent_name'], [])
for a in his_agreements:
print(dict(man, **a))

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/455794

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX