目录

  • 通过实例方法名字的字符串调用方法
  • 经典的参数错误
  • 内存与内存管理简介(了解)
    • 内存是什么?
    • 操作系统的内存管理
    • 进程内的内存管理
  • 内存分配
    • 内存池机制
    • 缓冲池机制
  • 垃圾回收机制
    • 介绍
    • OS模块
    • psutil模块
    • Python内部的引用计数机制
    • 手动启动垃圾回收
    • 循环引用
    • 标记清除(Mark and Sweep)和分代回收(Generational)
      • 分代回收
    • 调试内存泄漏
  • 用 pdb 进行代码调试
    • 如何使用 pdb
    • 用 cProfile 进行性能分析

通过实例方法名字的字符串调用方法

我们有三个图形类 Circle,Triangle,Rectangle

他们都有一个获取图形面积的方法,但是方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口

class Triangle:def __init__(self,a,b,c):self.a,self.b,self.c = a,b,cdef get_area(self):a,b,c = self.a,self.b,self.cp = (a+b+c)/2return (p * (p-a)*(p-b)*(p-c)) ** 0.5class Rectangle:def __init__(self,a,b):self.a,self.b = a,bdef getArea(self):return self.a * self.bclass Circle:def __init__(self,r):self.r = rdef area(self):return self.r ** 2 * 3.14159
  • getattr(x,“y”,None) --> 等同于 x.y 当x中不含有y时,返回None。
  • map(func,iterable) --> 将iterable中的元素一一映射到func函数中处理,并且返回新的map对象。

经典的参数错误

def add(a,b):a += breturn aa = 1
b = 2
c = add(a,b)
print(c)
print(a,b)      a = [1,2]
b = [3,4]
c = add(a,b)
print(c)
print(a,b)      a = (1,2)
b = (3,4)
c = add(a,b)
print(c)
print(a,b)

注意:

  • 列表为可变类型

    • li += 1 相当于改变li本身
    • li = li + 1 相当于li是两个变量 id不一致 返回的是 原本的li
  • 元组为不可变类型

    • tu += 1 也就是重新创建了一个tu变量 id不一致

内存与内存管理简介(了解)

内存是什么?

从计算机存储器的作用来讲,存储器可以分为主存储器,辅助存储器和缓冲存储器。

其中主存储器,也称主存、内存或可执行存储器,是与 CPU 直接进行信息交换的存储器,它的读写速度相对较快,容量相对较小,通常用来保存进程运行时的程序和相应数据以供 CPU 使用;而辅存不能与 CPU 直接进行信息交换,它的容量较大,读写速度相对较慢。缓冲存储器常用于两个速度不同的部件之间,比如 CPU 和主存之间设置的高速缓冲存储 Cache(也可将内存的概念扩展为主存和高速缓冲存储器的合集)。

操作系统的内存管理

由于内存的容量有限,很难承载系统以及用户进程所需的全部信息,所以操作系统需要对内存空间进行存储管理。在操作系统层面上内存管理的实现相当复杂,其大致功能包括内存空间的划分与动态分配、回收、扩充,以及存储保护,地址转换等等。

进程内的内存管理

操作系统对各个进程的内存进行管理,同时我们也需要管理我们编写的程序所对应进程内的内存。从可用内存中申请内存并且具有足够内存来进行相关操作,以及在适当的时间释放内存,这些都是我们的程序和系统能够正常运行的前提。

在 C / C++ 中,我们需要手动的进行内存管理,比如 C 语言中通过 mallocfree 函数来申请给定字节数的内存以及释放对应的内存。但在 Python 中,我们无须手动进行内存的申请和释放,Python 在内部帮我们完成了大量涉及到内存管理的操作,包括内存分配及垃圾回收。

内存分配

内存池机制

什么是 PyMalloc ?

在 Python 中,有很多常用的数据结构,包括列表、字典等。比如在列表中,我们不仅可以保存其他不同类型的对象,而且可以非常方便的使用 appendextend 等方法对其进行动态的扩充。针对这些常用对象的一系列操作,会在 Python 中造成内存的频繁分配和释放,同时像 intlist 等 Python 对象的分配和释放通常涉及到的数据量相对较小,因此 Python 在内部引入了内存池机制,实现了小块内存的管理器(称为 PyMalloc )用于提高处理小块内存的效率,这样避免了在底层频繁的 mallocfree 操作对效率带来的影响。

分配策略

在内存分配中,Python 以 512 bytes 为界限对大内存和小内存进行划分,不超过 512 bytes 的内存申请,会通过 PyMalloc 管理器进行处理,超过 512 bytes 的内存申请,则会通过 C 中的 malloc 来进行处理。在管理器的内部,主要包括 blockpoolarena 层级, 其中 block是 Python 内存管理中的最小单元,一个 pool 中包含多个 block,多个 pool 构成一个 arena。 同时,由于内存池机制,Python 并不会将释放的内存立即归还操作系统。

缓冲池机制

在内存池机制的基础之上,Python 为了提高常用对象的创建和释放效率,又进一步对整数、字符串等对象建立了对象缓冲池。比如对于 [-5, 256] 内的小整数,Python 已经在内部创建好了对应的小整数对象的缓冲池。

垃圾回收机制

介绍

我们知道,Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。

Python 中一切皆对象。因此,你所看到的一切变量,本质上都是对象的一个指针。

那么,怎么知道一个对象,是否永远都不能被调用了呢?

就是当这个对象的引用计数(指针数)为 0 的时候,说明这个对象永不可达,自然它也就成为了垃圾,需要被回收。

OS模块

与操作系统交互的库

psutil模块

与系统交互的库,能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息。它主要用来做系统监控,性能分析,进程管理。

通过以下代码检测程序在运行时的内存消耗

import os
import psutildef show_info(start):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss/1024./1024print(f"{start}一共占用{memory:.2f}MB")def func():show_info("initial")a = [i for i in range(1000000)]   show_info("created")func()
show_info("finished")
  • a是局部变量时,在返回到函数调用处时,局部变量的引用会注销。这时,列表a所指代对象的引用数为0,Python便会执行垃圾回收,因此之前占用的内存被收回了。
  • a是全局变量的时,即使函数体内代码执行完毕,返回到函数调用处时,对列表a的引用仍然是存在的,所以对象不会被垃圾回收,依然占有大量内存。

Python内部的引用计数机制

我们可以通过sys.getrefcount()这个函数,来了解Python内部的引用计数机制

import sysa = [1,2,3]print(sys.getrefcount(a))
  • getrefcount本身也会引入一次计数。

手动启动垃圾回收

如果我们可以手动删除完对象的引用,然后强制调用gc.collect()清除没有引用的对象,其实也就是手动的启动对象的回收。

循环引用

如果有两个对象,它们互相引用,并且不再被别的对象所引用,那么它们应该被垃圾回收吗?

import gc
def show_info(start):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss/1024./1024print(f"{start}一共占用{memory:.2f}MB")def func():show_info("initial")a = [i for i in range(100000)]b = [i for i in range(100000)]show_info("after a,b created")# 相互引用a.append(b)b.append(a)func()
gc.collect()
show_info("finished")

总而言之,当双向引用的时候,引用计数虽然还在,但我们可以手动拉起回收,进行释放内存。所以,引用次数是垃圾回收的 **充分非必要条件。

**

**
**

标记清除(Mark and Sweep)和分代回收(Generational)

为了解决引用计数在垃圾回收中无法处理循环引用的问题,Python 引入了标记清除和分代回收来检测和打破循环引用 。相对准确的说,标记清除是追踪回收( Tracing garbage collection )中的一种基础算法,其涉及到两个主要过程,即标记过程和清除过程,在标记过程中将所有可达(reachable)对象进行标记,在清除过程中将所有未标记的对象进行清除。分代回收则是基于标记清除基础上的,一种空间换时间的实现策略。

分代回收

分代回收将 Python 对象划分成 3 代,包括 0、1、2 代。对于新创建的对象,会被放入 0 代。若一个对象在经过一次垃圾回收后没有被清除,则它会被放入下一代中。对于每一代对象来说,都具有触发垃圾回收的相关阈值(收集频率)。关于这个过程的细节,官方文档中给出了比较明确的描述:

垃圾回收器把所有对象分类为三代,取决于对象幸存于多少次垃圾回收。新创建的对象会被放在最年轻代(第 0 代)。如果一个对象幸存于一次垃圾回收,则该对象会被放入下一代。第 2 代是最老的一代,因此这一代的对象幸存于垃圾回收后,仍会留在第 2 代。

为了判定何时需要进行垃圾回收,垃圾回收器会跟踪上一次回收后,分配和释放的对象的数目。当分配对象的数量减去释放对象的数量大于阈值 threshold0 时,回收器开始进行垃圾回收。起初只有第 0 代会被检查。当上一次第 1 代被检查后,第 0 代被检查的次数多于阈值 threshold1 时,第 1 代也会被检查。相似的, threshold2 设置了触发第 2 代被垃圾回收的第 1 代被垃圾回收的次数。

分代回收的主要目的是降低回收中需要处理的对象数量,提高垃圾回收效率。我们可以使用 gc 模块来执行或优化垃圾回收的相关过程,并获取更多的调试信息,比如通过 gc.get_threshold() 来获取当前的回收阈值;通过 gc.disable() 关闭垃圾回收,这通常在程序中确定不存在循环引用时使用。

另外,一个常见的问题是 CPython 在退出时一定会释放所有内存吗?答案是否定的,当 Python 解释器退出时,会进行内存清理,试图释放每个对象的内存,但不一定会释放所有内存,比如全局命名空间中引用的某些对象、循环引用下 C 扩展库中分配的某些内存都有可能不被释放(一部分相关细节可以通过 gc.garbage 了解)。

调试内存泄漏

在Python中通过引用计数和垃圾回收来管理内存,但是在一定情况下也会产生内存泄露

  • 第一是对象被另一个生命周期特别长的对象所引用
  • 第二是循环引用中的对象定义了__del__函数

objgraph,一个非常好用的可视化引用关系的包。在这个包中的 show_refs() ,它可以生成清晰的引用关系图。

import objgraph
a = [1,2,3]
b = [4,5,6]a.append(b)
b.append(a)objgraph.show_refs(a)

.dot文件转图片:https://onlineconvertfree.com/

用 pdb 进行代码调试

如何使用 pdb

首先,要启动 pdb 调试,我们只需要在程序中,加入 import pdbpdb.set_trace() 这两行代码就行了

a = 1
b = 2
import pdb
pdb.set_trace()
c = 3
print(a + b + c)

这时,我们就可以执行,在 IDE 断点调试器中可以执行的一切操作,比如打印,语法是"p ":

(pdb) p a
1
(pdb) p b
2

除了打印,常见的操作还有“n”,表示继续执行代码到下一行

(pdb) n
-> print(a + b + c)

而命令l,则表示列举出当前代码行上下的 11 行源代码,方便开发者熟悉当前断点周围的代码状态

(pdb) l
1    a = 1
2    b = 2
3    import pdb
4    pdb.set_trace()
5  ->  c = 3
6    print(a + b + c)

命令“s“,就是 step into 的意思,即进入相对应的代码内部。

当然,除了这些常用命令,还有许多其他的命令可以使用

参考对应的官方文档:https://docs.python.org/3/library/pdb.html#module-pdb)

用 cProfile 进行性能分析

除了要对程序进行调试,性能分析也是每个开发者的必备技能。

日常工作中,我们常常会遇到这样的问题:在线上,我发现产品的某个功能模块效率低下,延迟高,占用的资源多,但却不知道是哪里出了问题。

这时,对代码进行 profile 就显得异常重要了。

这里所谓的 profile,是指对代码的每个部分进行动态的分析,比如准确计算出每个模块消耗的时间等。

计算斐波拉契数列,运用递归思想

def fib(n):if n == 0:return 0elif n == 1:return 1else:return fib(n-1) + fib(n-2)def fib_seq(n):res = []if n > 0:res.extend(fib_seq(n-1))res.append(fib(n))return resfib_seq(30)

接下来,我想要测试一下这段代码总的效率以及各个部分的效率

import cProfilecProfile.run('fib_seq(30)')

参数介绍

  • ncalls:函数被调用的次数。如果这一列有两个值,就表示有递归调用,第二个值是原生调用次数,第一个值是总调用次数。
  • tottime:函数内部消耗的总时间。(可以帮助优化)
  • percall:是tottime除以ncalls,一个函数每次调用平均消耗时间。
  • cumtime:之前所有子函数消费时间的累计和。
  • filename:lineno(function):被分析函数所在文件名、行号、函数名。

Python高级-编程技巧-1.3 Python垃圾回收及性能分析相关推荐

  1. python高级编程技巧

    个人博客点这里 如何在列表,字典,集合中根据条件筛选数据 方法1:通过迭代来进行判断筛选 解决方案 函数式编程: 如何统计序列中元素的出现频度 解决方案:使用collections.Counter对象 ...

  2. Python高级编程技巧-线程与进程(进阶讲解)

    1.0 互斥锁和死锁 1.1 互斥锁 当多个线程想要同时修改某一个共享数据时,就需要进行同步控制. 某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变 ...

  3. python高级编程知识点_(转)python 高级编程技巧学习笔记

    转自https://www.jianshu.com/p/104cec085611,部分图出不来,mark一下,关键时候供查看. 第二章 数据结构相关话题 2.1.筛选数据 两种方式 filter函数: ...

  4. python高级编程 豆瓣_《Python高级编程》读书笔记:方法解释顺序浅析

    Python在2.2引入了New-style object(ref),而且在2.3引入了新的方法解释顺序(Method resolution order,以下简称MRO),新的MRO解决了多继承下的方 ...

  5. python高级应用_Python高级编程技巧

    Python 高级编程技巧 本文展示一些高级的 Python 设计结构和它们的使用方法.在日常工作中,你可以根据需要 选择合适的数据结构, 例如对快速查找性的要求. 对数据一致性的要求或是对索引的要求 ...

  6. python function at 0x00000_python高级编程技巧 - BugError

    由python高级编程处学习 Python 列表解析语法 [] 和生成 器 () 语法类似 [expr for iter_var in iterable] 或 [expr for iter_var i ...

  7. python队列线程池_实例详解:python高级编程之消息队列(Queue)与进程池(Pool)

    今天为大家带来的内容是:python高级编程之消息队列(Queue)与进程池(Pool),结合了实例的形式详细分析了Python消息队列与进程池的相关原理.使用技巧与操作注意事项!!! Queue消息 ...

  8. 【Python高级编程】

    Python高级编程:技巧代码的玄学与艺术 一.编程语言介绍 Python 作为一门优秀的编程语言,有着很多优势: 简单易学 Python有简单的语法,易于阅读和学习,很适合初学者.它的设计哲学是&q ...

  9. python多线程挂了_python多线程输入的问题 python高效编程技巧13(如何在线程之间实现...

    python3 创建线程时不用args传参,执行线程时为什如果创建线程时在target里就传入了参数,为什么在启动线程时,线程不是在Python多线程下,每个线程的执行方式: 有什么了不起,大不了继续 ...

最新文章

  1. 如何查看 phtml文件 并有代码颜色提示
  2. NOJI 148 fibonacci数列(二) 矩阵乘法二分幂
  3. nginx中的try_files指令解释
  4. 写csv文件_机器学习Python实践——数据导入(CSV)
  5. 耳前瘘管是否必须切除
  6. java request获取域,Java Web - Servlet(13)HttpServletRequest详解(获取请求数据、请求分派、请求域)(二)...
  7. androidpn的学习研究(八)androidpn 中业务类XmppIoHandler实现分析
  8. BZOJ4066 简单题(KD-Tree)
  9. javascript高级编程笔记04(基本概念)
  10. c语言坦克大战源代码vc 6.0,c语言编写坦克大战源代码.doc
  11. 宇视项目VM相关笔记
  12. CardView 整理
  13. 肖文吉mysql_疯狂软件教育中心肖文吉老师_MYSQL视频教程
  14. 双十二苏州老百姓学会了数字人民币APP钱包注册及使用
  15. java求长方形的表面积程序_Android Studio实现长方体表面积计算器
  16. 服务器硬盘灯不亮 阵列是正常的,服务器磁盘阵列出现故障有哪些解决办法?...
  17. 学校计算机有麦克风吗,一体机有麦克风功能吗
  18. 火狐浏览器滚动条兼容问题
  19. win10打开网路邻居计算机,win10系统下网上邻居不能访问其他电脑的解决方法
  20. 递归算法中的小Tips

热门文章

  1. 基于MATLAB/SIMULINK的DFIG有功无功功率控制
  2. SQL自定义排序规则
  3. Flutter学习-单子布局Widget
  4. 远程访问及控制——SSH远程管理及TCP Wrappers 访问控制
  5. python+selenium+webdriver
  6. 钣金缺口lisp_AtuoCAD二次开发---钣金系统开发
  7. Python基础-推导式
  8. 1.银行中的等额本金C程序
  9. 酷雷曼VR+抖音小程序丨解锁亿级流量密码
  10. Python 计算混淆矩阵,计算Kappa系数,总体精度