python——
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 = 0self.end = enddef __iter__(self):return selfdef __next__(self):if self.start < self.end:self.start + = 1return self.startelse: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 = 0self.end = enddef __iter__(self):return selfdef __next__(self):if self.start < self.end:self.start + = 1return self.startelse: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 ret14 else:
---> 15 raise StopIteration16 17 a = MyRange(5)StopIteration:
可以看见一个很明显的好处是,每次产生的数据,是产生一个用一个,什么意思呢,比如我要遍历[0, 1, 2, 3…]一直到10亿,如果使用列表的方式,那么是会全部载入内存的,但是如果使用迭代器,可以看见,当用到了(也就是在调用了next)才会产生对应的数字,这样就可以节约内存了,这是一种懒惰的加载方式。
注:
(1)构造函数
构造函数也被称为构造器,当创建对象的时候第一个被自动调用的函数,系统默认提供了一个无参的构造函数 per = Person()
-
语法:
def __ init__(self,arg1,arg2,...):函数体
-
说明:
当你没有在类中定义构造函数的话,系统默认提供了一个无参的构造函数arg1,arg2,...可以自己定义,但是,一般情况下,构造函数的形参列表和成员变量有关构造函数的特点:创建对象;给对象的成员变量赋值
-
构造函数和成员函数之间的区别:
成员函数的函数名可以自定义,但是,构造函数的函数名是固定的__init__成员函数需要被手动调用,但是,构造函数在创建对象的过程中是自动被调用的对于同一个对象而言,成员函数可以被调用多次,但是,构造函数只能被调用一次
(2)class、instance、object、method、function
class :类
instance:实例
object:对象,和instance等同
method:类中的方法
function:普通函数,类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self
——————
python里面的self,是谁啊?
(3)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'>
(4)Iterable、Iterator
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 StopIterationclass 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 iteratorTraceback (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 StopIterationclass 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 StopIterationdef __iter__(self):return Noneclass 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
(5)生成器
生成器是一个可以快速创建迭代器的工具
- 简单的生成器:生成器表达式
我们可以用列表推导(生成式)来初始化一个列表:
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来访问里面的每一个元素。但他们并不是迭代器。
- 在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
import sysdef fibonacci(n): # 生成器函数 - 斐波那契a, b, counter = 0, 1, 0while True:if (counter > n): returnyield aa, b = b, a + bcounter += 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
- send和throw函数
注意上面说的yield表达式的返回值,我们以前使用的都是yield start这种格式,其实yield是有返回值的,默认情况下都是None,我们修改一下上面的MyRange
def MyRange(end):start = 0while start < end:x = yield start # 这里增加了获取返回值print(x) # 打印出来start += 1m = MyRange(5)
print(next(m))
print(next(m))
#输出
0
None
1
yield执行顺序上面已经说明了,这里打印了一个None,就是yield的返回值,那么说了这么多,就是为了说明send函数的参数,会作为yield的返回值的。
def MyRange(end):start = 0while start < end:x = yield startprint(x)start += 1m = MyRange(5)
print(next(m))
print(m.send(10))#输出
0
10
1
我们在来看看throw这个函数,它是让yield产生一个异常,接收三个参数,异常类型,异常值,trackback对象,其中后面两个可选,同send一样,会返回下一个值。
如果这个异常不在生成器函数里面进行处理,会抛出到调用者
def MyRange(end):start = 0while start < end:try:x = yield startexcept Exception as e:print(e)start += 1m = 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生成器的理解