赞
踩
python里面有很多的以__(注意:这里是两个下划线)开始和结尾的函数,利用它们可以完成很多复杂的逻辑代码,而且提高了代码的简洁性,下面以迭代器的概念引入相关内容。
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
>>> list=[1,2,3,4]
>>> it = iter(list) # 创建迭代器对象
>>> print (next(it)) # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>
迭代器对象可以使用常规for语句进行遍历:
>>>list=[1,2,3,4]
>>>it = iter(list) # 创建迭代器对象
>>>for x in it:
>>> print (x, end=" ")
1 2 3 4
it = iter(list) 这一句可以不写,因为python内部会对for语句后面的对象进行如下的变换:
for x in iter(list)
把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。
iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
next() 方法(Python 2 里是 next())会返回下一个迭代器对象。我们就可以通过next函数访问这个对象的下一个元素了,并且在你不想继续有迭代的情况下抛出一个StopIteration的异常(for语句会捕获这个异常,并且自动结束for)
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
class A(object): def __init__(self,end): self.start = 0 self.end = end def __iter__(self): return self def __next__(self): if self.start < self.end: self.start + = 1 return self.start else: raise StopIteration a = A(5) for i in a: print(i)
输出:
1
2
3
4
5
把for语句换成next函数也可以:
class A(object): def __init__(self,end): self.start = 0 self.end = end def __iter__(self): return self def __next__(self): if self.start < self.end: self.start + = 1 return self.start else: raise StopIteration a = A(5) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) # 其实到这里已经完成了,我们在运行一次查看异常
输出:
0 1 2 3 4 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-51-d93a95b5b2c9> in <module>() 21 print(next(a)) 22 print(next(a)) ---> 23 print(next(a)) # 其实到这里已经完成了,我们在运行一次查看异常 <ipython-input-51-d93a95b5b2c9> in __next__(self) 13 return ret 14 else: ---> 15 raise StopIteration 16 17 a = MyRange(5) StopIteration:
可以看见一个很明显的好处是,每次产生的数据,是产生一个用一个,什么意思呢,比如我要遍历[0, 1, 2, 3…]一直到10亿,如果使用列表的方式,那么是会全部载入内存的,但是如果使用迭代器,可以看见,当用到了(也就是在调用了next)才会产生对应的数字,这样就可以节约内存了,这是一种懒惰的加载方式。
构造函数也被称为构造器,当创建对象的时候第一个被自动调用的函数,系统默认提供了一个无参的构造函数 per = Person()
语法:
def __ init__(self,arg1,arg2,...):
函数体
说明:
当你没有在类中定义构造函数的话,系统默认提供了一个无参的构造函数
arg1,arg2,...可以自己定义,但是,一般情况下,构造函数的形参列表和成员变量有关
构造函数的特点:创建对象;给对象的成员变量赋值
构造函数和成员函数之间的区别:
成员函数的函数名可以自定义,但是,构造函数的函数名是固定的__init__
成员函数需要被手动调用,但是,构造函数在创建对象的过程中是自动被调用的
对于同一个对象而言,成员函数可以被调用多次,但是,构造函数只能被调用一次
class :类
instance:实例
object:对象,和instance等同
method:类中的方法
function:普通函数,类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self
——————
python里面的self,是谁啊?
self代表类的实例,而非类
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
T = Test()
T.prt()
输出
<__main__.Test object at 0x000001EDDE8BFFD0>
<class '__main__.Test'>
<__main__.Test object at 0x000001EDDE7C45F8>
<class '__main__.Test'>
从执行结果可以很明显的看出,self 指代引用类的method的实例,代表当前实例的地址,可以通过self.xxx来改变实例的属性
而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
class Test:
def prt(food):
print(food)
print(food.__class__)
t = Test()
t.prt()
T = Test()
T.prt()
输出:
<__main__.Test object at 0x000001EDDE7A69E8>
<class '__main__.Test'>
<__main__.Test object at 0x000001EDDE8BFFD0>
<class '__main__.Test'>
Iterable: 有迭代能力的对象,一个类,实现了__iter__,那么就认为它有迭代能力,通常此函数必须返回一个实现了__next__的对象,如果自己实现了,你可以返回self,当然这个返回值不是必须的;
Iterator: 迭代器(当然也是Iterable),同时实现了 iter__和__next__的对象,缺少任何一个都不算是Iterator,比如下面例子中,A()可以是一个Iterable,但是A()和B()都不能算是和Iterator,因为A只实现了__iter(),而B只实现了__next__()。
class B(object):
def __next__(self):
raise StopIteration
class A(object):
def __iter__(self):
return B()
a = A()
b = B()
print(next(a))
print(next(b))
输出:
Traceback (most recent call last):
File "d:/objectdetection/models/yolov3/Rebar_Detection-master/test.py", line 12, in <module>
print(next(a))
TypeError: 'A' object is not an iterator
Traceback (most recent call last):
File "d:/objectdetection/models/yolov3/Rebar_Detection-master/test.py", line 12, in <module>
print(next(b))
File "d:/objectdetection/models/yolov3/Rebar_Detection-master/test.py", line 3, in __next__
raise StopIteration
StopIteration
我们可以使用collections里面的类型来进行验证:
class B(object): def __next__(self): raise StopIteration class A(object): def __iter__(self): return B() from collections.abc import * a = A() b = B() print(isinstance(a, Iterable)) print(isinstance(a, Iterator)) print(isinstance(b, Iterable)) print(isinstance(b, Iterator))
True
False
False
False
可以对B类进行修改:
class B(object): def __next__(self): raise StopIteration def __iter__(self): return None class A(object): def __iter__(self): return B() from collections.abc import * a = A() b = B() print(isinstance(a, Iterable)) print(isinstance(a, Iterator)) print(isinstance(b, Iterable)) print(isinstance(b, Iterator))
True
False
True
True
生成器是一个可以快速创建迭代器的工具
我们可以用列表推导(生成式)来初始化一个列表:
list5 = [x for x in range(5)]
print(list5) #output:[0, 1, 2, 3, 4]
我们用类似的方式来生成一个生成器,只不过我们这次将上面的[ ]换成( ):
gen = (x for x in range(5))
print(gen)
#output: <generator object <genexpr> at 0x0000000000AA20F8>
看到上面print(gen) 并不是直接输出结果,而是告诉我们这是一个生成器。那么我们要怎么调用这个gen呢。
有两种方式:
第一种:
for item in gen:
print(item)
#output:
0
1
2
3
4
第二种:
print(next(gen))#output:0
print(next(gen))#output:1
print(next(gen))#output:2
print(next(gen))#output:3
print(next(gen))#output:4
print(next(gen))#output:Traceback (most recent call last):StopIteration
好了。现在可以考虑下背后的原理是什么了。
从第一个用for的调用方式我们可以知道生成器是可迭代的。更准确的说法是他就是个迭代器。
我们可以验证一下:
from collections import Iterable, Iterator
print(isinstance(gen, Iterable))#output:True
print(isinstance(gen, Iterator))#output:True
str,list,tuple,dict,set这些都是可迭代的,就是可用for来访问里面的每一个元素。但他们并不是迭代器。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
import sys def fibonacci(n): # 生成器函数 - 斐波那契 a, b, counter = 0, 1, 0 while True: if (counter > n): return yield a a, b = b, a + b counter += 1 f = fibonacci(10) # f 是一个迭代器,由生成器返回生成 while True: try: print (next(f), end=" ") except StopIteration: sys.exit() from collections import Iterable, Iterator print(isinstance(f, Iterable))#output:True print(isinstance(f, Iterator))#output:True #output 0 1 1 2 3 5 8 13 21 34 55
注意上面说的yield表达式的返回值,我们以前使用的都是yield start这种格式,其实yield是有返回值的,默认情况下都是None,我们修改一下上面的MyRange
def MyRange(end):
start = 0
while start < end:
x = yield start # 这里增加了获取返回值
print(x) # 打印出来
start += 1
m = MyRange(5)
print(next(m))
print(next(m))
#输出
0
None
1
yield执行顺序上面已经说明了,这里打印了一个None,就是yield的返回值,那么说了这么多,就是为了说明send函数的参数,会作为yield的返回值的。
def MyRange(end): start = 0 while start < end: x = yield start print(x) start += 1 m = MyRange(5) print(next(m)) print(m.send(10)) #输出 0 10 1
我们在来看看throw这个函数,它是让yield产生一个异常,接收三个参数,异常类型,异常值,trackback对象,其中后面两个可选,同send一样,会返回下一个值。
如果这个异常不在生成器函数里面进行处理,会抛出到调用者
def MyRange(end): start = 0 while start < end: try: x = yield start except Exception as e: print(e) start += 1 m = MyRange(5) print(next(m)) print(m.throw(Exception, 'Some Exception')) #输出 0 Some Exception 1
总结:
生成迭代器的方法有很多种。
1、同时实现了 __iter__和__next__的对象,缺少任何一个都不算是Iterator。
2、生成器是生成迭代器的工具。
————
【Python魔术方法】迭代器(iter__和__next)
Python3 迭代器与生成器
对Python生成器的理解
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。