Python 函数:写了这么多年,这几个参数用法你未必全用过
函数是 Python 里最基础的代码组织方式。把一段逻辑包起来,起个名字,需要的时候调用一下——就这么简单。
但 Python 函数的参数系统比大多数语言都灵活,位置参数、默认参数、关键字参数、*args、\kwargs**,一套下来能覆盖各种调用场景。
最基本的:定义和调用
用 def 定义一个函数,括号里放参数,冒号后面缩进写函数体。
1 | def say_hello(): |
带参数的:
1 | def print_max(a, b): |
跟 Java 的方法比起来,Python 函数有几个明显不一样的地方:
- 不需要声明返回值类型
- 可以返回多个值(本质是返回元组)
- 参数传递非常灵活(下面重点讲)
参数系统:五种参数,各有各的用途
1. 位置参数:最基础的,按顺序传
最基本的参数类型,调用时必须按定义顺序传递。
1 | def introduce(name, age, city): |
没什么好说的,大多数函数都是这么写的。缺点就是调用的时候必须记住顺序,参数多了容易搞混。
2. 默认参数:给参数一个”备用值”
1 | def greet(name, greeting="Hello"): |
这个特性解决的是:大多数情况下用默认值,偶尔需要定制。比如日志函数,默认级别是 INFO,但特殊场景可以传 ERROR。
注意一个坑:默认参数在函数定义时就被创建了,如果用可变对象(列表、字典)当默认值,会出问题。 后面会说怎么解决。
默认参数必须放在位置参数后面:
1 | # 这样不行,语法错误 |
3. 关键字参数:调用的时候指名道姓
1 | def describe_person(name, age, city): |
关键字参数的好处是调用的时候清晰,参数多了不容易搞混。而且可以只传一部分参数(配合默认参数用)。
位置参数必须在关键字参数之前:
1 | describe_person("Bob", city="Shanghai", age=22) |
4. *args:收拢多余的位置参数
场景:你写一个求和函数,但不确定调用的时候会传几个数。
1 | def sum_numbers(*args): |
另一个实际场景:日志函数,第一个参数是日志级别,后面跟着任意数量的消息:
1 | def log(level, *messages): |
*args 解决的是:函数参数数量不固定的情况。
5. **kwargs:收拢多余的关键字参数
场景:你要写一个通用的配置函数,调用的时候可能传各种不同的配置项。
1 | def print_user_info(**kwargs): |
**kwargs 解决的是:函数参数名称和数量都不固定的情况。
这五个参数可以组合使用,顺序是固定的:
1 | def func(位置参数, 默认参数, *args, 关键字参数, **kwargs): |
一个完整的例子:
1 | def example(a, b=10, *args, c=20, **kwargs): |
这个顺序记不住也没关系,IDE 会提示,但理解”位置参数先、然后是默认、然后是收尾”的逻辑就够了。
函数的返回值:单个、多个、或者不返回
返回单个值:
1 | def add(a, b): |
返回多个值:实际上返回的是元组,但 Python 会自动解包
1 | def get_user(): |
这个特性写起来很爽,函数返回一堆相关的值,调用方一行就能拆开。
没有 return,默认返回 None:
1 | def do_nothing(): |
作用域:变量在哪儿能用
局部变量:函数里面定义的,外面用不了
1 | def my_func(): |
全局变量:外面定义的,函数里面可以读
1 | global_var = "我是全局的" |
想修改全局变量?用 global 声明
1 | count = 0 |
不加 global 的话,你在函数里给 count 赋值,Python 会认为你在创建一个同名的局部变量,全局的 count 不受影响。
模块:把函数放进别的文件里
写一个 my_module.py:
1 | def greet(name): |
在另一个文件里导入它:
1 | # 方式1:导入整个模块 |