# 函数

# 定义函数

一个任务:把每个值乘 2 次方并除以 5 再加一
你可以这样做:

a = 10
a = a**2/5+1
print(a)

但假如你要在不同的地方做 x 次操作呢?
就可以用到函数了:

def demo(num):
    return num ** 2 / 5 + 1
a = demo(10)
print(a)

以下是函数的语法:

def 函数名(形参):
    代码

return 后面是函数的返回值,需要使用变量 "接住"
在遇到 return 关键字之后,函数会自动退出

return 不是必须的,没有 return 则会在函数执行完后退出

# 参数

参数分为形参和实参,定义时的叫形参,填入的是实参

# 标准参数和类型标注

def demo(num:int|float) -> float:
    return num+1

num 是标准参数,不填会报错: TypeError: demo() missing 1 required positional argument: 'num'
表示多个类型,则有多种标注方法:

  • 3.9 版本以上: int|float , | 符号代表 A 类型或 B 类型
  • Union [int, float], 需要先导入 typing 模块
    -> float 表示返回值是 float 类型,方便 IDE 分析

类型标注和返回值标注不是必须的,但建议使用

# 缺省形参和特殊参数

def demo(num: int | float = 10) -> float:
    return num ** 2 / 5 + 1
a = demo()
print(a)

num: int | float = 10 中的 = 10 表示缺省
如果没有填入参数就使用 10 作为参数输入

def demo(*args):
    for i in args:
        print(i)
demo(1,2,3)

这个函数会将传入的所有元素输出
*args 表示不知道会输入多少个元素,把所有参数做成元组处理

def demo(**kwargs):
    for k,v in kwargs.items():
        print(k,v)
demo(k1=10,k2=12)

**kwargs 代表把传入的参数作为字典处理,因此传入必须使用 键=值 这种方法

看不懂 **kwargs.items() 是什么意思就去复习一下字典

# 函数名

函数名和变量的命名规则很像,但可以使用小驼峰命名法

例:numOutput
首字母小写,剩下的单词开头大写

# 作用域

python 的作用域分为以下几种:
Local 局部作用域,简写为 L
Enclosing 闭包作用域,简写为 E
Global 全局作用域,简写为 G
Built-in 内置作用域,简写为 B
变量和函数的搜寻的顺序:L->E->G->B

gvar = 0 # 全局作用域
def demo():
    evar = 1 # 对于 demo2 的闭包作用域,对于 demo 的局部作用域
    def demo2():
        lvar = 2 #局部作用域

在作用域外不能调用作用域内的变量与函数

gvar = 0 # 全局作用域
def demo():
    evar = 1 # 对于 demo2 的闭包作用域,对于 demo 的局部作用域
    def demo2():
        lvar = 2 #局部作用域
demo2()
# NameError: name 'demo2' is not defined. Did you mean: 'demo'?

把 demo2 套在 demo 里就叫闭包函数

加入作用域内有同名变量或更改变量值,需要使用 globalnonlocal 声明变量
语法: nonlocal/global 变量

global 是全局变量,nonlocal 是闭包变量

# typing 模块

typing 模块是 python 内置的类型表示模块,对于函数类型标注非常有帮助

# 可迭代容器数据

举个例子,这个函数可以遍历可迭代容器数据并输出:

def printSeq(seq: list):
    for i in seq:
        print(i)

最开始使用 list 进行类型标注,但假如要使用元祖,python 就会报错。
而且如果只是单纯加上元祖,后续出现新的类型还需要改代码
这个时候就可以用到 list 的抽象类 typing.Sequence

def printSeq(seq: typing.Sequence):
    for i in seq:
        print(i)

就可以实现接受所有的可迭代容器数据

# 可能为 None 的参数

在实际开发中,部分参数需要传入 None 来实现功能,虽然可以用 |Union 来解决,但有更优雅的方法:

def numAbs(n: int, mode: typing.Optional[bool]):
    if mode is None:
        return 0
    else:
        return abs(n)

typing.OptionalUnion 是等价的,只是前者默认包含 None

typing 包含有大量抽象类供类型标注,可以查看官方文档

typing 中部分内置类 (区别为首字母大写) 不要使用,请使用标准的内置类
这些冗余的类会在 python3.9 发行五年后的第一个版本移除