如何派生内置不可变类型并修改实例化行为
练习需求
自定义一个新类型的元组,对于传入的可迭代对象,只保留其中int类型且值大于0的元素。
需求:定义IntTuple类
IntTuple([2, -2, ‘jr’, [‘x’, ‘y’], 4])
得到(2, 4)
单独将IntTuple([2, -2, ‘jr’, [‘x’, ‘y’], 4])取出正数并且大于0可以用for循环遍历,此处用推导式简化。但是可以发现。(推导式)外部部的元组()打印的结果是生成器。
如果使用列表[ 推导式 ]的形式则打印出[2, 4]。是我们想要的元素,但是格式并不是我们想要的。
在不使用tuple强转而是只用继承的方式将列表转为元组,会发现报错。
打印self发现就是传入的实参内容。而self是new方法创建的。所以原因出在new创建上面。
_new _方法复习加原因说明:
创建一个类,实例化调用后会自动执行init方法内容。可以打印A._init _。而init的参数self,是由new方法创建的。
即第27行的调用可以被28 29两行解释执行。以上是new方法的创建和执行。下面是之前问题的解说。
列表的形成过程:
第一步: list._new _(list, ‘abc’)
返回一个空列表
第二步:赋值new变量a
此时变量a还是一个空列表
第三步:init中调用变量a list._init _(a, ‘abc’)
最后得到列表[‘a’, ‘b’, ‘c’ ]
元组的形成过程:
发现元组在new方法中已经完成了初始化,生成了内容。
而列表则需要init初始化才能生成内容。
解决方法:
直接改写new方法即可。
注意new方法中形参处需要变动
如果需要init的话则直接打印self即可。同时注意init的形参要有。
如何为创建大量实例节省内存
1、slots关闭动态绑定
2、slots关闭动态绑定同时加上单例模式(会比方法1消耗更少内存)
_ slots_属性:声明实例有哪些属性(关闭动态绑定)
结合下文练习需求进行说明。
练习需求
在游戏开发中,有个玩家类 player。每有一个在线玩家,在服务器内则有一个player的实例,当在线人数很多时,将产生大量实例(百万级)。
如何降低这些大量实例的内存开销?
使用_slots _关闭动态绑定:
要在元组中填入所有类拥有的属性,否则会报错
将类中的属性都填入后,就会关闭动态绑定属性,只能使用slots元组中的属性。
这样做能减少内存
查看对象所占的字节
sys模块中 getsizeof:返回对象的字节大小(正数)
内存跟踪使用(跟踪内存的使用情况)
tracemalloc模块
通过行数即‘lineno’方法快照统计,发现不够准确,在采集到的55行和54行数据和理论不符。
理论上:使用关闭动态绑定的55行数据应该小于54行没有关闭动态绑定的,但是上图中相反,是应为13行的数据没有统计进54行数据中。此时应该使用 ‘filename’根据文件统计。
根据文件统计,需要54 55行分开调用分别统计
实例化次数越多关闭绑定动态与不关闭动态差距越大。
python中的with语句
自定义类使用上下文管理器(上下文管理协议)
with语句处理对象必须有_enter _方法及_exit _方法。并且_enter _方法在语句体(with语句包括起来的代码块)执行之前进入运行。_exit _方法在语句体执行完毕退出后自动运行。
上下文表达式:with open(‘文件名.文件类型‘, '使用方式‘)as f
上下文管理器:open(‘文件名.文件类型‘, '使用方式‘)
资源对象:f
例:
上下文表达式:with open(‘jvran.txt‘, 'r‘)as f
上下文管理器:open(‘jvran.txt‘, 'r‘)
在类中使用enter exit两种魔法方法:
注意:enter需要设置返回值。否则会报错。
exit的参数:当使用with有错误时会抛出异常。
exc_type:异常类
exc_val:异常值
exc_tb:追踪信息
contextlib简化剩下问管理器
当我们创建方法的时候就无法像自定类一样加入魔法方法。此时,使用contextlib
导入 contextlib模块,使用装饰器:contextlib.contextmanager
在方法内部使用yield作为分界线。在yield上方的代码表示enter的内容。
在yield下方的内容表示exit的内容。
创建可管理的对象属性
在面向对象中,方法被看作对象的接口,直接访问对象的属性可能不安全或者不灵活,但使用调用方法在形式上不如直接访问属性简洁。 即调用方法的时候不加括号也能使用比如:t.test()调用方法时候通过property使用后能直接t.test 就能调用【没有括号】
property():方法
@property装饰器
练习需求
定义类AgeDemo。通过访问器访问年龄。通过设置器设置年龄。当年龄不是int类型时主动抛出异常。
已知:
A.get_age():访问器
A.set_age():设置器
要求:
通过A.age 访问。同时也能A.age = 20 这样设置年龄。
当在外部直接访问属性时会出现图中问题。即没有触发set_age()方法。
想要当外部访问属性并直接赋值当时候能直接触发set_age()方法:
使用装饰器property:
使用装饰器时 要
注意:方法的名字要统一不能再是上图中的get_age:访问 set_age:设置了。而是根据装饰器来进行,property装饰器下方的达标访问方法。再装饰方法名.setter装饰器代表设置方法。
如图所示:
然后通过统一的函数名称进行触发。
让类支持比较操作
自定义类的实例间可以使用: <, <=, >, >=, ==, !=符号进行比较。
使用到自定义比较的时候:如一个矩形类,比较两个矩形的实例时候比较的是这两个矩形的面积
两个类直接比较会报错
可以通过魔法方法进行比较。
魔法方法 | 含义 |
---|---|
_ ge_ | 大于 |
_ it_ | 小于 |
_ gte_ | 大于等于 |
_ lte_ | 小于等于 |
_ eq_ | 等于 |
_ ne_ | 不等 |
复习:
- 数字与数字之间判断大小是比较数字的大小
- 字符串之间判断大小是通过ascall码进行比较大小
- 集合之间判断大小是包含关系,例如{1, 2, 3}>{4} 4是否包含在集合123中
类比较大小
通过之前的比较魔法方法进行写入。
注意:魔法方法的形参中self 指比较的前面数 other指比较的数。
如图中:self指实参rect1。other指实参rect2.
当魔法方法中只有大于或者小于的时候使用大于等于或者小于等于比较会报错。
如果想要实现必须加入等于的魔法方法【eq魔法方法和gte魔法方法】,此时会觉得很复杂,很使用多魔法方法。而我们只要求大于等于的判断。
如果只写一个魔法方法比如 eq 则会报错:
从functools 导入 total_ordering方法,在装饰@total_ordering【装饰类】
装饰后,只需要两个比较魔法方法即可,比如大于 加 等于或者小于等于 魔法方法。 小于 加 等于或者大于等于 魔法方法。
上述方法是一个类中的比较方法。【自己与自己比较】
两个类的比较
方法1:
再写个类同时写入比较的魔法方法。当第一个类绑定了@total_ordering 装饰器 也能进行大小于等于的比较。如果取消装饰器只能进行大于、小于、等于的比较。
方法2:
结合抽象基类。注意类要继承抽象基类以及特定方法的重写
装饰器@total_ordering 绑定在抽象基类上,同时设定各个类中重写方法中area。
python程序执行过程与字节码
python文件有 .py 文件和 .pyc 文件
.py ——> python程序执行过程——> python解释性语言
先讲程序代码编译成字节码——> 然后启动虚拟机执行字节码
编译器将.py 文件中的python代码编译成字节码——> 虚拟机逐行执行编译器生成的字节码
.pyc文件就是编译生成的对象保存在pyc文件中,省去了反复翻译的过程。只有在.py有修改的情况下才会重新编译。