8. 复合语句

8.7. 函数定义¶

函数定义就是对用户自定义函数的定义(参见 标准类型层级结构 一节):

funcdef: [decorators] "def" funcname [type_params] "(" [parameter_list] ")"

["->" expression] ":" suite

decorators: decorator+

decorator: "@" assignment_expression NEWLINE

parameter_list: defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]

| parameter_list_no_posonly

parameter_list_no_posonly: defparameter ("," defparameter)* ["," [parameter_list_starargs]]

| parameter_list_starargs

parameter_list_starargs: "*" [star_parameter] ("," defparameter)* ["," [parameter_star_kwargs]]

| "*" ("," defparameter)+ ["," [parameter_star_kwargs]]

| parameter_star_kwargs

parameter_star_kwargs: "**" parameter [","]

parameter: identifier [":" expression]

star_parameter: identifier [":" ["*"] expression]

defparameter: parameter ["=" expression]

funcname: identifier

函数定义是一条可执行语句。 它执行时会在当前局部命名空间中将函数名称绑定到一个函数对象(函数可执行代码的包装器)。 这个函数对象包含对当前全局命名空间的引用,作为函数被调用时所使用的全局命名空间。

函数定义并不会执行函数体;只有当函数被调用时才会执行此操作。 [4]

一个函数定义可以被一个或多个 decorator 表达式所包装。 当函数被定义时将在包含该函数定义的作用域中对装饰器表达式求值。 求值结果必须是一个可调用对象,它会以该函数对象作为唯一参数被唤起。 其返回值将被绑定到函数名称而非函数对象。 多个装饰器会以嵌套方式被应用。 例如以下代码

@f1(arg)

@f2

def func(): pass

大致等价于

def func(): pass

func = f1(arg)(f2(func))

不同之处在于原始函数并不会被临时绑定到名称 func。

在 3.9 版本发生变更: 函数可使用任何有效的 assignment_expression 来装饰。 在之前版本中,此语法则更为受限,详情参见 PEP 614。

可以在函数名及其形参列表开头圆括号之间加方括号给出一个 类型形参 的列表。 这将向静态类型检查器指明该函数是泛型函数。 在运行时,类型形参可以从函数的 __type_params__ 属性中提取。 请参阅 泛型函数 了解详情。

在 3.12 版本发生变更: 类型形参列表是在 Python 3.12 中新增的。

当一个或多个 形参 具有 形参 = 表达式 这样的形式时,该函数就被称为具有“默认形参值”。 对于一个具有默认值的形参,其对应的 argument 可以在调用中被省略,在此情况下会用形参的默认值来替代。 如果一个形参具有默认值,后续所有在 "*" 之前的形参也必须具有默认值 --- 这个句法限制并未在语法中明确表达。

Default parameter values are evaluated from left to right when the function

definition is executed. This means that the expression is evaluated once, when

the function is defined, and that the same "pre-computed" value is used for each

call. This is especially important to understand when a default parameter value is a

mutable object, such as a list or a dictionary: if the function modifies the

object (e.g. by appending an item to a list), the default parameter value is in effect

modified. This is generally not what was intended. A way around this is to use

None as the default, and explicitly test for it in the body of the function,

for example:

def whats_on_the_telly(penguin=None):

if penguin is None:

penguin = []

penguin.append("property of the zoo")

return penguin

函数调用的语义在 调用 一节中有更详细的描述。 函数调用总是会给形参列表中列出的所有形参赋值,或是用位置参数,或是用关键字参数,或是用默认值。 如果存在 "*identifier" 这样的形式,它会被初始化为一个元组来接收任何额外的位置参数,默认为一个空元组。 如果存在 "**identifier" 这样的形式,它会被初始化为一个新的有序映射来接收任何额外的关键字参数,默认为一个相同类型的空映射。 在 "*" 或 "*identifier" 之后的形参都是仅限关键字形参因而只能通过关键字参数传入。 在 "/" 之前的形参都是仅限位置形参因而只能通过位置参数传入。

在 3.8 版本发生变更: 可以使用 / 函数形参语法来标示仅限位置形参。 请参阅 PEP 570 了解详情。

形参可以带有 注解,其形式为在形参名后加 ": expression"。 任何形参都可以带注解,甚至 *identifier 或 **identifier 这样的形参也可以。 (作为特例,*identifier 这样的形参可以有 ": *expression" 形式的注解。) 函数可以带有“返回”注解,其形式为在形参列表后加 "-> expression"。 这些注解可以是任何有效的 Python 表达式。 注解的存在不会改变函数的语义。 有关注解的更多信息,请参阅 标注。

在 3.11 版本发生变更: 形式为 "*identifier" 的形参可以带有 ": *expression" 标注。 参见 PEP 646。

创建匿名函数(未绑定到一个名称的函数)以便立即在表达式中使用也是可能的。 这需要使用 lambda 表达式,具体描述见 lambda 表达式 一节。 请注意 lambda 只是简单函数定义的一种简化写法;在 "def" 语句中定义的函数也可以像用 lambda 表达式定义的函数一样被传递或赋值给其他名称。 "def" 形式实际上更为强大,因为它允许执行多条语句和使用标注。

程序员注意事项: 函数属于一等对象。 在一个函数内部执行的 "def" 语句会定义一个局部函数并可被返回或传递。 在嵌套函数中使用的自由变量可以访问包含该 def 语句的函数的局部变量。 详情参见 命名与绑定 一节。

参见

PEP 3107 - 函数标注最初的函数标注规范说明。

PEP 484 —— 类型注解标注的标准含意定义:类型提示。

PEP 526 - 变量标注的语法变量声明的类型提示功能,包括类变量和实例变量。

PEP 563 - 延迟的标注求值支持在运行时通过以字符串形式保存标注而非立即求值来实现标注内部的向前引用。

PEP 318 - 函数和方法的装饰器引入了函数和方法的装饰器。 类装饰器是在 PEP 3129 中引入的。

Copyright © 2088 一键全脑游戏活动站 - 脑力挑战专属福利 All Rights Reserved.
友情链接