Python高级编程技巧-垃圾回收及性能分析

1.0实例方法名字的字符串调用方法

题目解析:
我们有三个图形类

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

#1.首先需要先创建三个图形的类Triangle、Rectangle、Circle,它们都有一个获取面积的方法。
#三角形,矩形,园,在新建一个py文件,创建一个统一获取面积的函数
p1 = Triangle(3,4,5)
P2 = Rectangle(4,6)
p3 = Circle(2)
#定义获取面积的函数
def unified_mj(p):
	list_xin = ["Triangle","Rectangle","Circle"]
	for name in list_xin:
		f = getattr(p,name,None):#getattr用于返回一个对象属性值。
		if f:
			return f()
print(unified_mj(p1))
print(unified_mj(p2))	
print(unified_mj(p3))

#也可以使用map()高阶函数
li = ["Triangle","Rectangle","Circle"]
jieguo = list(map(unified_mj,li))
print(jieguo)

#• getattr(x,"y",None) --> 等同于 x.y   
#当x中不含有y时,返回None。
#• map(func,iterable)  --> 将iterable中的元素一一
#映射到func函数中处理,并且返回新的map对象。

2.0 垃圾回收机制

在Python中,一切皆对象。所以,每一个变量,实际上都是对象的一个指针。所以,当这个对象的引用计数(指针数)为0的时候,说明它也变成了垃圾,需要被放到回收箱中。

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

如下:

from os
from psutil
def show_info(start):
	#获取当前进程id
	pid = os.getpid()
	#获取当前堆成对象
	p = psutil.Process(pid)
	#返回该对象内存的消耗
	info = p.memory_full_info()
	#获取独自占有的物理内存,换算单位MB
	memory = info.uss/1024./1024 
	print(f"{start}一共占用{memory:2f}MB")
def func():
	show_info("initial") #打印我所消耗的内存
	a = [i for i in range(100000)]#列表推导式,会消耗内存
	show_info("created") #打印我所消耗的内存
func()
show_info("finished") #打印我所消耗的内存

#a为局部变量,当它的引用结束时,他会被回收。
#当a是局部变量时,在返回到函数调用处时,局部变量的引用会注销。这时,列表a所指代对象的引用数为0,Python便会执行垃圾回收,因此之前占用的内存被收回了。
#当a是全局变量的时,即使函数体内代码执行完毕,返回到函数调用处时,对列表a的引用仍然是存在的,所以对象不会被垃圾回收,依然占有大量内存。

3. 0 Python内部的引用计数机制

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

import sys
a = [1,2]
print(sys.getrefcount(a)) #这里的引用次数为两次,因为getrefcount也算一次
b = a
print(sys.getrefcount(a))#到这里了为三次,getrefcount不会进行累加
c = b 
d = c 
print(sys.getrefcount(a))#到这里了为五次

4.0 如何手动去进行垃圾回收

如果我们可以手动删除对象的引用,然后强制调用gc.collect()清除没有引用的对象

import sys
import gc

a = [1,2,3]
a = None #相当于a将变量指向了Nong
print(sys.getrefcount(a))
del a  #相当于自己把对象的引用删掉,本质上对象还是没有被删除
gc.collect() #手动启动回收
print(a)

4.1 相互引用

def show_info(start):
    #获取当前进程id
    pid = os.getpid()
    #获取当前堆成对象
    p = psutil.Process(pid)
    #返回对象的内存消耗
    info = p.memory_full_info()
    #获取进程独自占用的内存,换算单位MB
    memory = info.uss/1024./1024
    print(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 create")
    a.append(b)
    b.append(a)

func()
gc.collect()
show_info("finished")
#引用次数是垃圾回收的 充分非必要条件。

5.0 调试内存和泄露

虽然python有自动回收机制,但在某些时候还是会有内存泄漏。如下:
1.对象被一个生命周期特别长的对象所引用
2.循环引用中定义了__den__函数

objgraph,一个非常好用的可视化引用关系的包。在这个包中的 show_refs() ,它可以生成清晰的引用关系图。
import objgraph
a = [1,2,3]
b = [4,5,6]
a.append(b)
b.append(a)
objgraph.show_refs(a)

6.0 用cProfile进行性能分析

  • 除了要对程序进行调试,性能分析也是每个开发者的必备技能。
  • 日常工作中,我们常常会遇到这样的问题:在线上,我发现产品的某个功能模块效率低下,延迟高,占用的资源多,但却不知道是哪里出了问题。
  • 这时,对代码进行 profile 就显得异常重要了。
    这里所谓的 profile,是指对代码的每个部分进行动态的分析,比如准确计算出每个模块消耗的时间等。
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        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 res
fib_seq(30)
#接下来,测试一下这段代码的效率及各个部分的效率
import cProfile
cProfile.run("fib_seq(30)")
#得到的结果
 Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.763    1.763 <string>:1(<module>)
7049123/31    1.763    0.000    1.763    0.057 demo1.py:74(fib)
     31/1    0.000    0.000    1.763    1.763 demo1.py:81(fib_seq)
        1    0.000    0.000    1.763    1.763 {built-in method builtins.exec}
       31    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
       30    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值