python函数
python函数
函数啊函数
多解决问题,踩的坑多了,就有经验了
- 函数作用: 以功能(完成一件事)为导向的代码块,一个函数就是一个功能.
- 随调随用,不用不调
- 减少代码重复性,增强了代码可读性
- 函数的结构与调用:
- def func(args):..expr..return a,b
- 函数名 加上 括号 才执行
- return的功能:
- 1-结束函数,函数里面的while循环内部遇到return也会跳出.
- 2-给函数外部返回一个或多个值(元组). 返回就是传给一个变量一个值
- 调用一次函数,就执行一次
- 函数的返回值
- return 单个值 type是值本身
- return 多个值 是以元组的形式返回给函数的执行者
- 没有return 实际返回是None
- 一个函数可以有多个return –根据条件
- 函数的参数\传参
- 形参角度 : 位置参数, 默认参数, 仅限关键字参数, 万能参数
- 实参角度 : 位置参数, 关键字参数, 混合参数
- 形参的顺序 : 位置参数, *args, 默认参数, 仅限关键字参数, **kwargs
- 位置参数: 按顺序一一对应
- 关键字参数:一一对应
- 混合参数: 位置参数一定要在关键字前面
- 默认参数的设置是经常用的
- 万能参数args/**kwargs : 函数定义时,代表聚合元组和字典,执行时代表打散,拆成元组和字典
- 形参角度的参数顺序: (位置参数 *args, 默认参数, 关键字参数, **kwargs)
- 定义函数时括号参数加* ** 代表聚合
- 执行函数时括号加* ** 代表打散 表体里的参数加* ** 也是打散
def func(*args, **kwargs): print(args) print(kwargs) func(*'abc', *[1, 2, 3], *[66, 77], **{'a': 1, 'b': 2}, **{'tt': 100, 'yy': 101}) ## ('a', 'b', 'c', 1, 2, 3, 66, 77) ## {'a': 1, 'b': 2, 'tt': 100, 'yy': 101} *的另类用法 list1 = [1,2,3,4,5,6] a, b, *c = list1 print(a, b, c) # 1, 2, [3,4,5,6] *x, y = list1 print(x, y) = [1,2,3,4,5], 6
- 三元运算符–列表的和字典的三元表达式
- aa = list[:2] if expr else 1
- return a if a>b else b
- list1 = [a for a in range(10)] # 列表三元表达式
- dict1 = {key: value for key, value in enumerate(list1)} # 字典三元表达式
- 名称空间 和 作用域
- 内置名称空间: python内置函数的预置空间print input ord chr id isinstance….
- 全局名称空间: 记录变量名与值/函数名与函数体的对应关系 py文件root就是全局空间
- 局部名称空间:执行时开辟,函数运行结束关闭….也叫临时空间 +
- 加载顺序: 内置–全局–局部
- 取值顺序: 局部–全局–内置 就近原则
- 作用域: 2个 全局域和局部域
- 全局作用域–内置名称空间和全局名称空间
- 局部作用域–局部名称空间
- 局部作用域可以”引用”全局作用域的变量,但不能修改这个变量
- local–eclose–global–builtin ==>LEGB就近原则
- 函数的嵌套–高阶函数–函数里面套函数
- 循环一个列表,实际是按索引去循环的,不要在循环时改变列表的大小.出错.
- 内置函数–
- globals() 返回的是字典: 字典里面的键值对:全局作用域的所有内容
- locals() 返回的是字典: 字典里面的键值对:当前作用域的所有内容
a=1 b=2 def func(): c=3 print(locals()) print(globals()) func() ###========----------内容如下 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001393FED4820>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:\\software\\python\\pythonProject\\fuxitigao\\1106.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x000001393FE13EB0>} --- {'c': 3}
- 默认参数的一个陷阱–坑.—-如果默认参数为可变列表?? (列表竟然是异次空间一直存在…)
+如果你的默认参数指向的是可变数据类型,那么无论你调用多少次这个默认参数,都是同一个.def func(name, alist=[]): alist.append(name) return alist ret1 = func('张三') print(id(ret1), ret1) # 2185581476224 ['张三'] import time time.sleep(2) # 停了2秒 ret2 = func("李四") print(id(ret2), ret2) # 2185581476224 ['张三', '李四'] 结果还是列表2个值
- 关键字–nonlocal global
- global
- 在局部作用域声明一个全局变量 e.g.把函数内部定义的变量声明为全局变量
- 在局部作用域可以修改全局作用域的变量 e.g. 把函数外部定义的变量 声明为函数内部可修改的.(限于字符串,数字)。
- 以上两个要在心中记出具体场景 (限于字符串,数字)。
- 只有当函数执行后,函数内部声明的全局变量,才可以被全局调用.“`
count = 1 def func(): global count count += 1 print(count) # 1 #此时函数体没有执行,不起作用 func() print(count) # 2 #此时函数体已经执行,起作用了 值被修改
- nonlocal 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
- nonlocal 不能操作全局变量,只能在局部域的各层之间,用于内层引用外层函数定义的变量
- global
- 函数名的运用—-这个函数名的运用–非常重要
- 函数名 + 括号 ,就能执行这个函数
- 函数名实际是个变量,指向一个内存地址 ,实际是这个内存地址加括号去执行函数体
- 函数名可以做列表和字典的元素 e.g. 传入一个列表3个函数,循环list后i()执行
- 函数名可以做为函数的参数 e.g. def abc() func(abc)
- 函数名可以作为函数的返回值
def func1(): print("in func1") def func2(x): x() print("in func2") return x ret = func2(func1) ret() #执行结果-------------- in func1 # 执行func2(func1)的运行结果 in func2 # 执行func2(func1)的运行结果 in func1 ###--返回值
- 格式化输出
- f”我叫{name.upper().center(30,”=”)}, 今年{age:02f} ” 太他妈好用了
- f”我叫{dict[‘name11’]}, 今年{dict[‘age’} ” 可以取值
- f’今年多少岁{age ** 2}’ 可以放表达式
- f’最终计算结果是{sum(list_A)}‘ 可以放函数
- 迭代器:
- 可迭代对象
- 内部含有__iter__方法的,就是可迭代对象
e.g. str1 = 'abcde / str1.__iter__
- 目前的可迭代对象:str list tuple dict set range 文件句柄
- 内部含有__iter__方法的,就是可迭代对象
- 获取对象的方法
str1 = 'abc' / print(dir(str1))
会显示这个对象的所有方法,是个列表 - 判断一个对象是否是可迭代对象
if __iter__ in dir(object)
返回True就是可迭代对象 - 小结: 能直接显示,拥有方法比较多,但非常占内存,不能for循环(目前能循环是for内部我的淘宝转化)
- 迭代器定义
- 内部含有__iter__和__next__方法的,就是迭代器
- 判断一个对象是否是迭代器
__iter__和__next__ in dir(object
目前只有文件句柄是迭代器 - 迭代器的取值 迭代器可以直接取值,可迭代对象通过for循环并经转化后也可取值。
- 可迭代对象如何转化成迭代器—- iter方法(ob)–next(ob)
s = 'abcde' s1 = iter(s) # 发音器函数iter 生成一个迭代器 (也可以s1.__iter__转成迭代器) s2 = next(s1) print(next(s1)) # a next生成的是第1个值 print(next(s1)) # b next生成的是第2个值 print(next(s1)) # c next生成的是第3个值 ---另一个方法生成迭代器 用自身的__iter__ 两种方法是一样的 s3 = s.__iter__() s4 = s3.__next__() print(s4) # a s4 = s3.__next__() print(s4) # b s4 = s3.__next__() print(s4) # c
- while循环模拟for循环机制 面试经常考while模拟for
l1 = [11,22,333,44,515] obj = iter(l1) while 1: try: print(next(obj)) except StopIteration: break
这块代码要背过….硬背 - 小结:
- 特征:内部含有__iter__和__next__方法
- iter(iteration)可将可迭代对象转成迭代器,next(object)可以取值
- 迭代器的优点:
- 节省内存。 迭代器在内存中相当于只占一个数据的空间:因为每次取值都上一条数据会在内存释放,加载当前的此条数据。
- 惰性机制。 next一次,取一个值,绝不过多取值。
- 有一个迭代器模式可以很好的解释上面这两条:迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。
- 迭代器的缺点:
- 速度慢
- 不能直观的查看里面的数据。
- 取值时不走回头路,只能一直向下取值。
- 可迭代对象与迭代器的对比
- 可迭代对象:是一个私有的方法比较多,操作灵活(比如列表,字典的增删改查,字符串的常用操作方法等),比较直观,但是占用内存,而且不能直接通过循环迭代取值的这么一个数据集。
- 应用:当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
- 迭代器: 是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
- 应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)。
- 可迭代对象
- 占位符: pass,,,,先占位,先把总体理清,有思路或者时间后再所内容填充上.
- 生成器:
-
什么是生成器? 在python,生成器与迭代器是一个东西.本质一样.唯一区别是:生成器是我们自己用python代码生成的数据结构,迭代器都是提供的或者转化得来的.
- 获取生成器的三种方式
- 生成器函数 s = (i**2 for i in range(100))
- 生成器表达式-自己写的 function + yield
- python内部提供了一些.
- 获取生成器的三种方式
-
yield
- 一个yield,对应一个next
- return 和yield的区别:
-
yield 有什么作用?———-转迭代器的时候有可能会先把内存爆,但是在定义生成器的时候不会.
- 大文件转成迭代器,用iter生成,需要先调到内存把内存撑爆再转,比较危险.
- 真正的节省内存
-
yield return的区别
- return 结束函数,给执行者返回值
- 只要函数中有yield,它就是生成器.
- 生成器函数中可以存在多个yield,一个yield对应一个next
- yield不会结束函数
- 生成器实际上就是一只会下蛋的母鸡, 只有list\next\for循环才会执行下蛋
-
yield from: 优化了内层循环,提高了效率. 一行yield from 相当于多行yield
def func4(): l1 = [1,2,3,4,5] yield from l1 #yield from相当于把各项聚合了,等价于分开各行写 ret = func4() print(next(ret)) # 1 print(next(ret)) # 2 print(next(ret)) # 3 将这个列表变成了一个迭代器返回
-
- 生成器表达式 列表推导式
- 列表推导式 就是用一行代码构建一个有规律比较复杂的列表
- 循环模式: list1 = [ i**2 for i in range(1, 100)/dict / set / str] –这个例子是错的,应是())
- 筛选模式: list2 = [i for i in range(1,100) if i % 2 ==0]
list3 = [i**2 for i in range(1,11)] list4 = [i for i in range(1,101) if i % 2 ==0] list4 = [i for i in range(2,100,2)] list5 = [f'python{i}期' for i in range(1,101)] # 列表套列表,,,有难度 names = [['tom', 'billy', 'jefferson', 'andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # 找到嵌套列表中名字含有两个e的所有名字 (有难度) names2 = [] for i in names: for ii in i: if ii.count('e') >= 2: names2.append(ii) print(names2) # ['ABCDE', 'ZHANGSAN', 'LISI', 'MONIKA'] # 神奇的推导式, names3 = [name.upper() for i in names for name in i if name.count('e') >= 2] print(names3) # ['JEFFERSON', 'WESLEY', 'STEVEN', 'JENNIFER']
- 生成器表达式
- 把列表生成式的方括号 换成 小括号 ,就成了生成器
- 也有筛选模式,也可以循环套循环
- 列表生成式占内存,生成器表达式节省内存.
- 好好弄清一筐鸡蛋和一只母鸡的区别. 表现形式是什么?
## 列表的方括号换成小括号 ,就成了生成器表达式 ```js list5 = (i for i in range(10)) print(list5, type(list5)) for i in list5: print(i)
list5 = (i for i in range(10)) print(list5, type(list5)) # <generator object <genexpr> at 0x000001DFE485A810> <class 'generator'> for i in list5: print(i) # 直接把迭代器循环打印出来,节省内存 # -----------------看代码写结果 v = [i % 2 for i in range(10)] # 这是一个列表推导式 print(v) v = (i % 2 for i in range(10)) # 这是一个生成器表达式 print(v) for i in v: # 可以直接使用for循环直接取数 print(i)
- 列表推导式\迭代器\生成器 —小结
- 1.有毒: 列表推导式只能构建比较复杂且有规律的列表,不要太着迷
- 2.超过三层循环才能构建的,不建议用列表推导式
- 3.排错不行,不能debug
- 11.简单,一行搞定
- 12.装逼
- 21.怎么让生成器产生值? < 重要–next, for循环, list直接转化 这3个方法才能让迭代器下蛋 重要 >
- 列表推导式 与 生成器表达式 的区别
- 写法上: [] () 其他完全一样
- 生成器是自己写的,迭代器是iter转化的
- 字典的推导式
# 字典生成式 list7 = ['name', 'age', 'class'] list8 = ['张三', '18', '三'] dic1 = {list7[i]: list8[i] for i in range(len(list7))} print(dic1) # {'name': '张三', 'age': '18', 'class': '三'} dic2 = {key:value for value, key in enumerate(list7)} # {'name': 0, 'age': 1, 'class': 2} print(dic2)
##1\ 迭代器取值的面试题 默写 ``` def demo(): for i in range(4): yield i haha = demo() t1 = (i for i in haha if i <2) t2 = (i for i in t1) print(list(t1)) #这里已经取完值了,迭代器已经走到了最后 print(list(t2)) # 迭代器已经到了最后,取不到值,所以为空 ``` ##2\ 生成器最难的题 明白原理与运行的时间 ```js def add(n,i): return n+i def test(): for i in range(4): yield i g=test() for n in [1,10]: g = (add(n,i) for i in g) print(g) print(list(g)) # [20, 21, 22, 23] # 我的理解 # 1\开始运行后, for 循环之前的都没运行 # 2\for循环执行了2次,每次生成两个g的地址 // 因为生成器只有next list for循环才会执行 # 3\第1次n=1,第2次n=10,但是第2次的n把第1次的n覆盖了,n=10 --关键 # 4\开始list后,运行的g是for循环第2次的g, in g为第一次循环的g, in g取的是生成器[0,1,2,3] # 5\画图理解的快 ``` ![img.png](img.png)
- 集合的推导式
- 匿名函数: 一句话函数,比较简单的函数
- 示例 lambda a,b(形参) : a+b 返回 # 冒号前是形参,冒号后是返回值
mm = lambda a, b: a+b #mm相当于函数名, lambda相当于def, a,b相当于形参 print(mm(1,3)) # 4 # 两个题 # 写匿名函数:接收一个可切片的数据,返回索引为0与2的对应的元素(元组形式)。 la1 = lambda x:(x[0], x[2]) ss = la1('abcde') print(ss) # ('a', 'c') # 写匿名函数:接收两个int参数,将较大的数据返回。 la2 = lambda a, b: a if a>b else b print(la2(55, 66)) # 66
- lambda经常与内置函数结合,经常用
- 内置函数
- 71个内置函数 要经常练习一下
** eval() # ** help() # *** callable() # int() # float() # bin() # oct() # hex() # **divmod() # 分页时使用 # **round() # pow() x的y次幂 / 对单取余 # ***bytes() # s = s.encode('utf-8') # s1 = bytes(s1,'utf-8') # **ord() # **chr() 可应用于随机验证码,超出ascii找unicode,ascii只有128个字符 # print(ord("中")) # print(chr(20013)) # ***repr() 原形毕露函数,debug调试时很有用/格式化输出时也用到 # all() # any() # abs() # enumerate() # filter() # map() # open() # range() # print() # len() # list() # dict() # 创建字典的七种方式 要熟悉 //直接创建 元组解构 关键字定义 fromkeys updates 字典推导式 # str() # float() # reversed() # set() # sorted() # sum() 可以设置初始值 # tuple() # type() # ***zip() # dir() # ###################最重要的,最牛的,加key的内置函数 # max() # min() # reduce() 累加效果from functools import reduce
求绝对值最小的值对应的键 min函数 key和lambda组合使用
``` key的用法很玄妙啊 dic = {'a':3,'b':-2,'c':1} # 求最小的键 # 求最小的键对应的值 # 求最小的值 # 求绝对值最小的值 # 求最小的值对应的键 # 求绝对值最小的值对应的键 print("--") num1 = min(dic) print(num1) # a求最小的键 num2 = dic[min(dic)] print(num2) # 3 这是求最小的键对应的值 num3 = min(dic.values()) print(num3) # -2 求最小的值 num4 = min(dic.values(), key=abs) print(num4) # 1 求绝对值最小的值 num5 = min(dic, key=lambda x:dic[x]) print(num5) # b 值最小的key num6 = min(dic, key = lambda x: abs(dic[x])) print(num6) # c 绝对值最小的值对应的key ```
- 闭包
- 闭包只能存在于嵌套函数中
- 内层函数对外层函数非全局变量的引用(使用), 就会形成闭包
- 被引用的非全局变量称为自由变量,与内层函数有一个绑定关系
- 自由变量不会在内存中消失
- 闭包的作用: 保证数据安全,自由变量在内存中不会消失,而且全局还引用不到。
- 闭包的定义:
- 闭包是嵌套在函数中的函数。
- 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
- 闭包应用在哪里?
- 可以保存一些非全局变量但是不易被销毁、改变的数据。
- 装饰器。
这是一个典型的闭包... def func(): l1 = [] def inner(num): l1.append(num) total_amount = sum(l1) return total_amount/len(l1) return inner haha = func() l1 = haha(100) print(l1) # 100.0 l2 = haha(150) print(l2) # 125.0 #### 装饰器肯定是闭包? 如果装饰器内层函数没有调用外层函数变量呢 #### 利用函数自带方法打印查看有无自由变量,有自由变量,即是闭包 print(haha.__code__.co_freevars) # ('l1',) # 发现自由变量 #### 打印出一个自由变量,那这肯定就是一个闭包. print(func().__code__.co_freevars)
- 字符串 列表 元组 按索引取值时的小坑
## 字符串 列表 元组 按索引取值时的小坑 ```js s1 = '1,2,3' # print(s1[10]) # 会报错,string index out of range print(repr(s1[10:])) # '' 结果是空,不报错 print(s1[10:]) # '' 结果是空,不报错 ```
- 装饰器 — 完美体现了开放封装原则
- 装饰器就是一个函数, 实际也就5行,有的人半年都没理解…
- 装饰器用在哪? 有什么作用?
- 装饰器本质:闭包 –>自由变量
def deco(func): def inner(*args, **kwargs): res = func(*args, **kwargs) return res return inner ---------------------- def deco(func): # 标准装饰器模版 ...为所欲为 你想做啥 def inner(*args, **kwargs): ...为所欲为 你想做啥 res = func(*args, **kwargs) # func函数在此处被执行,等号右边 ...为所欲为 你想做啥 return res # 返回func的return结果 return inner # inner = deco(func) ---- @deco # f = deco(f) 这一步非常重要,,加了语法糖,就是转义赋值 def f(a): print(111,a) f(123) #实际执行的是wrapper(f)(123)
- 怎么得到函数的名字? (func._ name _)
- 怎么得到现在的时间? import time/ now_time = time.strftime(“%Y-%m-%d %H:%M:%S”,time.localtime())