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关键字阻塞也会报错

image

二、自定义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)

image

三、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执行代码

image

四、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)

image

小练习:

# 这是一个求和函数
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)

image

六、常见内置函数

image

# 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里面所有元素的和
posted @ 2021-11-22 20:12  90啊  阅读(132)  评论(0编辑  收藏  举报