目录
一、函数用法和底层分析
(一)函数也是对象,内存底层分析
python中一切都是对象,执行def定义函数后,系统就创建了相应的函数对象。
#测试函数也是对象
def test01():
print("sxtsxt")
test01()
c = test01
c()
print(id(test01))
print(id(c))
print(type(c))
(二)变量的作用域(全局变量和局部变量)
1.全局变量:
(1)在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束。
(2)全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。
(3)全局变量一般做常量使用。
(4)函数内要改变全局变量的值,使用global声明一下。
2.局部变量:
(1)在函数体中(包含形式参数)声明的变量。
(2)局部变量的引用比全局变量快,有限考虑使用。
(3)如果局部变量和全局变量同名,则在函数内隐藏局部变量,只使用同名的局部变量。
#测试全局变量、局部变量
a = 3 #全局变量
def test01():
b = 4 #局部变量
print(b*4)
global a #如果要在函数内改变全局变量的值,增加global关键字声明
a = 300
test01()
test01()
print(a)
3.输出局部变量和全局变量
print(locals())
print(globals())
(三)局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是循环的时候
#测试局部变量、全局变量的效率
import math
import time
def test01():
start = time.time()
for i in range(10000000):
math.sqrt(30)
end = time.time()
print("耗时{0}".format((end-start)))
def test02():
b = math.sqrt
start = time.time()
for i in range(10000000):
math.sqrt(30)
end = time.time()
print("耗时{0}".format((end-start)))
test01()
test02()
(四)参数的传递
函数的参数传递本质上是:从实参到形参的赋值操作。所有事物赋值操作都是“引用的赋值”。Python 中参数的传递都是“引用传递”,不是“值传递”。
a.对“可变对象”进行“写操作”,直接作用于对象本身。
b.对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间。(起到其他语言的“值传递”效果,但不是“值传递”。)
可变对象:字典、列表、集合、自定义的对象等
不可变对象:数字、字符串、元祖、function等
1.传递可变对象的引用
#参数的传递
#传递可变对象
a = [10,20]
print(id(a))
print(a)
print("*********************")
def test01(m):
print(id(m))
m.append(300)
print(id(m))
test01(a)
print(a)
结果:
14667336
[10, 20]
*********************
14667336
14667336
[10, 20, 300]
2.传递不可变对象的引用
a = 100
def f1(n):
print("n:",id(n))
n = n+200
print("n:",id(n))
print(n)
f1(a)
print("a:",id(a))
结果:
n: 1739910624
n: 12205952
300
a: 1739910624
3.浅拷贝和深拷贝
浅拷贝:不拷贝子对象的内容,知识拷贝子对象的引用
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象
#测试浅拷贝、深拷贝
import copy
def testCopy():
'''测试浅拷贝'''
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)
def testDeepCopy():
'''测试深拷贝'''
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)
testCopy()
print("**********************")
testDeepCopy()
4.传递不可变对象包含的子对象是可变的情况:是浅拷贝
#传递不可变对象时,不可变对象里面包含了子对象是可变的,则方法内修改了可变对象,源对象也发生变化
a = (10,20,[5,6])
print("a:",id(a))
def test01(m):
print("m:",id(m))
m[2][0] = 888
print(m)
print("m:",id(m))
test01(a)
print(a)
结果:
a: 44709480
m: 44709480
(10, 20, [888, 6])
m: 44709480
(10, 20, [888, 6])
(五)参数的几种类型
1.位置参数
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数,成为“位置参数”。
2.默认值参数
可以为某些参数设置默认值,这些参数在传递时可选。默认值参数放到位置参数后面。
3.命名参数:关键字参数
#测试参数的类型:位置参数,默认值参数,命名参数
def test01(a,b,c,d):
print("{0}-{1}-{2}-{3}".format(a,b,c,d))
def test02(a,b,c=15,d=25): #默认值参数,必须位于其他参数后面
print("{0}-{1}-{2}-{3}".format(a,b,c,d))
test01(10,20,30,40) #位置参数
#test01(10,20) #参数个数不匹配,报错
test01(d=20,b=10,a=40,c=30) #命名参数,通过形参名称来匹配
test02(2,3)
test02(2,3,4)
4.可变参数
(1)*param(一个星号),将多个参数收集到一个“元祖”对象中
(2)**param(两个星号),将多个参数收集到一个“字典”对象中
5.强制命名参数
在带星号的“可变参数”后面增加新的参数,必须在调用的时候“强制命名参数”。
(六)lambda表达式和匿名函数
f = lambda a,b,c:a+b+c
def test02(a,b,c):
return a+b+c
print(test02(2,3,4))
print(f(2,3,4))
g = [lambda a:a*2,lambda b:b*3]
print(g[0](6))
h = [test02,test02] #函数也是对象
print(h[0](3,5,6))
(七)eval()函数:将字符串str当成有效的表达式求值并返回计算结果
#测试eval()函数
s = "print('abcdef')"
eval(s)
a = 10
b = 20
c = eval("a+b")
print(c)
dict1 = dict(a=100,b=200)
d = eval("a+b",dict1)
print(d)
(八)递归函数
自己调用自己的函数
#测试递归函数的基本原理
def test01(n):
print("test01:",n)
if n == 0:
print("over")
else:
test01(n-1)
print("test01***",n)
def test02():
print("test02")
test01(4)
结果:
test01: 4
test01: 3
test01: 2
test01: 1
test01: 0
over
test01*** 0
test01*** 1
test01*** 2
test01*** 3
test01*** 4
#使用递归函数,计算阶乘
def factorial(n):
if n ==1:
return 1
else:
return n*factorial(n-1)
result = factorial(5)
print(result)