如何派生内置不可变类型并修改其实例化行为
定义一种新类型的元组,对于传入的可迭代对象,只保留其中int类型且大于0的元素,例如:
IntTuple([2,-2,'jr',['x','y'],4]) => (2,4)
如何继承内置tuple实现IntTuple
在此之前我们先做一个测试
class Normal(object):
def __init__(self, item):
print(self)
class IntTuple(tuple):
def __init__(self, item):
print(self)
class MyList(list):
def __init__(self, item):
print(self)
a = [2,-2,'jr',['x','y'],4]
IntTuple(a)
Normal(a)
MyList(a)
# (2, -2, 'jr', ['x', 'y'], 4)
# <__main__.Normal object at 0x0000022FD88E92E8>
# []
从结果看,继承不同的对象,展现出了不同的效果,为什么呢?我们知道__init__魔法方法是实例化方法,谁调用它它就是谁,为什么在tuple中,self就是传入的对象呢?说明在self之前,该对象已经被创建好了,是谁创建的self呢?
class IntTuple(object):
# 创建一个新的对象并返回该对象
def __new__(cls, *args, **kwargs):
print('__new__', cls, *args)
return object.__new__(cls)
def __init__(self, item):
print('__init__')
a = [2,-2,'jr',['x','y'],4]
IntTuple(a)
# __new__ <class '__main__.IntTuple'> [2, -2, 'jr', ['x', 'y'], 4]
# __init__
# 用另一种方式表达上面的效果
l = list.__new__(list, 'abc')
print(l)
list.__init__(l, 'abc')
print(l)
# 等于 l = list('abc')
t = tuple.__new__(tuple, 'abc')
print(t)
# 等于t = tuple('abc')
# []
# ['a', 'b', 'c']
# ('a', 'b', 'c')
cls 即使IntTuple本身
列表__new__一个对象后是一个空对象,元组__new__一个对象就是对象本身
class IntTuple(tuple):
def __new__(cls, item):
# print('__new__', cls, *args)
f = (i for i in item if isinstance(i, int) and i > 0)
return super().__new__(cls, f)
a = IntTuple([2, -2, 'jr', ['x', 'y'], 4])
print(a)
# (2, 4)
slots
__slots__的作用是组织实例化类时分配__dict__,默认情况下每个类都有一个dict,通过__dict__访问,这个dict维护了这个实例所有属性,例子如下:
class A(object):
# 类属性
x = 1
def __init__(self):
pass
a = A()
print(a.__dict__)
a.id = 1
print(a.__dict__)
# {}
# {'id': 1}
可以看到dict不保存类属性,只保存实例属性
__slots__是一个元组,包含了当前能访问到的属性,此时类的实例只能拥有slots中的变量,不能再新增变量。注意,定义了slots后不再有dict
class Base(object):
__slots__ = ('x', 'y')
def __init__(self, x):
self.x = x
b = Base(1)
print(b.x)
b.x = 2
print(b.x)
b.y = 3
print(b.y)
b.z = 4
print(b.z)
# 1
# 2
# 3
# AttributeError: 'Base' object has no attribute 'z'
slots中没有z,在实例化中增加z时,出现报错
应用场景
- 当需要用到的变量是可知的时候
- 有时候我们只想使用固定的属性,不想任意绑定其他属性
slots在继承中
class Base(object):
__slots__ = ('x', 'y')
def __init__(self, x):
self.x = x
class BaseSON(Base):
def __init__(self):
pass
b = Base(1)
bs = BaseSON()
print(dir(b))
print(dir(bs))
# ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'x', 'y']
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']
在b中含有__slots__,在BS中含有__dict__,就是说当继承父类(含有__slots__)子类中不受影响,子类可以动态的添加变量。
因为有dict的存在可以动态的增删改,但如果需要大量实例化,会非常耗费内存。
from memory_profiler import profile
class A(object):
def __init__(self, item):
self.item = item
@profile
def main():
# 列表推导式
f = [A(23333) for i in range(100000)]
if __name__ == '__main__':
main()
Line # Mem usage Increment Line Contents
================================================
6 18.1 MiB 18.1 MiB @profile
7 def main():
8 38.1 MiB 0.7 MiB f = [A(23333) for i in range(100000)]
Mem usage表示该行运行后,python解释器的内存使用情况
Increment 表改行代码执行前后的内存变化
MiB,mebibyte(1Mib = 1.05MB)
memory_profiler也很消耗内存,@profile用来标记需要追踪的代码
from memory_profiler import profile
class A(object):
# 使用slots
__slots__ = 'item'
def __init__(self, item):
self.item = item
@profile
def main():
f = [A(23333) for i in range(100000)]
if __name__ == '__main__':
main()
# Line # Mem usage Increment Line Contents
================================================
7 18.1 MiB 18.1 MiB @profile
8 def main():
9 23.7 MiB 0.3 MiB f = [A(23333) for i in range(100000)]
使用slots前内存消耗0.7MiB,使用后消耗0.3
参考自:https://blog.csdn.net/sxingming/article/details/52892640
上下文管理器
我们知道通过with打开某个CSV,操作过后不需要手动close,它可以自动关闭。如果用with调用一个对象呢?
class file_text(object):
def file(self):
print('我裂开了')
with file_text as ft:
ft.file()
#AttributeError: __enter__
属性错误
class file_text(object):
# 打开资源
def __enter__(self):
print('start')
# 需要返回self对象,否则报错
return self
def demo(self):
print('我裂开了')
# 释放资源
def __exit__(self, exc_type, exc_val, exc_tb):
print('end')
with file_text() as ft:
ft.demo()
exc_type,异常类
exc_val,异常值
exc_tb,追踪信息
import contextlib
@contextlib.contextmanager
def file_open(filename):
# 相当于__enter__方法
print('open')
yield {}
# 相当于__exit__方法
print('end')
with file_open('c/VCC') as fo:
print('file operation')
# open
# file operation
# end
如何创建可管理的对象
class A(object):
def __init__(self, age):
self.age = age
def get(self):
return self.age
def set(self, age):
if not isinstance(self.age, int):
raise TypeError('需要传入整数')
else:
self.age = age
# x = property(getx, setx, delx, "I'm the 'x' property.")
x = property(get, set)
a = A(18)
a.x = 20
print(a.x)
# 20
类之间的比较
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 '求矩形的面积'
def __lt__(self, other):
return self.area() < other.area()
r = rect(1, 2)
r2 = rect(3, 4)
print(r < r2)
print(r)
- __str__,打印实例对象时,返回字符串
- __eq__(self, other), ==
- __ne__(self, other),!=
- __lt__,<
- __gt__,>
- __le__,<=
- __ge__,>=