Python运行浅析
2021-06-08 06:05
标签:改变 types target 代码执行 software 分割 value soft har Python解释器并不将Python编译成机器码运行,而是由Python虚拟机逐条解释,这也是Python为什么被称之为解释行语言,但是Python虚拟机并不会直接执行.py文件,其是由Python虚拟机执行解释之后的字节码 先有类型(Type),而后才能生成实例(instance),Python中一切都是对象,包括实例内的对象都包含一个标准头,通过头部信息既可以知道明确的数据类型 头部信息由引用计数和类型指针组成,前者在对象被引用的时候计数增加,当超出作用域或者被释放的时候计数减小,等于0的时候会被虚拟机回收(某些被缓存的对象计数器永远不会为0) 类型指针指向具体的类型对象,其中包含了继承关系,静态成员信息等,所有的内置数据类型都可以从types模块中找到 Python的名字实际上是一个字符串对象,其和所对应的值一起在名字空间中构造成一项 Python有多种名字空间,被称之为 从上述结果展示可以看出名字空间即为一个字典对象,可以通过字典创建 名字的作用仅仅是在某个时刻将名字空间内的某个对象进行关联,其本身并不包含任何对象信息,只有通过对象的头部信息才能获知晓相关的数据类型,进而进行相应的数据查找,因为名字的弱类型特征,我们可以在程序的运行过程之中,随时更改其数据类型 在函数外部 为提升工作性能,Python在操作系统中开辟了内存池以便减少对内存的分配与回收操作,对于字节小于 根据需求,虚拟机每次从操作系统中申请一块256KB取名为 以便快速查找空闲区域进行分配 ?于 256 字节的对象,直接? 当所有的arena的总容量超出限制 对象总是按照引用传递,即通过复制指针使多个变量指向一个内存地址,因为 浅拷贝是对一个对象父级(外层)拷贝,不会拷贝(内部)子级 深拷贝是对对象内部外部都进行拷贝(递归) Python默认使用引用计数,对象被引用计数会增加一次,当对象计数为0的时候,该对象内存资源会被回收,要么所对应的block块被置为空闲要么将资源返回给操作系统 某些内置类型,例如小整数由于缓存原因计数永远不会为0,直到进程结束由虚拟机调用释放函数进行清理 除了直接引用,Python还支持弱引用,允许在不增加计数,不影响释放的情况下间接引用对象,但不是所有数据类型都支持若引用,例如列表,字典等 Python除引用计数之外,还有专门处理循环的 引发循环引用问题的一般都是容器类对象,例如 在Python GC中将回收对象分成三个等级代龄, GC首先检查 包含 如果包含 Python运行浅析 标签:改变 types target 代码执行 software 分割 value soft har 原文地址:https://www.cnblogs.com/SR-Program/p/14533563.html运行环境
代码执行
虚拟机运行过程简介
__builtins__
模块 该模块包含所有的数据类型与方法sys
模块 该模块包含了sys.path
与所有modules
等重要信息import
机制 Exception。
__main__
准备所需要的名称空间site.py
执行 site-packages
中第三方模块添加到搜索路径中py
文件 执行前将 __main__
__dict__
做为名字空间传递Python文件运行过程
py
文件边写成字节码文件PyCodeObject
得到字节码指令且执行该指令等.Pyc文件
.pyc
文件是字节码在磁盘上的表现python test.py
会将该文件编译成字节码但是并不会生成 .pyc
文件test.py
文件导入模块例如 re
模块此时python会对re
进行编译成字节码文件 生成 re.pyc
文件然后对字节码进行解释执行test.pyc
文件可以使用内置的模块py_compile
也可以使用 python -m test.py
.pyc
与.py
文件则会使用 .pyc
运行 如果 .pyc
的编译时间早于 .py
的编译时间则会执行 .py
文件并且更新.pyc
文件python -m test.py
类型和对象
引用计数
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import sys
b = 10086
print(sys.getrefcount(b))
c = b # 对与b进行引用 引用计数增加
print(sys.getrefcount(b))
del c # 删除对上述b的引用 引用技术减小
print(sys.getrefcount(b))
类型指针
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import types
x = 10
print(x.__class__) # 查看内置的类型
print(type(x) is types.IntType) # True
print(x.__class__ is type(x) is types.IntType is int) # True
y = x
print(id(x), id(y))
print(id(int), id(types.IntType)) # 同一类型 内存地址一样
名字空间
x = 10 # 我们习惯将x称为变量 但是准确来说其应该称为名字
{"name":"object"}
关联globals
的模块名字空间,被称之为 locals
的函数堆栈名字空间,不同的名字空间决定了对象的作用域以及生存周期# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
x = 10
print(type(globals())) # 字典
print(globals().get(‘x‘)) # 字典取值
key:value
键值对的方法对名字空间中添加名字# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
globals()[‘name‘] = ‘SR‘ # 添加键值对
print(globals()) # 查看名字空间
print(globals().get(‘name‘)) # 获取名字空间所对应的值
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
print(type(globals()[‘name‘]))
globals()[‘name‘] = int # 将原本与字符串关联的名字指向整形
print(globals()[‘name‘])
locals()
与 globals()
作用完全相同,但是在函数内部 locals()
则是获取当前函数的名字空间,其中存储的是函数参数,局部变量信息等# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
print(locals() is globals()) # True
print(locals())
print(id(locals())) # 140543843598912
print(id(globals())) # 140543843598912
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
def test(x):
y = x + 100
print(locals()) # {"y": 223, "x": 123}
print(locals() is globals()) # True
frame = sys._getframe(0) # 获取当前堆栈帧
print(locals()) {‘y‘: 223, ‘x‘: 123, ‘frame‘: }
print(globals()) # {‘test‘:
内存管理
256
字节对象,将直接从内存池中获取存储空间arena
的内存,并且将该内存分割成多个 pool
,在每个pool
中在进行划分大小相同的 block
block
大小是8的倍数,因此假如需要开辟13字节大小的空间,需要 block
大小为16内存池来获取空闲块,所有块都用链表与头信息进行管理malloc
在堆上分配内存。程序运?中的绝?多数对象都?于这个阈值,因此内存池策略可有效提升性能。64MB
的时候,其不会再次请求arena
内存,而是直接在堆上为对象分配内存,另外空闲的arena
也会被释放交还操作系统引用传递
arena
也是作用在堆上.因此无论何种类型何种大小都是直接存储在堆上,Python没有值类型与引用类型# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
a = object()
b = a
print(a is b) # True
print(id(a))
print(id(b))
def test(x):
print(id(x))
print("\n")
test(a)
深浅拷贝
浅拷贝
父级对象为可变类型
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = [i * i for i in range(10)] # 生成一个新的列表
l2 = copy.copy(l)
print(l is l2) # False
print(id(l)) # 140221885816416
print(id(l2)) # 140221885958048
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = [i * i for i in range(10)]
l2 = copy.copy(l)
l.append(99) # 修改原值
print(l) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 99]
print(l2) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
父级对象为不可变类型
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
a = ‘hello‘
b = copy.copy(a)
print(a is b) # True
print(id(a)) # 140178072833984
print(id(b)) # 140178072833984
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
a = ([1, 2, 3],)
b = copy.copy(a)
print(b) # 拷贝之后的值 ([1, 2, 3],)
a[0].append(‘4‘)
print(a) # 修改之后的原值 ([1, 2, 3, 123123123],)
print(b) # 修改之后的拷贝的值 ([1, 2, 3, 123123123],)
print(id(a)) # 33803008
print(id(b)) # 33803008
深拷贝
父级对象为可变对象
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = [i * 2 for i in range(10)]
l1 = copy.deepcopy(l)
print(l1)
print(id(l)) # 140562607271520
print(id(l1)) # 140562607270368
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = [i * 2 for i in range(10)]
l1 = copy.deepcopy(l)
l.append(20) # 修改原值
print(l1)
print(id(l))
print(id(l1))
父级对象为不可变对象
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = ([i for i in range(10)],)
l1 = copy.deepcopy(l)
print(id(l)) # 140310375300368
print(id(l1)) # 140310375107536
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import copy
l = ([i for i in range(10)],)
l1 = copy.deepcopy(l)
l[0].append(10)
print(l1) # ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],)
垃圾回收
引用计数
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import sys
class User(object):
def __del__(self):
print("计数为0 释放资源")
user = User()
print(sys.getrefcount(user)) # 2
a = user
print(sys.getrefcount(user)) # 3
del a # 删除最后一个引用
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import sys
import weakref
class User(object):
def test(self):
print("测试弱引用")
def callback(x): # 回调函数会在原对象被回收的时候被引用
print("weakref object:%s" % x)
print("target object dead!")
user = User()
we = weakref.ref(user, callback) # 创建弱对象引用
print(sys.getrefcount(user)) # 2 计数为2是getrefcount形参增加的 上述若引用并没有增加计数
print(we() is user) # 通过弱引用可以访问原对象
we().test() # 弱引用访问原对象类中的方法
del user # 删除原对象 会调用回调函数callback
print(hex(id(we))) # 回调函数中的参数为弱对象 因为原对象已经被删除
print(we() is None) # 此时原对象被删除 因此弱对象只能返回None
分代清除
GC
list
set
object
等,对于该类容器对象,一般都是由 GC
管理,但是并不是说此类对象就是有 GC
进行管理,当没有发生循环引用的时候还是由积极性更高的计数器进行回收# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import gc
class User(object):
def __del__(self):
print(hex(id(self)), ‘will be dead‘)
disable_gc = gc.disable() # 关闭gc
user = User() # 实例化一个对象
print(hex(id(user))) # 0x7fa46c869150
del user # 回收对象,引用计数不会依赖与gc
GEN0
管理新加入的新生代,GEN1
管理上次清理依然存活的中青代,GEN2
是两次清理依然存活的生命周期极其漫长的对象,每一个等级代龄都有一个最大的阈值,每次 GEN0
超出最大阈值都将会被回收GEN2
如果超出阈值,则合并 GEN2
GEN1
GEN0
追踪链表,如果没有超出链表,则检查 GEN1
同时提升存活对象的代龄,而那些被回收的对象则存放在专门的列表中等待回收__del__
的对象永远不会被GC
回收,直到进程终止# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import gc
print(gc.get_threshold()) # 获取每级的阈值 (700, 10, 10)
print(gc.get_count()) # 获取追踪的对象数量 (558, 8, 0)
GC
对循环引用进行清除# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import gc
import weakref
class User(object):
pass
def callback(x):
print("will be dead %s" % x)
a = User()
b = User()
dis = gc.disable() # 停止gc 查看引用计数能力
wa = weakref.ref(a, callback)
wb = weakref.ref(b, callback)
# 循环引用
a.b = b
b.a = a
# 删除引用
del a
del b
# 调用弱引用
wa()
wb()
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
able = gc.enable() # 开启gc机制
# 删除引用
del a
del b
# 调用弱引用
wa()
wb()
__del__
则GC对循环引用无作用# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import gc
import weakref
class User(object):
def __del__(self):
pass
def callback(x):
print(‘\n‘)
print("will be dead %s" % x)
res = gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK) # 输出详细的回收信息
a = User()
b = User()
# dis = gc.disable()
wa = weakref.ref(a, callback)
wb = weakref.ref(b, callback)
# 循环引用
a.b = b
b.a = a
# 删除引用
del a
del b
co = gc.collect() # 防止未达到阈值 手动回收
res1 = wa()
res2 = wb()
下一篇:JAVA语法学习