1.如何派生内置不可变类型并修改其实例化行为
我们想自定义一种新类型的元祖,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素,例如:
IntTuple([2,-2,'jr',['x','y'],4])=>(2,4)
- 如何继承内置tuple,实现IntTuple?
- 了解对象创建的步骤
- 实现创建时的数据判断
# 对象到底是谁创建的
class A(object):
def __new__(cls, *args, **kwargs):
print("A.__new___", cls, args)
return object.__new__(cls)
def __init__(self, *args):
print("A.__init__")
a = A(1, 2)
>>>A.__new___ <class '__main__.A'> (1, 2)
>>>A.__init__
# 结论:对象是通过__new__方法创建的
# __init__方法只是初始化了此对象
#注意:必须要有__new__方法必须返回,返回格式:return object.__new__(cls)
a = A(1, 2) 其实相当于:a = A.new(A, 1, 2) A.init(a, 1, 2)
# 实现创建时的数据判断
class IntTuple(tuple):
def __new__(cls, iterable):
# 生成器
f = [i for i in iterable if isinstance(i, int) and i > 0]
#列表推导式
# 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.如何为创建大量实例节省内存
在游戏中,定义了玩家类Player,每有一个在线玩家,在服务器内则有一个Player的实例,当在线人数很多时,将产生大量实例(百万级)
如何降低这些大量实例的内存开销
解决方案:
- 动态绑定属性:_dict_
- 通过在类中定义_slots_属性,声明实例有哪些属性,关闭了动态绑定,节省了内存的开销
import tracemalloc
class Player1(object):
def __init__(self, uid, name, status=0, level=1):
self.uid = uid
self.name = name
self.status = status
self.level = level
class Player2(object):
__slots__ = ('uid', 'name', 'status', 'level')
def __init__(self, uid, name, status=0, level=1):
self.uid = uid
self.name = name
self.status = status
self.level = level
tracemalloc.start()
p1 = [Player1(1, 2, 3) for _ in range(100000)] # 16.8 MiB
p2 = [Player2(1, 2, 3) for _ in range(100000)] # 7837 KiB
#执行100000条数据后发现,没有使用__slots__占用内存是16.8M 使用了__slots__占用内存仅仅才7837K 。
end = tracemalloc.take_snapshot()
top = end.statistics('filename')
for stat in top[:10]:
print(stat)
3.Python中的with语句
通过类实现上下文管理器
通过实现类中的enter方法和exit方法
注意:在exit方法中的几个参数,可以实现异常抛出。
# 上下文管理器
class Sample(object):
# 获取资源
def __enter__(self):
print('start')
return self
def demo(self):
print('this is demo')
# 释放资源 Traceback
def __exit__(self, exc_type, exc_val, exc_tb):
# <class 'AttributeError'> 异常类
print(exc_type, '-')
# 'Sample' object has no attribute 'dems' 异常值
print(exc_val, '-')
# <traceback object at 0x000002D58D8D0A48> 追踪信息
print(exc_tb, '-')
print('end')
with Sample() as sample:
sample.dems()
上下文管理器协议
contextlib简化上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(filename):
print("file open")
yield {}
print("file end")
with file_open('lib1.py') as f:
print("file operation")
4.如何创建可管理的对象属性
在面向对象编程中,我们把方法看做对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如访问属性简洁。
所有通过两种方式来实现这个需求:
- 通过装饰器实现:
'''
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x'
'''
class A:
@property # get
def S(self):
return self.age
@S.setter # set
def S(self, age):
if not isinstance(age, int):
raise TypeError('Type Error')
self.age = age
a = A(18)
print(a.S)
a.S = 22
print(a.S)
>>>18
>>>22
- 通过property实现:
'''
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
'''
class A:
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('Type Error')
self.age = age
# property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
R = property(get_age, set_age)
a = A(18)
a.R = 20
print(a.R)
>>>20
5.如何让类支持比较操作
有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的类,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积
- str方法在打印类的时候被调用
- 字符串的比较是通过字符的ord函数,一个一个比较
- 列表进行比较时,比的是是否包含
- 类的比较,只要内部实现_lt_和_eq_
- 并且引入:from functools import total_ordering
并且在类的开头使用@total_ordering装饰器 - 在引入total_ordering后,只要实现lt和eq,如果不导入,则必须将很多比较方法都实现了。
# 同类不同对象的比较
from functools import total_ordering
@total_ordering
# 使用装饰器的作用可以使lt和eq方法覆盖所有的运算符
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __lt__(self, other):
return self.area() < other.area()
def __eq__(self, other):
return self.area() == other.area()
rect1 = Rect(1, 2)
rect2 = Rect(1, 2)
print(Rect(1, 2) > Rect(1, 2))
>>> False
# 不同类不同对象的比较
from functools import total_ordering
import math
@total_ordering
# 使用装饰器的作用可以使lt和eq方法覆盖所有的运算符
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __lt__(self, other):
return self.area() < other.area()
def __eq__(self, other):
return self.area() == other.area()
class Cirle(object):
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2*math.pi
def __lt__(self, other):
return self.area() < other.area()
def __eq__(self, other):
return self.area() == other.area()
c = Cirle(8)
rect = Rect(1, 2)
print(c >= rect)
>>> True
# 通过抽象基类的方式实现
from functools import total_ordering
import math
import abc
@total_ordering
class Shape(metaclass=abc.ABCMeta):
@abc.abstractmethod
def area(self):
pass
def __lt__(self, other):
return self.area() < other.area()
def __eq__(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 Cirle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2*math.pi
c = Cirle(8)
rect = Rect(1, 2)
print(c == rect)
>>>False
6.如何在环状数据结构中管理内存
- 弱引用
import weakref
a = weakref.ref(A())
那么,a就是弱引用,假如A()对应的其它引用断开,那么系统也会调用A中的del函数释放引用。
双向循环链表
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
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...')
7.通过实例方法名字的字符串调用方法
我们有三个图形类
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
shape1 = Triangle(3, 4, 5)
shape2 = Rectangle(4, 6)
shape3 = Circle(1)
def get_area(shape):
method_name = ['get_area', 'getArea', 'area']
for name in method_name:
f = getattr(shape, name, None)
## 使用getattr函数判断类中是否含有某个方法
if f:
return f()
print(get_area(shape1))
>>> 6.0
print(get_area(shape2))
>>> 24.0
print(get_area(shape3))
>>> 3.14159
# 使用map函数实现
'''
# map 函数举例
def demo (x)
return x**2
demo(4) >>> 16
map(demo,[1,2,3])
>>><map at xxx>
list(map(demo,[1,2,3]))
>>> [1,4,9]
'''
shape_list = [shape1, shape2, shape3]
area_list = list(map(get_area, shape_list))
print(area_list)
>>>[6.0, 24.0, 3.14159]