目录
如何派生内置不可变类型并修改其实例化行为
练习需求
我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
需求,定义IntTuple类
>>> IntTuple([2,-2,"jr",["x","y"],4])
(2,4)
回顾__new__
以下代码,执行结果为:__
- A.new,init
- B.init,new
- C.new
- D.init
class Demo(object):
def __new__(cls, *args, **kwargs):
print("__new__")
def __init__(self):
print("__init__")
d = Demo()
1.__new__方法是创建对象的方法
• 1.此处重写了父类的方法
• 2.需调用父类的__new__方法创建对象
• 3.需将对象返回出来给__init__方法
2.__init__方法为初始化方法
• 注意:当创建完对象时,自动调用它
class Demo(object):
def __new__(cls, *args, **kwargs):
print("__new__")
return object.__new__(cls)
#return super().__new__(cls)
def __init__(self):
print("__init__")
d = Demo()
练习实现
class IntTuple(tuple):
def __new__(cls, iterable ):
f = [i for i in iterable if isinstance(i,int) and i>0]
return super().__new__(cls,f)
int_t = IntTuple([2,-2,"jr",["x","y"],4])
print(int_t) # (2, 4)
如何为创建大量实例节省内存
练习需求
在游戏开发中,有一个玩家类Player,每有一个在线玩家,在服务器内则有一个player的实例,当在线的人数很多时,将产生大量实例(百万级)
如何降低这些大量实例的内存开销?
解决方案:定义类的 slots 属性,声明实例有哪些属性(关闭动态绑定)
class Player(object):
def __init__(self, uid, name, status):
self.uid = uid
self.name = name
self.status = status
class Player2(object):
__slots__ = ("uid", "name", "status")
def __init__(self, uid, name, status):
self.uid = uid
self.name = name
self.status = status
# 实例化两个类
p1 = Player("1","zs",1)
p2 = Player2("2","ls",1)
"""
跟踪内存的使用
"""
import tracemalloc
tracemalloc.start() # 开始跟踪内存分配
# pla_1 = [Player(1,2,3) for i in range(10000)] # size=1722 KiB
pla_2 = [Player2(1,2,3) for i in range(10000)] # size=711 KiB
snapshot = tracemalloc.take_snapshot() # 快照,当前内存分配
top = snapshot.statistics("filename") # 快照对象的统计 监测文件
for start in top[:10]:
print(start)
使用 __dict__
字典主要是为了提升查询效率,所以必须使用空间换时间
少量的实例,使用字典存储,问题不大。但如果像我们的业务达到数百万个实例,字典占用的总空间就比较大。
这个__slots__
相当于告诉解释器,实例的属性都叫什么。而且既然需要节省内存,推荐定义时使用元组,而不是列表。
__slots__是否会继承?
__slots__
不影响子类实例,不会继承,除非子类里面自己定义了__slots__
python中的with语句
自定义类使用上下文管理器
with语句处理对象必须有 __enter__
方法及 __exit__
方法。并且__enter__
方法在语句体(with语句包括起来的代码块)执行之前进入运行,__exit__
方法在语句体执行完毕退出后自动运行。
contextlib简化上下文管理器
@contextlib.contextmanager
def file_open(filename):
# __enter__函数
print("file open")
yield {}
# __exit__函数
print("file close")
with file_open("test.txt") as f:
print("file operation")
如何创建可管理的对象属性
在面向对象编程中,我们把方法看作对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如直接访问属性简洁。
实现需求:
- 定义类AgeDemo
- 通过访问器访问年龄
- 通过设置器设置年龄
- 年龄不是int类型则主动抛出异常
如何让类支持比较操作
有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积
from functools import total_ordering
@total_ordering
class Rect(object):
def __init__(self,w,h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __str__(self):
return f"({self.w},{self.h})"
def __lt__(self, other):
return self.area() < other.area()
reac1 = Rect(1,2)
reac2 = Rect(3,4)
print(reac1)
print(reac2)
print(reac1 < reac2)
print(reac1 > reac2)
- @total_ordering装饰器就只需要完成__lt__与__gt__两个方法 就可以全部实现
如何在环状数据结构中管理内存
在python中,垃圾回收器通过引用计数来回收垃圾对象,当一个对象引用计数为0,或者只剩下弱引用时,这个对象会被释放。
弱引用
弱引用不增加引用计数,使用弱引用访问对象得到对象引用
In [1]: import weakref
In [2]: class B:
...: def __del__(self):
...: print("__del__")
...:
In [3]: b1 = B()
In [4]: b2 = weakref.ref(b1)
In [5]: b1 = None
__del__
双链表
变量head指向节点1,节点1右引用节点2,节点2右引用节点3。
节点3左弱引用节点2,节点2左弱引用节点1。
当变量head指向None时,节点1对象被释放,节点1的右引用节点2被释放,节点2的右引用节点3被释放。
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
# 如果左右节点都用弱引用,3后面的节点引用数字都是3,会自动被消灭掉,所以采用一半实,一半虚
# 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(1000)
head = None
import time
for _ in range(1000):
time.sleep(1)
print('run...')
input('wait...')