py学习
变量赋值和引用
【变量赋值的本质】
- 每当定义一个新变量时,就会在内存中开辟一块空间用于数据存储。
- 所以不同的变量,他们的内存地址是不同的。使用
id()
获取内存地址,使用is
判断变量内存地址是否相同。
name = "zhangsan"
age = 18
# 如上述两个变量的赋值,就是定义了两个变量,变量名分别是name和age,
# 本质上分别开辟了两块不同的内存控制,存放字符串"zhangsan"和数字18,
# 当我们访问name或age时,就会在对应的内存空间上获取值。
【变量引用的本质】
- 把一个变量的内存地址,同时关联到另一个新变量上,于是两个变量对应一块内存地址。
a = 100
b = a
# 此时a和b指向同一块内存空间,即id(a) == id(b)
通过以下两个示例,可以看出不可变类型变量和可变类型变量在引用时的区别。
- 示例1:将a指向另一块新的内存空间,b依然指向自己的内存空间
a = 100
b = a
a = 200
print(a, b) # 200 100
- 示例2:a和b指向同一块内存空间,只要这块内存空间上的元素发生变化时,关联的变量都受影响。
a = [1,2,3,4]
b = a
a[0] = 100
print(a[0], b[0]) # 100, 100
# 像字典、集合都属于可变类型的变量,引用时都存在上述现象
小整数池
python在内存上有一定的优化,比如小整数池和字符串驻留。
- 【小整数池】:python解释器会在程序开始运行的时候就预先开辟一块内存空间,存放一部分整数。以后程序需要用到一些变量绑定到这些值时,直接绑定到这个内存地址上,不需要再逐个开辟空间(开辟空间访问硬件需要资源消耗),这是python的一种资源优化机制。范围:-5~256
- 【字符串驻留】:Python的驻留机制对相同的字符串只保留一份拷贝,后续创建相同字符串时,不会开辟新空间,而是把该字符串的地址赋给新创建的变量。范围:英文字母、数字、下划线
a = 100
b = 100
print(a is b) # True
a = 500
b = 500
print(a is b) # False
a, b = 500, 500
print(a is b) # True 在同一行定义两个相同值的变量,此时解释器做了优化,执行一个内存地址
a = "abc"
b = "abc"
print(a is b) # True
a = "abc!"
b = "abc!"
print(a is b) # False
垃圾回收机制
变量使用前需要先定义。
定义变量就是在内存中开辟一块空间用于数据储存,如果需要使用很多数据就需要开辟很多内存空间。
内存空间是有限的,如果定义了很多不在使用的变量,会导致内存浪费,甚至内存溢出的问题。
这就需要我们在变量使用后及时释放这个变量所占用的内存空间。
但是这个释放内存空间的工作是复杂且有分险的,幸运的是python解释器帮我们做了这部分工作。
python解释器内存主要使用引用计数的方式做垃圾回收。
什么是垃圾:垃圾指的是没有绑定变量名的值,也就是无法在访问的值,即没有用的值。
如何回收垃圾:通过引用计数的方式,每当这个值被引用,即被绑定到一个新的变量名上,引用计数加1
x = 10 # x绑定给值10,10的引用计数=1
y = x # y绑定给值10,10的引用计数=2
z = 10 # z绑定给值10,10的引用计数=3
# 通过赋值可以将变量名绑定到值,将引用计数增加
# 通过del 方法可以将变量名和值解绑,将引用计数减少
del z # z与它所绑定的值解绑,即值10的引用计数减为2
# 注意 del不是不是删除z的意思本质是接触变量名的绑定,内存回收的目的
import sys
sys.getrefcount(a) # 查看引用计数
def f():
import sys
a = 10
print(sys.getrefcount(a))
b = a
print(sys.getrefcount(a))
c = [a]
print(sys.getrefcount(a))
del b
print(sys.getrefcount(a))
del c
print(sys.getrefcount(a))
python垃圾回收机制,除了引用计数之外,还有【标记回收】、【分代回收】。
# 标记清除解决循环引用造成的问题
l1=[]
l2=[]
l1.append(l2)
l2.append(l1)
del l1, l2
# 分代回收解决引用计数效率低的问题
迭代器
【可迭代对象(iterable)】指的是可以依次返回其内部成员的对象。比如字符串、列表、字典、文件等都是可迭代对象。
【迭代器(iterator)】内置函数iter(可迭代对象)得到迭代器,本质上迭代器也是可迭代对象。
# 可迭代对象, 从语法方面说就是那些具有__iter__的方法的对象
# 迭代器,从语法上说就是那些同时具有__next__和__iter__方法的对象。
- 迭代器调用__next__方法会调用迭代器中的下一个值;
- 迭代器调用__iter__方法返回迭代器本身;
示例:
>>> s={1,2,3} # 可迭代对象s
>>> i=iter(s) # i是迭代器
>>> next(i) # 返回迭代器中下一个值
1
>>> next(i)
2
>>> next(i)
3
>>> next(i) #抛出StopIteration的异常,代表无值可取,迭代结束
迭代器优缺点:
- 优点:惰性取值;每次只取一个数据,不占内存。
- 缺点:只能往后依次取值,不能返回头往前取值。就像象棋中的卒,只进不退。