函数

函数是可重用的程序代码块,函数不仅可以实现代码的复用,更能实现代码的一致性。函数只是只是对代码实现了封装,并增加了函数调用、传递参数、 返回计算结果等内容。一个程序由一个一个的任务组成;函数就是代表一个任务或者一个功能(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()
相关推荐
笨鸟先飞,勤能补拙6 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能7 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂8 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study8 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境9 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境9 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1239 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy123931021610 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb0010 小时前
一个项目带你入门AI应用开发01
python
05664610 小时前
Python康复训练——常用标准库
开发语言·python·学习