python夯实基础日记-函数详解
函数也是对象_内存分析
Python中,“一切都是对象”。实际上,执行def定义函数后,系统就创建了相应的函数对象。
在定义时就已经创建好了函数对象,调用时无需创建,只是反复调用
执行如下程序,然后进行解释:
#定义时创建了test01变量存在栈中,test01的id指向堆中的值对象
def test01():print('daxian')
#函数带括号表示调用了该函数,执行了函数里面的代码
test01()
c = test01 #将test01中的值拷贝给c;c的id也指向了堆中值对象
c()#函数是对象,可以作为参数传递,也可以作为返回值返回print(id(test01))#test01和c的id相同
print(id(c))
print(type(c))#c也成为了funtion
71.变量的作用域(全局变量_局部变量)_栈帧内存分析
变量起作用的范围称为变量的作用域,不同作用城内同名变量之间互不影晌,变量分为:全局变量、局部变量。
全局变量:
1.在函数和定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束
2.全局变量降低了函数的通用性和可读性。尽量免全局变量的使用
3.全局量一般做常量使用。
4.函数内要改变全局变量的值,使global声明一下
局部变量:
1.在函数体中(包含形式数参数)声明的量
2.局部变量的引用比全局变量快,优先考虑使用
3.如果局变量和全局变量同名,则在函数内隐藏全局变量,只使用同可名的局部变量
【操作】全局变量的作用域测试
#测试全局变量和局部变量
a = 3 #全局变量
def test01():b = 4 #局部变量'''global a 想要使用全局a时需要global声明'''print(b*10)a = 300 #局部变量print(a)print(locals())#打印输出局部变量print(globals())#打印输出全局变量
test01() #当test01被调用会创建一个栈帧,栈帧里可以访问外面,外面不能访问栈帧里,当调用结束后栈帧销毁,就像胶卷
#print(b) 局部变量被使用会出错
print(a) #这个a是全局变量,和test()里的不是同一个a
72.局部变量和全局变量_效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。
在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运行速度
#测试局部变量,全局变量的效率
import math
import time
def test01():start01 = time.time()for i in range(10000000):math.sqrt(30)end01 = time.time()print('耗时{}:'.format(end01-start01))
test01()def test02():b = math.sqrt ’‘’把函数对象赋给b,b就成为一个函数了math相当于一筐水果,sqrt是一个苹果,b相当于一个纸片如果在循环中使用math.sqrt(),就要每次去框里取,b = math.sqrt相当于把纸片当成了math.sqrt,循环中每次去使用纸片就可以了‘’‘# print(type(b)) →'''class 'builtin_function_or_method''''start02 = time.time()for i in range(10000000):b(30)end02 = time.time()print('耗时{}:'.format(end02 - start02))
test02()
73.参数的传递_传递可变对象_内存分析
函数的参数传递本质上就是:从实参到形参的赋值操作。 **Python中“一切皆对象”,所有的赋值操作都是“引用的赋值”。**所以,Python中参数的传递都是“引用传递”,不是“值传递”,具体操作时分为两类:
1.对“可变对象”进行“写操作”,直接作用于原对象本身。传递了可变对象以后,原对象和被传递的对象是同一个对象(引用)
2.对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间。(起到他语言的“值传递”效果,但不是“值传递”)
可变对象有:
字典、列表、集合、自定义的对象等
不可变对象有:
数字、字符串、元组、 function等
【操作】参数传递:传递可变对象的引用
b = [10,20]
def f2(m):print(id(m))m.append(30)print(id(m))
f2(b)
print(id(b))
传递可变对象的引用
传递参数是可变对象(例如:列表、字典、自定义的其他可变对象等),实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
74.参数的传递_传递不可变对象_内存分析
传递参数是不可变对象(例:int、foat、字符串、元组布尔值),实际传递的还是对象的引用。在”赋值操作”时,由于不可变对象无法修改,系统会新创建一个对象。
【操作】参数传递:传递不可变对象的引用
a = 100 #定义全局变量
def f1(n):print('n:',id(n)) #传递进来的是a对象的地址,n在栈帧内也指向堆内100这个对象n = n + 200 #由于a是不可变对象,因此创建新的对象n;栈帧的引用消亡,重新指向300对象print('n',id(n)) #n已经变成了新的对象print(n)
f1(a) #a传递给n
print('a',id(a))
输出结果:
n: 4419577744
n 4422324464
300
a 4419577744
显然,通过id值我们可以看到n和a一开始是同一个对象。给n赋值后,n是新的对象。
75.浅拷贝和深拷贝_内存分析
为了更深入的了解参数传递的底层原理,我们需要讲解一下“浅拷贝和深拷贝”。我们可以使用内置函数:copy(浅拷贝)、 deepcopy(深拷贝)
浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。(只拷贝自己,不拷贝儿子和孙子)
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象。(自己,儿子,孙子都拷贝)
浅拷贝实例:
import copy
a = [10,20,[5,6]]
b = copy.copy(a)print('a:',a)
print('b',b)b.append(30)
b[2].append(7)print('浅拷贝')
print('a:',a)
print('b',b)
输出结果:
a: [10, 20, [5, 6]]
b [10, 20, [5, 6]]
浅拷贝
a: [10, 20, [5, 6, 7]]
b: [10, 20, [5, 6, 7], 30]
深拷贝实例
import copy
a = [10,20,[5,6]]
b = copy.deepcopy(a)print('a:',a)
print('b',b)b.append(30)
b[2].append(7)print('浅拷贝')
print('a:',a)
print('b:',b)
输出结果:
a: [10, 20, [5, 6]]
b [10, 20, [5, 6]]
深拷贝
a: [10, 20, [5, 6]]
b: [10, 20, [5, 6, 7], 30]
76.参数的传递_不可变对象含可变子对象_内存分析
传递不可变对象用的是浅拷贝
传递参数是不可变对象(例如:int、float、字符串、元祖、布尔值),实际传递的还是对象的引用,但是在’写操作‘时,会创建一个新的对象拷贝。这个拷贝使用的是浅拷贝,不是深拷贝。
例1:
a = 10
print('a:',id(a))def test01(m):print('m:',id(m))#浅拷贝,m和a引用都指向10m = 20#m的引用指向20,原来指向print(m)print('m:', id(m))test01(a)#a传递进函数内
输出结果:
a: 4531346512
m: 4531346512
20
m: 4531346832
例2:
#传递不可变对象时,若不可变对象里面包含的子对象是可变的,则方法内修改了这个可变子对象,源对象也发生了变化
a = (10,20,[5,6])
print('a:',id(a))def test01(m):print('m:',id(m))#浅拷贝,m和a引用都指向10#m[0] =1 元组内的元素不能修改,会报错m[2][0] = 888print(m)print('m:', id(m))test01(a)#a传递进函数内
print(a)
输出结果:
a: 4396437088
m: 4396437088
(10, 20, [888, 6])
m: 4396437088
(10, 20, [888, 6])
77.参数的类型_位置参数_默认值参数_命名参数
位置参数
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数,称为:“位置参数”
def f1(a,b,c):print(a,b,c)f1(2,3,4)
f1(2,3)#报错,位置参数不匹配 TypeError: f1() missing 1 required positional argument: 'c'
默认值参数
我们可以为某些参数设置默认值,这样这些参数在传递时就是可选的。称为“默认值参数”。默认值参数放到位置数后面。
def f1(a,b,c=10,d=20): #默认值参数必须位于其他参数后面print(a,b,c,d)f1(2,3)
f1(2,3,4)
'''
2 3 10 20
2 3 4 20
'''
命名参数
我们也可以按照形参的名称传递参数,称为“命名参数”,也称“关键字参数”
def f1(a,b,c):print(a,b,c)f1(2,3,4)
f1(c=10,b=15,a=1)
'''
2 3 4
1 15 10
'''
78.参数的类型_可变参数_强制命名参数
可变参数
可变参数指的是“可变数量的参数”。分两种情况:
1.* param(一个星号),将多个参数收集到一个“元组”对象中
- **param(两个星号),将多个参数收集到一个“字典”对象中
def f(a,b,*c):print(a,b,c)def f2(d,e,**f):print(d,e,f)def f3(a,b,*c,**d):print(a,b,c,d)f(1,2,3,4,5)
f2(6,7,name = 'daxian',age = 18,job = 'teacher')
f3(1,2,3,4,5,name = 'daxian',age = 18,job = 'teacher')
强制命名参数
在带星号的“可变参数”后面增加新的参数,必须是“强制命名参数”
def f(*a,b,c):print(a,b,c)#f(1,2,3) 会报错,由于a是可变参数,将2,3,4全部收集,造成b,c没有赋值
f(1,b = 2,c = 3)
输出结果:
(1,) 2 3
79.lambda表达式和匿名函数
lambda表达式可以用来声明匿名函数。 lambda函数是一种简单的、在同一行中定义函数的方法, lambda函数实际生了一个函数对象。
lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回
lambda表达式基本语法:
lambda arg1,arg2,arg3… : <表达式>
arg1,arg2,arg3表示函数参数,<表达式>表示函数体。运行结果是表达式的计算结果。
【操作】lambda 表达式使用
f = lambda a,b,c : a + b + c
print(f)
print(f(1,2,3,))g = [lambda a:a*3, lambda b:b*4, lambda c:c*5]
print(g)
print(g[0](1),g[1](2),g[2](3))h = [f,f,f]
print(h[0](2,3,4))
输出结果:
<function <lambda> at 0x100758830>
6
[<function <lambda> at 0x100758a70>, <function <lambda> at 0x100758b00>, <function <lambda> at 0x100758b90>]
3 8 15
9
80.eval()函数用法
功能:将字符串str当成有效的表达式来求值并返回算结果。
语法:eval(source,globals[,locals])-> value
参数
source:一个Python表达式或函数compile()返回的代码对象
globals:可选,必须是dictionary
locals:可选。任意映射对象
s= 'print("abcd")'
eval(s)a = 10
b = 20
c = eval('(a+b)')
print(c)dict1 = dict(a=20,b=20)
d = eval('a+b',dict1)#在dict1中的a和b
print(d)
81.递归函数_函数调用内存分析_栈帧的创建
递归函数指的是:自己调用自己的函数,在函数体内部直接或间接的自己调用自己。递归类似于大家中学数学学习过的“数学归纳法”。
每个递归函数必须包含两个部分:
1.终止条件
表示递归什么时候结束。一般用于返回值,不再调用自己。
2.递归步骤
把第n步的值和第n-1步相关联
递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数时,谨慎使用。
内存调用分析
def test01():print('test01')test02()#test01()会报错def test02():print('test02')test01()
输出结果:
test01
test02
如上图,执行test01()时候,先启动一个test01栈帧,再启动一个test02栈帧,test02栈帧执行完后先消亡,test01()执行结束后再消亡。
如果在test01()中加入test01():
如上图:第一次创建一个test01栈帧,test01()内代码执行到第二行,又创建第二个test01栈帧,第二个test01()代码执行到第二行,又创建一个test01栈帧,如此循环反复,直到栈内存满了就会报错,因此要设置一个停止的条件。
---------------------------------------
如上图,当在第四次设置了停止条件,第三个栈帧就会执行test01()中之后的代码,代码执行结束,第三个test01栈帧就会消亡,执行第二个test01()剩余代码,执行结束后第二个test01栈帧消亡,知道所有栈帧消亡。
def test01(n):print(n)if n == 0:print('over')else:test01(n-1)
test01(4)
类似过滤器
def test01(n):print('test01:',n)if n == 0:print('over')else:test01(n-1)print('test01***',n)test01(4)
如上图,创建第一个栈帧,代码执行到test01(3),print(‘test01***’,4)未执行;创建第二个栈帧,代码执行到test01(2),print(‘test01***’,3)未执行…。直到over后,执行print(‘test01***’,0),然后第5个栈帧消亡,执行print(‘test01***’,1),然后第4个栈帧消亡…直到所有栈帧消亡。因此会输入如下结果:
test01: 4
test01: 3
test01: 2
test01: 1
test01: 0
over
test01*** 0
test01*** 1
test01*** 2
test01*** 3
test01*** 4
82.递归函数_阶乘计算案例
计算10以内的阶乘:
#使用递归函数计算阶乘
def factoria(n):if n == 1 :return 1else:return n*factoria(n-1) #套娃m = int(input('请输入1-10的整数:'))
for i in range(1,m+1):print('{0}!={1}'.format(i,factoria(i)))
以m=5为例子,分析内存:
求斐波那契数列第6项
def fibs(n):if n == 1 or n == 2:return 1else:return fibs(n-1)+fibs(n-2)
print(fibs(6))
83.嵌套函数_内部函数_数据隐藏
嵌套函数 :
在函数内部定义的函数。
【操作】嵌套函数定义、语法结构(
def f1():print('f1() running')def f2():print('f2 runnig')f2()
f1()
定义在f1()内,调用也在f1()内,f2()只为f1()服务。
一般在什么情况下使用嵌套函数?
1.封装-数据隐藏
外部无法访问“嵌套函数
2.贯彻 DRY(Don’t Repeat Yourself)原则
嵌套函数,可以让我们在函数内部避免重复代码
3.闭包
后面会详细讲解。
【操作】使用嵌套函数避免重复代码
def printChineseName(familyName,name):print('{0} {1}'.format(familyName,name))
def printEnglishName(name,familyName):print('{0} {1}'.format(name,familyName))'''
将上面的函数封装到嵌套函数中
'''
def printName(isChinese,familyName,name):def inner_print(a,b):print('{0} {1}'.format(a,b))if isChinese: #如果是中文名,把姓传给inner()中的a,名传给inner()中的binner_print(familyName,name)else:#如果是英文名,把姓传给inner()中的b,名传给inner()中的ainner_print(name,familyName)printName(True,'ding','daxian')
printName(False,'daxian','ding')
84.nonlocal_global
nonlocal:
用来声明外层的局部变量。
global
用来声明全局变量。
【操作】使用 nonlocal声明外层局变量
a =100def outer():b = 10def inner():nonlocal b #,声明外部函数的局部变量,不声明nonlocal就不能在inner内修改b,会报错print('inner:', b)b = 20global aa = 1000inner()print('inner:', b)print('inner:', a)outer()
85.LEGB规则
Python在查找“名称”时,是按照LEGB规则查找的:
Local–>Enclosed–>Global–>Built in(从内到外)
local指的就是函数或者类的方法内
Enclosed指的是嵌套函数(一个函数包裹另一个函数,闭包)
Global指的是横块中的全局变量
Built in指的是 Python为自己保留的特殊名称。
如果某个name映射在局部(local)命名空间中没有找到,接下来就会在闭包作用域( enclosed)进行搜索,如果闭包作用域也没有找到, Python就会到全局( global)命名空间中进行查找,最后会在内建(built-in)命名空间搜索(如果一个名称在所有命名空间中都没有找到,就会产生一个 (NameError)。
#测试LEGB
str('str(30)')
# str='global str'
def outer():#str = 'outer'def inner():#str='inner'print(str)inner()outer()
python夯实基础日记-函数详解相关推荐
- python夯实基础日记-类详解
97.方法没有重载_方法的动态性 方法没有重载 在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可.方法签名包含3个部分:方法名.参数数量.参数类型. Python中,方法的的参数没有类型 ...
- python average函数详解_python基础之函数详解
Python基础之函数详解 一.函数的定义 到现在为止,我们已经掌握了Python的基本语法和数据类型等相关基础知识了,以进行一个项目的编写了,这个时候,就会发现,很多代码需要我们进行复制粘贴,这简直 ...
- Python seek()和tell()函数详解
Python seek()和tell()函数详解 在讲解 seek() 函数和 tell() 函数之前,首先来了解一下什么是文件指针. 我们知道,使用 open() 函数打开文件并读取文件中的内容时, ...
- Python中的bbox_overlaps()函数详解
Python中的bbox_overlaps()函数详解 想要编写自己的目标检测算法,就需要掌握bounding box(边界框)之间的关系.在这之中,bbox_overlaps()函数是一个非常实用的 ...
- python基础知识~ 函数详解2
python~函数详解2 1 生成器函数 定义 如果函数有yield这个关键字,就是生成器函数.生成器函数() 获取的是生成器,不执行函数 须知 yield和return一样,都可以返回数 ...
- python夯实基础日记-for循环、优化技巧、函数
057.for循环结构_遍历各种可迭代对象_range对象 for循环 通常用于可迭代对象的遍历,for循环的语法格式如下: for 变量 in 可迭代对象: 循环体语句 Python中的可迭代对象: ...
- python夯实基础日记-字典、集合、分支、循环
43.字典_特点_4种创建方式_普通_dict_zip_formkeys 字典简介和特点 字典是"键值对"的无序可变序列,字典中的毎个元素都是一个"键值对",包 ...
- 【python基础】python中常用字符串函数详解
文章目录 1 字符串查询(index,find) 2. 字符串大小写转换操作(upper.lower.swapcase.capitalize和title) 3. 字符串对齐(center,just和z ...
- python读取数据的函数详解_你了解文件缓存机制吗?磁盘文件如何读写?Python中open函数详解...
我们知道,在使用Python打开一个文件时,一般使用的是open()函数,但是你真正了解这个函数么?文件打开后如何进行缓存?对于大文件它是如何处理的?今天,小编带你来详细了解一下-- Python如何 ...
最新文章
- Kotlin 使用list.add 时候报错的处理方法
- Delphi开发人员的编程习惯
- 【Linux】一步一步学Linux——compress命令(了解)(71)
- Java API —— Map接口
- mysql 以 db 结尾_MySQL的高级部分
- windows下同时安装python2与python3
- 【Xamarin】MonoTouch - iOS 使用 UIImagePickerController 打开图片库和相机选择图片修改头像...
- 用c++实现简单单链表,双链表,二叉树类
- OpenGL基础14:摄像机
- Android屏幕共享权限,chrome屏幕共享权限
- 定义结构体的三种方法(C语言)
- ap计算机知识点总结,AP统计学考试知识点汇总
- PM、GAN、InfoGAN、对抗自编码模型对比
- python去除字符串中表情字符
- 乐视X625手机刷机(全三个版本)官方包附刷机教程OEM解锁
- 关于 邵雍 渔樵问对 千古奇闻的思考
- 局域网IP变成广域网的IP
- HCIP之路重点LSA
- 百度网盘提速法,不用插件,不用安装脚本
- android应用间相互调用
热门文章
- win10 明明可以上网但显示无Internet的问题(已解决)
- 2022年5月8日 解决手机连接电脑无法选择“传输文件”
- dashboard 1.8
- COUNT、COUNTA、MAX三个函数在excel计数上的应用对比和案例
- VSCode 的 Live Share 功能终于来了
- windows10安装更新很慢ndows,Windows10下载更新一直不动,进度为0怎么办?
- webug4.0总结篇
- WeBug3.0靶场环境搭建与搭建资源分享
- python爬取股票行情_python爬取历史所有股票价格
- linux下 18 个实用的终端命令行工具