Python深入一点的理解

python的一个内在思想是,代码和程序本身是可以被处理的,这进一步增加了语言的灵活性。如python可以很容易的获取当前对象的成员名字和值。这不像一般的静态式编程语言,如Java等,要想获取成员名称和值,需要比较复杂的反射。

###Python的垃圾回收###
Python的垃圾回收采用引用计数,绝大部分对象都通过这种方式处理。这种方式优点是直观,实现简单,将回收消耗均摊到程序运行过程中(因为用计数法当计数为0时可以立即回收)。缺点是执行效率低,还有一个致命问题,就是会产生循环引用,导致无法回收,这就和内存泄漏一样严重了,Java放弃了使用它。Python采用一定的方式来弥补它,叫做标记-清除法
标记,清除法
对于引用记数法没有排除的对象,在需要的时候,就采用标记-清除法来处理。
Python会采用某种方式来对循环引用进行摘除。先将能够引用其他对象的对象称为容器,Python先将所有未清除的容器的引用的拷贝一份到一个双向链表中,然后遍容器,然后进入寻找他们对其他对象的应用,找到一个就将改对象的被引用数减一,并且这样递归处理整棵引用树。最后那些循环引用就会被摘除。此时,就可以回收引用数=0的对象了。
貌似Python的这个处理并不会很及时有效,还是需要手动处理和避免。
分代回收
程序对象的生命周期长度也满足二八定律,少量对象生命周期很长,很大部分生命周期短。有相关的研究表明面向对象的不管哪种程序设计语言,哪种应用都满足这种规律。回收时,长的大概率还要用,新的大概率不用,故可以分代,将长的划分到一代,低频回收,创建不就或短的高频回收,留存下来的加到长生命周期带中。
对垃圾回收进行相关的设置,调优
GC模块中提供相应的接口、支持。
调优方法:
手动垃圾回收,关闭垃圾回收,然后需要是主动调用。比如游戏过程中不回收,游戏结束或者非关键操作过程中回收。或者关键过程中关闭,非关键过程在开启。
调高垃圾回收阈值
手动处理引用相关
避免循环引用,手动解除循环引用,使用弱引用(通过weakref),Python还提供查找循环引用的工具。

执行Python代码的是解释器,这对应于Java有虚拟机.虚拟机的定义有2个,一种是类似Vmware的系统虚拟机,另一种是虚拟机称之为程序虚拟机,诸如JVM,CLR就是最常见到的虚拟机。

PyCodeObject和pyc文件。
P有CodeObject也就是Python的字节码文件,当python程序第一次运行时,Python根据.py文件会生成PyCodeObject,并保存到内存当中,以供使用,当Python程序运行结束时,Python解释器则将PyCodeObject到pyc文件中。
当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。
所以我们应该这样来定位PyCodeObject和pyc文件,我们说pyc文件其实是PyCodeObject的一种持久化保存方式。pyc的目的主要是缓存加速,不过这个对大部分的Python开发者都没必要了解,还是想多了Q^Q.

python的包导入,包寻找

工作流程概括如下
首先搜索内置模块
然后遍历 sys.path,他就是一个列表,包含很多路径,按顺序查找:
里面通常包含这些
当前目录,即脚本所在目录,这里面的包会自动加入
PYTHONPATH 指定的目录
标准库目录
第三方包目录(site-packages)
处理 .pth 文件中指定的额外路径
处理导入语句,根据绝对或相对导入找到正确的模块

sys.path.insert(0, ‘/path/to/old/version/of/A’)
放入的路径什么时候查找
这样的代码片段来动态添加路径到sys.path时,这个新添加的路径会被放在sys.path列表的最前面,这意味着它将成为Python导入系统查找模块的首个位置。

python寻找库时先到先得原则,后面遇到同名的,不会导入。
即如果我在PYTHONPATH 制定xxx包替换第三方包,后续找到的第三方包就无效了

放入目录,python会查找一级子文件,不会递归查找,似乎一级目录具有__init_的会被查找,想要多级子目录,可以手动制定,或者在__init__中制定

也就是说我有两个同名的包A和B,一开始我想用A,把A放入,然后想用B了,我把A删了,再导入B,是可以的是吧

方案四:局部导入处理

在使用库A的老版本的特定脚本或模块中,你可以局部地修改sys.path,只为这个导入操作添加老版本的路径。这样做可以最小化对全局sys.path的影响:

import sys
# 保存原始sys.path
original_sys_path = sys.path.copy()

# 添加老版本A的路径
sys.path.insert(0, '/path/to/old/version/of/A')
import A  # 导入老版本
# 恢复原始sys.path
sys.path = original_sys_path

理论上,是可以这样操作的
这里有个关键的复杂性——Python 导入系统的缓存机制。当你尝试导入一个模块或包时,Python 首先检查sys.modules看看这个模块是否已经被导入。如果已经在sys.modules中,Python 将直接从缓存中加载该模块,而不会重新导入或从新的路径中加载它。因此,即使你已经将B的路径添加到了sys.path,如果在sys.modules中已经有一个同名的A,那么尝试导入B时,Python 实际上还是会使用缓存中的A。

要让Python忽略缓存并从新路径中加载B,你需要在导入B之前从sys.modules中删除关于这个包的条目。例如:

import sys

# 移除包A的路径
sys.path.remove('/path/to/package_A')

# 删除sys.modules中的包A条目,确保可以重新导入
if 'package_name' in sys.modules:
    del sys.modules['package_name']

# 添加包B的路径
sys.path.append('/path/to/package_B')

# 现在尝试导入B
import package_name

注意:
谨慎操作:动态地修改导入路径和操作sys.modules可能导致难以调试的问题,特别是在大型项目中。这种做法应当谨慎使用。
依赖管理:如果可能,更好的做法是使用虚拟环境(virtual environments)来管理不同的依赖,或者重构代码以避免需要两个同名但不同内容的包。
模块状态:删除sys.modules中的条目并重新导入模块,不会重置该模块的任何全局状态。如果模块定义了全局变量,并在导入时修改了这些变量的值,那么这些变量的状态将在重新导入时保持不变,除非模块内部有逻辑来重置这些状态。
综上所述,虽然技术上可行,但实际应用中要谨慎考虑是否真的需要这样做,以及如何安全地实现这一过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值