Python变量与内存管理
–与C语言中的变量做对比,更好的理解Python的变量。
变量
变量在C语言中
全局变量:其存放在内存的静态变量区中。
局部变量:代码块中存放在内存的代码区当中,当被调用后存放在内存栈区。
而Python的变量存储,则是使用类似堆的方式管理内存,由Python内部机制统一分配回收内存。
Python中的变量与变量存储–引用与对象
Python作为OOP(面向对象)编程,一直信奉着一个信条,就是万物皆对象。
所谓对象,它可以看成经过一系列的抽象以后,将具有共性的一类物体进行实例化(具象化)的个体,就如同我们每个人就是人类里面的一个对象。
class A():
name = "123"
def __init__(self):
pass
def funa(self):
pass
def funa():
pass
if __name__ == "__main__":
Fun = funa
Variable = 1
ClassA = A()
ListA = [1,2,3]
DictA = {'d':1,'e':2}
TupleA = (1,2,3)
Str = "python"
print(type(Fun))
print(type(Variable))
print(type(ClassA))
print(type(ListA))
print(type(DictA))
print(type(TupleA))
print(type(Str))
输出的是:
class ‘function’
class ‘int’
class ‘main.A’
class ‘list’
class ‘dict’
class ‘tuple’
class ‘str’
很明显,Python中不管是基础数据类型,类,函数,所有的一切都是作为一个类的对象存储在内存,也可以单纯的看做一个值。
而Python的变量就是作为一个引用,读取对象所存储的信息,与C面向过程所不同,Python变量即对象的引用,通俗来说就是指向值的名称。
所以Python的变量只是不过对于一块指定内存的引用,也即对对象的引用,或者称为指向值的名称,相对于全局变量,局部变量的赋值只是引用另一块内存。C语言中一个变量代表一块特定的内存,而Python不像C语言,可以看成数据已经存放在内存之中了,被Python的变量对内存进行引用。即使变量不存在了,内存里值也不会受到任何影响。
if __name__ == "__main__":
a = 1
b = 2
print(id(a))
print(id(b))
a = b
print(id(a))
print(id(1))
print(id(2))
sys.exit(0)
输出的是:
10919424
10919456
10919456
10919424
10919456
从输出结果来看,很明显同一块内存数据其实是可以被多个变量引用,且常量和变量的内存地址是相对应的。
def funa():
a = 1
print(id(a))
if __name__ == "__main__":
a = 1
funa()
print(id(a))
a = 2
print(id(a))
funa()
sys.exit(0)
输出结果:
10919424
10919424
10919456
10919424
从输出结果可以看出,若是当全局变量和局部变量的数值一致时,其对应的内存地址是一致的,当全局变量被赋予其他值时,其内存地址发生改变,而局部变量未有变化。
总结:Python变量的定义和赋值是同时进行的,Python的全局变量和局部变量的定义声明时,是基于内存已有数据的基础上,为变量分配地址进行引用,变量即对象的引用,而不是分别分配一块内存进行赋值,所以变量不进行赋值的话就会出现未定义的错误,,这时就会出现一个问题,这将会造成一个问题就是对象和数据将会越来越多,会消耗很大的内存空间,这时将会启动Python的垃圾回收机制,当某一段内存块的引用计数为0时进行回收,这个是后话了。
变量的作用域—看不见的字典
C语言中每一对大括号作为一个代码块,if,for,while,switch语句是可以加上大括号的作为一个块级作用域,for,while()语句在括号中定义的变量是包含在大括号里面的,就是包含在大括号的作用域里,而每一个代码块就是一个局部的作用域,所有代码块内部变量优先级大于代码块外的同名变量。
Python的作用域,就如同是Python的基础类型中的一部字典,在这部字典里记录着值(对象)与指向值的名称(变量),不同的作用域组成了不同的字典,而Python中能改变变量作用域的关键字只有class,def,lamba,所以在Python的关键语句(if,for,while…)中是不进行作用域的划分的,所以在(if,for,while…)语句对变量进行赋值,其变量的作用域可以被外部所引用。
并且Python不存在块级作用域,在嵌套作用域中会生成作用域链,由内到外,引用时优先选取内部同名变量。
在类与实例的作用域中
class A():
name = 'x'
what = 'xx'
print('A name id =',id(name))
def __init__(self,name,age):
self.name = 'xxx'
self.age = 18
def set(self):
self.name = 'xxx'
self.age = 18
global name
name = 'xx'
print('set global name = ',id(name))#与A.what的内存地址相同
print('set.name = ',self.name)#作用域是对于变量而言的而不是内存而言
if __name__ == "__main__":
a = A('xx',18)
a.set()
print('A id = ', id(A))
print('a id = ', id(a))
print('A.name = ',id(A.name))
print('a.name id = ',id(a.name))
print('A.what id = ', id(A.what))
print('a.what id = ',id(a.what))
print('A.set id = ', id(A.set))
print('a.set id = ', id(a.set))
sys.exit(0)
输出的是:
A name id = 140654891768216
set global name = 140654890720536
set.name = xxx
A id = 20336920
a id = 140654890787168
A.name = 140654891768216
a.name id = 140654890720704
A.what id = 140654890720536
a.what id = 140654890720536
A.set id = 140654690844744
a.set id = 140654891845576
所以,作用域是对于变量而言的而不是内存而言,类与实例的作用域也是嵌套的
参考LEGB法则:
Local(本地作用域)–>Enclosing(闭包作用域)–>Global(全局作用域)–>Built-in(内建作用域)
函数内部–>嵌套函数内部–>模块内部–>Python内建
LEGB法则: 当在函数中使用未确定的变量名时,Python会按照优先级依次搜索4个作用域,以此来确定该变量名的意义。首先搜索局部作用域(L),之后是上一层嵌套结构中def或lambda函数的闭包作用域(E),之后是全局作用域(G),最后是内建作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,则会发出错误。
变量作用域在定义时已经设定好,与调用的位置无关。
name ="???"
def funa():
print(name)
def funb():
name = "123"
funa()
if __name__ == "__main__":
funa()
sys.exit(0)
输出的是:
???
所以变量的作用域与是否调用无关,在变量定义时所处作用域已经设定完成。
变量的生命周期—只要被需要便存在
C语言的局部变量是在函数调用完毕后进行自动销毁,释放栈区。
而基于Python存储方式的特殊性,所以变量在函数调用完毕之后,并未立刻销毁,对于Python的变量和变量所引用的对象,是使用类似堆的方式管理内存,由Python内部机制统一分配回收内存,当内存的某一对象或者变量的引用计数为0时则由Python的内存管理机制收回内存,或者对对象手动del掉对象以释放内存,不过del掉的对象不影响对象中依然被外部变量有引用的值。
class A():
def __init__(self):
self.a = 123
def funa(self):
c = a+1
print(id(c))
variable = None
classA = None
def initA():
global classA
global variable
e = A()
variable = e.a
classA = e
print(id(e))
print(id(e.a))
if __name__ == "__main__":
a = 1
e = None
initA()
print(id(classA))
print(id(variable))
del classA
#print(id(classA))
print(id(variable))
sys.exit(0)
输出结果是:
39697096
1409448784
39697096
1409448784
1409448784
当函数被调用完,只要类实例还被引用,那么类实例依然存在类似c++的new,当del对象时,不影响对象还在被外部变量引用的值。
当我们若是del掉classA后,再输入print(id(classA)),会出现如下错误:
Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/pysidetest/demo.py", line 69, inprint(id(classA)) NameError: name 'classA' is not defined



