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

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

练习需求
我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
需求,定义IntTuple类。
回顾__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)

列表推导式:
1.列表推导式前身

li = []
for i in range(10):
    print(i)
    if i % 2 == 0:
        li.append(i)
print(li)

列表推导式写法:

print([i for i in range(10) if i % 2 == 0])

列表推导式嵌套循环

for i in range(10):
    for j in range(10):
        print(i, j)

列表推导式写法:

print([(i, j) for i in range(10) for j in range(10)])

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

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

import sys
import tracemalloc
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", "sige1", 1)
    # print(set(dir(p1))
    print(dir(p1))
    print(dir(p2))
    print(len(dir(p1)))
    print(len(dir(p2)))
    print(p1.__dict__)
    """
        集合 - --> 差集
        {'__weakref__', '__dict__'}
        - __weakref__ 弱引用
        - __dict__    动态绑定属性的
        """
    # 动态为p1添加属性
    p1.__dict__["level"]=1
    print(p1.__dict__)
    # 动态为p1删除属性
    del p1.__dict__["level"]
    print(sys.getsizeof(p1.__dict__))
    """
       p2能吗?不能动态绑定属性  由于__slots__
       """
    # print(p2.__dict__)  # 报错
    # p2.level = 1  # 不能动态绑定 报错
    #
    # p2.__slots__ = (1,)  # 报错 只读
    tracemalloc.start()
    pla_1=[Player(1,2,3) for i in range(10000)]
    pla_2 = [Player(1, 2, 3) for i in range(10000)]
    snapshot=tracemalloc.take_snapshot()
    top=snapshot.statistics("filename")
    for start in top[:10]:
        print(start)

使用 dict 字典主要是为了提升查询效率,所以必须使用空间换时间少量的实例,使用字典存储,问题不大。但如果像我们的业务达到数百万个实例,字典占用的总空间就比较大。
这个 slots 相当于告诉解释器,实例的属性都叫什么。而且既然需要节省内存,推荐定义时使用元组,而不是列表。
__slots__是否会继承?slots 不影响子类实例,不会继承,除非子类里面自己定义了 slots

3.python中的with语句

自定义类使用上下文管理器
with语句处理对象必须有 enter 方法及 exit 方法。并且 enter 方法在语句体(with语句包括起来的代码块)执行之前进入运行, exit 方法在语句体执行完毕退出后自动运行。

import contextlib
"""
异常捕获复习
"""
# try:
#     print("123")
#     raise KeyError
# except KeyError as e:
#     print("KeyError")
# finally:
#     print("不论有无异常 都执行")
"""
异常捕获文件关闭
"""
# try:
#     f = open("test.txt", "w")
# except Exception as e:
#     print(e)
# finally:
#     print("i am close")
#     f.close()
"""
使用 with open()
"""
# with open("test.txt", "r") as f:
#     f.read()
"""
自定义类是否可以使用上下文管理器
"""
# class Demo(object):
#     def __enter__(self):
#         print("start")
#         return self
#     def __exit__(self, exc_type, exc_val, exc_tb):
#         """
#         参数 只有当 异常时 才有会值
#         :param exc_type: 异常类
#         :param exc_val: 异常值
#         :param exc_tb: 异常的跟踪信息
#         :return:
#         """
#         print(exc_type, exc_val, exc_tb)
#         print("end")
#     def test(self):
#         print("i am test")
# with Demo() as d:
#     # 调用 __enter__
#     # d.test()
#     d.t()
#     # 才会调用 __exit__方法 释放内存
"""
使用@contextlib.contextmanager实现上下文管理器
"""
@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时直接返回退出函数
"""

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

在面向对象编程中,我们把方法看作对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如直接访问属性简洁。
实现需求:定义类AgeDemo
通过访问器访问年龄
通过设置器设置年龄
年龄不是int类型则主动抛出异常

#方法一
class AgeDemo:
    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
# property(getx,setx,delx,"i'm the "x" property.")
    age_pro = property(fget=get_age,fset=set_age)
a=AgeDemo(25)
a.age_pro = "abc"
print(a.age_pro)
#方法二
class AgeDemo:
    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(25)
a.age_test="asd"
print(a.age_test)

5.如何让类支持比较操作

有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积。

from functools import total_ordering
import math
@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()
    def __le__(self, other):
        return self.area()<=other.area()
    def __lt__(self, other):
        return self.area()<other.area()
r1=Rect(1,2)
r2=Rect(3,4)
print(r1>r2) # 类与类之间比较 直接报错   添加 __gt__魔法方法 可以比较 结果:False
print(r1<=r2)
@total_ordering
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()

    def __le__(self, other):
        return self.area() <= other.area()

    def __lt__(self, other):
        return self.area() < other.area()
c=Circle(4)
r=Rect(5,7)
print(c==r)
print(c>r)
``
```python
#类比较优化
"""
将共同代码抽出 强制子类 实现area方法
"""
import abc
from functools import total_ordering
import math
@total_ordering
class Shape(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def area_res(self):
        pass
    def __gt__(self, other):
        return self.area_res() > other.area_res()
    def __ge__(self, other):
        return self.area_res() >= other.area_res()
class Rect(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h
    def area_res(self):
        return self.w * self.h
    def __str__(self):
        return f"({self.w},{self.h})"
class Circle(Shape):
    def __init__(self, r):
        self.r = r
    def area_res(self):
        return self.r ** 2 * math.pi
c = Circle(4)
r = Rect(1, 2)
print(c == r)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值