1. 函数空间角度
Python中一切皆对象。实际上,执行def函数定义后,系统就创建了相应的函数对象。调用的时候直接去调用这个函数对象。
def test01():
print('123')
test01() #123
c = test01
c() #123
Python解释器遇到函数定义的时候,解释器只是象征性的将函数名读入内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。
- 读到def函数定义时,在堆内存中创建一个函数对象
- 在栈内存保存了test01的变量(它的值是函数的内存地址)
- 通过函数名+()的形式实现函数的调用
- 当读到c=test01,把test01的值拷贝给了c, 所以test01和c指向相同的对象。
1.1 变量的作用域
-
全局变量
-
在函数和类之外声明的变量
-
全局变量降低了函数的通用性和可读性,应尽量避免使用全局变量
-
全局变量一般做常量使用
-
函数内要更改全局变量的值,使用global声明一下
-
局部变量
-
在函数体中声明的变量
-
局部变量的引用比全局变量快,优先考虑使用
-
如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量
a = 3 #全局变量
def func():
b = 4 #局部变量
print(b*10)
print(b) # NameError: name 'b' is not defined
print(a) #3
- 函数调用
当函数调用时,会在栈内存中创建一个栈帧,存放变量与值的对应关系(即变量存储对象的地址),函数结束栈帧消失。
- global
如果要在函数内更改全局变量的值,使用global声明变量
2. 参数的传递
2.1 传递可变对象
传递参数是可变对象(例如:列表、字典),实际本质还是对象的引用。在函数体中不创建新的对象,而是可以直接修改所传递的对象
b = [10,20]
def f2(m):
m.append(30)
f2(b)
print(b) #[10, 20, 30]
m和b存储的都是对象的内存地址,m进行列表操作便是对原列表进行操作
2.2 传递不可变对象
实际传递的还是对象的引用。在’‘赋值操作’'时,由于不可变对象无法修改,系统会创建一个对象。
a = 100
def f1(n): #传递进来的是a对象的地址
n = n+200 #由于a是不可变对象,因此创建新的对象n
print(n)
f1(a) #300
print(a) #100
3. 深浅拷贝
浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响原对象
- 浅拷贝举例
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) #[10, 20, [5, 6, 7]]
print('b',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) #[10, 20, [5, 6]]
print('b',b) #[10, 20, [5, 6, 7], 30]
4. 递归
自己调用自己,要有明确的终止条件
- 练习:阶乘计算
def factorial(n):
if n == 1 :
return 1
else:
return n*factorial(n-1)
print(factorial(5))