1、派生内置不可变类型并修改其实例化行为
self对象是谁创建的?
# self对象是谁创建的?
# __new__方法
class B:
pass
class A(B):
def __new__(cls, *args, **kwargs):
print("A.__new__", cls, args)
# return object.__new__(cls) # 这里如果注释掉,下面的init方法不会执行 需要返回object
return super().__new__(cls) # 这里语句可以代替上面的执行 建议写上面的object.__new__(cls)
def __init__(self, *args):
print("A.__init__")
a = A(1, 2)
# A.__new__ <class '__main__.A'> (1, 2)
# A.__init__
a = A.__new__(A, 1, 2) # 上面输出的语句可以翻译为下面执行的两句代码
# A.__new__ <class '__main__.A'> (1, 2)
A.__init__(a, 1, 2)
# A.__init__
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
创建list的分解过程
C:\Users\Administrator
λ ipython
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: list('abc')
Out[1]: ['a', 'b', 'c']
In [2]: l = list.__new__(list,'abc')
In [3]: l
Out[3]: []
In [4]: list.__init__(l,'abc')
In [5]: l
Out[5]: ['a', 'b', 'c']
# 上面两语句代表的是创建list的分解过程
In [6]: tuple('abc')
Out[6]: ('a', 'b', 'c')
In [7]: t = tuple.__new__(tuple,'abc')
In [8]: t
Out[8]: ('a', 'b', 'c')
In [9]: tuple.__init__(t,'abc')
In [10]: t
Out[10]: ('a', 'b', 'c')
# 元祖不可修改的原因:在new方法中就已经创建好了,不需要init方法,所以通过init方法不能修改元祖数据
2.如何为创建大量实例节省内存
在游戏中,定义了玩家类player,每有一个在线玩家,在服务器内则有一个player的实例,当在线人数很多时,将产生大量实例(百万级)
如何降低这些大量实例的内存开销?
解决方案:
定义类的__slots__属性,声明实例有哪些属性(关闭动态绑定)
import sys # 导入系统模块
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
p1 = Player1('001', 'a')
p2 = Player2('002', 'b')
print(dir(p1))
print(len(dir(p1))) # 30
print(dir(p2))
print(len(dir(p2))) # 29
# weakref 弱引用
# __dict__ 动态绑定属性
print(set(dir(p1)) - set(dir(p2))) # {'__weakref__', '__dict__'}
p1.x = 6
print(p1.__dict__) # {'uid': '001', 'name': 'a', 'status': 0, 'level': 1, 'x': 6}
# __dict__ 动态绑定属性 极其浪费内存
p1.__dict__['y'] = 7
print(p1.__dict__) # {'uid': '001', 'name': 'a', 'status': 0, 'level': 1, 'x': 6, 'y': 7}
# p2.y = 7 # AttributeError: 'Player2' object has no attribute 'y'
print(p2.name)
# p2.__slots__ = ('x') # AttributeError: 'Player2' object attribute '__slots__' is read-only
# 防止动态绑定
print(sys.getsizeof(p1.__dict__)) # 占用内存152 50+52<152 多出来的内存就是__dict__占用的 浪费内存
print(sys.getsizeof(p1.name)) # 占用内存50
print(sys.getsizeof(p1.uid)) # 占用内存52
这部分代码只需要了解,需要使用就复制
import tracemalloc # 跟踪内存的使用
tracemalloc.start() # 打开跟踪
p1 = [Player1(1, 2, 3) for _ in range(100000)] # _下划线代表可能不需要这个循环的值 size=6274 KiB
p2 = [Player2(1, 2, 3) for _ in range(100000)] # size=7837 KiB
end = tracemalloc.take_snapshot()
# top = end.statistics('filename') # 监控整个文件占用内存 size=28.3 MiB, count=399993, average=74 B
top = end.statistics('lineno') # size=14.5 MiB, count=199987, average=76 B
for stat in top[:10]:
print(stat)
'''
E:/python_learn/如何为创建大量实例节省内存.py:16: size=14.5 MiB, count=199987, average=76 B
E:/python_learn/如何为创建大量实例节省内存.py:66: size=7837 KiB, count=100004, average=80 B
E:/python_learn/如何为创建大量实例节省内存.py:65: size=6274 KiB, count=100001, average=64 B
E:/python_learn/如何为创建大量实例节省内存.py:68: size=448 B, count=1, average=448 B
G:\python3.6.5\lib\tracemalloc.py:522: size=64 B, count=1, average=64 B
'''
3.Python中的with语句
上下文管理器协议
try:
f = open('test.txt', 'w')
# print('hello')
raise KeyError
except KeyError as e:
print("Key Error")
# else:
except IndexError as e:
print('IndexError')
f.close
except Exception as e:
print('Except')
f.close
finally:
# 不管有没有异常都会运行
print('end')
f.close
# with open('test.txt', 'w') as f:
# f.read()
# 上下文管理器 简化异常处理
class Sample(object):
# 获取资源
def __enter__(self):
print('1')
return self
# 释放资源
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type) # <class 'AttributeError'> 异常类
print(exc_val) # 'Sample' object has no attribute 'dem' 异常值
print(exc_tb) # <traceback object at 0x00000221B953C2C8> 追踪信息
print('3')
def demo(self):
print('2')
with Sample() as sample: # AttributeError: __enter__
sample.demo() # 当调用的方法错误时候,错误信息才会被追踪 有输出值
contextlib简化上下文管理器
import contextlib
@contextlib.contextmanager # 装饰器
def file_open(filename):
print("file open") # yield上面的语句相当于__enter__函数
yield {} # 返回空字典 file operation 对文件的操作
print("file end") # yield下面的语句相当于__exit__函数
with file_open('lib1.py') as f:
print("file operation")
4.如何创建可管理的对象属性
在面向对象编程中,我们把方法看做对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如访问属性简洁。
# a.get_key() # 访问器
# a.set_key() # 设置器
# 创造一个形式上是:属性访问
# 实际上是:调用方法
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('TypeError')
self.age = age
R = property(get_age,set_age)
# property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
@property # 装饰器 get方法实现
def S(self):
return self.age
@S.setter # set方法
def S(self, age):
if not isinstance(age, int):
raise TypeError('TypeError')
self.age = age
'''上面的源码
class C(object):
@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
'''
a = A(18)
# 文件读取的 str
a.age = '20'
a.set_age(20)
print(a.get_age())
a.R = 20 # 形式上是属性访问,实际上是方法调用
print(a.R)
a.S = 22 # 设置值和获取值会自动识别方法
print(a.S)
5、让类支持比较操作
有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较
In [11]: a=2
In [12]: b=1
In [13]: a>b
Out[13]: True
In [14]: a.__lt__(b) # 小于 比较大小实际调用的方法
Out[14]: False
In [15]: a.__ge__(b) # 大于等于
Out[15]: True
In [16]: a<b
Out[16]: False
In [17]: s1='abc'
In [18]: s2='abd'
In [19]: s1>s2 # 比较的是ASCII码
Out[19]: False
In [20]: ord('c')>ord('d') # 比较的是ASCII码
Out[20]: False
In [21]: s1.__ge__(s2)
Out[21]: False
# 集合比较的是是否包含的关系,不是大小
In [22]: {1,2,3} > {4}
Out[22]: False
In [23]: {1,2,3} < {4}
Out[23]: False
In [24]: {1,2,3} == {4}
Out[24]: False
In [25]: {1,2,3} > {1}
Out[25]: True
In [26]: {1,2,3} > {1,3}
Out[26]: True
In [27]: {1,2,3} > {1,4}
Out[27]: False
In [28]: {1,2,3} > {2,3,4}
Out[28]: False
例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积
from functools import total_ordering # 引入方法
from abc import ABCMeta,abstractmethod # 抽象基类运用
import abc # 直接引入abc也行
class Shape(metaclass=ABCMeta):
@abstractmethod
def area(self): # 父类中的方法,子类中必须要重写,可以应用在这个地方
pass
def __lt__(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 str(self):
return "(%s, %s)" % (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(3, 4)
print(rect1.area() > rect2.area()) # 不是类之间的比较
print(rect1 < rect2) # 类的比较
print(rect1 > rect2) # 类的比较 内部自动转换成 rect2 < rect1
print(rect1 >= rect2) # 类的比较 False
print(rect1 <= rect2) # 类的比较 True
print(rect1 == rect2) # 类的比较 False
import math
class Cirle(Shape):
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) # False
6.如何在环状数据结构中管理内存
In [30]: class BB:
...: def __del__(self):
...: print('del')
...:
In [31]: import weakref
In [32]: a = BB()
In [33]: a2 = weakref.ref(a) # 弱引用 不占用引用计数,可以被直接回收
In [34]: a3 = a2()
In [35]: del a
In [36]: del a3
del
In [37]: del a2
弱引用在双向链表中的运用
双向循环链表
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...')