函数

函数是可重用的程序代码块,函数不仅可以实现代码的复用,更能实现代码的一致性。函数只是只是对代码实现了封装,并增加了函数调用、传递参数、 返回计算结果等内容。一个程序由一个一个的任务组成;函数就是代表一个任务或者一个功能(function)。

函数的定义与调用

定义语法:

python 复制代码
    def  函数名 ([参数列表]) :      
        '''文档字符串'''       
        函数体/若干语句

通过上面的定义来简单的创建一个函数:

python 复制代码
def add(a, b, c):  
    """  
    :param a: 参数a  
    :param b: 参数b  
    :param c: 参数c  
    :return:  
    """  
    sum = a + b + c  
    print("{0},{1},{2}三个数的和是:{3}".format(a, b, c, sum))  
    return sum  
  
 
add(10, 20, 30)  
add(30, 40, 50)

def是创建函数的关键字,python中一切皆对象,当执行到def时,会创建一个函数对象,并将函数名变量指向这个对象,完成绑定。

参数列表:多个参数参数之间使用逗号隔开,定义时的形式参数不需要声明类型,也不需要指定函数返回值类型,调用时的实际参数必须与形参列表一一对应

return返回值:如果函数体中包括return语句,那么则结束函数执行并返回值;如果不包含return语句,那么在函数执行完毕之后,则会返回None值

形参与实参

形参:形参也叫形式参数,就是函数在定义时,在()内定义的参数.如上面的函数add(a, b, c),其中a,b,c就是形参。

实参:实参也叫实际参数,就是函数在调用时,给函数提供的实际的值。如上面的函数add(10, 20, 30),其中10,20,30就是实参。

python 复制代码
def printMax(a, b):  
    if a > b:  
    return a  
    else:  
    return b  
  
  
print(printMax(20, 15)) # 20  
print(printMax(11, 10)) # 11

上面的 printMax 函数中,在定义时写的 printMax(a,b) 。 a 和 b 称为"形式参 数",简称"形参"。也就是说,形式参数是在定义函数时使用的。 形 式参数的命名要符合"标识符"命名规则。

在调用函数时,传递的参数称为"实际参数",简称"实参"。上面代码 中, printMax(10,20) , 10 和 20 就是实际参数。

文档字符串(函数的注释)

文档字符串就是对函数的解释说明,让调用者知道函数的功能,以及如何调用。文档字符串一般写在函数体的最上面,如:

python 复制代码
def print_star(n):  
"""  
传入n,打印多个星号  
:param n: 参入的数字  
:return: n个星号拼接的字符串  
"""  
    return "*" * n  
  
  
print(help(print_star))  
# print_star(n)  
# 传入n,打印多个星号  
# :param n: 参入的数字  
# :return: n个星号拼接的字符串  
#  
# None  
  
  
# print(print_star.__doc__)  
# 传入n,打印多个星号  
# :param  
# n: 参入的数字  
# :return: n个星号拼接的字符串

文档字符串可以通过:help(函数名) 和 函数名._ doc _ 获取到

return

函数的返回内容,有时候执行一个函数时,我们需要它返回一个结果就可以使用return语句

如果函数体中包含return语句且有返回值:则结束函数执行,并将值返回出去

如果函数体中不包含return语句,则函数结束时,返回None值

要返回多个值,使用列表、元组、字典、集合将多个值"存起来"即可

python 复制代码
def print_star(n):  
    print("*" * n)  
print_star(5)  
  
  
def my_avg(a, b):  
    return (a + b) / 2  
print(my_avg(20, 30)) # 25.0  
  
  
def printShape(n):  
    s1 = "#"*n  
    s2 = "*"*n  
    return [s1,s2]  
print(printShape(5)) # ['#####', '*****']

函数也是对象

前面提到过,当python执行到def时,会创建一个函数对象,并将函数名变量指向它。本质就是赋值,那么我们也可以创建另一个变量,也去指向这个对象,通过这个变量也可以去执行这个函数对象

python 复制代码
def print_star(n):  
    print("*"*n)  
    
print(print_star) # <function print_star at 0x0000011E412D4DC0>  
print(id(print_star)) # 1631933451712  
# 执行 def 定义函数后,系统就创建了相应的函数对象,函数名就是指向这个对象的变量  
# 将变量c也指向这个函数对象,并运行函数内部的代码  
c = print_star  
print(c) # <function print_star at 0x000001F998C34DC0>  
print(id(c)) # 1631933451712  
# 执行c这个函数  
c(5) # *****

变量的作用域

全局:声明在def和class之外的变量,其作用域是从变量的声明到模块结束。如果在函数内部想要改变外面的全局变量,需要使用global进行声明(如果不修改这个全局变量,则不需要这一步),再使用。如果想要获取到所有的全局变量,则使用globals()

局部:声明在def/class中的变量,其作用域在函数执行完毕之后就销毁。当函数内部与外部有着同名变量,则优先使用自己作用域下的变量。获取函数内部所有的局部变量:locals()

python 复制代码
a = 300 # 全局变量  
def f1():  
    global a # 如果需要修改全局变量的值,需要加上这一段  
    print(a)  
    a = 100  
  
f1() # 300  
f1() # 100  
print(a) # 100  
  
# 就近原则  
a = 300 # 全局变量  
def f1():  
    a = 100  
    print(a)  
  
f1() # 100  
f1() # 100  
print(a) # 300  
  
def f1(a,b,c):  
    print(locals()) # {'a': 1, 'b': 2, 'c': 3}  
    print("*******")  
    print(globals()) # 所有的全局变量  
  
f1(1,2,3)

参数的传递

参数的传递:其实就是对形参赋值,python只有引用传递,没有值传递。

可变参数:列表,字典...,那么不会拷贝一个新对象,对形参进行修改本质上就是对源对象进行修改

python 复制代码
a = [20,30]  
def f1(m):  
    print(id(m))  
    m.append(50)  
    print(m,id(m)) # [20, 30, 50] 2728819248896  
  
f1(a)  
print(a,id(a)) # [20, 30, 50] 2728819248896

不可变参数:元组,字面量,在对形参修改之前,形参与实参指向的是同一个变量,如果对形参进行修改,由于不可变对象无法修改,会拷贝一个新的对象,而这个形参就指向这个新对象。

python 复制代码
def f1(m):  
    print(id(m)) # 1919563209168  
    m=100 # 修改了数据,直接创建了新对象,并指向它  
    print(m,id(m)) # 1919563205968  
f1(a)  
print(a,id(a)) # 1919563209168

不可变对象内的可变对象:a(1,2,[5,6]),如果对内部的列表进行修改,其实也是作用在源对象上

python 复制代码
a = (10,20,[5,6])  
print("a",a,id(a)) # a (10, 20, [5, 6]) 2457480691328  
  
def f1(m):  
    print("m",m,id(m)) # m (10, 20, [5, 6]) 2457480691328  
    m[2][0] = 888  
    print("m",m,id(m)) # m (10, 20, [888, 6]) 2457480691328  
  
f1(a)  
print("a",a,id(a)) # a (10, 20, [888, 6]) 2457480691328

拷贝

浅拷贝:就像上面提到的不可变对象内的可变对象(1,2,[5,6]),元组不可变,但是列表可变当对不可变对象进行修改时,则会拷贝一个新对象,但是如果对内部列表改变,则源对象也会改变,这就是浅拷贝;拷贝对象,但是不拷贝子对象的内容,只拷贝子对象引用,即源对象的子对象id = 新对象的子对象id。所以在修改子对象的时候,其源对象的子对象也会改变。

python 复制代码
import copy  
def testCopy():  
    a = [20,30,[5,6]]  
    b = copy.copy(a)  
    # 对b进行操作  
    b.append(40)  
    b[2].append(7)  
  
print(a) # [20, 30, [5, 6, 7]]  
print(b) # [20, 30, [5, 6, 7], 40]  
  
testCopy()

深拷贝:还是拿上面为例:(1,2,[5,6]), 深拷贝就是不仅拷贝对象,连子对象的内存也全部(递归)拷贝一份,即源对象的子对象id ≠ 新对象子对象的id。修改新对象的子对象不会影响到源对象。

python 复制代码
def testDeepCopy():  
    a = [20,30,[5,6]]  
    b = copy.deepcopy(a)  
    # 对b进行操作  
    b.append(40)  
    b[2].append(7)  
  
print(a) # [20, 30, [5, 6]]  
print(b) # [20, 30, [5, 6, 7], 40]  
  
testDeepCopy()

参数类型

位置参数:首先形参与实参在个数上要一致,实参按照先后顺序给形参赋值,上面所使用的都是位置参数

python 复制代码
# 1.位置参数:上面定义的其实就是位置参数,实参按照顺序给形参赋值,实参顺序调整可能影响整个函数的值  
def print_num(a,b,c,d):  
    print(a,b,c,d)  
  
print_num(1,2,3,4) # 1 2 3 4  
print_num(2,1,4,3) # 2 1 4 3  
# 位置参数形参和实参的个数必须一致,否则报错  
# TypeError: print_num() missing 1 required positional argument: 'd'  
# print_num(1,2,3)

默认参数:当有些参数是可选的,不一定要传递,但是位置参数又必须传递,可以使用默认参数,默认参数是指在如果不传递某个参数,在形参列表中去指定默认值,这样当这个参数没有传递的时候

使用的是默认值,默认参数要放在参数列表的最后。

python 复制代码
# 默认参数其实就是给形参一个默认值,当没有传入该参数时使用默认值,这使得这个参数变成可选参数  
# 默认参数要放在最后,也即是说默认参数不能放在非默认参数后面  
def print_num(a,b,c,d=4):  
    print(a,b,c,d)  
  
print_num(1,2,3,5) # 1 2 3 5  
print_num(1,2,3) # 1 2 3 4

命名参数:在位置参数中,如果参数的顺序传递错误,那么可能函数返回的结果就不一致;为避免这个问题可以使用命名参数,命名参数是指在进行参数传递时,使用(形参名=实参, 形参名=实参)这样的形式,使形参与实参通过通过形参进行传递。

python 复制代码
# 默认参数之能放在最后是因为:实参是按照顺序给形参赋值,当只传入3个参数时  
# 如果默认参数不是最后一个,那么这个默认参数将会被赋值,第四个参数将会没有值,从而报错  
# 命名参数,根据形参的名称来赋值  
def print_num(a,b,c,d):  
    print(a,b,c,d)  
  
print_num(a=1,b=2,c=3,d=4) # 1 2 3 4  
print_num(c=3,b=2,a=1,d=4) # 1 2 3 4

可变参数:参数列表的个数不固定

*param:将多余的参数收集到列表中存储,param指向这个列表

**param:将多余的(a=1)这样类型的参数收集到字典中,param指向这个字典中

python 复制代码
# *param:多余的参数将会组成元组  
def print_num(a,b,*c):  
    print(a,b,c)  
  
print_num(3,4) # 3 4 ()  
print_num(1,2,3,4) # 1 2 (3, 4)  
print_num(1,2,3,4,"a") # 1 2 (3, 4, 'a')  
  
# **params:多余的参数将会转换为字典  
def print_num(a,b,**c):  
    print(a,b,c)  
    
print_num(3,4,name="hhc",age=21) # 3 4 {'name': 'hhc', 'age': 21}

强制命名参数:当param不是最后一个参数时,那么param会接收所有剩余参数,那么在*param后面的变量如果不是默认参数时,则没有值赋值给该量,那么程序将会报错。如果有这样类似的情况,就必须使用命名参数。

python 复制代码
# 当*a是第一个形参时,如果后面的形参也想要赋值,则必须带参数名  
def print_num(*a,b,c):  
    print(a,b,c)  
print_num(1,2,3,b=4,c=5) # (1, 2, 3) 4 5

lambda表达式

声明一个匿名函数,生成一个函数对象,这个函数对象没有变量去指向它,需要手动指向,在lambda表达式中,只能有一条语句,该条语句的结构就是这个函数的返回值

python 复制代码
a = lambda a,b,c: a+b+c  
  
print(a) # <function <lambda> at 0x00000185416AC700>  
print(a(1,2,3)) # 6  
print(a(2,3,4)) # 9  
  
g = [lambda a:a*2, lambda b:b*3, lambda c:c*4]  
print(g[0](6),g[1](7),g[2](8)) # 12 21 32  
    
g = [lambda a:a*2, lambda a:a*3, lambda a:a*4]  
print(g[0](6),g[1](7),g[2](8)) # 12 21 32

eval()函数

eval(str):将str当成一条可执行的语句,去执行它

python 复制代码
eval("print('123哈哈')") # 123哈哈  
  
def sum(a,b):  
    c = f"{a}+{b}"  
    eval("print(a+b)")  
  
sum(3,4) # 7  
sum(73,54) # 127

函数的递归

递归函数指的是:自己调用自己的函数,在函数体内部直接或间接 的自己调用自己。每个递归函数必须包含两个部分:

1 终止条件 表示递归什么时候结束。一般用于返回值,不再调用自己。

2 递归步骤 把第n步的值和第n-1步相关联。

python 复制代码
def my_recursion(n):  
    print("start:",n)  
    if n==1:  
        print("recursion over!")  
    else:  
        my_recursion(n-1)  
    print("end:",n)  
  
my_recursion(3)  
  
# 递归2
def factorial(n):  
    if n == 1:  
        return n  
    return n*factorial(n-1)  
  
print(factorial(5)) # 120

# 递归3
def num_reverse(num):  
    str_num = str(num)  
  
    if len(str_num) == 1:  
        return str_num  
    return num_reverse(str_num[1:]) + str_num[0]  
  
print(num_reverse(3245))  
  
# 递归4
# 1/2 + 2/3 +... n/n+1  
def test01(n):  
    if n == 1:  
        return 1/2  
    return n/(n+1) + test01(n-1)  
  
print(test01(3))

嵌套函数

python 复制代码
def outer():  
    print("outer....")  
  
    def inner():  
        print("inner....")  
    inner()  
   
# outer....  
# inner....  
outer()  
  
def printChineseName(name,familyName):  
    print("{0} {1}".format(familyName,name))  
def printEnglishName(name,familyName):  
    print("{0} {1}".format(name, familyName))  
  
def printName(isChinese,name,familyName):  
  
    def inner_print(a,b):  
        print("{0} {1}".format(a,b))  
  
    if isChinese:  
        inner_print(familyName,name) # hc h  
    else:  
        inner_print(name,familyName) # George Bush  
  
printName(True,"hc","h")  
printName(False,"George","Bush")

nonlocal关键字

nonlocal 用来在内部函数中,声明外层的局部变量

global 函数内声明全局变量,然后才使用全局变量

python 复制代码
a =100  
def outer():  
    b = 200  
    def inner():  
        nonlocal b  
        print("innerb:",b) # 200  
        b = 300  
  
        global a  
        print("innera:",a) # 100  
        a = 1000  
    inner()  
    print("outerb:",b) # 300  
    print("outera:",a) # 1000  
  
outer()
相关推荐
西西弗Sisyphus12 分钟前
Python 处理图像并生成 JSONL 元数据文件 - 灵活text版本
开发语言·python
Taichi呀12 分钟前
PyCharm 快捷键指南
ide·python·pycharm
Stara051119 分钟前
基于注意力机制与iRMB模块的YOLOv11改进模型—高效轻量目标检测新范式
人工智能·python·深度学习·神经网络·目标检测·计算机视觉·yolov11
Python猫1 小时前
付费专栏·Python潮流周刊电子书合集(epub、pdf、markdown)下载
python·计算机·pdf·电子书·资料
强化学习与机器人控制仿真1 小时前
openpi 入门教程
开发语言·人工智能·python·深度学习·神经网络·机器人·自动驾驶
BuLingLings2 小时前
vue3+flask+sqlite前后端项目实战
python·sqlite·flask
AA-代码批发V哥3 小时前
正则表达式: 从基础到进阶的语法指南
java·开发语言·javascript·python·正则表达式
缘友一世3 小时前
Pytorch常用统计和矩阵运算
人工智能·pytorch·python
alpha xu3 小时前
Qwen智能体qwen_agent与Assistant功能初探
数据库·人工智能·python·oracle·智能体·千问agent
蓝莓味柯基3 小时前
Python3正则表达式:字符串魔法师的指南[特殊字符]‍♂️
开发语言·python·正则表达式