基本使用

基本使用.png

函数定义和调用阶段的坑

  1. ### 判断以下函数是否会报错
  2. # 1.
  3. def foo():
  4. bar()
  5. print("from foo")
  6. def bar():
  7. print("from bar")
  8. foo()
  9. """
  10. 答案:不会报错,因为在foo()被调用之前,bar和foo都已经定义了,有了对应的内存空间
  11. """
  12. # 2.
  13. def foo():
  14. bar()
  15. print("from foo")
  16. foo()
  17. def bar():
  18. print("from bar")
  19. """
  20. 答案:会报错,在foo()时,bar并没有被定义,所以找不到对应的内存地址
  21. """

函数的参数

函数的参数.png

默认参数

  1. ### 写出以下函数的输出结果
  2. m=2
  3. def func(x,y=m): # y==>2的内存地址
  4. print(x,y)
  5. m=33
  6. func(1)
  7. """
  8. 答案:1 2
  9. - 原因:默认参数的值是在函数定义阶段被赋值的
  10. - 过程:在定义时y=m,其实是y指向了2的内存地址,所以后面定义的m,与定义时无关了。
  11. """
  12. m = [1111]
  13. def func(x, y=m):
  14. print(x, y)
  15. m.append(33)
  16. func(1)
  17. """
  18. 答案:1 [1111,33]
  19. - y=[]的内存地址,append并不会改变地址,只是原地添加
  20. - 修好不要使用可变类型,因为这样可能受其他语句影响
  21. """

args和*kwargs

*和被用到形参中**

  1. # *args :位置形参
  2. # **kwargs:关键字形参
  3. def func(x, y, *args):
  4. print(x, y, args)
  5. func(1, 2, 3, 4, 5, 6)
  6. """
  7. 输出:1 2 (3, 4, 5, 6)
  8. - *:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组赋值给紧随其后的变量名args
  9. """
  10. def func(x, y, *args, **kwargs):
  11. print(x, y, args, kwargs)
  12. func(1, 2, 3, 4, k=5, z=6, m=7)
  13. """
  14. 输出: 1 2 (3, 4) {'k': 5, 'z': 6, 'm': 7}
  15. """

*和被用到实参中**

  1. # 实参中带*,可以先*将值打散成位置实参
  2. 应用:将l中的内容空格隔开输出,最后一个不要空格
  3. l = [1, 2, 3, 4, 5, 6]
  4. print(*l)
  5. >>> 1 2 3 4 5 6
  6. ## **示例
  7. def func(k1, k2, k3):
  8. print(k1, k2, k3)
  9. dic = {"k1": 3, "k2": 2, "k3": 3}
  10. func(*dic) # func('k1','k2','k3')
  11. func(**dic) # func(k1=1,k2=2,k3=3) # 这里是关键字参数,形参和实参的名字要相同
  12. print(*dic) # k1 k2 k3
  13. print(**dic) # 报错==>因为print中并没有对应可以接收关键字的key,所以会报错
  1. def test(k1, k2, k3):
  2. print("===>test", k1, k2, k3)
  3. dic = {"k1": 3, "k2": 2, "k3": 3}
  4. test(*dic) # func('k1','k2','k3')
  5. test(**dic) # func(k1=1,k2=2,k3=3)
  6. def func(x1, x2, x3): # 这里的参数需要是data3,data2,data1
  7. print(x1, x2, x3)
  8. dic = {"data3": 1, "data2": 2, "data1": 3}
  9. func(*dic)
  10. func(**dic)
  11. ***********运行结果***********
  12. ===>test k1 k2 k3
  13. ===>test 3 2 3
  14. data3 data2 data1
  15. Traceback (most recent call last):
  16. File "D:/mycode/test_top/direction1/leetcode_49.py", line 11, in <module>
  17. func(**dic)
  18. TypeError: func() got an unexpected keyword argument 'data3'
  19. Process finished with exit code 1

形参和实参中都带或*

  1. ### 形参和实参中都带*
  2. def func(x, y, *args):
  3. print(x, y, args)
  4. func(1, 2, [3, 4, 5, 6])
  5. func(1, 2, *[3, 4, 5, 6])
  6. >>>1 2 ([3, 4, 5, 6],) # 注意,若元组中只有一个元素,需要加逗号
  7. >>>1 2 (3, 4, 5, 6)
  8. ### 形参和实参中都带**
  9. def func(x,y,**kwargs):
  10. print(x,y,kwargs)
  11. func(**{'y':222,'x':111,'a':333,'b':444})
  12. >>>111 222 {'a': 333, 'b': 444}
  1. # 定义
  2. def index(x, y):
  3. print(x, y)
  4. def wrapper(*args, **kwargs): # 形参: args=(1,) kwargs={'y':2,'z':3}
  5. index(*args, **kwargs) # 实参:index(*(1,),**{'y':2,'z':3})
  6. 解析为:index(1,z=3,y=2)
  7. # 调用
  8. wrapper(1, z=3, y=2)
  9. """
  10. - 调用wrapper函数需要遵循index的参数规则(目前多了z=3,会报错)
  11. - 参数不能多,不能少
  12. """

命名关键字参数(了解)

  1. # 命名关键字参数:在定义函数时,*后定义的参数,如下所示:
  2. def fun(x,y,*,a,b):
  3. print(x,y)
  4. print(a,b)
  5. """
  6. 命名关键字参数必须按照key=value的形式为其传值
  7. """
  8. >>> func(1,2,b=222,a=111)

名称空间和作用域

名称空间和作用域.png

gloabl和nonlobal

  1. # 示例一:局部和全局使用相同的名字
  2. x = 111
  3. def foo():
  4. x = 222
  5. print("===>foo:", x) # 222
  6. foo()
  7. print("===>main:", x) # 111
  8. # 示例2 :通过局部去修改全局变量的值,需要使用global(注:不可变类型)
  9. x = 111
  10. def foo():
  11. global x # 声明该x为全局的x,不需要新的内存空间了
  12. x = 222
  13. print("===>foo:", x) # 222
  14. foo()
  15. print("===>main:", x) # 222
  16. # 示例3 :通过局部去修改全局变量的值(可变类型可以直接改)
  17. x = [111]
  18. def foo():
  19. x.append(222) # 原地修改的,所以没有global问题不大
  20. print("===>foo:", x) # [111,222]
  21. foo()
  22. print("===>main:", x) # [111,222]
  1. # 示例1:函数外层仍为函数
  2. x = 1
  3. def func1():
  4. x = 2
  5. def func2():
  6. x = 3
  7. print("====>func2:", x) # 3
  8. func2()
  9. print("====>func1:", x) # 2
  10. func1()
  11. print("====> main:", x) # 1
  12. # 示例2:
  13. x = 1
  14. def func1():
  15. x = 2
  16. def func2():
  17. nonlocal x # 声明该x为当前函数外部函数里的x(不会上升到全局变量,若外部函数没有则报错)
  18. x = 3
  19. print("====>func2:", x) # 3
  20. func2()
  21. print("====>func1:", x) # 3
  22. func1()
  23. print("====> main:", x) # 1
  24. # 示例3:
  25. x = 1
  26. def func1():
  27. #######
  28. def func2():
  29. nonlocal x # 声明该x为当前函数外部函数里的x(不会上升到全局变量,若外部函数没有则报错)
  30. x = 3
  31. print("====>func2:", x)
  32. func2()
  33. print("====>func1:", x)
  34. func1()
  35. print("====> main:", x)
  36. >>> SyntaxError: no binding for nonlocal 'x' found
  37. """
  38. nonlocal:会从当前函数的局部往外层套着的函数里查找,一直到外层函数不再套函数为止。
  39. - 可变类型依然不需要声明
  40. """

装饰器

装饰器.png

函数对象

  1. def func():
  2. print("===>func")
  3. """
  4. 函数对象:即把函数当成变量名使用
  5. - 不加括号去调用,而是直接传函数名
  6. - func ==> 本质是内存地址
  7. """
  8. # 1. 函数对象:可以被赋值
  9. f = func
  10. print(f, func) # 两个相同的内存地址
  11. # 2. 函数对象:可以作为函数的参数
  12. def foo(x):
  13. print("===>foo", x)
  14. x() # func仍然可以被调用
  15. foo(func)
  16. # 3. 函数对象:可以作为另外一个函数的返回值
  17. def new(x):
  18. return x
  19. res = new(func)
  20. print(res)
  21. res()
  22. print(res()) # None(该函数没有返回值)
  23. # 4. 函数对象:可以作为容器中的元素
  24. ls = [func, 2]
  25. ls[0]()
  26. dic = {"k1": func}
  27. dic["k1"]()

函数对象的应用案例

  1. def login():
  2. print("登录成功")
  3. def tranfer():
  4. print("转账记录")
  5. def check_banlance():
  6. print("查询余额")
  7. def withdraw():
  8. print("提现")
  9. while True:
  10. print("q==>退出\n0==>登录\n1==>转账\n2==>余额\n3==>提现")
  11. choice = input("请输入对应编号已完成对应功能>>>").strip()
  12. if choice == "q":
  13. break
  14. if not choice.isdigit():
  15. print("请输入正确的编号")
  16. continue
  17. elif choice == "0":
  18. login()
  19. elif choice == "1":
  20. tranfer()
  21. elif choice == "2":
  22. check_banlance()
  23. elif choice == "3":
  24. withdraw()
  25. else:
  26. print("该编号不存在")
  27. """
  28. - if...elif...else过长,过于冗余
  29. - 另外如果新加功能的话,需要从新写if...else...
  30. """
  1. def login():
  2. print("登录成功")
  3. def tranfer():
  4. print("转账记录")
  5. def check_banlance():
  6. print("查询余额")
  7. def withdraw():
  8. print("提现")
  9. dic = {
  10. ### 注:这里使用元组可能更好,因为列表是可变类型,元组是不可变类型
  11. 'q': ['退出', None],
  12. '0': ['登录', login],
  13. '1': ['转账', tranfer],
  14. '2': ['余额', check_banlance],
  15. '3': ['提现', withdraw]
  16. }
  17. while True:
  18. for key in dic:
  19. print('%s:%s' % (key, dic[key][0]))
  20. choice = input("请输入对应编号已完成对应功能>>>").strip()
  21. if choice == "q":
  22. break
  23. if not choice.isdigit():
  24. print("请输入正确的编号")
  25. continue
  26. else:
  27. if dic.get(choice):
  28. dic.get(choice)[1]()
  29. else:
  30. print("该编号不存在")

all 和 any 的用法

在写torch.fx时,源码中的用法

  1. patterns = set([operator.add, torch.add, "add"])
  2. # Go through all the nodes in the Graph
  3. for n in traced.graph.nodes:
  4. # If the target matches one of the patterns
  5. if any(n.target == pattern for pattern in patterns):
  6. # Set the insert point, add the new node, and replace all uses
  7. # of `n` with the new node
  8. with traced.graph.inserting_after(n):
  9. new_node = traced.graph.call_function(torch.bitwise_and, n.args, n.kwargs)
  10. n.replace_all_uses_with(new_node)
  11. # Remove the old node from the graph
  12. traced.graph.erase_node(n)
  13. # Don't forget to recompile!
  14. traced.recompile()
  15. """
  16. any(iterator): iterator种有一个满足即为True
  17. all(iterator): iterator种有全部满足才为True
  18. """

闭包函数

  1. """
  2. 闭包函数=名称空间与作用域+函数嵌套+函数对象
  3. """
  4. # 1. 名称空间与作用域+函数嵌套(目前还没有使用函数对象)
  5. def f1():
  6. x = 33
  7. def f2():
  8. print("f2中的x:", x)
  9. f2()
  10. x = 111
  11. f1() # "f2中的x:", 33
  12. # 2.加上函数对象
  13. def f1():
  14. x = 33
  15. def f2():
  16. print("f2中的x:", x)
  17. return f2
  18. x = 111
  19. f1()() # "f2中的x:", 33 ==>这里是两个括号相当于 f2()
  20. '''
  21. 函数传参有两种方式:可能会遇到一种传参不满足需求的情况
  22. '''
  23. # 1. 方式1:形参
  24. def func(x):
  25. print(x)
  26. func(1)
  27. # 2.方式二:闭包函数
  28. def f1(x):
  29. # x=3
  30. def func():
  31. print(x)
  32. return func
  33. f2=f1(3)
  34. f2()

装饰器详解

装饰器属于名称空间与作用域+函数对象+函数嵌套+闭包函数的综合应用

装饰器的引导过程

  1. """
  2. 现有一个函数,本身的功能是两数之和
  3. 需求:现在要求输出函数的运行时间
  4. """
  5. import time
  6. def add(x, y):
  7. time.sleep(2)
  8. print(x + y)
  9. """
  10. 方法一: 在add函数内部直接修改
  11. - 但是在程序庞大的情况下,直接修改可能导致错误,因此尝试不修改原函数但添加该功能
  12. """
  13. def add(x, y):
  14. start = time.time()
  15. time.sleep(2)
  16. print(x + y)
  17. stop = time.time()
  18. print(stop - start)
  19. add(1, 2)
  20. """
  21. 方法二: 在调用该函数前后修改
  22. - 问题:若该函数调用多次,那则需要在每一个函数调用前后都加上该代码
  23. - 容易造成代码冗余
  24. """
  25. start = time.time()
  26. add(2, 3)
  27. stop = time.time()
  28. print(stop - start)
  29. start = time.time()
  30. add(4, 5)
  31. stop = time.time()
  32. print(stop - start)
  33. """
  34. 优化方法二:将代码冗余部分写成函数
  35. - 1. 将冗余部分写入函数体,每次去调用新写的函数
  36. - 2. 但是参数会被写死,进一步改进参数(*args,**kwargs)
  37. - 3. 参数修改完成了,但是被装饰的函数add写死了(闭包改进)
  38. """
  39. # 第二步更新后
  40. def wrapper(*args,**kwargs):
  41. start = time.time()
  42. add(*args, **kwargs)
  43. stop = time.time()
  44. print(stop - start)
  45. wrapper(2,3)
  46. # 第三步更新后
  47. def outter(func):
  48. # func=add
  49. def wrapper(*args, **kwargs):
  50. start = time.time()
  51. func(*args, **kwargs)
  52. stop = time.time()
  53. print(stop - start)
  54. return wrapper # 返回的是wrapper的内存地址
  55. # 若是被调用后又赋值给了add,那么原来的add在使用上完全没改变,但功能多了
  56. add = outter(add)
  57. # 用户使用新的add(该add是被wrapper装饰过的add,内存地址由原来的add变成了现在的wrapper)
  58. add(3, 4)
  59. # 新定义一个函数名为mul,让该函数也输出运行时间
  60. def mul(x, y):
  61. time.sleep(2)
  62. print(x * y)
  63. # 那么调用方式会变成
  64. mul = outter(mul)
  65. mul(3, 4)
  66. """
  67. 方法二的进一步优化:
  68. - 在以上的写法中我们都没有考虑返回值的问题
  69. - 但其实,修改过程需要函数本身不变、函数调用方式不变、返回值也不变
  70. - 因此可以进一步改写装饰器,该方法也为装饰器的常用写法
  71. """
  72. def outter(func):
  73. def wrapper(*args, **kwargs):
  74. start = time.time()
  75. res = func(*args, **kwargs)
  76. stop = time.time()
  77. print(stop - start)
  78. return res # 为被装饰的函数func的返回值
  79. return wrapper # 返回的是wrapper的内存地址
  80. mul = outter(mul)
  81. res = mul(3, 4)
  82. print(res)

装饰器语法糖

  1. import time
  2. def outter(func):
  3. def wrapper(*args, **kwargs):
  4. start = time.time()
  5. res = func(*args, **kwargs)
  6. stop = time.time()
  7. print(stop - start)
  8. return res # 为被装饰的函数func的返回值
  9. return wrapper # 返回的是wrapper的内存地址
  10. @outter #该语法等于 add=outter(add)
  11. def add(x, y):
  12. time.sleep(2)
  13. return x + y
  14. @outter
  15. def mul(x, y):
  16. time.sleep(2)
  17. return x * y
  18. # add = outter(add)
  19. res = add(3, 4)
  20. print(res)
  21. # mul = outter(mul)
  22. res = mul(3, 4)
  23. print(res)
  24. """
  25. 原本代码中:
  26. add = outter(add)
  27. mul = outter(mul)
  28. 可以使用 @装饰器名称 写在被装饰函数的前面
  29. """

装饰器补充

  1. def add(x, y):
  2. """两数相加"""
  3. time.sleep(2)
  4. return x + y
  5. print(add.__name__) # 该函数原函数的名称
  6. print(help(add)) # 帮助文档,看的是文档的注释信息===> 原函数.__doc__
  7. 返回结果:
  8. >>>add
  9. >>>两数相加
  1. @outter
  2. def add(x, y):
  3. """两数相加"""
  4. time.sleep(2)
  5. return x + y
  6. print(add.__name__)
  7. print(help(add))
  8. 返回结果:
  9. >>>wrapper
  10. >>>None
  11. """
  12. 出现原因:
  13. - 在add函数被装饰过后,即使原函数不变、调用方式不变、返回值不变,但是还有函数的内置属性
  14. - 如果想进一步使装饰过后的函数,内置属性也相同需要在写装饰器时加上内置属性
  15. """
  16. def timmer(func):
  17. def wrapper(*args, **kwargs):
  18. pass
  19. wrapper.__name__ = func.__name__
  20. wrapper.__doc__ = func.__doc__
  21. return wrapper
  22. # 但是这种写法没有办法写全所有的内置属性,还很麻烦,因此python提供了库
  23. """最终写法:"""
  24. from functools import wraps
  25. def timmer(func):
  26. @wraps(func)
  27. def wrapper(*args, **kwargs):
  28. res = func(*args, **kwargs)
  29. return res
  30. return wrapper

装饰器模板

  1. from functools import wraps
  2. def outter(func):
  3. @wraps(func) # 使得func函数的内置属性不变
  4. def wrapper(*args, **kwargs):
  5. """添加新功能"""
  6. res = func(*args, **kwargs)
  7. """添加新功能"""
  8. return res # 为被装饰的函数func的返回值
  9. return wrapper # 返回的是wrapper的内存地址
  10. """
  11. 注意:闭包函数最后返回的是wrapper函数对象,并没有加括号调用
  12. - 方便记忆,可以理解为:被装饰的函数func原本是在 全局变量 里的,经过闭包写在了 局部变量里,
  13. 因此最后需要把被装饰的函数返回到 全局变量 里
  14. """
  1. #套三层
  1. 加载顺序自下而上
  2. 运行顺序自上而下
  3. def func1(add):
  4. print("func1")
  5. def wrapper1(*args, **kwargs):
  6. print("wrapper1")
  7. res = add()
  8. return res
  9. return wrapper1
  10. def func2(add):
  11. print("func2")
  12. def wrapper2(*args, **kwargs):
  13. print("wrapper2")
  14. return add()
  15. return wrapper2
  16. @func1
  17. @func2
  18. def func():
  19. print("hello world")
  20. func()
  21. """
  22. func2
  23. func1
  24. wrapper1
  25. wrapper2
  26. hello world
  27. """

迭代器

迭代器.png

  1. # 循环取值:这种取值办法不依赖索引
  2. ls = [1, 2, 3]
  3. ls_iter = ls.__iter__()
  4. """ls下有__iter__,是可迭代对象"""
  5. print(ls_iter)
  6. """
  7. ls_iter:调用了可迭代对象下的__iter__转为迭代器对象
  8. 迭代器对象.__next__:可以得到迭代器的下一个值
  9. """
  10. # 方法一:
  11. value = ls_iter.__next__()
  12. print(value) # 1
  13. value = ls_iter.__next__()
  14. print(value) # 2
  15. value = ls_iter.__next__()
  16. print(value) # 3
  17. # value = ls_iter.__next__()
  18. # print(value) # StopIteration
  19. # 方法二:使用while迭代
  20. while True:
  21. try:
  22. value = ls_iter.__next__()
  23. print(value)
  24. except StopIteration:
  25. break # 抛出异常后退出循环即可
  26. """
  27. 可迭代对象:字符串、列表、元组、字典、集合、文件对象
  28. 迭代器对象:文件对象
  29. """
  30. # 方法3: for循环
  31. # 文件使用迭代器
  32. with open('a.txt', mode='rt', encoding='utf-8') as f:
  33. # f.__iter__()
  34. # f.__next__()
  35. for line in f: # f.__iter__()
  36. print(line)

生成器

引入

生成器:本质是一个自定义的迭代器。也包含了__iter____next__

  1. # 引入
  2. def func():
  3. print("aaa")
  4. return 'a'
  5. res=func()
  6. print(res)
  7. """
  8. 如果使用的是return,我们调用这个函数时,便会出现调用结果
  9. 但是如果使用的是yield,调用该函数的时候,其实并没有运行
  10. """
  11. # 生成器例子
  12. def func():
  13. print("第一次")
  14. yield 1
  15. print("第二次")
  16. yield 2
  17. print("第三次")
  18. yield 3
  19. print("第四次")
  20. g = func()
  21. print(g) # 迭代器对象
  22. g = g.__iter__() # iter(迭代器对象)之后还是本身
  23. print(g)
  24. """
  25. 生成器:
  26. - 函数调用之后赋值给一个值,这个值是生成器对象。
  27. - 内置有__iter__和__next__方法
  28. - 使用next(迭代器对象)就会被赋值
  29. - 但是会在遇见yield时停下来,将yield后的值,当作本次调用的返回值
  30. - yield后没有值的话,默认为None
  31. """
  32. # 方法一:
  33. while True:
  34. try:
  35. value=g.__next__()
  36. print(value)
  37. except StopIteration:
  38. break
  39. # 方法二:
  40. for value in g:
  41. print(value)

yield

  1. yield与return一样用在函数中(上述例子就是)
  2. yield用在表达式中(并发编程的时候使用的比较多) ```python def student(name): print(“你的名字是:%s” % name) while True:
    1. x = yield None
    2. print('你的名字是 %s, 今年 %s 岁了' % (name, x))

gen = student(‘python’) print(gen) #

gen.send(‘19’) # can’t send non-None value to a just-started generator

gen.send(None) # 你的名字是:python

gen.send(“18”) # 你的名字是 python, 今年 18 岁了 gen.send(‘20’) # 你的名字是 python, 今年 20 岁了 gen.close() # 关闭生成器

gen.send(‘12’) # 关闭之后在传值会导致错误

“”” yield在表达式中:

  • yield在表达式中会接收send()传入的值
  • 并且一开始传入的值只能是None
  • 该生成器可以使用close()关闭,但是关闭之后不可以再传值了
  • gen.send() 就相当于 g = next(gen) “”” ```

    解析式

    生成式.png

    列表、字典、集合、生成器

    ```python

    1. 列表生成式

    l = [‘wxx_py’, ‘yxx_py’, ‘exx_py’, ‘a_java’, ‘b_java’] new_ls = [name for name in l if name.endswith(‘py’)] print(new_ls) “””
  1. 将l中的元素转为大写
  2. 去掉后缀py “”” new_ls = [name.upper() for name in l] new_ls = [name[:-2] for name in l if name.upper().endswith(‘PY’)]

    for name in l:

    if name.upper().endswith(‘PY’):

    name = name.upper()[:-2]

    new_ls.append(name)

    print(new_ls)

2.字典生成式(key:value必须有)

items = [(‘name’, ‘egon’), (‘age’, 18), (‘gender’, ‘male’)] dic = {name[0]: name[1] for name in items} dic_new = {k: v for k, v in items} print(dic) print(dic_new)

3.集合生成式

key = [‘name’, ‘age’, ‘gender’] set1 = {value for value in key if value != ‘gender’} print(set1)

4. 生成器表达式

g = (i for i in range(10) if i > 7) print(g, type(g)) # g是一个生成器,此时g中没有东西,需要使用next print(next(g)) # 8 print(next(g)) # 9

  1. <a name="TFuo7"></a>
  2. ### 生成器解析式案例
  3. ```python
  4. 文件字符数总和(注:\n也属于一个字符)
  5. with open('../file_n.txt', mode="rt", encoding='utf-8') as f:
  6. # 方法一:
  7. res = 0
  8. for line in f:
  9. res += len(line)
  10. print(res)
  11. # 方法二:
  12. res = [len(line) for line in f]
  13. print(res)
  14. # 方法三:效率最高
  15. res = sum(len(line) for line in f)
  16. print(res)
  17. """
  18. 方法3的效率最高是因为生成器的内部是没有内容的,不然如果是列表还需要占空间占内存
  19. """