高级编程技巧——类与对象深度问题与解决技巧

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

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

练习需求

  • 我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
  • 需要,定义InTuple
    1.InTuple([2,-2,‘jr’,[‘x’,‘y’],4])
    2.(2,3)
 class InTuple(tuple):
    #重写了父类的构造方法
    def __new__(cls,iterable):
    #iterable里面元素,为整数且>0的值
    #列表推导式
          f=[i for i in iterable if isinstance(i,int) and i>0]
          return super().__new__(cls,f)

int_tuple=InTuple([2,-2,'jr',['x','y'],4])
print(int_tuple)

回顾__new__

  • 以下代码,执行结果为:__
  • A__new__,__ init__
  • B__init__,__ new__
  • C__new__
    __ init__
class Demo(object):
    def __new__(cls,*args,**kwargs):
        print('__new__')

    def __init__(self):
        print('__init__')
d=Demo()   # __new__

1、 __ new__ 方法是创建对象的方法

  • 1 .此处重写了父类的方法
  • 2 .需调用父类的__ new__方法创建对象
  • 3 .需将对象返回出来给__ init__方法

2、__ init__方法为初始化方法

  • 注意:当创建完对象时,自动调用它

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

练习需求

  • 在游戏开发中,有一个玩家类Player,每有一个在线玩家,在服务器内则有一个player,当在线的人数很多时,将产生大量实例(百万级)
  • 如何降低这些大量实例的内存开销?

**解决方法:**定义类的__ solts __ 属性,声明实例有哪些属性(关闭动态绑定)

import sys
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

#主程序入口
if __name__=='__main__':
    p1=Player('1','sige',1)
    p2=Player2('2','tianhao',1)

    # print(dir(p1))
    # print(dir(p2))

    # print(len(dir(p1)))  # 29  '__dict__'
    # print(len(dir(p2)))  # 28

    '''
    集合--->差集
    '__weakref__','__dict__'
    __weakref__ 弱引用
    __dict__   动态绑定属性的 
    '''
    # print(set(dir(p1))-set(dir(p2)))
    print(p1.__dict__)  # {}

    # 动态为p1添加属性
    p1.__dict__['level']=1
    print(p1.__dict__)

    # 动态为p1删除属性
    del p1.__dict__['level']
    print(p1.__dict__)

    print(sys.getsizeof(p1.__dict__))  # 能够看到内存所占大小

    '''
    p2能吗? 不能动态绑定属性   由于__slots__
    '''
    print(p2.__dict__)  # AttributeError: 'Player2' object has no attribute '__dict__'
    p2.level=1    # AttributeError: 'Player2' object has no attribute 'level'   不能动态绑定
    p2.__slots__=(1,)# AttributeError: 'Player2' object attribute '__slots__' is read-only  报错,只读
  • 使用__ dict __ 字典主要是为了提升查询效率,所以必须使用空间换时间
  • 少量的实例,使用字典字典存储,问题不大。但是如果像我们的业务到达百万个实例,字典占用的空间就比较大。
  • 这个__slots__ 相当于告诉解释器,实例的属性都叫什么,而且既然需要节省内存,推荐定义时使用元组,而不是列表。

__ slots __ 是否会继承?
__ slots__ 不影响类实例,不会继承,除非子类里面自己定义了
__ slots __

2.3 Python中的with语句

自定义类使用上下文件管理

  • with 语句处理对象必须有 __ enter __ 方法及 __exit __ 方法。并且 enter __ 方法在语句体(with 语句包括起来的代码块)执行之前进入运行, exit __ 方法在语句体执行完毕退出后自动运行。

contextlib简化上下文管理器

import contextlib
@contextlib.contextmanager
def file_open(filename):
    print('file open')
    # 函数加上yield 关键字之后,整个函数变成  生成器
    yield {}
    print('file close')

with file_open('test.txt') as f:
    # __enter__
    print('file operation')
    # __exit__
  • yield 与return 类似 程序遇到yield 会将内容进行返回

  • 不同之处
    yield 在下次访问的时候 会接着 yield 后面的代码继续执行
    return 时直接返回退出函数

2.4 如何创建可管理的对象属性

  • 在面向对象编程中,我们把方法看作对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如直接访问简洁。

实现需求

  • 定义类AgeDemo
  • 通过访问器访问年龄
  • 通过设置器设置年龄
    • 年龄不是int类型主动抛出异常
  '''
A.get_age()  # 访问器
A.set_age()  # 设置器

通过
A.age  访问
A.age= 20 设置

定义类AgeDemo
通过设置器设置年龄
年龄不是int 类型则主动抛出异常
'''

 class AgeDemo(object):

     def __init__(self,age):
         self.age=age
     #访问
     def get_age(self):
         return self.age
     # 设置
     def set_age(self,age):
         if not isinstance(age,int):
             raise TypeError('TypeError')
         self.age=age
 a=AgeDemo(18)
 a.age='20'   # 直接访问到属性
 print(a.age) # 20 str 直接访问到属性 不安全

# 当赋值的时候,直接触发set_age()方法

   # property(getx, setx, delx, "I'm the 'x' property.")

     age_pro= property(fget=get_age, fset=set_age)

 a=AgeDemo(18)
 a.age_pro=20   # 赋值---> fset
 print(a.age_pro) # 访问---> fget

 a=AgeDemo(18)
 a.age_pro='23'  # 赋值---> fset
 print(a.age_pro) # 访问---> fget


class AgeDemo(object):

    def __init__(self,age):
        self.age=age

    @property
    def age_test(self):
        return self.age

    @age_test.setter
    def age_test(self,age):
        if not isinstance(age,int):
            raise TypeError('TypeError')
        self.age=age
a=AgeDemo(18)
a.age_test=20
print(a.age_test)
 

2.5 如何让类支持比较操作

  • 有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==.!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比两个矩形的实例时,比较的是他们的面积。
a=2
b=1
print(a>1)
print(a.__gt__(b))  # gt--->   >

print('='*100)

# 集合比较的  包含关系
print({1,2,3}>{4})   # False   不是比较大小
print({1,2,3} > {2,3})  # True

# 求矩形面积的类
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 __gt__(self,other):
        return self.area() > other.area()

    def __ge__(self, other):   # >=  <=
        return self.area() >= other.area()

r1=Rect(1,2)
r2=Rect(3,4)

print(r1)
print(r2)

print(r1 >r2)
'''
TypeError: '>' not supported between instances of 'Rect' and 'Rect'

类与类比较,直接报错 添加 __gt__魔法方法  可以进行比较
'''
print(r1 >= r2)
print(r1 <= r2)

'''
圆这个类  矩形面积与圆面积作比较
'''
import math
class Circle(object):
    def __init__(self,r):

        self.r=r

    def area(self):
        return self.r**2*math.pi

    def __gt__(self, other):
        return self.area() > other.area()

    def __ge__(self, other):
        return self.area() > other.area()

c=Circle(2)
r=Rect(2,2)
print(c==r)

'''
将共同代码抽出 强制子类  实现area 方法
'''
import abc
from  functools import total_ordering
import math
@total_ordering
class Shape(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def area(self):
        pass

    def __gt__(self, other):
        return self.area() > other.area()

    def __ge__(self, other):
        return self.area() >= other.area()


class Rect(Shape):
    def __init__(self,w,h):
        self.w=w
        self.h=h

    def area(self):
        return self.w*self.h

class Circle(object):
    def __init__(self,r):

        self.r=r

    def area(self):
        return self.r**2*math.pi

c=Circle(4)
r=Rect(1,2)
print(c==r)
  • @total_ordering 装饰器就只需要完成__ it __ 与 __ gt __ 两个方法 就可以全部实现。

2.6 如何在环状数据结构中管理内存

  • 在Python 中,垃圾回收器通过引用计数来回收垃圾对象,当一个对象引用计数为0,或者只剩下弱引用时,这个对象会被释放。
C1
C
C2
C3

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

弱引用

  • 弱引用不增加引用计数,使用弱引用访问对象得到对象引用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值