Python函数基础,涉及到的有函数定义、参数传递、匿名函数和装饰器等等
函数基础
函数定义
语法
1 | def 函数名(参数): |
注意事项:
- 函数代码块以
def
关键词开头,一个空格之后接函数标识符名称和圆括号(),再接个冒号。 - 任何传入的参数必须放在圆括号中间,参数不必定义类型
- 函数的第一行语句后可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- 使用return结束函数。默认返回None。
- return语句依然在函数体内部,不能回退缩进。直到函数的所有代码写完,才回退缩进,表示函数体结束。
return可以返回什么?
- 什么都不返回,仅仅return:
return
- 数字/字符串/任意数据类型:
return 'hello'
- 一个表达式:
return 1+2
- 一个判断语句:
return 100 > 99
- 一个变量:
return a
- 一个函数调用:
return func()
- 甚至是返回自己!:
return self
- 多个返回值,以逗号分隔:
return a, 1+2, "hello"
简而言之,函数可以return几乎任意Python对象。
问题来了,怎么接收多个返回值呢?
那就用多个参数来接收
1 | def func(): |
参数的传递
参数传递的是变量参数代表的实际对象的地址,Python函数中参数的传递分两种情况
- 不可变的参数类型:例如数字,字符串,传进去,进去后再怎么操作,也不会影响外部的变量,因为传的是实际对象的地址,你函数拿到这个地址,你只能查,不能改,因为参数是不可变的
- 可变的参数类型:例如列表,把列表对象的实际地址传进去了,你函数拿到地址,可以查,还可以append,直接把列表里的内容改了
还有一个注意的地方,传参的时候要注意位置对应(可以指定参数名=值的这种方式,可以不按照参数位置来传参),还要注意自己传的数据类型对不对,因为Python是弱数据类型,传参的时候不会检查你传的数据类型是否正确,到执行到的时候才会抛异常
默认参数
函数传参支持默认参数,不过,默认参数要放在参数列表的最后面;如果有多个默认参数,那么最常用的默认参数要优先往前放,给个demo吧
1 | def print_hi(name, age, score='C', subject='English'): |
注意!默认参数尽量指定不可变的数据类型,如果可变的话,看代码
1 | def func(a=[]): |
第一次调用函数时会在内存中创建一个空的列表对象,然后a指向这个空列表对象,然后对这个对象append操作,后两次调用依然时对同一个列表对象进行操作,运行结果如下:
1 | ['A'] |
如何改造?
1 | def func(a=None): |
动态参数
动态参数顾名思义就是传入的参数是动态的,可以是0个1个2个3个或N个,动态参数要放在常规参数和默认参数后面
1.*args
一个星号表示接收任意个参数。调用时,会将实际参数打包成一个元组传入形式参数。如果参数是个列表,会将整个列表当做一个参数传入,如果想把这个列表中的值当作一个一个的参数传进函数咋传?往列表前加个*
号
1 | def func(*args): |
2.**kwargs
两个星表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典
1 | def func(**kwargs): |
1 | k1 v1 |
而如果我们这样传递一个字典dic呢?我们希望字典内的键值对能够像上面一样被逐一传入。往前面加俩星号就行
1 | def func(**kwargs): |
3.“万能”参数
当*args
和**kwargs
组合起来使用,理论上能接受任何形式和任意数量的参数,在很多代码中我们都能见到这种定义方式。需要注意的是,*args
必须出现在**kwargs
之前。
匿名函数
当我们在创建函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。这省去了我们挖空心思为函数命名的麻烦,也能少写不少代码,很多编程语言都提供这一特性。匿名函数用好了,会有画龙点睛的效果,没用好,就容易“画虎不成反类犬”,需要我们在平时的代码过程中,多学、多看、多琢磨。
Python语言使用lambda
关键字来创建匿名函数。
所谓匿名,即不再使用def
语句这样标准的形式定义一个函数。
- lambda只是一个表达式,而不是一个代码块,函数体比def简单很多。
- 仅仅能在lambda表达式中封装有限的逻辑。
- lambda 函数拥有自己的命名空间。
其形式通常是这样的:**lambda 参数: 表达式
**。
例如:lambda x: x * x
。它相当于下面的函数:
1 | def f(x): |
关键字lambda表示匿名函数,冒号前面的x表示函数参数,x*x是执行代码。
匿名函数只能有一个表达式,不用也不能写return语句,表达式的结果就是其返回值。 匿名函数没有函数名字,不必担心函数名冲突,节省字义空间。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
1 | lambda x: x * x f = |
也可以把匿名函数作为别的函数的返回值返回。
1 | def add(string, i): |
推导式
属于是高阶玩法了,语法糖,比较花里胡哨
列表推导式
列表推导式是一种快速生成列表的方式。其形式是用方括号括起来的一段语句,如下例子所示:
1
2
3
4
5lis = [x * x for x in range(1, 10)]
print(lis)
------------------------------------
结果:[1, 4, 9, 16, 25, 36, 49, 64, 81]相当于:
1
2
3
4
5lis = []
for i in range(1, 10):
lis.append(i*i)
print(lis)字典推导式
自然就是用花括号括起来的语句,快速生成字典
1
2
3
4
52 for x in (2, 4, 6)} dic = {x: x**
dic
{2: 4, 4: 16, 6: 36}
type(dic)
<class 'dict'>集合推导式
也是花括号,不过差别是集合没有key,只有value,字典是用冒号分开的
key:value
这种新式1
2
3
4
5for x in 'abracadabra' if x not in 'abc'} a = {x
a
{'d', 'r'}
type(a)
<class 'set'>元组推导式
不是用圆括号了,而是使用
tuple()
括号里放推导式这种形式创建1
2
3
4
5
6
7
8tup = tuple(x for x in range(9))
print(tup)
print(type(tup))
------------------------
结果:
(0, 1, 2, 3, 4, 5, 6, 7, 8)
<class 'tuple'>
迭代器
在介绍迭代器之前,先说明下迭代的概念:
迭代:通过for循环遍历对象的每一个元素的过程。
Python的for语法功能非常强大,可以遍历任何可迭代的对象。
在Python中,list/tuple/string/dict/set/bytes都是可以迭代的数据类型。
可以通过collections模块的Iterable类型来判断一个对象是否可迭代:
1 | from collections import Iterable |
迭代器
迭代器是一种可以被遍历的对象,并且能作用于next()函数。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。迭代器通常要实现两个基本的方法:iter()
和 next()
。
字符串,列表或元组对象,甚至自定义对象都可用于创建迭代器:
1 | 1,2,3,4] lis=[ |
或者使用for循环遍历迭代器:
1 | lis = [1,2,3,4] |
很多时候,为了让我们自己写的类成为一个迭代器,需要在类里实现__iter__()
和__next__()
方法。
总结:Python的迭代器表示的是一个元素流,可以被next()函数调用并不断返回下一个元素,直到没有元素时抛出StopIteration
错误。可以把这个元素流看做是一个有序序列,但却不能提前知道序列的长度,只能不断通过next()函数得到下一个元素,所以迭代器可以节省内存和空间。
迭代器(Iterator)
和可迭代(Iterable)
的区别:
- 凡是可作用于for循环的对象都是可迭代类型;
- 凡是可作用于next()函数的对象都是迭代器类型;
list、dict、str
等是可迭代的但不是迭代器,因为next()函数无法调用它们。可以通过iter()
函数将它们转换成迭代器。- Python的for循环本质上就是通过不断调用
next()
函数实现的。
生成器
不太明白
装饰器
必须要懂,跟Spring里面的AOP
有点类似,可以对一个函数加一个环绕通知,在函数前和函数后加一些逻辑
作为许多语言都存在的高级语法之一,装饰器是你必须掌握的知识点。
装饰器(Decorator):从字面上理解,就是装饰对象的器件。可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出。装饰器有很多种,有函数的装饰器,也有类的装饰器。装饰器在很多语言中的名字也不尽相同,它体现的是设计模式中的装饰模式,强调的是开放封闭原则。装饰器的语法是将@装饰器名,放在被装饰对象上面。
1 |
|
装饰器无参数
装饰器的一般写法,被装饰的方法带不带参数都可以,因为用了万能参数*args, **kwargs
1 | def auth(func): |
跑一下:
1 | if __name__ == '__main__': |
装饰器带参数
1 | def action(is_auth=True, is_log=True): |
跑一下:
1 | if __name__ == '__main__': |