1、通过实例方法名字的字符串调用方法
①getattr(object, name, default=None):得到一个对象中的name方法,如果没有则返回默认值
②map(func, *iterables) :第一个传递函数名称,第二个传递一个可迭代的对象,返回一个map object,转成list得以显现
③练习的例子
'''
都有一个获取图形面积的方法,但是方法名字不同,
设计一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口
'''
class Rectangle:
def __init__(self,a,b):
self.a,self.b = a,b
def getArea(self):
return self.a * self.b
class Circle:
def __init__(self,r):
self.r = r
def area(self):
return self.r ** 2 * 3.14159
应用①②的知识可以设计出来,结果为
def get_area(shape):
func_list = ['getArea', 'area']
for name in func_list:
f = getattr(shape, name, None)
if f:
return f()
raise ImportError
if __name__ == '__main__':
shape1 = Rectangle(1, 2)
shape2 = Circle(6)
list1 = [shape1, shape2]
print(list(map(get_area, list1)))
2、可变与不可变的深入了解
①可变数据类型为列表(list)、字典(dict)、集合(set)
②不可变的数据类型为数字、字符串、元组
③每一个对象都是由id(内存的存储地址)、value(保存的真实数据)、type(类型)组成
④经典的参数错误,问题如图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201219202114907.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTA4ODg0MQ==,size_16,color_FFFFFF,t_70)
解释:因为列表是可变数据类型,改变的是它的value,它的id不变,也就是储存的地址没变,而元组是不可变的数据类型,相当于重新生成了一个a
3、python内存管理
3.1前言
在 C / C++ 中,我们需要手动的进行内存管理,比如 C 语言中通过 malloc(分配内存) 和 free 函数来申请给定字节数的内存以及释放对应的内存。但在 Python 中,无须手动进行内存的申请和释放,Python 在内部完成了大量涉及到内存管理的操作,包括内存分配及垃圾回收。
3.2内存池
在 Python 中,有很多常用的数据结构,包括列表、字典等。针对这些常用对象的一系列操作,会在 Python 中造成内存的频繁分配和释放,因此 Python 在内部引入了内存池机制,实现了小块内存的管理器(称为 PyMalloc )用于提高处理小块内存的效率,这样避免了在底层频繁的 malloc 和 free 操作对效率带来的影响。
3.3缓冲池机制
在内存池机制的基础之上,Python 为了提高常用对象的创建和释放效率,又进一步对整数、字符串等对象建立了对象缓冲池。比如对于 [-5, 256] 内的小整数,Python 已经在内部创建好了对应的小整数对象的缓冲池。
4、垃圾回收机制
4.1介绍
Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量,计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。为了解决oom及时回收不用的变量,当这个对象的引用计数(指针数)为 0 的时候,则对象永不可达,自然它也就成为了垃圾,需要被回收。
4.2测试
①os模块操作系统模块,自带的模块不需要安装
②psutil与系统交互的库,能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息。它主要用来做系统监控,性能分析,进程管理。需要安装pip install psutil
import os
import psutil
def show_info(start):
pid = os.getpid() # 创建一个进程
p = psutil.Process(pid)
info = p.memory_full_info()
memory = info.uss/1024./1024
print(f"{start}一共占用{memory:.2f}MB")
def func():
show_info("initial")
a = [i for i in range(1000000)]
show_info("created")
func()
show_info("finished")
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020121919521779.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTA4ODg0MQ==,size_16,color_FFFFFF,t_70)
③当a是局部变量时,在返回到函数调用处时,局部变量的引用会注销。这时,列表a所指代对象的引用数为0,Python便会执行垃圾回收,因此之前占用的内存被收回了。
④当a是全局变量的时,即使函数体内代码执行完毕,返回到函数调用处时,对列表a的引用仍然是存在的,所以对象不会被垃圾回收,依然占有大量内存。
4.3引用计数sys.getrefcount()
import sys
a = [1,2,3]
print(sys.getrefcount(a))
结果是2,因为它本身也算做一次引用,垃圾回收的充分非必要条件就是当对象的引用计数为0的时候
4.4循环引用
import gc
def show_info(start):
pid = os.getpid()
p = psutil.Process(pid)
info = p.memory_full_info()
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 created")
a.append(b) # 相互引用
b.append(a) # 相互引用
func()
gc.collect()
show_info("finished")
当双向引用的时候,即使函数结束后并不需要它,可它的引用计数还在,但我们可以手动拉起回收,进行释放内存。通过gc.clloect()对所有内存数据进行回收
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201219200159841.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTA4ODg0MQ==,size_16,color_FFFFFF,t_70)
5、用 cProfile 进行性能分析
cProfile.run(‘想要测试的模块’)
import cProfile
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
cProfile.run('fib_seq(30)')
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201219200909874.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTA4ODg0MQ==,size_16,color_FFFFFF,t_70)
①ncalls:函数被调用的次数。如果这一列有两个值,就表示有递归调用,第二个值是原生调用次数,第一个值是总调用次数。
②tottime:函数内部消耗的总时间。(可以帮助优化)
③percall:是tottime除以ncalls,一个函数每次调用平均消耗时间。
④cumtime:之前所有子函数消费时间的累计和。
⑥filename:lineno(function):被分析函数所在文件名、行号、函数名。