类与对象深度问题与解决技巧

抽象基类

概念及特点

  • 抽象基类(abstract base class,ABC):抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
  • 抽象基类两个特点:1.抽象基类不能被实例化 2.子类继承抽象基类必须重写指定方法
  • 总的来说抽象基类就是定义各种方法而不去实现的类,任何继承自抽象基类的类必须重写该方法,否则不能实例化
  • 纯虚成员函数通过使用装饰器@abstractmethod定义,继承自抽象基类的子类必须重写纯虚成员函数

应用场景

  • 检查某个类中是否有某种方法

    • 定义demo类,类中包含__len__方法,导入抽象基类中的sized类在这里插入图片描述
  • 通过查看Sized源码,在python安装目录下的lib文件夹,打开collection.abc在这里插入图片描述

  • 通过Sized源码,可以看出通过定义纯虚函数,判断一个对象是不是Sized类,在于是否重写了len魔法方法在这里插入图片描述

  • 由于上面的代码里demo类重写了len魔法方法,故demo类实例化的对象d属于Sized类在这里插入图片描述

  • 强制子类必须实现父类的方法

  • 即通过定义纯虚函数,强制子类必须实现父类方法,否则无法实例化在这里插入图片描述

  • 上图可以看出,由于抽象基类demo中定义了纯虚函数__len__,而子类中没有重写__len__方法,故而无法进行实例化,我们在Demo子类重写len魔法方法,在进行实例化,查看结果在这里插入图片描述

  • 可以看出,Demo子类成功的进行了实例化

类与对象深度问题

如何为创建大量实例节省内存

问题描述
  • 在游戏开发中,有一个玩家类Player,每有一个在线玩家,在服务器内则有一个player的实例,当在线的人数很多时,将产生大量实例(百万级),如何降低这些大量实例的内存开销?
解决方法
  • 定义类的__slots__属性,声明实例中有哪些属性,关闭动态绑定
  • 它的作用是阻止实例化时为类分配dict,默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性,由于每次实例化都会分配一个新的dict,因此存在空间浪费
  • __slots__是一个元组,包含了当前能访问到的属性
  • 当定义了slots之后,slots中定义的变量就变成了类的描述符,类的实例只能拥有slot中定义的变量,不能再增加新的变量
  • 定义了slot之后,类就不再有dict属性在这里插入图片描述
  • 因此我们通过在类的定义中增加slot,减少实例化后的内存,并检查定义和不定义slot所占用的内存区别
  • 首先定义两个类,一个定义slot,一个不定义在这里插入图片描述
  • 实例化两个类,并查看实例化一定次数之后它们所占用的内存在这里插入图片描述
  • 最后是两个不同类的结果在这里插入图片描述
    在这里插入图片描述

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

问题描述
  • 我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
解决方法
  • 首先只保留其中的int类型且值大于0的元素,通过一些代码进行筛选很容易实现,而我们需要考虑的是如何将这些代码添加进类的定义中
  • 为了实现上述问题,需要了解一下__new__方法和__init__方法的基本知识
    • __new__在创建实例对象时有python解释器自动调用,一般不用自己定义,默认调用该类的直接父类的__new__方法实例化对象,__new__方法自己定义时必须要有返回值,返回实例化出来的实例,即__init__方法中的默认参数self,如果没有返回值,则__init__方法不会执行,即没有生成self参数 在这里插入图片描述
    • 因此实例化一个对象的步骤是先通过__new__方法返回要实例化的对象,再通过__init__方法,上图中的代码如要正常执行__init__方法中的print语句,应添加返回值,返回值为固定写法,return super().new(cls)或者父类名.new(cls)在这里插入图片描述
      在这里插入图片描述
  • 因此有筛选功能的代码应该添加到__new__方法或者__init__方法中,具体要看元组是通过哪一步生成实例对象的,可以通过下面的代码查看在这里插入图片描述
  • 从上面的结果我们可以看出元组在new函数执行完之后就已经创建好了实例对象,因此想要解决上述问题,我们需要做的是重写__new__方法在这里插入图片描述

with语句简化上下文管理器

问题描述
  • with语句处理对象必须有 __enter__ 方法及 __exit__ 方法。并且 __enter__ 方法在语句体(with语句包括起来的代码块)执行之前进入运行, __exit__ 方法在语句体执行完毕退出后自动运行。那么如何自定义类使用上下文管理器。
解决方法
  • 首先明确几个概念
  • 上下文表达式: with open(‘juran.txt’, ‘r’) as f
  • 上下文管理器:open(‘juran.txt’, ‘r’)
  • 资源对象: f
  • 如果想用with语句处理对象,那么对象必须有__enter__ 方法及 __exit__ 方法。在这里插入图片描述
  • 定义了__enter__ 方法及 __exit__ 方法之后,对象即可以通过with语句处理在这里插入图片描述
  • 注意__enter__函数必须返回self,类似于__new__函数
  • __exit__函数的参数含义如下:
    • exc_type 异常类
    • exc_val 异常值
    • exc_tb 追踪信息
  • 具体可以通过contextlib装饰器实现简化在这里插入图片描述

如何让类支持比较操作

问题描述
  • 有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积
解决方法
  • 通过在类中定义具体的比较魔法方法,可以让类支持比较操作
  • 一些用于比较的魔法方法如下
    • 大于__gt__
    • 小于__lt__
    • 大于等于__ge__
    • 小于等于__le__
  • 接下来是具体实现代码在这里插入图片描述
  • 注意:当类中定义了大于魔法方法之后,自动支持小于比较,而要支持大于等于则必须定义大于等于魔法方法,同样只定义一个,就可以实现大于等于和小于等于
  • 除了上述方法,我们还可以通过total_ordering装饰器完成,我们只需定义__gt__和__eq__两个魔法方法,其他的比较运算由total_ordering通过这两个魔法方法完成,具体实现如下在这里插入图片描述
  • 这里如果我们不止要求出矩阵的面积,还有比如圆或者其它图形,并让它们互相比较,可以通过定义一个抽象基类,定义好比较函数,并让每个子类重写求面积的函数,来实现这个需求,具体实现代码如下:
from functools import total_ordering
from _collections_abc import ABCMeta,abstractmethod
import math
@total_ordering
class shape(metaclass=ABCMeta):
    def __init__(self,l,h):
        pass
    @abstractmethod
    def area(self):
        pass
    def __gt__(self, other):
        return self.area() > other.area()
    def __eq__(self, other):
        return self.area() == other.area()
class Rect(shape):
    def __init__(self,l,h):
        self.l=l
        self.h=h
    def area(self):
        return self.h*self.l
class Circle(shape):
    def __init__(self,r):
        self.r=r
    def area(self):
        return self.r**2*math.pi

通过实例方法名字的字符串调用方法

问题描述
  • 假设我们有三个图形类,他们都有一个获取图形面积的方法,但是方法名字不同,我们如何实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口
解决方法
  • 实现一个统一的获取面积的函数
  • 假设我们有以下三种类,每个类关于面积的函数名称都不一样在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 通过定义一个统一的获取面积函数,通过getattr来实现,getattr的作用是获取当前对象中的指定方法并返回,里面的参数第一个是当前对象,第二个是需要获取的方法,以字符串表示,如果不希望获取不到指定方法时报错,可以传入一个None在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值