函数
随着程序变大,你会在多个地方写出相同的逻辑。函数让你只写一次逻辑,给它命名,然后到处使用。在一个地方修复它,每一次调用都会自动获得修复。
def greet(name):
return f"Hello, {name}!"
print(greet("小明")) # "Hello, 小明!"
print(greet("小红")) # "Hello, 小红!"写一次,到处使用,一处修复。
定义函数
def 关键字开启一个函数定义,后面跟着名称、括号、冒号和缩进的函数体。函数在被调用之前什么都不做。用 def 定义它,然后用名称加 () 调用它。
def say_hello():
print("Hello!")
say_hello() # 调用函数参数与实参
参数是你的函数期望的输入。在括号内列出它们。当你调用函数时,你传入的值会按顺序与参数匹配。
def greet(name, greeting):
print(f"{greeting}, {name}!")
greet("小明", "Hello") # "Hello, 小明!"
greet("小红", "Hi") # "Hi, 小红!"参数 vs 实参
参数是函数定义中的名称。实参是你调用函数时传入的实际值。在实际使用中,人们常常混用这两个词。阅读文档时知道这个区别即可。
默认值
你可以给参数赋一个默认值。如果调用者没有提供该实参,就使用默认值。带默认值的参数必须位于没有默认值的参数之后。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("小明") # "Hello, 小明!"
greet("小明", "Hi") # "Hi, 小明!"带默认值的参数必须位于没有默认值的参数之后。
关键字参数
调用函数时,你可以为实参命名。这让调用变得可读,尤其对于有许多参数的函数,并且可以以任何顺序传递它们。
def describe_player(name, score, level):
print(f"{name} | Score: {score} | Level: {level}")
describe_player("小明", 87, 5) # 位置参数
describe_player(name="小明", level=5, score=87) # 关键字,任意顺序
describe_player("小明", level=5, score=87) # 混合:位置参数在前返回值
return 将一个值返回给调用者。没有 return,函数会返回 None。一旦 return 执行,函数立即退出。该块中后续的任何代码都会被跳过。
def add(a, b):
return a + b
result = add(3, 4) # result = 7
print(result)return 也会立即退出函数。该块中之后的任何代码都不会运行。
返回多个值
Python 允许通过用逗号分隔来返回多个值。调用者会收到它们作为一个元组,并可以在一行中将它们解包到不同的名称中。
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([3, 7, 1, 9, 4])
print(low, high) # 1 9low, high = ... 语法是解包:Python 将每个返回值赋给相应的名称。
作用域
在函数内部创建的变量只存在于该函数内部。你无法从外部看到它们。在所有函数之外定义的变量在任何地方都可见,但如果没有显式声明,你无法在函数内部修改它们。
def calculate():
result = 42 # 该函数的局部变量
return result
calculate()
print(result) # NameError, result 在外面不存在count = 0
def increment():
global count # 声明你想修改全局变量
count += 1
increment()
print(count) # 1使用 global 应该是最后的手段。它使代码更难推理。优先使用传入值并返回值的方式。
*args 和 **kwargs
有时你不知道函数会接收多少个实参。*args 将任意数量的位置实参收集到一个元组中。**kwargs 将任意数量的关键字实参收集到一个字典中。args 和 kwargs 这两个名称只是约定;星号才是关键。
def total(*args):
return sum(args)
total(1, 2, 3) # 6
total(1, 2, 3, 4, 5) # 15def display(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
display(name="小明", score=87, level=5)你可以将它们与常规参数混合使用。常规参数排在最前面:
def describe(title, *tags, **metadata):
print(f"{title} | tags: {tags} | meta: {metadata}")
describe("Python intro", "beginner", "python", author="小明", year=2024)文档字符串
文档字符串是函数顶部的一个字符串,描述它做什么。Python 编辑器和工具用它来在你悬停在函数调用上时显示帮助。使用三引号,对简单函数写一行。
def normalise(value, min_val, max_val):
"""Scale a value to the 0-1 range given the known min and max."""
return (value - min_val) / (max_val - min_val)def build_url(base, version, resource, *, secure=True):
"""
Build an API endpoint URL.
Returns a fully-qualified URL string. If secure is False,
the URL will use http instead of https.
"""
scheme = "https" if secure else "http"
base = base.replace("https://", "").replace("http://", "")
return f"{scheme}://{base}/{version}/{resource}"为任何从名称和签名无法明显自解释的函数写一个文档字符串。
类型提示
类型提示让你注释函数期望和返回的类型。Python 在运行时不强制执行它们,但编辑器使用它们在你运行任何东西之前捕获错误。冒号前的 -> 指定返回类型。
def greet(name: str, score: int) -> str:
return f"{name} scored {score}"def log(message: str) -> None:
print(f"[LOG] {message}")def top_scores(scores: list[int], n: int) -> list[int]:
return sorted(scores, reverse=True)[:n]类型提示是可选的,但对任何会从多个地方被调用的函数都是有价值的。它们是工具可以验证的文档。
函数作为值
Python 中的函数是值,就像字符串或数字一样。你可以将它们赋值给变量,并将它们传递给其他函数。这就是 sorted() 接受 key= 函数的方式。
def double(x):
return x * 2
def apply(func, value):
return func(value)
apply(double, 5) # 10将函数作为实参传递在 sorted()、map() 和 filter() 中经常出现。你也会在 Lambda、推导式和 zip 章节中看到它。
实战
两个协同工作的函数:letter_grade 将分数转换为字母,summarise 为列表中的每个分数调用它:
def letter_grade(score: int) -> str:
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
def summarise(scores: list[int]) -> None:
total = sum(scores)
avg = total / len(scores)
grades = [letter_grade(s) for s in scores]
print(f"Average: {avg:.1f}")
print(f"Grades: {', '.join(grades)}")
summarise([87, 92, 74, 65, 91])
