流程图
派生不可变类型
我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素,例如:
IntTuple([2,-2,‘jr’,[‘x’,‘y’],4]) => (2,4)
如何继承内置tuple 实现IntTuple
以IntTuple继承tuple类创建数值型元组为例。
解决这个问题,必须要清楚的一个问题就是:self对象到底是谁创建的?为了清楚这个问题,我们可以在__init__方法中打印一下self,发现,这个元组已经在传入的时候就被创建了,那么就说明我们一定要在__init__方法前改变传入的参数。
class IntTuple(tuple):
'''实现数值型的元组'''
def __new__(cls, other):
f = [i for i in other if isinstance(i,int) and i >= 0]
return super().__new__(cls, f)
b = IntTuple([2,3,4,-1,'s','r',[2,3,4],(2,3)])
print(b)
如何节省内存创建大量实例
在游戏中,定义了玩家类player,每有一个在线玩家,在服务器内则有一个player的实例,当在线人数很多时,将产生大量实例(百万级)
如何降低这些大量实例的内存开销?
解决方案:
定义类的__slots__属性,声明实例有哪些属性(关闭动态绑定)
动态绑定
__dict__方法实现。
创建一个类
class Demo(object):
def init(self,x):
self.x = x
d1 = Demo(1)
d1.dict[‘y’] = 2
代码
import tracemalloc
import sys
class Player1(object):
def __init__(self, uid, name, gender, age):
self.uid = uid
self.name = name
self.gender = gender
self.age = age
class Player2(object):
__slots__=['uid', 'name', 'gender', 'age']
# 这里可以用[]、{}、()
def __init__(self, uid, name, gender, age):
self.uid = uid
self.name = name
self.gender = gender
self.age = age
player1 = Player1(1,'qinghuan','female',18)
player2 = Player2(2,'qinghuan','female',18)
'''拥有__dict__魔法方法的时候可以实现动态添加属性,当没有这个方法时,
不能够实现动态添加属性,也就是说,__slots__声明了这个类的属性,并且不能添加'''
player1.x = 6
player1.__dict__['y'] = 7
print(player1.__dict__)
print(player2.__dir__())
player2.x = 8
print(player2.__dict__)
如何验证节省了内存?
‘’‘使用__slots__声明变量的作用是出现大量用户时,可以节省内存空间’’’
利用sys模块
print(sys.getsizeof(player1.__dict__))
print(sys.getsizeof(player1.name))
print(sys.getsizeof(player1.uid))
利用tracemalloc
tracemalloc.start()
# p1 = [Player1(1, 2, 3, 4) for _ in range(10000)] # 1726 KiB
p2 = [Player2(1, 2, 3, 4) for _ in range(10000)] # 789 KiB
end = tracemalloc.take_snapshot()
top = end.statistics('filename')
for stat in top[:10]:
print(stat)
上下文管理器
直接操作文件
try:
f = open('test.txt','w')
f.write("qinghuan")
except Exception as e:
print(e)
finally:
f.close()
使用上下文管理器
# 使用上下文管理器
with open('test.txt','w') as f:
f.write('qinghuan test')
类和函数也能够使用上下文管理器器吗?
如果我们在使用的过程当中,想要类和函数实现用with语句调用应该怎么做?
使用with打开类
如果类要实现用with进行操作,必须要在类中包括__enter__方法和__exit__方法,最后再用with语句打开。
class Content(object):
def __init__(self):
print("我在初始化")
def __enter__(self):
print("进入类了")
return self
def demo(self):
print("清欢")
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type,'-')
print(exc_val,'-')
print(exc_tb,'-')
print("退出")
with Content() as content:
content.dems()
使用with打开函数
如果要用with语句打开函数生成器不能够少,如果没有生成器,那么将不能实现此功能。当然,还需要导入一个上下文管理器的模块(contextlib)。
import contextlib
@contextlib.contextmanager
# def Sample():
# print("进入文件")
# yield
# print("文件操作")
# print("结束")
def sample():
print("进入文件")
yield
print("文件操作")
print("结束")
with sample() as f:
pass
创建可管理的对象属性
A.get_key() # 访问器
A.set_key() # 设置器
这样去调用比较麻烦,我们要怎么样才可以实现以下方式去自动调用访问器和设置器呢?
A.key
A.key = ‘jr’
形式上 属性访问
实际上 调用方法
class Demo(object):
def __init__(self,name,age):
self.name = name
self.age = age
def get_info(self):
self.info = "我叫{},今年{}".format(self.name,self.age)
return self.info
def set_info(self,name):
self.name = name
f = property(get_info,set_info)
player = Demo("qinghuan",18)
print(player.get_info())
player.f = 'qinghuan2'
print(player.f)
class Demo(object):
def __init__(self,name,age):
self.name = name
self.age = age
@property
def get_info(self):
self.info = "我叫{},今年{}".format(self.name,self.age)
return self.info
@get_info.setter
def get_info(self,name):
self.name = name
a = Demo('qinghuan',18)
print(a.get_info)
a.get_info = "qinghuan2"
print(a.get_info)
类的比较操作
假设这里有一个计算面积的类Rect,并且有两个实例化的对象r1,r2,要怎么样才能实现判断r1>r2输出布尔值呢?
类的比较
from functools import total_ordering
import math
@total_ordering
class Rect1(object):
def __init__(self,length,width):
self.length = length
self.width = width
def area(self):
return self.length*self.width
def __le__(self, other): #实现了>=,<=
return self.area()<other.area()
def __eq__(self, other): # 实现了==
return self.area() == other.area()
#没有实现 >,<(lt,gt),是否需要每一个写一遍?不需要
r1 = Rect1(1,2)
r2 = Rect1(3,4)
class Cycle(object):
def __init__(self,r):
self.r = r
def area(self):
return math.pi*self.r**2
r3 = Cycle(1.5)
#如何实现 print(r1>r2)
print(r1<r2)
print(r2>r3)
利用抽象基类简化代码
from functools import total_ordering
import math
import abc
@total_ordering
class Square(metaclass=abc.ABCMeta):
@abc.abstractmethod
def area(self):
pass
def __le__(self, other): #实现了>=,<=
return self.area()<other.area()
def __eq__(self, other): # 实现了==
return self.area() == other.area()
#没有实现 >,<(lt,gt),是否需要每一个写一遍?不需要
class Rect1(Square):
def __init__(self,length,width):
self.length = length
self.width = width
def area(self):
return self.length*self.width
r1 = Rect1(1,2)
r2 = Rect1(3,4)
class Cycle(Square):
def __init__(self,r):
self.r = r
def area(self):
return math.pi*self.r**2
r3 = Cycle(1.5)
#如何实现 print(r1>r2)
print(r1<r2)
print(r2>r3)
在环状结构中管理内存
弱引用
弱引用没有占用引用计数。
import weakref
class Demo(object):
def __init__(self):
print("初始化")
def operation(self):
print("test")
def __del__(self):
print("释放方法")
a = Demo()
b = weakref.ref(a)
c = b
print(c is a)
print(b is a)
del c
del a
print("hahah")
代码
代码来源:如何在环状数据结构中管理内存
import weakref
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def add_right(self, node):
self.right = node
# node.left = self
node.left = weakref.ref(self)
def __str__(self):
return 'Node:<%s>' % self.data
def __del__(self):
print('in __del__: delete %s' % self)
def create_linklist(n):
head = current = Node(1)
for i in range(2, n + 1):
node = Node(i)
current.add_right(node)
current = node
return head
head = create_linklist(100)
head = None
import time
for _ in range(100):
time.sleep(1)
print('run...')
input('wait...')