Python高级-编程技巧-1.2 类与对象深度问题及解决技巧

如何派生内置不可变类型并修改其实例化行为

练习需求

我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中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...')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值