zoukankan      html  css  js  c++  java
  • Python 迭代器 & __iter__方法

    看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。

    __iter__

    如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

    迭代器就是重复地做一些事情,可以简单的理解为循环,在python中实现了__iter__方法的对象是可迭代的,实现了next()方法的对象是迭代器,这样说起来有点拗口,实际上要想让一个迭代器工作,至少要实现__iter__方法和next方法。很多时候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就会占用太多的内存,而且使用迭代器也让我们的程序更加通用、优雅、pythonic。

    我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器a,b
        def __iter__(self):
            return self # 实例本身就是迭代对象,故返回自己
        def next(self):
            self.a, self.b = self.b, self.a + self.b # 计算下一个值
            if self.a > 100000: # 退出循环的条件
                raise StopIteration();
            return self.a # 返回下一个值
    for n in Fib():
    ...     print n
    ...
    1
    1
    2
    3
    5
    ...

    __getitem__

    Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

    Fib()[5]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'Fib' object does not support indexing

    要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

    class Fib(object):
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
    
    
    
    >>> f = Fib()
    >>> f[0]
    1
    >>> f[1]
    1
    >>> f[2]
    2
    >>> f[3]

    但是list有个神奇的切片方法:

    >>> range(100)[5:10]
    [5, 6, 7, 8, 9]
    
    #对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:
    
    class Fib(object):
        def __getitem__(self, n):
            if isinstance(n, int):
                a, b = 1, 1
                for x in range(n):
                    a, b = b, a + b
                return a
            if isinstance(n, slice):
                start = n.start
                stop = n.stop
                a, b = 1, 1
                L = []
                for x in range(stop):
                    if x >= start:
                        L.append(a)
                    a, b = b, a + b
                return L
    
    #现在试试Fib的切片:
    
    >>> f = Fib()
    >>> f[0:5]
    [1, 1, 2, 3, 5]
    >>> f[:10]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    
    
    #但是没有对step参数作处理:
    
    >>> f[:10:2]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

    也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

    此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。

    与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

    总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

    公众号请关注:侠之大者
  • 相关阅读:
    as2 loadvars
    Playing with Content-Type – XXE on JSON Endpoints
    Hostile Subdomain Takeover using HerokuGithubDesk + more
    XSS for domain takeover
    XSS via XML POST
    dns 查询中的ANY类型
    crossDomain、allowDomain()、allowScriptAccess三者的关系
    ReadingWriting files with MSSQL's OPENROWSET
    Github html文件在线预览方法
    boostrap莫泰对话框宽度调整
  • 原文地址:https://www.cnblogs.com/kamil/p/5206709.html
Copyright © 2011-2022 走看看