Python的学习-8-函数
函数
函数是可复用的程序代码块。函数的作用,不仅可以实现代码的复用,更能实现代码的一致性。一致性指,只修改函数的代码,则所有调用该函数的地方都能得到体现。
函数的基本概念
- 一个程序由一个个人物组成;函数就是代表一个任务或者一个功能
- 函数是代码复用的通用机制
python函数的分类
Python中函数分为如下几类:
- 内置函数
我们使用的str(),list()等都是内置函数 - 标准库函数
我们可以通过import语句导入库,然后使用其中定义的函数。这种标准库函数一般是官方提供的,一般是解释器自带的,不需要下载安装。 - 第三方库函数
第三方一般是指除官方提供之外的,高质量的库,需要下载安装。 - 用户自定义函数
用户自定义的函数,满足自身开发需求的函数。
函数的定义和调用
函数定义语法格式:
def 函数名(参数列表):
"""文档字符串(函数说明)"""
函数体
核心要点
-
使用def定义函数,Python指定def时,创建一个函数对象,并将这个对象绑定到函数变量上。
-
参数列表
(1)圆括号内时形式参数列表,多个参数逗号隔开
(2)形式参数不需要声明类型,也不需要指定函数返回值类型
(3)无参数也要保留圆括号
(4)实参列表必须与形参列表一一对应 -
return返回值
(1)函数体中包含return,则结束函数并返回
(2)函数体中无return,返回None
(3)要返回多个值,可以使用列表,元组,字典,集合等把要返回的值组合在一起。 -
调用函数之前,必须要先定义函数
(1)内置函数对象会自动创建
(2)标准库和第三方库通过import导入模块时,会创建函数对象。
形参和实参
def function(var1,var2):
'''
文档字符串
:param var1:
:param var2:
:return:
'''
print(var1,var2)
function("Hello","World")
help(function.__doc__)
在上面这个函数中function()中,var1和var2是形式参数,他们的作用范围仅仅是在本函数中。在调用函数时,我们在圆括号中书写的内容则是实际参数。形参和实参必须一一对应。
调用help(函数名.doc)可以打印输出函数的文档字符串。
变量
变量的作用域(全局变量和局部变量)
全局变量:
1.在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束。
2.全局变量降低了函数的通用性和可用性,尽量避免使用。
3.全局变量一般做常量使用
4.函数内部要改变全局变量的值,使用global声明
局部变量:
1.在函数体(包括形式参数)声明的变量
2.局部变量的引用比全局变量快
3.若局部变量和全局变量同名,则在函数内隐藏全局变量,优先使用局部变量
Python在创建函数对象的时候,同时会在栈中创建一个栈帧,栈帧会用于保存该函数的局部变量名,这些变量名指向堆中的变量值,哈桑农户调用完毕之后,栈帧删除。
在编译原理中,在符号表的章节中有一段关于作用域的内容,感兴趣的可以自行搜索相关信息。
参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。Python一切皆对象,所有的赋值操作都是引用的赋值操作。所以python中参数传递都是”引用传递“(也就是地址传递)。
具体操作有两类:
- 对”可变对象"进行写操作,直接作用域对象本身
- 对”不可变对象“进行"写操作",会产生一个新的"对象空间",并用新的值填充这块空间。(起到值传递的效果,实际上也是地址传递,只不过原地址被覆盖)
可变对象有:
字典,列表,集合,自定义的对象等
不可变对象有:
数字,字符串,元组,函数等
传递可变对象和不可变对象的引用
具体可以看下面的代码。
#传递可变对象的引用
a = [10,20]
def f2(n):
print("n:",id(n))
n.append(30)
print("a的ID:",id(a))
f2(a)
print("a的ID:",id(a))
print(a)
b = 100
def f3(n):
print("n:",id(n))
n += 200
print("n:",id(n))
print(n)
print("B的ID:",id(b))
print(b)
f3(b)
print("B的ID:",id(b))
print(b)
浅拷贝和深拷贝
浅拷贝和深拷贝可以使用内置函数:copy()和deepcopy()。
- 浅拷贝:不拷贝子对象的内容,只拷贝子对象的引用
- 深拷贝:连子对象的内存全部拷贝,对子对象的修改不会影响源对象
#测试浅拷贝和深拷贝
import copy
def TestCopy():
"""测试i浅拷贝"""
print("浅拷贝")
a = [10,20,[50,60]]
b = copy.copy(a)
print("a",a)
print("b",b)
print("对子对象b[2]进行操作")
b.append(30)
b[2].append(70)
print("a", a)
print("b", b)
def TestDeepCopy():
"""测试深拷贝"""
print("深拷贝")
a = [10,20,[50,60]]
b=copy.deepcopy(a)
print("a:",a)
print("b:",b)
print("对子对象b[2]进行操作")
b.append(80)
b[2].append(7)
print("a:", a)
print("b:", b)
TestCopy()
TestDeepCopy()
"""
浅拷贝
a [10, 20, [50, 60]]
b [10, 20, [50, 60]]
对子对象b[2]进行操作
a [10, 20, [50, 60, 70]]
b [10, 20, [50, 60, 70], 30]
深拷贝
a: [10, 20, [50, 60]]
b: [10, 20, [50, 60]]
对子对象b[2]进行操作
a: [10, 20, [50, 60]]
b: [10, 20, [50, 60, 7], 80]
"""
总结:传递不可变对象时,不可变对象里面包含的子对象是可变对象。若方法内修改了这个可变对象,则源对象也发生变化。
参数的几种类型
位置参数
函数调用时,实参默认按参数列表的位置与形参一一对应,需要个数和顺序都对应。
def f1(a,b,b):
print(a,b,c)
f1(1,2,3)
默认值参数
在形参列表中对形参进行赋值,就可以设置参数默认值。这个时候默认值参数的位置可以不传递实参。
def f1(a=0,b=0,c=0):
print(a,b,c)
f1(1,2)
f1()
命名参数
在调用函数的时候,按照形参的名字传递参数,不必遵循位置参数对应原则。
def f1(a,b,c):
print(a,b,c)
f1(1,2,3)
f1(c=10,a=0,b=5)
可变参数
可变参数指的是“可变数量的参数”。分两种情况:
- *param,将多个参数收集到一个元组对象中
- **param,将多个参数收集到一个字典对象中
def f1(a,b,*c):
print(a,b,c)
f1(8,9,19,20,21)
# 8 9 (19, 20, 21)
def f2(a,b,**c):
print(a,b,c)
f2(8,9,name="rain",age=19)
# 8 9 {'name': 'rain', 'age': 19}
强制命名参数
在带*的可变参数后面增加新的参数,必须是命名参数。因为从逻辑上来说,不指明参数的传递,那么第一个的可变参数就不知道药将哪几个参数组合在一起。
def f1(*a,b,c):
print(a,b,c)
f1("%","$",b=1,c=3)
# ('%', '$') 1 3
lambda表达式和匿名函数
lamnda表达式可以用来声明匿名函数。
lambda函数是一种简单的,在同一行中定义函数的方法。
lamda函数实际生成了一个函数对象。
lamda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。
lambda表达式的基本语法如下:
lambda arg1,arg2,arg3 : <表达式>
arg1/arg2/arg3为函数的参数。<表达式>相当于函数体。
f = lambda a,b,c :a+b+c
print(f)
print(f(1,2,3))
g=[lambda a: a*2,lambda b :b**2]
print(g[0](1),g[1](1))
"""结果
<function <lambda> at 0x000001BF5FF7F040>
6
2 1
"""
eval函数
功能:将字符串str当成有效的python表达式来求职并返回计算结果
语法:eval(source,global,locals )
参数:
- source:一个Python表达式或函数compile()返回的代码对象
- globals:可选。必须是dictionary
- locals:可选。任意映射对象
s="print('Hello World')"
eval(s)
a=10
b=20
c = eval("a+b")
print(c)
dict = dict(a=100,b=200)
d=eval("a+b")
print(d)
d=eval("a+b",dict)
print(d)
递归函数
递归函数就是:自己调用自己的函数。
每个递归函数包含两个部分:
- 终止条件
表示递归什么时候结束。 - 递归步骤
把第n步和第n-1步相关联
递归在算法涉及和分析中是一种很重要的解决思路。
LEGB规则
Python在查找变量名时,按照LEGB规则去寻找
Local→Enclosed→Global→Built in
- Local:指的是函数或者类的方法内部
- Enclosed:指的是嵌套函数
- Global:指的是模块中的全局变量
- Built in:指的是Python为自己保留的特殊名称
# 可以自行对不同的str注释之后查看效果
str = "global"
def outer():
str="outer"
def inner():
str="inner"
print(str)
inner()
outer()