Python 函数用法和底层分析
目录
函数:可重用的程序代码块
要点:单一功能,有效切割
函数简介
函数是代码复用的通用机制
Python中函数分类
- 内置函数
- 标准库函数:通过import 语句导入库
- 第三方库函数
- 用户自定义函数
函数的定义和调用
定义
def 函数名([参数列表]):
‘’‘文档字符串’’’
函数体/若干语句
Python 执行def 时,会创建一个函数对象,并绑定到函数名变量上
(1) 内置函数对象会自动创建
(2) 标准库和第三方库函数,通过import 导入模块时,会执行模块中的def 语句
形参和实参
形式参数是在定义函数时使用的,形式参数的命名只要符合“标识符”命名规则即可
在调用函数时,传递的参数称为“实际参数”,简称“实参”
文档字符串
函数体开始部分的函数定义说明
调用**help(函数名.doc)**可以打印输出函数的文档字符串
返回值
return 返回值要点:
- 如果函数体中包含return 语句,则结束函数执行并返回值;
- 如果函数体中不包含return 语句,则返回None 值;
- 要返回多个返回值,使用列表、元组、字典、集合将多个值“存起来”即可
函数内存分析
执行def定义函数后,系统就创建了相应的函数对象
调用的时候直接反复调用该对象(创建栈,指向相同的堆(地址相同),执行完后栈被删除)
变量的作用域
全局变量
- 在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块
结束。 - 全局变量降低了函数的通用性和可读性,应尽量避免全局变量的使用;
- 全局变量一般做常量使用;
- 函数内要改变全局变量的值,使用global 声明一下
局部变量
- 在函数体中(包含形式参数)声明的变量;
- 局部变量的引用比全局变量快,优先考虑使用(如在循环较多的情况下);
- 如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量(优先使用局部变量)
nonlocal 关键字
用来声明外层的局部变量
#测试nonlocal、global 关键字的用法
a = 100
def outer():
b = 10
def inner():
nonlocal b #声明外部函数的局部变量
print("inner b:",b)
b = 20
global a #声明全局变量
a = 1000
inner()
print("outer b:",b)
outer()
print("a:",a)
inner b: 20
outer b: 20
a: 1000
LEGB规则
Python 在查找“名称”时,是按照LEGB 规则查找的:
Local–>Enclosed–>Global–>Built in
Local 指的就是函数或者类的方法内部
Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)
Global 指的是模块中的全局变量
Built in 指的是Python 为自己保留的特殊名称
如果一个名称在所有命名空间中都没有找到,就会产生一个NameError
参数
参数的传递
引用传递,非值传递(python中一切皆对象,所有的赋值都是引用的赋值)
1. 对“可变对象”进行“写操作”,直接作用于原对象本身。
2. 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间(起到其他语言的“值传递”效果,但不是“值传递”)
可变对象有:字典、列表、集合、自定义的对象等
不可变对象有:数字、字符串、元组、function 等
浅拷贝和深拷贝
copy():浅拷贝,不拷贝内容,只拷贝引用
deepcopy():深拷贝,连同拷贝子对象的内存,修改子对象不会影响源对象
浅拷贝:
>>> import copy
>>> a=[10,20,[5,6]]
>>> b=copy.copy(a)
>>> print("a:",a)
a: [10, 20, [5, 6]]
>>> print("'b:",b)
'b: [10, 20, [5, 6]]
>>> b.append(30)
>>> b[2].append(7)
>>> print("a:",a)
a: [10, 20, [5, 6, 7]]
>>> print("b:",b)
b: [10, 20, [5, 6, 7], 30]
深拷贝(不影响原对象):
>>> a=[10,20,[5,6]]
>>> b=copy.deepcopy(a)
>>> print("a:",a)
a: [10, 20, [5, 6]]
>>> print("b:",b)
b: [10, 20, [5, 6]]
>>> b.append(30)
>>> b[2].append(7)
>>> print("a:",a)
a: [10, 20, [5, 6]]
>>> print("b:",b)
b: [10, 20, [5, 6, 7], 30]
传递不可变对象是浅拷贝
参数的类型
- 位置参数
- 默认值参数(放到位置参数后面)
- 命名参数
- 可变参数
*param:将多个参数收集到一个元组对象中
**param:将多个参数收集到一个字典对象中 - 强制命名参数:在可变参数后增加的新参数必须是强制命名参数
lambda表达式和匿名函数
简单的、在同一行中定义函数的方法
只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值
基本语法:
lambda arg1,arg2,arg3… : <表达式>
eval( )函数
将字符串str 当成有效的表达式来求值并返回计算结果
语法:
eval(source[, globals[, locals]]) -> value
source:一个Python 表达式或函数compile()返回的代码对象
globals:可选,必须是dictionary
locals:可选,任意映射对象
>>> s="print(a+b)"
>>> a=10
>>> b=20
>>> dict={"a":100,"b":200}
>>> eval(s)
30
>>> eval(s,dict)
300
递归函数
递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己
递归结构包括两个部分:
- 定义递归头,递归地结束条件,否则将陷入死循环
- 递归体,什么时候需要调用自身方法。
过程理解:
def test(n):
print("call:", n)
if n == 0:
print("over")
else:
n = n - 1
test(n)
print("return", n)
test(4)
call: 4
call: 3
call: 2
call: 1
call: 0
over
return 0
return 0
return 1
return 2
return 3
Process finished with exit code 0
def factorial(n):
if n > 1:
return n*factorial(n-1)
else:
return 1
# 递归遍历目录下所有文件
import os
allfile = []
def getFiles(path,level):
childFiles = os.listdir(path)
for file in childFiles:
filepath = os.path.join(path,file)
if os.path.isdir(filepath):
getFiles(filepath,level+1)
allfile.append("\t"*level+filepath)
getFiles(os.getcwd(),0)
for f in reversed(allfile):
print(f)
嵌套函数(内部函数)
- 封装- 数据隐藏:外部无法访问嵌套函数
- 贯彻DRY(Don’t Repeat Yourself) 原则:嵌套函数,可以让我们在函数内部避免重复代码
- 闭包