函数
五章—函数的应用和底层分析
函数是可重用的程序代码块。函数的作用,不仅可以实现代码的复用,更能实现代码的一致性。一致性指的是,只要修改函数的代码,则所有调用该函数的地方都能得到体现。
Python 函数的分类
(1)内置函数
我们前面使用的 str()、list()、len()等这些都是内置函数,可以拿来直接使用。
(2)标准库函数
我们可以通过 import 语句导入库,然后使用其中定义的函数
(3) 第三方库函数
Python 社区也提供了很多高质量的库。下载安装这些库后,也是通过 import 语句导入,然后可以使用这些第三方库的函数
(4)用户自定义函数
用户自己定义的函数,显然也是开发中适应用户自身需求定义的函数。今天我们学习的就是如何自定义函数.
形参和实参
在python函数中,在定义时写的 函数的标识符称为“形式参数”,简称“形参”。也就是说,形式参数是在定义函数时使用的。 形式参数的命名只要符合“标识符”命名规则即可。
在调用函数时,传递的参数称为“实际参数”,简称“实参”
help语句调用查看函数解释
调用 help(函数名.doc)可以打印输出函数的文档字符串
def print_star(n):
'''根据传入的 n,打印多个星号'''
print("*" * n)
help(print_star)
return语句
(1) 返回值
(2)结束函数运行
def x(a,b):
print(a+b,"a和b的值")
return(a+b) #返回值
def y(a,b):
print(a)
return #结束运行 往后的程序并未运行
print(b)
x(30,40)
y(50,60)
执行 def 定义函数后,系统就创建了相应的函数对象。对象可以看作堆里的内存块。因此函数同样可以想字符串赋值一样把它的功能赋给字符。
变量的作用域(全局变量和局部变量)
变量起作用的范围称为变量的作用域,不同作用域内同名变量之间互不影响。变量分为:全局变量、局部变量。
a=5 #全局变量
b=1
def Yan(a,b):
a=10
b=40 #局部变量
print(a,b)
Yan(a,b) # 全部调用局部变量
如果在函数中的变量与函数外的全局变量重复则调用时,用的是局部变量,如想使用全局变量则使用global语句。
局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。
import math
import time
def Myfunc1():
start=time.time()
for i in range(1000000):
math.sqrt(30)
end=time.time()
print(end-start)
def Myfunc2():
start=time.time()
b=math.sqrt
for i in range(1000000):
b(30)
end=time.time()
print(end-start)
Myfunc1()
Myfunc2()
参数的传递
所有的赋值操作都是“引用的赋值”。Python 中参数的传递都是“引用传递”,不是“值传递”。
传递参数是可变对象(例如:列表、字典、自定义的其他可变对象等),实际传递的是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
# 传递可变对象的引用
a=[20,30]
print(id(a))
def fob(m):
print('m:',id(m))
m.append(40)
print(id(m))
fob(a)
print('a:',id(a))
print(a)
传递参数是不可变对象(例如:int、float、字符串、元组、布尔值),实际传递的还是对象的引用。在”赋值操作”时,由于不可变对象无法修改,系统会新创建一个对象。
# 传递不可变对象的引用
a=100
print(id(a))
def fin(m):
print(id(m))
m=m+2
print(id(m))
fin(a)
print(id(a))
浅拷贝和深拷贝
浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象
# 深copy和浅copy
import copy
a=[100,200,4,[1,5,6]]
b=copy.copy(a)
b.append(30)
b[3].append(7)
print(a)
print(b)
# 深copy和浅copy
import copy
a=[100,200,4,[1,5,6]]
b=copy.deepcopy(a)
b.append(30)
b[3].append(7)
print(a)
print(b)
传递不可变对象包含的子对象是可变的情况
a=(30,40,50,[5,6,7])
print('a:',id(a))
def myfunc2(m):
print('m:',id(m))
m[3][0]=567
print('m:',id(m))
print('m:',m)
myfunc2(a)
print(a)
函数中的参数分类
位置参数:函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数。
默认值参数:可以为某些参数设置默认值,这样这些参数在传递时就是可选的。称为“默认值参数”。默认值参数放到位置参数后面
命名参数:可以按照形参的名称传递参数,称为“命名参数”,也称“关键字参数”
# 位置参数 默认值参数 命名参数
def myfunc01(a,b,c,d):
print('{0}-{1}-{2}-{3}'.format(a,b,c,d))
def myfunc02(a, b, c=33, d=66):
print('{0} - {1} - {2} - {3}'.format(a, b, c, d))
myfunc01(10,20,30,40)
myfunc02(10,2,3)
myfunc02(10,2)
可变参数指的是“可变数量的参数”。分两种情况:
(1)*param(一个星号),将多个参数收集到一个“元组”对象中。
(2)**param(两个星号),将多个参数收集到一个“字典”对象中。
强制命名参数:在带星号的“可变参数”后面增加新的参数,必须在调用的时候“强制命名参数”。
lambda 表达式可以用来声明匿名函数。lambda 函数实际生成了一个函数对象。
eval()函数
功能:将字符串 str 当成有效的表达式来求值并返回计算结果。
a="print('abcd')"
eval(a)
a=10
b=15
dict1=dict(a=12,b=13)
print(dict1)
c=eval('a+b',dict1) #若不加括号后面的dict1则默认执行前面赋值的a和b
print(c)
递归函数
递归函数指的是:自己调用自己的函数,在函数体内部直接或间接的自己调用自己。递归类似于大家中学数学学习过的“数学归纳法”。
# 测试递归函数的基本原理
def myfunc1():
print('myfunc()')
myfunc1()
myfunc1() # 会无限循环
注意:该程序会无限循环,最终报错,没有终止条件。
# 测试递归函数的基本原理
def myfunc1(n):
print('myfunc()',n)
if n==0:
print('over')
else:
myfunc1(n-1)
print('myfunc1()*****',n)
myfunc1(5) # 有终止条件会无限循环
递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数据时,谨慎使用。
练习递归函数求阶乘:
# 递归函数求阶乘
def myfunc1(n):
if n==1:
return 1
else:
return n*myfunc1(n-1)
s=myfunc1(6)
print(s)
嵌套函数(内部函数):在函数内部定义的函数
(1)封装 - 数据隐藏
外部无法访问“嵌套函数”。
(2)贯彻 DRY(Don’t Repeat Yourself) 原则
嵌套函数,可以让我们在函数内部避免重复代码。
(3)闭包
内部函数和外部函数之间关系的测试
#测试嵌套函数
def outmyfunc1():
print('a=20')
def innermyfunc1():
print('b=30')
innermyfunc1() #内部函数则需内部调用,若在外部调用则报错显示没有定义
outmyfunc1()
嵌套函数打印chinesenae和englishname的练习
def name(ischinesename,name,familyname): #ischinesename是个判断条件的参数用于内部函数的条件
def innername(a,b):
print('{0}{1}'.format(name,familyname))
if ischinesename:
print('{0}{1}'.format(familyname,name))
else:
print('{0}{1}'.format(name, familyname))
name(True,'邦','刘')
nonlocal语句
# 测试nonlocal语句
a=100
def myout():
b=13
print('b:',b)
global a #声明是全局变量,即是把全局变量更改了
a=1000
print('a:', a)
def myinner():
nonlocal b #声明是非局部变量即是外层函数的b也更改了
b=18
print('b:',b)
myinner()
myout()
print('a:',a)
LEGB规则
LEGB 规则
Python 在查找“名称”时,是按照 LEGB 规则查找的:
Local–>Enclosed–>Global–>Built in
Local 指的就是函数或者类的方法内部;
Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包);
Global 指的是模块中的全局变量;
Built in 指的是 Python 为自己保留的特殊名称。
# LEGB规则测试程序
def outer():
str='outer你好'
def inner():
str='inner你好'
print(str)
inner()
outer()