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
sdjl
V2EX  ›  Python

[ Python ]请问为什么这个 Class 是 iterable 的,如何修改 for 行为,具体内容请进帖

  •  
  •   sdjl · 2013-10-10 16:34:49 +08:00 · 3796 次点击
    这是一个创建于 4066 天前的主题,其中的信息可能已经有所发展或是发生改变。
    问题描述:
    我正在使用 pymongo ,Cursor.find() 函数返回一个可迭代的 results ,比如

    results = c.find()
    for i in c:
    print i

    但是 i 是一个 dict ,因此不能这样使用 i.name, 而必须是 i['name']

    所以我希望可以通过修改 c 的 next 函数让 i 变成 storage 类型(一种可以通过 i.name 代替 i['name']的类)

    因此我使用了如下的办法:


    这里的 c 如果为 instance 时此方法是可行的,但是 c 却是一个 Class ,因此此法行不通

    读了一下 pymongo 的代码(/usr/lib/python2.7/dist-packages/pymongo )也没有找到原因

    我想问的问题是:

    1 、find 函数是如何返回 class ,而不是 instance 的? def find 的最后一句是:
    return Cursor(self, *args, **kwargs)

    2 、pymongo 是如何让 c (<class 'pymongo.cursor.Cursor'>)可迭代的?

    3 、如何实现我想要的功能?
    17 条回复    1970-01-01 08:00:00 +08:00
    mengzhuo
        1
    mengzhuo  
       2013-10-10 16:45:34 +08:00
    “所以我希望可以通过修改c的next函数让i变成storage类型(一种可以通过i.name代替i['name']的类)“

    改写__getitem__成__getattr__,但是这不符合”属性必须明确“的原则。_所以继续用着吧_。

    可迭代是添加__next__ __iter__两个函数
    sdjl
        2
    sdjl  
    OP
       2013-10-10 16:46:46 +08:00
    for i in c:
    print i

    这里写错了, 应该是 for i in results
    sdjl
        3
    sdjl  
    OP
       2013-10-10 16:48:04 +08:00
    @mengzhuo 抛错

    AttributeError: 'Cursor' object has no attribute '__next__'
    sdjl
        4
    sdjl  
    OP
       2013-10-10 16:50:50 +08:00
    注明,代码中id(it)和id(c)是相等的, print type(it)和print type(c)均显示:
    <class 'pymongo.collection.Collection'>
    keakon
        5
    keakon  
       2013-10-10 16:52:06 +08:00
    1. 返回的就是 Cursor 类的对象。
    2. 定义 __iter__ 和 next 方法。
    3. next 是个方法,你把这个方法替换成了一个对象,然后去调用这个对象,自然会失败。
    不建议你把 Cursor.next 方法给替换掉,你在使用时自己转换下又不会怀孕:
    for i in c:
    i = storage(**i)
    sdjl
        6
    sdjl  
    OP
       2013-10-10 16:52:58 +08:00
    写错了, print type(it) 是 <class 'pymongo.cursor.Cursor'>
    sdjl
        7
    sdjl  
    OP
       2013-10-10 16:54:48 +08:00
    @keakon 这里我可以把it.next和c.next,以及it.__iter__和c.__iter__均设置为None

    也不会影响for语句的执行
    sdjl
        8
    sdjl  
    OP
       2013-10-10 17:02:38 +08:00
    @keakon 嗯,返回的确实是instance, 用 inspect.isclass(results)查看了,不好意思
    sdjl
        9
    sdjl  
    OP
       2013-10-10 17:07:26 +08:00
    如果我直接调用 results.next() 那么结果是正确的,能得到storage类型

    所以我怀疑是否没用到pymongo.cursor.Cursor的next函数,尝试删除此next函数,for语句抛错,表明应该确实使用了此next函数

    但是我在it(is c)上“重新定义”next函数,或删除next函数,均没有任何效果
    sdjl
        10
    sdjl  
    OP
       2013-10-10 17:17:22 +08:00
    也就是说以下代码不会抛出异常,可以正常运行,很奇怪
    binux
        11
    binux  
       2013-10-10 17:20:28 +08:00
    dict是内置对象
    thedevil5032
        12
    thedevil5032  
       2013-10-10 17:23:24 +08:00
    piglei
        13
    piglei  
       2013-10-10 17:35:56 +08:00   ❤️ 1
    刚刚我稍微读了一下pymongo的源码,pymongo的Cursor对象是靠实现__getitem__方法来实现可迭代对象的,例如:

    # -*- coding: utf-8 -*-

    class IterTester(object):

    def __getitem__(self, index):
    if index == 10:
    raise StopIteration()
    return index

    if __name__ == '__main__':
    for x in IterTester():
    print x

    具体的你可以再仔细看看源码。

    另外如 @keakon 所说,不建议你修改Cursor的方法来实现类似功能,麻烦且不实用。
    sdjl
        14
    sdjl  
    OP
       2013-10-10 17:44:43 +08:00
    @piglei 那就只能自己写一个继承类了吧? 我去掉__getitem__后for语句也能正确执行

    hepochen
        15
    hepochen  
       2013-10-10 17:46:37 +08:00
    patch不是这种写法, patch之后,确保patch的代码,运行于所patch的类被实例化之前。

    from pymongo.collection import Collection
    old_func = Collection.find

    def find(self, *args, **kwargs):
    old_func(self, *args, **kwargs)
    your_code = 'here'

    Collection.find = find
    hepochen
        16
    hepochen  
       2013-10-10 17:59:13 +08:00
    @sdjl __getitem__ 和 next 两个函数在pymongo中是应对不通场景的调用,所以都有,只去掉其中一个,呃,这仍然是可遍历的。

    还是建议自己打patch,调用比较方便,也可以不用管后面的实现。之前有个版本,作者对__getitem__函数的处理就不一样,测试没有跑全就发布了,然后就bug了。
    sdjl
        17
    sdjl  
    OP
       2013-10-10 18:08:56 +08:00
    @hepochen 嗯,也许只能这样更方便一些了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3394 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:44 · PVG 19:44 · LAX 03:44 · JFK 06:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.