通过实例方法名字的字符串调用方法
书写GetArea类
from math import pi
class GetArea:
def __init__(self):
pass
def triangle(self, a, b, c):
p = (a + b + c) / 2
return (p * (p - a) * (p - b) * (p - c)) ** 0.5
def rectangle(self, w, h):
return w * h
def circle(self, r):
return pi * r ** 2
实例化这个GetArea类
getarea = GetArea()
如果我们只有字符串,如何直接调用该字符串对应的变量呢?这时候就要使用getattr
函数了
attribute = 'triangle'
f = getattr(getarea, attribute, None)(3, 4, 5)
print(f) # 6
# None的含义是当shape不含有area属性的时候,返回None而不会报错
运用map
函数可以轻松进行批量处理
attribute = 'circle'
r_list = [1, 2, 3]
area = map(getattr(getarea, attribute, None), r_list)
print(list(area))
# [3.141592653589793, 12.566370614359172, 28.274333882308138]
垃圾回收
获取当前进程的内存消耗需要导入os
,psutil
模块,定义show_info
函数进行封装
import os
import 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")
一个简单的应用
show_info("initial")
a = [i for i in range(1000000)] # 列表推导式 [0~9999999]
show_info("created")
# initial一共占用20.43MB
# created一共占用59.17MB
对于一个函数中的局部变量,如果没有返回到外部,引用完了之后就会被删去该变量,变量占用的内存也会被回收
def func():
show_info("initial")
# 因为a是局部变量 局部变量在函数执行完毕 引用就会注销
a = [i for i in range(1000000)] # 列表推导式 [0~9999999]
show_info("created")
func()
show_info("finished")
# initial一共占用20.41MB
# created一共占用59.17MB
# finished一共占用20.77MB
对于一个函数中的全局变量,引用完了之后不会被删去,变量占用的内存也不会被回收
def func():
show_info("initial")
# a为全局变量的时候 即使函数体内代码执行完毕 返回到函数调用处时,对列表a的引用仍然时存在的
# 所以对象不会被垃圾回收,依然占有大量内存
global a
a = [i for i in range(1000000)] # 列表推导式 [0~9999999]
show_info("created")
func()
show_info("finished")
# initial一共占用20.46MB
# created一共占用59.28MB
# finished一共占用59.28MB
对于一个函数中的局部变量返回出去并被其他变量接受之后,该变量不会被删去,变量占用的内存也不会被回收
def func():
show_info("initial")
a = [i for i in range(1000000)] # 列表推导式 [0~9999999]
show_info("created")
return a
a = func()
show_info("finished")
# initial一共占用20.46MB
# created一共占用59.26MB
# finished一共占用20.85MB
引用次数
sys.getrefcount
用于统计一个变量的引用次数,即变量地址的访问次数
import sys
a = [1,2,3]
print(sys.getrefcount(a)) # 2次
b = a
print(sys.getrefcount(a)) # 3次
c = b
d = c
print(sys.getrefcount(a)) # 5次
"""
1.getrefcount()只计一次引用次数
2.变量赋值 b变量指向了a所在内存地址
"""
通过sys.getrefcount
,我们就可以知道当一个变量被一个函数应用时的内存地址访问次数
a = [1,2,3]
def func(a):
# 4次:a本身一次 函数调用一次 函数参数一次 getrefcount一次
print(sys.getrefcount(a))
func(a) # 4次
手动启动回收
运用gc.collect()
可以没有被引用的内存进行回收
循环引用
import os
import psutil
import gc
"""
引用次数 为0 的时候 一定会启用 垃圾回收吗
垃圾回收 一定是引用次数 为0的时候吗
充分非必要条件
"""
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(1000000)] # 列表推导式 [0~9999999]
b = [i for i in range(1000000)] # 列表推导式 [0~9999999]
show_info("created")
# 相互引用
a.append(b)
b.append(a)
a = func()
gc.collect() # 手动回收 如果引用次数 不为0时 手动回收 也是可以的
show_info("finished")
# initial一共占用20.47MB
# created一共占用97.88MB
# finished一共占用20.86MB
其实这个我还是不太理解
查看泄露
用objgraph.show_refs
查看内存的分配情况
上代码
import objgraph
a = [1,2,3]
b = [4,5,6]
a.append(b)
b.append(a)
objgraph.show_refs(a)
objgraph.show_refs(b)
生成关于a
,b
的内存分布情况,并保存为C:\Users\ADMINI~1\AppData\Local\Temp\objgraph-wkzkjd85.png
,
C:\Users\ADMINI~1\AppData\Local\Temp\objgraph-das8cpbd.png
这2张图
a的内存分布
b的内存分布
很明显,b无法再循环引用自己
调试模式
使用模块pdb
,在python文件中输入pdb.set_trace()
就可以调试了
性能了解
使用模块cProfile
,就可以了解每一个函数运行花费的时间
经典参数需注意的
列表是可变数据类型,元组是不可变数据类型
列表增添、删减元素,不会改变内存地址
元组增添、删减元素,会改变内存地址
特别要注意的是,如果对一个函数中输入一个列表,并对列表的元素进行改动,那么即使接下来不输出这个列表,这个列表的元素也会有不可逆的改变