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):被分析函数所在文件名、行号、函数名。