目标
- 掌握 Python 函数–定义、参数类型
- 掌握 Python 函数–变量作用域
正确理解 Python 函数,能够更好地理解 Python 装饰器、匿名函数(lambda)、函数式编程等高阶技术。
文章较长,整体内容包含如下思维导图
第一部分 重新认识函数
1. 定义
Python 中函数的定义如下:
注意点:
- 函数代码块以
def关键词
开头,一个空格之后接函数标识符名称
和圆括号()
,再接个冒号
。
- 圆括号中间存放参数
- 使用 return 结束函数。默认返回 None。
>>> def sum(a, b): ... return a + b ... >>> sum
<function sum at 0x10af4f158>
>>> dir(sum) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] >>> sum.__name__ 'sum' >>> sum.__module__ '__main__' >>>
|
2 调用函数
使用函数名后跟圆括号的方式调用函数。
调用的同时要根据函数的定义体,提供相应个数和类型的参数
,每个参数之间用逗号分隔。
3. 函数是对象
在 Python 中万物皆为对象,函数也不例外,
- 可以作为对象可以赋值给一个变量
- 可以作为元素添加到集合对象中
- 可作为参数值传递给其它函数
- 可以当做函数的返回值
函数身为一个对象,拥有对象模型的三个通用属性:id、类型、和值。
>>> def add(a, b): ... return a + b ... >>> id(add) 4472644200 >>> type(add) <class 'function'> >>> add <function add at 0x10a972268>
|
变量赋值
>>> newAdd = add >>> newAdd(1, 2) 3 >>> add is newAdd True
|
其实上侧的,只是不同的名字指向同一个内存地址,同时这块地址被多次引用。
了解了函数是对象,以及上侧的几个性质,更得牢记一切都是对象。
既然是对象,性质都类似,其余的集合、参数、返回值,思考下就明白了
第二部分 参数类型
其实就是下侧那个括号里的数据,但是需要注意的是,传的是什么
,怎么传
定义
完整的语法
func(positional_args, keyword_args, *tuple_nonkw_args, **dict_kw_args)
|
函数的参数的分类
- 位置参数(必选参数)
- 默认参数
- 可变参数 *args
- 可变参数 **kwargs
- 必选参数
1. 位置参数
定义:调用
时候的传的参数
必须与定义
时的参数
一一对应。
注意事项:
不能多也不能少。
>>> def sum(a, b): ... return a + b
>>> print(sum(1, 2)) 3
>>> print(sum(1)) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sum() missing 1 required positional argument: 'b'
>>> print(sum(b=10, a=4)) 14
|
2. 默认值
定义:在函数声明的时候,可以给某个参数指定默认值
注意事项:
位置:需要放在位置参数
之后
默认参数需要在位置参数的后边
def power(x, n = 2): return x**n ret1 = power(10) ret2 = power(10, 4) ret3 = power(n=1, x=100)
|
来个例子
主要涉及:内存地址、参数默认值
def func(a=[]): a.append("A") return a
print(func()) print(func()) print(func())
|
三思
三思
三思
答案如下:
['A'] ['A', 'A'] ['A', 'A', 'A']
|
如果真正理解了,Python 对象的变量名与内存地址,这个问题还是很简单的。
- 默认参数 a 指向的空列表对象就会被创建,假如内存地址为
0x123
;
- 之后每次调用,都会在原地址后追加
A
;
- 可以试着在函数内部打印 a 的内存地址,应该是一致的。
如何避免这种操作?
让 a 的默认值指向一个不可变对象即可,如:数字、None 等
3. 可变参数 *args
在 python 里面,函数在声明的时候,参数中可以使用(变量名)的方式来接受不确定长度的参数
,但是在 python 里面大家约定俗成使用`args接受不定长参数,之后将这些参数放到一个
tuple`里面,可以通过访问 args 来获取这些不定长参数。
例子一
>>> def changeFunc(*args): ... print(type(args)) ... print(args) ... >>> changeFunc("a", "b", 3) <class 'tuple'> ('a', 'b', 3)
|
例子二
>>> changeFunc(["a", "b", 3]) <class 'tuple'> (['a', 'b', 3],)
|
为何打印出来是这样?
因为是数组是作为一个整体传过来的。
如何将 list 的元素转换为 tuple 的元素?结果与例子一一致?
答案: 参数前加个星号,解压参数列表.
>>> tmpList = ["a", "b", 3] >>> print(*tmpList) a b 3 >>> print(tmpList) ['a', 'b', 3]
>>> changeFunc(*tmpList) <class 'tuple'> ('a', 'b', 3)
|
当参数已经在列表或元组中
但需要为需要单独位置参数的函数, 需要解压参数, 然后再一一传进去
针对字典带上*号,解压的是 key
>>> dict = {"a": 1, "b": 2, "c": 3} >>> print(*dict) a b c
|
4. 可变参数 *args
定义:*args
参数表示未知的位置参数序列长度,而\*\*kwargs
代表包含关键字和值映射关系的字典,它可以包含任意数量的关键字和值映射,并且在函数定义中“*args”必须位于“**kwargs”前面。
*args 必须在**kwargs 之前。
默认值参数必须再必选参数之前。
>>> def kwargs(first, second=2, *args, **kwargs): ... print(first, second) ... print(args) ... print(kwargs) ...
>>> kwargs(1, 3, "a", "b", "c", {"key1": "value"}) 1 3 ('a', 'b', 'c', {'key1': 'value'}) {} >>> list = ["a", "b", "c"] >>> dict = {"key1": "value"}
>>> kwargs(1, 3, *list, **dict) 1 3 ('a', 'b', 'c') {'key1': 'value'}
|
>>> def test(**kw, error): File "<stdin>", line 1 def test(**kw, error): ^ SyntaxError: invalid syntax
|
5. 关键字参数
位置参数可以不带参数名,但是星号后边的就必选带上参数名
>>> def func(loction, second, *, must): ... print(loction, second, must) ... >>> func(1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: func() takes 2 positional arguments but 3 were given
>>> func(1, 2, must=3) 1 2 3
|
来一个汇总的骚操作
>>> def student(name, age=18, *args, sex, classroom, **kwargs): ... print(name, age, args, sex, classroom, kwargs) ...
>>> student("howard") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: student() missing 2 required keyword-only arguments: 'sex' and 'classroom'
>>> student("howard", sex="1", classroom="python") howard 10 () 1 python {}
>>> dict {'key1': 'value'} >>> student("howard", sex="1", classroom="python", **dict) howard 10 () 1 python {'key1': 'value'}
|
本文重点注意事项:
- 牢记函数为第一类对象;
- 可选参数注意解压参数;
参考
函数作为参数的问题
Python 中的函数详解