0%

函数

Python 函数:写了这么多年,这几个参数用法你未必全用过

函数是 Python 里最基础的代码组织方式。把一段逻辑包起来,起个名字,需要的时候调用一下——就这么简单。

但 Python 函数的参数系统比大多数语言都灵活,位置参数、默认参数、关键字参数、*args、\kwargs**,一套下来能覆盖各种调用场景。

最基本的:定义和调用

def 定义一个函数,括号里放参数,冒号后面缩进写函数体。

1
2
3
4
def say_hello():
print("Hello, Python!")

say_hello() # 调用,记得加括号

带参数的:

1
2
3
4
5
6
7
def print_max(a, b):
if a > b:
print(f"{a} 更大")
else:
print(f"{b} 更大")

print_max(5, 7) # 7 更大

跟 Java 的方法比起来,Python 函数有几个明显不一样的地方:

  • 不需要声明返回值类型
  • 可以返回多个值(本质是返回元组)
  • 参数传递非常灵活(下面重点讲)

参数系统:五种参数,各有各的用途

1. 位置参数:最基础的,按顺序传

最基本的参数类型,调用时必须按定义顺序传递。

1
2
3
4
def introduce(name, age, city):
print(f"{name}, {age}岁, 来自{city}")

introduce("Alice", 20, "Beijing") # 必须按顺序

没什么好说的,大多数函数都是这么写的。缺点就是调用的时候必须记住顺序,参数多了容易搞混。

2. 默认参数:给参数一个”备用值”

1
2
3
4
5
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")

greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!

这个特性解决的是:大多数情况下用默认值,偶尔需要定制。比如日志函数,默认级别是 INFO,但特殊场景可以传 ERROR。

注意一个坑:默认参数在函数定义时就被创建了,如果用可变对象(列表、字典)当默认值,会出问题。 后面会说怎么解决。

默认参数必须放在位置参数后面:

1
2
3
# 这样不行,语法错误
def func(a=1, b):
pass

3. 关键字参数:调用的时候指名道姓

1
2
3
4
def describe_person(name, age, city):
print(f"{name}, {age}岁, 来自{city}")

describe_person(age=20, name="Alice", city="Beijing") # 顺序随便

关键字参数的好处是调用的时候清晰,参数多了不容易搞混。而且可以只传一部分参数(配合默认参数用)。

位置参数必须在关键字参数之前:

1
2
describe_person("Bob", city="Shanghai", age=22)  
# describe_person(city="Shanghai", "Bob", age=22) # 错误, 位置参数不能在关键字后面

4. *args:收拢多余的位置参数

场景:你写一个求和函数,但不确定调用的时候会传几个数。

1
2
3
4
5
6
def sum_numbers(*args):
# args 是一个元组,装着你传进来的所有位置参数
return sum(args)

sum_numbers(1, 2, 3) # 6
sum_numbers(10, 20, 30, 40) # 100

另一个实际场景:日志函数,第一个参数是日志级别,后面跟着任意数量的消息:

1
2
3
4
5
6
7
def log(level, *messages):
for msg in messages:
print(f"[{level}] {msg}")

log("INFO", "用户登录", "IP: 192.168.1.1")
# [INFO] 用户登录
# [INFO] IP: 192.168.1.1

*args 解决的是:函数参数数量不固定的情况

5. **kwargs:收拢多余的关键字参数

场景:你要写一个通用的配置函数,调用的时候可能传各种不同的配置项。

1
2
3
4
5
6
7
8
9
10
def print_user_info(**kwargs):
# kwargs 是一个字典
for key, value in kwargs.items():
print(f"{key}: {value}")

print_user_info(name="Alice", age=20, city="Beijing", hobby="reading")
# name: Alice
# age: 20
# city: Beijing
# hobby: reading

**kwargs 解决的是:函数参数名称和数量都不固定的情况

这五个参数可以组合使用,顺序是固定的:

1
2
def func(位置参数, 默认参数, *args, 关键字参数, **kwargs):
pass

一个完整的例子:

1
2
3
4
5
6
7
8
9
10
11
def example(a, b=10, *args, c=20, **kwargs):
print(f"a={a}, b={b}")
print(f"args: {args}")
print(f"c={c}")
print(f"kwargs: {kwargs}")

example(1, 2, 3, 4, 5, c=30, d=40, e=50)
# a=1, b=2
# args: (3, 4, 5)
# c=30
# kwargs: {'d': 40, 'e': 50}

这个顺序记不住也没关系,IDE 会提示,但理解”位置参数先、然后是默认、然后是收尾”的逻辑就够了。

函数的返回值:单个、多个、或者不返回

返回单个值:

1
2
def add(a, b):
return a + b

返回多个值:实际上返回的是元组,但 Python 会自动解包

1
2
3
4
def get_user():
return "Alice", 20, "Beijing" # 等价于 return ("Alice", 20, "Beijing")

name, age, city = get_user() # 解包赋值

这个特性写起来很爽,函数返回一堆相关的值,调用方一行就能拆开。

没有 return,默认返回 None:

1
2
3
4
5
def do_nothing():
pass

result = do_nothing()
print(result) # None

作用域:变量在哪儿能用

局部变量:函数里面定义的,外面用不了

1
2
3
4
5
6
def my_func():
local = "我是局部变量"
print(local)

my_func() # 正常
# print(local) # 报错,NameError

全局变量:外面定义的,函数里面可以读

1
2
3
4
5
6
global_var = "我是全局的"

def my_func():
print(global_var) # 可以访问

my_func()

想修改全局变量?用 global 声明

1
2
3
4
5
6
7
8
count = 0

def increment():
global count # 告诉 Python:我要改的是全局变量
count += 1

increment()
print(count) # 1

不加 global 的话,你在函数里给 count 赋值,Python 会认为你在创建一个同名的局部变量,全局的 count 不受影响。

模块:把函数放进别的文件里

写一个 my_module.py

1
2
3
4
5
def greet(name):
return f"Hello, {name}!"

def add(a, b):
return a + b

在另一个文件里导入它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方式1:导入整个模块
import my_module
my_module.greet("Alice")

# 方式2:起个别名
import my_module as mm
mm.greet("Alice")

# 方式3:只导入需要的
from my_module import greet, add
greet("Alice")

# 方式4:导入全部(不推荐,容易命名冲突)
from my_module import *
greet("Alice")

欢迎关注我的其它发布渠道