全局变量和局部变量
全局变量:
- 在函数和类定义之外声明的变量。作用域为定义的整个模块,从定义位置开始直到模块结束。
- 全局变量会降低函数的通用性和可读性。应尽量避免全局变量的使用。
- 全局变量一般做常量使用。
- 函数内要改变全局变量的值,需要使用global 声明。
局部变量:
- 在函数体中声明的变量(包含形式参数)。
- 局部变量的引用比全局变量快,优先考虑使用。
- 如果局部变量和全局变量同名,则在函数内就近使用同名的局部变量。
a = 100 # 在函数和类定义之外声明的全局变量,整个模块都能调用
def variable():
b=2 # 在函数体中声明的变量
print(a*b) #打印全局变量a 的值
variable() # 局部变量b,仅调用函数时可使用
print(b) # 报错,【name 'b' is not defined】
# 全局变量和局部变量同名
a = 100
def variable1():
a = 3 # 同名局部变量
print(a)
variable1() # 3,函数就近使用内部同名局部变量
print(a) # 100,外部定义的同名全局变量不受影响
def variable2():
global a # 使用globl关键字,在函数内部改变全局变量
print(a) # 函数内未定义a,调用全局变量a
a = 300 # 改变全局变量
variable2() # 100
print(a) # 300
# 输出当前局部变量和全局变量
a = 100
def variable3(a,b,c):
print(a,b,c)
print(locals()) # 打印输出当前函数的局部变量
print(globals()) # 打印输出整个模块的全局变量
variable3(2, 3, 4)
局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,在特别强调效率或循环次数很多的代码内,优先考虑使用局部变量提高运行速度。
import math
import time
def variable1():
# math和time都是全局变量
start = time.time()
for i in range(10000000):
math.sqrt(30)
end = time.time()
print("variable1,耗时{0}".format((end - start)))
def variable2():
a = time.time
b = math.sqrt
start = a()
for i in range(10000000):
b(30)
end = a()
print("variable2,耗时{0}".format((end - start)))
variable1() # variable1,耗时1.7957453727722168
variable2() # variable2,耗时1.3006963729858398
参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。Python中一切皆对象,参数的传递都是“引用传递”,不是“值传递”。具体操作时分为两类:
- 对可变对象进行写操作,即直接作用于原对象本身,地址指向不变。可变对象有 – 字典、列表、集合、自定义的对象等。
- 对不可变对象进行写操作,则会产生一个新的对象存储新的值,地址指向改变。不可变对象有 – 数字、字符串、元组、function 等
传递可变对象的引用
传递参数是可变对象(列表、字典、自定义的其他可变对象等),实际传递的是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
a = [10, 20]
print('a指向对象:', id(a)) # 2721719603592
print(a) # [10, 20]
def function(m):
print("m指向对象:", id(m)) # a 和 m指向同一个对象
m.append(30)
print('对m更改后,m指向对象:', id(m)) # m是可变对象,不创建对象拷贝,直接修改这个对象
function(a)
print("调用函数后,a指向对象:",id(a))
print('调用函数后,a的值',a) # m直接在原对象更改,调用函数a更改
传递不可变对象的引用
传递参数是不可变对象(int、float、字符串、元组、布尔值),实际传递的还是对象的引用。由于不可变对象无法修改,函数体中会新创建一个对象。
a = 100
print('a的地址', id(a)) # 140722612186608
def function1(n):
print("n:", id(n)) # 140722612186608,传递进来不可变对象,与a指向相同地址
n = n + 200 # int为不可变对象,创建新的对象,n指向新创建的地址
print("n:", id(n)) # 218633954832,n已指向新的对象,与a指向不同对象
print(n) # 300
function1(a)
print('a的地址:', id(a)) # 140722612186608,调用函数a不改变
传递不可变对象包含的子对象是可变
a = (10, 20, [5, 6])
print("a:", id(a)) # 2572238684312
def function2(m):
print("m:", id(m)) # 2572238684312
m[2][1] = 666
print(m) # (10, 20, [5, 666])
print("m:", id(m)) # 2572238684312
function2(a)
print(a) # (10, 20, [5, 666])
元组对象不可变,但元组内子对象有可变对象列表m[2][0]。可变子对象任然可变,但元组的地址指向不变。
浅拷贝和深拷贝
用内置函数:copy(浅拷贝)、deepcopy(深拷贝)
浅拷贝 – 不拷贝子对象的内容,只是拷贝子对象的引用。
深拷贝 – 会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象
import copy
def 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)
def DeepCopy():
'''测试深拷贝'''
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)
Copy()
print("*************")
DeepCopy()
浅拷贝内存示意图:
深拷贝内存示意图: