一. 函数: 先定义, 后调用
1. 定义函数的语法: 实质上也是申请内存空间,代码块放入内存空间,内存地址绑定给函数名,形似定义变量
def 函数名(参数1, 参数2, 参数3, ...): # 函数名命名规则同变量名,小写加_,参数可无
"""
文档注释,可无
"""
代码1 # 定义阶段, 只检测语法, 不执行代码
代码2
代码3
...
return 返回值 # 可无,是否要返回值,依据为代码运行的结果是否要作进一步后续处理
2. 参数后可加类型提示, 通常省略, 加或不加均不影响函数本身的执行
def add(x: "数字", y: int, z: (int, float)) -> int: # ->表示返回值的形式
return x + y + z
二. 定义函数的三种形式
1. 无参函数 def func():
2. 有参函数,函数体代码需要外部传参数x,y,z,则def func(x, y, z):
3. 空函数
def func():
pass
三. 调用函数的语法: 通过函数名找到函数的内存地址,()触发内存空间里代码的运行,才会发现逻辑错误
函数名(值1, 值2, 值3, ...) # 定义了有参函数, 调用的时候一定要传值
四. 调用函数的三种形式
1. 语句形式: 没有返回值的函数, 不能赋值, 单纯调用
def max(x, y):
if x > y:
print(x)
else:
print(y)
max(11, 22)
2. 表达式形式: 有返回值的, 可用来赋值表达式, 或算数运算
def max(x, y):
if x > y:
return x
else:
return y
res = max(11, 22)
3. 当作参数传给另外一个函数
def max(x, y):
if x > y:
return x
else:
return y
max(max(11, 22), 33)
五. 函数返回值return
1. return是函数结束的标志,函数内可有多个return,可用来结束函数内while循环
2. return值,可以没有或只有return,返回None,可以返回一个值,也可以值1,值2,...返回一个元组,内含多个值
def func():
print("Hello1") # 本来print就有返回值,故不需要返回.但数字,运算结果如不return,就没返回值
return 1, 2.2, [1, 2, 3] # 只要执行一次,整个函数就终止,return的结果作为本次调用的返回值
print("前面return 1 其实已终止,这段不会打印")
return 2
func() # 打印结果为Hello1
print(func()) # 打印结果为Hello1和(1, 2.2, [1, 2, 3])
六. 函数的参数: 形参与实参
1. 形参: 函数定义时, def func(变量名):括号内的参数, 叫形式参数,形参
2. 实参: 函数调用时, func(变量值)括号内传入的值, 叫实际参数,实参
3. 位置参数: 按位置参数分为 位置形参, 位置实参
3.1 位置形参: 函数定义时,从左到右依次定义的形参def func(x, y):,必须被传值,多或少一个都不可
3.2 位置实参: 函数调用时,从左到右依次传入的值,位置与位置形参一一对应
4. 关键字实参: 函数调用时,按key=value的形式传值def func(y=2, x=1):,显式赋值,不管参数x,y顺序
def func(x, y):
print(x, y)
func(11, y=2) # 位置实参可以和关键字实参混用
func(y=2, 11) # 报错, 位置实参必须放在前面
func(11, x=22, y=33) # 报错, 不能为同一形参重复赋值
5. 默认形参: 函数定义时,已为形参赋值,称为默认形参.以下示例不推荐,y=m容易和外部耦合,最好显式赋值y=22
m = 22 # 但如果此处是m=[],同时后面不是m=33,而是m.append(44),打印结果为y=[44],因为m内存地址没变
def func(x, y=m): # 可以混用位置形参和默认形参,位置形参必在前面,(y=22,x)报错
print(x, y) # 打印结果y=22,默认形参的值只在定义时赋值一次,因为上句y拿到的是m的内存地址
m = 33
func(11) # 函数调用时, 默认形参x可以不用传值
func(11, 33) # 函数调用时传值, 则用传入的值
def func(name, hobby, hobbies=None): # 若hobbies=[],结果会叠加,故为每次调用都独立,默认形参必须不可变类型
if hobbies is None: # 因为默认形参的值只在定义时赋值一次,故定义首行不能用列表,而是调用时判断赋值
hobbies = []
hobbies.append(hobby)
print("%s 的爱好是 %s" % (name, hobbies))
func("u1", "cs")
func("u2", "mv") # 若hobbies=[],没有if判断,def func()只运行一次,而func()运行两次,u2的爱好是['cs','mv']
6. *与**在形参中接收溢出位置的实参, 汇总: 可变长的实参在函数调用时, 传入的值的个数不固定
6.1 *在形参中, 把溢出的位置实参存成元组的形式
def func(x, *args): # x=1 args=(2, 3)
print(x, args)
func(1, 2, 3)
6.2 **在形参中, 把溢出的关键字实参存成字典的形式
def func(x, **kwargs): # x=1 kwargs={'a': 2, 'b': 3, 'c': 4}
print(x, kwargs)
func(x=1, a=2, b=3, c=4)
7. *与**在实参中, 解压操作, 实参个数要和形参个数一样
def func(x, y, z):
print(x, y, z)
7.1 *在实参中, 把能被for循环的实参值解压成位置实参
func(*[1, 2, 3]) # func(1, 2, 3),实参个数要和形参个数一样
7.2 **在实参中, 把字典格式的实参值解压成关键字实参
func(**{"x": 1, "y": 2, "z": 3}) # key要和形参对应
8. *与**混用: *在前,**在后
def wrapper(*args, **kwargs): # 可以接受所有长度的形参个数, args=(1,2) kwargs={"a":1,"b":2}
index(*args, **kwargs) # wrapper传的值一摸一样地给了index,index(*(1,2),**{"a":1,"b":2})
wrapper(1, 2, a=1, b=2) # index(1,2,a=1,b=2)
七. 函数对象: 函数可以当变量去用
def func(): # func=函数的内存地址
print("from func")
1. 可以被赋值
f = func
f() # 调用函数,得到执行结果形式一定是函数名(), from func
2. 可以当作参数传给另外一个函数
def foo(f): # 注意,以下不同使用函数名,得到的3种执行结果
print(f) # 取函数名的内存地址的形式,print(函数名)
f() # 典型的调用函数,得到执行结果的形式, from func
foo(func) # 区别另一种,函数名(函数名())则是调用返回值的形式,None
3. 可以当作函数的返回值
def foo(f):
return f
print(foo(func)) # 打印函数名的内存地址
4. 可以当作容器类型的元素
l = [func, ]
print(l) # 打印列表中此函数名的内存地址,[<function func at 内存地址>]
l[0]() # 通过列表调用函数,得到执行结果,形式类似func()
4.1 示例
def login():
print("登录...")
def register():
print("注册...")
def tranfer():
print("转账...")
def withdraw():
print("提现...")
func_dic = {
"1": ["登录", login],
"2": ["注册", register],
"3": ["转账", tranfer],
"4": ["提现", withdraw]
}
while True:
for k, v in func_dic.items():
print(k, v[0])
choice = input("请输入您的命令编号,输入0退出:").strip()
if choice == "0":
break
if choice in func_dic:
func_dic[choice][1]()
else:
print("输入的指令错误")
八. 函数嵌套: 嵌套定义, 嵌套调用
1. 函数的嵌套调用: 在调用一个函数的过程中又调用了其他函数, 也可以调用自己
def max2(x, y):
if x > y:
return x
else:
return y
def max4(a, b, c, d):
res1 = max2(a, b)
res2 = max2(res1, c)
res3 = max2(res2, d)
return res3
print(max4(1, 2, 3, 4))
2. 函数的嵌套定义: 在一个函数内部又定义了其他函数
2.1 定义在函数内的函数, 只能函数内部使用, 一种封闭的效果
def f1():
def f2():
print('from f2')
x = 111
print(x)
f2()
f1()
2.2 封闭效果应用示例
from math import pi
def circle(radius, action=1):
def perimeter(radius):
return 2 * pi * radius
def area(radius):
return pi * (radius * radius)
if action == 1:
return perimeter(radius)
elif action == 2:
return area(radius)
print(circle(10, 1))
print(circle(10, 2))
3. 函数的嵌套定义 + 函数对象
def f1():
def f2(): # 把f2封闭在f1内
print('from f2')
return f2 # 打破封闭层级限制
res = f1() # f1()执行结果显示为空,实际只有返回f2内存地址,res=f2
print(res) # 打印结果为返回值f2的内存地址
res() # 执行f2内存地址中的代码块,打印from f2
九. 递归调用: 基于函数, 实现循环的方式
1. 定义: 在调用一个函数的过程中, 又直接或者间接地调用函数自己
def foo():
print('from foo')
foo() # 调用函数的过程中,直接调用自己
foo() # Python没有伪递归优化,会一直递归下去,内存地址不会回收
import sys
print(sys.getrecursionlimit()) # 递归的最大层级限制1000层
sys.setrecursionlimit(2000) # 改递归层级限制保护
def foo():
print('from foo')
bar() # 调用函数的过程中,间接地调用自己
def bar():
print('from bar')
foo()
foo()
2. 递归调用有两个阶段
2.1 回溯: 一层一层地递归调用下去. 回溯阶段内存一直被占用
2.2 递推: 在某一层结束递归调用, 开始向上一层一层地返回, 内存开始一层一层回收.
"""
# age(3) = age(2) + 10 # age(3)没有值,则找age(2),如此自上而下叫回溯.等到递推后,age(2)拿到值然后+10
# age(2) = age(1) + 10 # age(2)没有值,则找age(1),回溯占用内存.
# age(1) = 18 # age(1)拿到值后,开始自下而上,递推,age(2),age(3)...自此开始回收内存.
"""
def age(n):
if n == 1:
return 18
return age(n - 1) + 10 # 递推后age(2)拿到值+10后,这行age(3)才算运行完,内存才能回收
res = age(3)
print(res)
3. 递归调用思路: 基于函数,重复某段代码,控制结束条件,在某个点结束递归调用
3.1 应用场景: 取出每个列表中的每一个元素
nums = [1, [2, [3, [4, [5, [6, [7, [8, [9]]]]]]]]]
def func(l):
for item in l:
if type(item) is list:
func(item) # for item2 in item这个是要传入的参数:if type(item2) is list:...
else:
print(item)
func(nums)
3.2 应用场景2: 算法之二分法, 此算法用来查询, 模拟实现in关键字
nums = [-3, 11, 13, 25, 37, 39, 43, 52, 77, 84, 91] # 此列表必须有序,从大到小或从小到大
def search(list1, find_num):
print(list1) # 每次调用的时候, 可以打印看下每次列表被切成的样子
if len(list1) == 0:
print('not exists')
return
mid_index = len(list1) // 2
if find_num > list1[mid_index]: # on the right
search(list1[mid_index + 1:], find_num)
elif find_num < list1[mid_index]: # on the left
search(list1[:mid_index], find_num)
else:
print('find it')
search(nums, 84)
十. 匿名函数
1. 定义: 没有名字的函数,故只能调用一次就被回收,只适用于临时调用一次的场景
res = (lambda x, y: x + y)(1, 2) # 形参不加括号,无默认,可变长参数,自带return,故不写
print(res)
2. 匿名函数应用示例
salaries = {
"egon": 3000,
"alex": 5000,
"zhangsan": 1000,
}
print(max([11, 10, 44, 9])) # max方法可以打印最大值44
print(max(salaries)) # 打印zhangsan,max迭代取值key,返回必是key,z字母最大,不能取value最大值
def func(k): # 取薪资value最大的key
return salaries[k]
res = max(salaries, key=func) # func没有(),key是max内置的比较依据,但返回还是key
res = max(salaries, key=lambda k: salaries[k]) # 等同上面func
print(res)
res = min(salaries, key=lambda k: salaries[k]) # min方法取最小值
print(res)
res = sorted([11, 10, 44, 9], reverse=True) # sorted方法默认从小到大排序
print(res) # reverse是反向,从大到小,打印[44, 11, 10, 9]
res = sorted(salaries, key=lambda k: salaries[k]) # sorted按薪资排序
print(res)
十一. 三元表达式
res = 条件成立时的返回值或表达式 if 条件 else 条件不成立时的返回值或表达式
res = x if x > y else y # 自带return,故表达式会返回运行结果
print(res)
def max2(x, y): # 等同上面的三元表达式
if x > y:
return x
else:
return y
十二. 面向过程编程
1. 这是一种编程范式,编程的思想,或编程的套路,面向过程编程不等于用函数编程
2. 核心是过程二字,面向过程指以过程为核心,过程即做事的步骤,即先干什么,再干什么,然后干什么,比如脚本
3. 基于该思想写程序就好比是在设计一条条的流水线,一条流水线就是一个功能
4. 过程式的思想是把大的问题分解成多个子过程去实现,然后依次调用
5. 优点:复杂问题流程化、进而简单化
6. 缺点:扩展性差,某一环节变了,前后有关联的环节都要跟着变
7. 演示示例: 写一个数据远程备份程序, 分三步: 本地数据打包,上传至云服务器,检测备份文件可用性
import os, time
def data_backup(folder): # 第一步,本地数据打包
print("找到备份目录: %s" % folder)
print('正在备份...')
zip_file = '/tmp/backup_%s.zip' % time.strftime('%Y%m%d')
print('备份成功,备份文件为: %s' % zip_file)
return zip_file
def cloud_upload(file): # 第二步,上传至云服务器
print("\nconnecting cloud storage center...")
print("cloud storage connected")
print("upload [%s] to cloud..." % file)
link = 'https://www.xxx.cn/bak/%s' % os.path.basename(file)
print('close connection')
return link
def data_backup_check(link): # 第三步,检测备份文件可用性
print("\n下载文件: %s , 验证文件是否无损..." % link)
zip_file = data_backup(r"/Users/test") # 第一步调用,本地数据打包
link = cloud_upload(zip_file) # 第二步调用,上传至云服务器
data_backup_check(link) # 第三步调用,检测备份文件的可用性
网友评论