Python基础之生成器、常见内置函数
一、生成器对象
生成器其实就是自定义的迭代器。其实就是一个内部有yield关键字的函数,在定义阶段就是一个普通的函数,跟普通函数的区别是在第一次调用函数的时候不会执行代码,而是把函数先转换成生成器,然后用.next()方法才会执行代码。
执行代码的时候如果遇到yield函数会先停止(阻塞),继续调用才会往下走,再遇到yield又停止(阻塞)...
def func():
print('哈哈哈哈哈')
yield
print('嘿嘿嘿')
yield
res = func() # 先把函数变成生成器
res.__next__() # 然后使用双下next方法执行代码,遇到yield关键字就会停下
res.__next__() # 在上一次的基础上,再使用双下next方法执行代码,一样遇到yield关键字就会停下
res.__next__() # 在上一次的基础上,再使用双下next方法执行代码,后面没有代码执行了就会报错,后面有东西没有yield关键字阻塞也会报错
二、自定义range功能
这是一个小练习,模拟内置函数range()的功能,自己做一个跟range()功能一样的生成器。
# 先看一下range()函数的功能:
for i in range(3): # 起始位置是0(其实位置不写就默认是0),然后终止位置是2(顾头不顾尾),默认步长是1(步长可以改)
print(i)
# 模拟rang()的功能做一个生成器:
def my_range(start=0, stop=0, step=1): # 使用默认形参,如果传参就用传的值,如果不传就使用默认的
# 先看传参是几个,如果只传1个的话,用户想的是这个参是终止位置
# 因为传参按照从左到右的顺序,所以函数会默认把传的参数给到起始位置,所以我们就需要把传的参数给到终止位置,然后让起始位置等于0
if not stop:
stop = start
start = 0
while start < stop:
yield start
start += step
res = my_range(3)
for i in res:
print(i)
三、yield传值
yield传值是给函数体代码传参的第三种方式。第一种是形参传参,第二种是闭包函数传参。用.send(参数) = yield 的方式传。
def func(name):
print('哈哈哈哈哈哈')
while True:
a = yield
print('%s%s' % (a, name))
res = func('qql') # 第一次调用函数是转换成生成器,并不是执行函数
res.__next__() # 转换成生成器之后使用.__next__()方法转换成迭代器对象,后面就可以传值执行了
res.send('亲爱的') # 传参给yield执行代码
res.send('小宝贝') # 传参给yield执行代码
四、yield与return对比
1.yield:
后面跟返回值,支持多个并且组织成元组的形式返回
函数代码执行过程遇到yield会阻塞,不会直接结束,再次使用.next()会继续往下执行,直到遇到下一个yield会再次阻塞。
函数第一次调用会先转换成生成器,并且支持yield传参
2.return:
后面跟返回值,支持多个并且组织成元组的形式返回
函数体代码执行过程遇到return会直接结束
五、生成器表达式
生成器表达式内部代码只有在迭代取值的时候(使用__next__())才会执行。
迭代器对象和生成器对象都可以看成是工厂,为了节省空间,只有当我们索要数据的时候才会加工出数据。
语法结构:
res = (满足if条件后的变量 for i in 可迭代对象 if条件)
l = [11, 22, 33, 44, 55, 66, 77, 88, 99]
# 除了44之外,把列表的每个元素都加1生成新的列表
res = [i+1 for i in l if i != 44]
print(res)
小练习:
# 这是一个求和函数
def add(n, i):
return n + i
# 这个调用之前是函数 调用之后是生成器
def test():
for i in range(4): # i也就是0,1,2,3一共四个数
yield i
g = test() # 初始化生成器对象,第一次传参调用是不执行函数的,就只是把转化成生成器
for n in [1, 10]: # 注意这里只有两个参数1和10,不是1-9
g = (add(n, i) for i in g) # 这就是一个生成器生成式;传入1的时候不执行,传入10开始执行
# g = (add(n, i) for i in (add(n, i) for i in g))
# 第一轮for循环n=1不执行,因为生成器只有在迭代取值的时候才开始执行(使用__next__()方法后才开始执行)
# 第二轮for循环n=10开始执行函数代码,生成器里面的for循环第一次执行,i=0.
# 括号里面的g就是n=10,i=0执行函数后相加为10返回,然后给了i再执行括号for前面的函数n=10,i=后面的g也是10=20.
# 然后到生成器里面的第二次循环,i=1,n=10,先执行后面的g为10+1等于11,然后赋值给i再执行前面的函数n=10,i=11,g就变成21
# 然后到生成器里面的第三次循环,i=2,n=10,先执行后面的g为10+2等于12,然后赋值给i再执行前面的函数n=10,i=12,g就变成22
# 然后到生成器里面的第四次循环,i=3,n=10,先执行后面的g为10+3等于13,然后赋值给i再执行前面的函数n=10,i=13,g就变成23
res = list(g) # 把g的值组织成列表
print(res)
六、常见内置函数
# 1.abs() 绝对值
print(abs(123))
print(abs(-123))
# 2.all() any()
l = [11, 22, 33, 0]
print(all(l)) # 所有的元素都为True结果才是True
print(any(l)) # 所有的元素只要有一个为True结果就为True
# 3.bin() oct() hex() 进制数
print(bin(123))
print(oct(123))
print(hex(123))
# 4.bytes() str()
res = '金牌班 最牛逼'
res1 = bytes(res, 'utf8')
print(res1)
res2 = str(res1, 'utf8')
print(res2)
res1 = res.encode('utf8')
print(res1)
res2 = res1.decode('utf8')
print(res2)
# 5.callable() 是否可调用(查看能不能加括号运行)
s1 = 'jason'
def index():
pass
print(callable(s1),callable(index)) # False True
# 6.chr() ord()
print(chr(65)) # 根据ASCII码转数字找字符
print(ord('A')) # 65
# 7.complex() 复数
print(complex(123)) # (123+0j)
# 8.dir() 查看当前对象可以调用的名字
def index():
pass
print(dir(index))
print(index.__name__)
# 9.divmod() # 整除,余
print(divmod(101,10)) # 101整除10,余1》》》(10,1)
"""总数据100 每页10条 10页"""
"""总数据99 每页10条 10页"""
"""总数据101 每页10条 11页"""
num, more = divmod(233,10)
if more:
num += 1
print('总共需要%s页' % num)
# 10.eval()只能识别简单的语法 exec()可以识别复杂语法 都是将字符串中的数据内容加载并执行
res = """
你好啊
for i in range(10):
print(i)
"""
res1 = """
print('hello world')
"""
eval(res1)
exec(res1)
# 11.isinstance() 判断是否属于某个数据类型
print(isinstance(123, float)) # False
print(isinstance(123, int)) # True
# 12.pow() 次方
print(pow(4, 3)) # 4的3次方
# 13.round() 五舍六入,小数点后面小于5就舍,6及以上就入
print(round(4.8))
print(round(4.6))
print(round(8.5))
# 14.sum() 求和
l = [11, 22, 333, 44, 55, 66]
print(sum(l)) # 求列表l里面所有元素的和