V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xuegj1010
V2EX  ›  Python

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

  •  
  •   xuegj1010 · 2018-05-18 09:40:47 +08:00 · 3188 次点击
    这是一个创建于 2385 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写了两个生成器 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}
    

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

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

    19 条回复    2018-05-22 10:10:58 +08:00
    xuegj1010
        1
    xuegj1010  
    OP
       2018-05-18 09:41:19 +08:00
    好像没法编辑代码的格式啊
    ipwx
        2
    ipwx  
       2018-05-18 10:04:52 +08:00   ❤️ 1
    你的 agentman 怕不是一个迭代器。迭代器遍历完了就消耗完了,不会自动重启的。

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

    可以在一次循环结束调用生成器的 send 函数,手动指向第一个元素
    princelai
        8
    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
        9
    xuegj1010  
    OP
       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
        10
    xuegj1010  
    OP
       2018-05-18 14:27:01 +08:00
    @yonoho 具体怎么做,能不能举个列子
    luhuisicnu
        11
    luhuisicnu  
       2018-05-18 14:27:22 +08:00
    一个生成器只能遍历一次。
    princelai
        13
    princelai  
       2018-05-18 16:13:33 +08:00
    读 excel 为什么不用 pandas
    ipwx
        14
    ipwx  
       2018-05-18 19:08:16 +08:00
    @xuegj1010 agentman_data = AgentmanReader(AGENTMAN_PATH).parse_data()

    这一条就是根本原因。agentman_data 现在只是一个迭代器,只能用一次。
    xuegj1010
        15
    xuegj1010  
    OP
       2018-05-21 21:19:19 +08:00
    @ipwx 那应该怎么改呢?
    xuegj1010
        16
    xuegj1010  
    OP
       2018-05-21 21:19:40 +08:00
    @princelai 没用过 pandas,有空学习下
    xuegj1010
        17
    xuegj1010  
    OP
       2018-05-21 21:20:37 +08:00
    @yonoho 居然打不开
    ipwx
        18
    ipwx  
       2018-05-21 21:32:09 +08:00 via iPhone
    @xuegj1010 别存它,直接 for 函数调用
    yonoho
        19
    yonoho  
       2018-05-22 10:10:58 +08:00   ❤️ 1
    @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))
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1198 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:16 · PVG 07:16 · LAX 15:16 · JFK 18:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.