文章目录
一、如何派生内置不可变类型并修改其实例行为
1.1 练习需求
我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素需求,定义IntTuple类
>>> IntTuple([2,-2,"jr",["x","y"],4])
(2,4)
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()
1.2练习实现
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)
二、如何为创建大量实例节省内存
2.1练习需求
在游戏开发中,有一个玩家类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)
p2.__dict__['gender'] = 1 # 报错:'Player2' object has no attribute 'gender' 无法在外部添加
"""
跟踪内存的使用
"""
import tracemalloc
tracemalloc.start() # 开始跟踪内存分配
# 实例化一万次
# pla_1 = [Player1(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__
相当于告诉解释器,实例的属性都叫什么。而且既然需要节省内存,推荐定义时使用元组,而不是列表。
2.2 __slots__
是否会继承?
__slots__
不影响子类实例,不会继承,除非子类里面自己定义了 __slots__
三、python中的with语句
3.1 自定义类使用上下文管理器
with语句处理对象必须有 __enter__
方法及 __exit__
方法。并且 __enter__
方法在语句体(with语句包括起来的代码块)执行之前进入运行, __exit__
方法在语句体执行完毕退出后自动运行。
# with open('juran.txt', 'r') as f:
# pass
# 上下文表达式 with open('juran.txt', 'r') as f:
# 上下文管理器 open('juran.txt', 'r')
# 资源对象 f
class Jr(object):
def __enter__(self):
"""获取资源"""
print('start')
return self # __enter__需要一个返回值,没有返回值下面的资源对象就是一个None空值
def demo(self):
return 'hello ji'
def __exit__(self, exc_type, exc_val, exc_tb):
"""释放资源"""
# exc_type 异常类
# exc_val 异常值
# exc_tb 追踪信息
print('end')
with Jr() as ji:
print(ji.demo())
# 执行结果:start hello ji end
调用顺序:执行with后,先调用__enter__然后返回self,ji获取self后调用demo方法进行返回,with语句整体调用完成执行__exit__。
3.2 contextlib简化上下文管理器
import 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类型则主动抛出异常
五、 如何让类支持比较操作
有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积
"""
__gt__ 大于
__lt__ 小于
__gte__ 大于等于
__lte__ 小于等于
"""
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)
# 多个类进行比较
# 传入from functools import total_ordering用@total_ordering装饰器定义方法
from functools import total_ordering
# 多个类进行比较,类中的方法相似点很多,可以使用抽象基类进行简化代码
from abc import ABCMeta, abstractmethod
# 用装饰器定义比较方法
@total_ordering
# 定义父类抽象基类
class Shape(metaclass=ABCMeta):
@abstractmethod
def area(self):
pass
# self是函数本身,other是比较的第二个函数
def __gt__(self, other):
"""大于"""
return self.area() > other.area()
def __eq__(self, other):
return self.area() == other.area()
# @total_ordering
# 继承父类中的比较方法
class Rect(Shape):
def __init__(self, w, h):
self.w = w
self.h = h
# 不同新形状的面积计算方法不同,所有进行重写
def area(self):
return self.w * self.h
# def __gt__(self, other):
# """大于"""
# return self.area() > other.area()
#
# def __eq__(self, other):
# return self.area() == other.area()
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
# print(rect1 > rect2)
# print(rect1 >= rect2)
# print(rect1 != rect2)
# print(rect1 < rect2)
# print(rect2 > rect1)
# 导入π
import math
class Circle(Shape):
def __init__(self, r):
self.r = r
# 不同新形状的面积计算方法不同,所有进行重写
def area(self):
return self.r ** 2 * math.pi # π = math.pi
# def __gt__(self, other):
# """大于"""
# return self.area() > other.area()
#
# def __eq__(self, other):
# return self.area() == other.area()
c = Circle(8)
print(c <= rect1)
- @total_ordering装饰器就只需要完成__lt__与__gt__两个方法 就可以全部实现
六、通过实例方法名字的字符串调用方法
我们有三个图形类 Circle,Triangle,Rectangle
他们都有一个获取图形面积的方法,但是方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口
"""
我们可以实现一个统一的获取面积的函数
"""
class Triangle:
def __init__(self,a,b,c):
self.a,self.b,self.c = a,b,c
def get_area(self):
a,b,c = self.a,self.b,self.c
p = (a+b+c)/2
return (p * (p-a)*(p-b)*(p-c)) ** 0.5
class Rectangle:
def __init__(self,a,b):
self.a,self.b = a,b
def getArea(self):
return self.a * self.b
class Circle:
def __init__(self,r):
self.r = r
def area(self):
return self.r ** 2 * 3.14159
# 把类名当做参数shape传入
def get_area_from_shape(shape):
# 把类名传入列表进行循环判断
method_name = ['get_area', 'getArea', 'area']
for name in method_name:
# 判断shape里有没有name,没有就返回None
# getattr存在返回值,用f进行接收
f = getattr(shape, name, None)
# 如果f存在则返回f()
if f:
return f()
shape1 = Triangle(3, 4, 5)
shape2 = Rectangle(1, 2)
shape3 = Circle(6)
# print(get_area_from_shape(shape1)) # 6.0
# print(get_area_from_shape(shape2)) # 2
# print(get_area_from_shape(shape3)) # 113.09724
# 一个个输出太麻烦,把实例化后的传入列表用map把get_area_from_shape用到每一个shape_list中返回迭代器,用list转换成列表
shape_list = [shape1, shape2, shape3]
area_list = list(map(get_area_from_shape, shape_list))
print(area_list) # [6.0, 2, 113.09724]
# find查找,没找到返回-1,找到返回相应的下标
s = 'abc'
print(s.find('123')) # -1
print(s.find('a')) # 0
# hasattr判断有没有,getattr获取有没有
print(getattr(s, 'find')) # 返回一个方法,<built-in method find of str object at 0x0000022254F72B30>
print(getattr(s, 'find')('123')) # -1
print(getattr(s, 'pop')) # 报错:'str' object has no attribute 'pop'没有pop方法
# 要是不想报错,可以加入None,,没有就返回None
print(getattr(s, 'pop', None)) # None
f = getattr(s, 'find', None) # None
print(f('123')) # -1
- getattr(x,“y”,None) --> 等同于
x.y
当x中不含有y时,返回None。 - map(func,iterable) --> 将iterable中的元素一一映射到func函数中处理,并且返回新的map对象。