22. Python语言 Web 开发 之 高级编程语言 · 第二章 类 与 对象 深度问题及解决技巧

文章主题

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值