整理------函数
函数参数顺序
位置参数 > *args > 默认值参数 > **kwargs
动态参数的另一种传参方式 : 在实参位置上给一个序列,列表,可迭代对象前面加个*表示把这个序列按顺序打散
l = [11,22,33,44] s = "臣妾做不到" def fun(*args): print(args) fun(*l) #fun打印结果为: (11, 22, 33, 44) fun(*s) #fun打印结果为: ('臣', '妾', '做', '不', '到')
在形参的位置上的* 表示把接收到的参数组合成一个元组
如果是一个字典, 那么也可以打散. 不过需要用两个*
def fun(**kwargs): print(kwargs) dic = {'a':1, 'b':2} fun(**dic) # {'a': 1, 'b': 2}
函数的注释:
def chi(food, drink): """ 这里是函数的注释, 先写一下当前这个函数是干什么的, 比如我这个函数就是一个吃 :param :param food: 参数food是什么意思 :param :param drink: 参数drink是什么意思 :return :return: 返回的是什么东东 """ print(food, drink) return "very good"
命名空间
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始
的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕而被清空.
命名空间分类:
1. 全局命名空间--> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间
2. 局部命名空间--> 在函数中声明的变量会放在局部命名空间
3. 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序:
1. 内置命名空间
2. 全局命名空间
3. 局部命名空间(函数被执行的时候)
取值顺序:
1. 局部命名空间
2. 全局命名空间
3. 内置命名空间
我们可以通过globals()函数来查看全局作用域中的内容, 也可以通过locals()来查看局部作
用域中的变量和函数信息
print(globals()) #{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00466590>, '__spec__': None,
'__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/ZYP/PycharmProjects/python_ui/test_tools/test.py', '__cached__': None} def func(): a = 10 print(locals()) #{'a': 10} func()
对于可变数据类型可以直接进行访问. 但是不能改地址. 说白了. 不能赋值
l = [11,22,33,44] def fun(): l.append(55) print(l) # [11, 22, 33, 44, 55] fun() print(l) # [11, 22, 33, 44, 55]
a = 2 def w(): a +=1 print(a) w() #报错,不能赋值
迭代器
闭包
闭包就是内层函数, 对外层函数(非全局)的变量的引用. 叫闭包
def func1(): name = "zyp" def func2(): print(name) # 闭包 func2() func1() 结果: zyp
我们可以使用__closure__来检测函数是否是闭包. 使用函数名.__closure__返回cell就是闭包. 返回None就不是闭包
def func1(): name = "zyp" def func2(): print(name) # 闭包 func2() print(func2.__closure__) func1() 结果: zyp
闭包的作用就是让一个变量能够常驻内存. 供后面的程序使用
生成器和迭成器表达式
生成器
什么是生成器.生成器其实就是迭代器
在python中有三种方式来获取生成器:
1. 通过生成器函数
2. 通过各种推导式来实现生成器
3. 通过数据的转换也可以获取生成器
def func(): print("111") return 222 ret = func() print(ret) 结果: 111 222
将函数中的return换成yield就是生成器
def func(): print("111") yield 222 ret = func() print(ret) 结果: <generator object func at 0x10567ff68>
运行的结果和面⾯不一样. 为什么呢. 由于函数中存在了yield. 那么这个函数就是一个生成器函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了. 而是获取这个生成器我们可以直接执行__next__()来执行以下生成器.
def func(): print("111") yield 222 ret = func() print("ret",ret) a = ret.__next__() print("a",a) 结果 ret <generator object func at 0x0209AF60> 111 a 222
那么我们可以看到, yield和return的效果是一样的. 有什么区别呢? yield是分段来执行一个函数. return呢? 直接停止执行函数.
def func(): print("111") yield 222 print("333") yield 444 gener = func() ret = gener.__next__() print(ret) ret2 = gener.__next__() print(ret2) ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也就是 说. 和return⽆关了. print(ret3) 结果: 111 Traceback (most recent call last): 222 333 File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in <module> 444 ret3 = gener.__next__() # 最后⼀个yield执⾏完毕. 再次__next__()程序报错, 也 就是说. 和return⽆关了. StopIteration
当程序运行完最后一个yield. 那么后面继续进行__next__()程序会报错.
生成器有什么作用呢?
占内存少,用多少取多少
send和__next__()⼀样都可以让生成器执行到下一个yield.
def eat(): print("我吃什么啊") a = yield "馒头" print("a=",a) b = yield "饼" print("b=",b) c = yield "菜盒" print("c=",c) yield "GAME OVER" gen = eat() # 获取⽣成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("胡辣汤") print(ret2) ret3 = gen.send("狗粮") print(ret3) ret4 = gen.send("猫粮") print(ret4) 结果 我吃什么啊 馒头 a= 胡辣汤 饼 b= 狗粮 菜盒 c= 猫粮 GAME OVER
send和__next__()区别:
1. send和next()都是让生成器向下走一次
2. send可以给上面个yield的位置传递值, 不能给最后一个yield发送值. 在第一次执行生成器代码的时候不能使用send()
列表推导式, 生成器表达式以及其他推导式
列表推导式
lst = [i for i in range(1, 15)] print(lst)
常用写法:
[ 结果 for 变量 in 可迭代对象]
我们还可以对列表中的数据进行筛选筛选模式:
[ 结果 for 变量 in 可迭代对象 if 条件 ]
# 获取1-100内所有的偶数 lst = [i for i in range(1, 100) if i % 2 == 0] print(lst)
生成器表达式和列表推导式的语法基本上是一样的. 只是把[]替换成()
gen = (i for i in range(10)) print(gen) 结果: <generator object <genexpr> at 0x106768f10>
打印的结果就是一个⽣生成器. 我们可以使用for循环来循环这个生成器:
for i in gen: print(i)
生成器表达式也可以进行筛选:
# 获取1-100内能被3整除的数 gen = (i for i in range(1,100) if i % 3 == 0) for num in gen: print(num)
生成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存. 一次性加载. 生成器表达式几乎不占用内存. 使用的时候才分
配和使用内存
2. 得到的值不一样. 列表推导式得到的是一个列表. 生成器表达式获取的是个生成器
深坑==> 生成器. 要值得时候才拿值.
字典推导式:
dic = {"a":1,"b":2} new_dic = {dic[key]:key for key in dic} print(new_dic) #{1: 'a', 2: 'b'}
lst1 = ['11', '22', '33'] lst2 = ['aa', 'bb', 'cc'] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic) #{'11': 'aa', '22': 'bb', '33': 'cc'}
集合推导式:
集合推导式可以帮我们直接生成一个集合. 集合的特点: 无序, 不重复. 所以集合推导式自带去重功能
lst = [1, -1, 8, -8, 12] s = {abs(i) for i in lst} #绝对值abs print(s) # {8, 1, 12}
总结: 推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式