为了对类有更加深入的理解继續学习类相关知识。
8.1修改实例的字符串标识
或者产生一个一段有帮助意义的文本
最后写一种%强制__repr__格式化输出
8.2自定义字符串的输出格式
像让對象通过foramt()函数和字符串方法支持自定义的输出格式
__format__()方法在Python的字符串格式化功能中提供了一个钩子。需要强调的是对格式化代码的解释完全取决与类本身。因此格式化代码几乎可以为任何形式
8.3让对象支持上下问管理协议
上下文管理器最常用于需要管理类似文件、网络链接和锁这样的资源程序中
通过对前面的代码进荇修改,可以实现嵌套式的with语句一次创建多个链接
这样的情况下可以在with里面嵌套式使用,一次创建多个连接
8.4当创建大量实例时如何节省内存
当创建百万级别的实例,为此占用了大量的内存
可以在类里面增加__slot__属性减少对内存的使用,这样每个實例不在创建一个__dict__的字典属性而且围绕着一个固定长度的小型byte数组怎么转换为字符串来构建,
__slot__中列出的属性名会在内部映射到这个byte数组怎么转换为字符串特定的索引上但使用了这个也就不能为实例添加新的属性了。
根据我实际的测试结果来看内存的差距不大,可能数據标本不够大书中的要求,要百万级别的实例
__slot__一般用的比较少除非实例很多。__slot__可以阻止给用户实例添加新的属性但这是__slot__带来的副作鼡,主要还时用来优化用的
8.5 将名称封装到类中
问题:我们想将'私有'数据分装到类的实例上但是又需要考虑到Python缺乏对属性的访问控制问题
峩们一般单下划线的名称被认为属于内部使用,双下划线的名称会被重复名是为了继承这样的属性不能通过继承而覆盖。
一般我们命名私有方法属性,只要_单丅划线就可以了但如果设计到子类化处理,而且有些内部属性应该对子类进行影藏那么此时就应该使用双下划线开头。
单变量名与保留字冲突可以在名字后面加下划线
8.6 创建可管理的属性
在对实例属性的获取和设定上我们希望增加一些额外的处理过程。
通过propety把方法当做屬性来用
刚刚测试中发现使用特性的话,不能重写父类的__getattribute__方法要不然self.xxx执行特性会变成属性赋值。
这是通过调用property复制类属性创建特性
property屬性实际上就时把一系列方法绑定在一起
但当我们访问property属性时会自动触发这些方法的调用。
如果对属性的读取与复制没有处理任何任务鈈要写成下面的方式。
有几处不好第一让代码变的啰嗦,第二让程序变换很多
特别是如果稍后要对莫个属性增加额外的处理步骤的时候,在不修改代码的情况下把特性名修成与实例属性名一样,这样访问一个属性的语法并不会改改变(即访问普通属性与访问property属性的代碼一样)
简单的在一个方法上加上@property,都能够简单的通过属性的方法对方法进行调用
不要重复大量的出现重复性的property,可以通过流畅的Python书中介紹的通过描述符或者特性工厂函数完成同样的任务。
8.7 调用父类中的方法
我们想调用一个父类中的方法这个方法在子类中已经被覆盖。
調用父类(超类)中的方法可以使用super()函数完成。
super函数我前面写过书中也写了,多重继承的时候super比较好的是,就只能父类一次而且是根据__mro__表执行。
当使用super()函数时Python会继续从MRO中的写┅个类开始搜索。只要每一个重新定义过的方法(也就是覆盖方法)都使用了super()并且只调用了它一次,那么控制流最终就可以遍历整个MRO列表并且让每个方法只会被调用一次。
书中下面的有一个案例非常有意思下面上代码
A与B类完全没有关系,但A中的super居然调用到了B的spam这一切就是MRO列表能解释
由于super()可能会调用到我们不希望调用的方法,那么这里有一些基本准则
首相,确保在继承体系中所有相同的方法都有可兼容 调用签名(参数数量想同参数名称也想用)。
如果super()尝试去调用非直接父类的方法那么这就可以确保不会遇到麻烦。其次确保最頂层的类实现了这个方法通常是个好主意。这个沿着MRO列表展开的查寻链会因为最终找到了事件的方法而终止
8.8在子类中扩展属性
我们想在孓类中扩展莫个属性的功能,而这个属性在父类已经被特性或者描述符定义了
因为属性被定义为(getter,setter,delete)的集合而不仅仅时单独的方法,需要搞清楚时定义所有的方法还时定义其中的一个方法
上面的例子是定义了全部方法。为了调用setter函数之前的实现唯一能调用到这个方法的方式就时以类变量而不是实例变量的方式去访问。
后面通过描述符的写法更加好理解
如果只想扩展属性中的一个方法,可以用下面的方法而且这么做,前面定义过 属性方法都会拷贝过来
最后通过描述符的实例赋值类属性来看父类调用就更加容易理解了。
再次强调本章的关键:为了调用setter函数之前的实现,唯一能调用到这个方法的方式就时以父类变量而不是实例變量的方式去访问
8.9创建一种新形式的类属性或实例属性。
想创建一种新形式的实例属性可以拥有一些额外的功能,比如类型检查
解决方案用描述符实例添加类属性。
描述符是高级程序库和框架的作者们锁使用 的最为重要的笁具之一
下面一个大神写的通过装饰器工厂函数,批量给类上属性属性为描述符实例。
高手就时高手,通过了带参数的装饰器传入参数,然后给类属性赋值很好的学习到了
8.10 让属性具有惰性求值的能力
想将一个只读的属性定义为property属性方式,只有在访问它时才参与计算但┅旦访问了该属性,希望把值缓存到对象属性
这里其实用到了描述符只有__get__属性的时候,会被同名的自身属性覆盖流畅的Python书中有更加详細的介绍。流畅的Python书中没有案例当时我自己能力有限,没能想出实现方式这里有了。
通过测试来看,这个很想官方的property去掉了__set__与__delete__版本当我加上了__set__以后,就成为了不可覆盖的类型每次读取该属性,都会调用描述符
由于湔面的属性计算出来以后,可以被修改这样会非常的不安全,书中还有一种计算属性赋值以后就不会被修改的装饰器,我没看懂现茬测试看看。
# 装饰器函数返回一个特性,特性只能被读取 # 调用读取特性内部的函数,去查寻对象是否赋值属性
上面我已经对书中的一些代码做了解释应该89不离十正确,
# 装饰器函数返回一个特性,特性只能被读取 # 调用读取特性内部的函数,去查寻对象是否赋值属性
這样写也可以就更加明显了,返回了一个特性特性里面的fget去执行具体逻辑。
8.11 简化数据结果的初始化过程
我们编写了很多类把它们当莋数据结构来用。但是我们厌倦了编写高度重复且样式想同的__init__()函数
通过一个父类继承以后初始化实例属性,这種写法在Django里面好像看到过
加入实例化参数的时候,输入的时候有关键字传参,书中也有了完美的解决的方案
还有另外一种鈳能通过利用关键字传入额外的属性。
如果编写的程序中有大量的小型数据结构那么定义一个通用性的__init__()方法会特别有用。
对于属性的赋值书中说奣了为什么不能
因为如果子类有property属性验证的化,这样不安全如果子类有__slot__的属性的话,拿直接报错了
这个技术一个潜在的缺点就是会影響到IDE(集成开发环境)的文档和帮助功能。
需要编写一个功能函数逻辑还是很简单的,就是通过读取初始化函数里面的参数
8.12定义一个接口或抽象基类。
定义一个抽象基类可以执行类型检查或者确保子类实现特定方法。
书中还讲到了虚拟子类,这个其实流畅的Python書中讲的更加仔细但我差不多忘了很多了,晕死
Python的collections模块中定义了很多容器很迭代器相关的抽象基类numbers库中定义了和数值相关的抽象基类。io库中定义了和I/O处理相关的抽象基类
尽管抽象基类使得类型检查变得更容易,但不应该在程序中过度使用它Python的核心在于它使一种动态語言,它带来了极大的灵活性如果处处都强制实行类型约束,则会使代码变得更加复杂而这是不应该的。我们应该拥抱Python的灵活性
8.13实現一种数据模型或类型系统
我们想定义各种各样的数据结构,但是对于某些特定属性我们想对允许赋给它们的值添加一些限制
书中三种方法,一种通过描述符类的继承类装饰器,和元类
三种方式如果描述苻用的少的化我觉的第一种对方便,如果用的多用元类也可以,第二种的装饰器写法写的感觉不好通过装饰器给类属性赋值描述符實例有点麻烦。
书中的方法说类装饰器可以提供最大的灵活性和稳健性,第一这种解决方案步依赖与任何高级的机制,比如说元类苐二,装饰器可以很容易地根据需要在类定义上添加或者删除
最后书中用了类装饰器的解决方案取代mixin、多重继承以及super()函数的使用。
说实话,这个代码我要是看到会骂人,明明继承都够用了用了匿名函数,还要用装饰器工厂這是装逼大法的最高进阶了。
但这个类装饰器的方案要比采用mixin的方案快几乎一倍以上。
8.14实现自定义的容器
我们想实现一个自定义的类鼡来模仿普通的内建容器类型比较列表或者字典的行为,但是我们不知道需要定义什么方法实现
可以去继承然后实例一个collections.abc下面的基类,按照提示要求重写基类方法
这个collections.abc里面的基类还是非常丰富的我随便拿了个测试玩玩。
按照书上的例子继承父类定义一个Sequence,序列
序列书中测试,其实包含了索引、迭代、len、in甚至分片
从collections.abc里面定义的基类可以很好的用于检查关于各种基类的关系,可以茬流畅的Python书P268页查看各种基类具体的关系
书中最后定义了一个可变序列我按照书中的样式进行抄写,熟悉
定义叻可变序列,几乎支持可变列表所有的核心方法
我们想在访问实例的属性时能够将其委托到一个内部持有的对象上,这可以作为继承的玳替方案或者是为了实现一种代理机制
解决方案就是简单的情况下将一个实例赋值给另一个类的初始化属性,复杂一点可以通过定义__getattr__实現
在上通过__getattr__如果没有这个属性的时候,使用该方法
相对复杂一点点的操作可以通过委托给对象赋值属性,删除属性读取属性
委托有时候可以做诶继承的代替方案,但更多的时候委托跟继承是不同的
有时候直接使用继承可能没多大意思,或者我們想跟多的控制对象之间的关系(列如只暴露特定的方法、实现接口等)此时使用委托会很有用.
同样需要强调的是__getattr__()方法通常不实用与大蔀分名称以双下划线开头和结果的特殊方法。
实际应该写成这样
8.16 在类中定一个多个构建函数
我们正在编写一个类但是想让用户能够以多種方式创建实例,而不是局限与__init__()提供的这一种
使用类方法用类装饰器,因为里面第一个参数为类本身
类方法可以继承给子类这是非常恏用的
还有就是定义一个有着多个构建函数的类时,应该让__init__()函数尽可能的简单除了给属性赋值之外什么都不做。如果有其他需求可以茬其他备选的构造函数中选择更高级的操作。
这个也可以实现同样的效果但再运行中,表达不清楚代码维护也会不清晰
8.17 不通过调用init来創建实例
我们需要创建一个实例,但是处于某写原因想绕过__init__()方法用别的方式来创建。
通过__new__方法来实现__new__方法时不用加类方法装饰器,第┅个参数为类的方法
这个可以反序列化前面就是把JSON数据转换成模型了。
我们由一些十分有用的方法希望用他们来扩展其他类的功能。泹是需要添加方法的这些类之间并不一定属于继承关系。因此没法将这些方法直接关联到一个共同的基类上。
主要提供一个基础类以忣一些可选的定制化方法
下面上书中的Minix类
下面是一些简单的运行结果。
上面就可以看到mixin类与其他已囿的类的混合
mixin类绝对不是为了直接实例化而创建的,mixin没有__init__说明一般是没有状态属性的。
如果一定要定义一个拥有__init__()方法以及实例变量的嘚mixin类有极大的风险,最好用*args, 关键字参数**kwargs传参。
最后书中用了类装飾器原理也比较简单,进去一个类处理一个类,然后修改一些类定义的方法
其实有点问题,在初始化的时候没有激活__setitem__
8.19实现带有状态的对象或状态机
我们想实现一个状态机,或者让对象可以在不同的状态中进行操作但是我们并步希望代码里会洇此出现大量的条件判断
其实这个解决方案,我是真心看的好累用一个类定义一种状态是书中的方法。
下面是书中觉的使用if条件下的状態
书中用了一种更加优雅的方式,将每一种状态定义成一个单独的类然后在Connection类中使用这些状态类
代码是比较优雅,但逻輯量是多了不少后面还有更加骚的操作。
书中尽然通过修改实例的__class__修改实例所在的类但实例换了所属于的类,它的实例方法都将进行妀变
我下面写一个小测试。
经过這样的测试所以在状态切换的时候,可以通过self.__class__的切换让self拥有不同的类的实例方法。
8.20调用对象上的方法方法名以字符串的形式输出
我們想调用对象上的莫个方法,现在这个方法名保存在字符串中我们想过它来调用
getattr相对比较熟悉,取出属性是方法的话,就是可调用的屬性执行方法,填入参数
后面还有5个小结我粗粗看了一下,感觉还是比较难的
8.21 實现访问者模式
我们需要编写代码来处理或遍历一个由许多不同类型的对象组成的复杂数据结构,每种类型的对象处理的方式都不同列洳遍历一个树结构,根据遇到的树节点的类型执行不同的操作
书中使用了一个案例,一个嵌套的数学运算程序
通过递归的方式,剥洋葱一样一层一层剥开。书中还有一个堆栈机的代码看书不是很理解,先上上来看看
本节涵盖了两个核心思想。首先昰设计策略既把操作复杂数据结构的代码和数据结构本身进行了解耦。也就是说本节中没有任何一个Node类的实现有对数据进行操作。
相反所有对数据的处理都放在特定的NodeVisito类中实现。这种隔离使得代码变得非常通用
第二核心思想在于对访问者类本身的实现。通过一些小技巧再类中定义各种方法讲层层包裹中的各种情况都囊括了,如果没有匹配就报异常
8.22实现非递归的访问者模式
本章节是我看到现在最累的一个章节,用了到生成器函数并通过列表对数据进行压栈与弹栈,看了一天了其实还是有一些不明白,准备放弃中
我们使用访問者模式来遍历一个深度嵌套的树结构,但由于超出了Python的递归限制而奔溃我们想要去掉递归,但依旧保持访问者模式的编程风格
这里代码展示了洳何利用生成器和协程来控制程序的执行流
首先,再有关遍历树结构的问题中为了避免使用递归,常见的策略就是利用栈或者列队来實现算法
第二个要点在于生成器中yield语句的行为。
最后一个需要考虑的是如何传递结果
其实对于这种设计我真的看的很晕,虽然感觉很高大上但就我现在的水平,确实让我很晕有机会回头再看。
8.23 在环装数据结构中管理内存
我们的程序创建了环装的数据结构但是再内存管理上遇到了麻烦。
weakref.ref弱引用这个函数可以引用变量,但不影响变量的引用数
这是一个我弱引用的测试,在测试Φ经常发现,变量已经删除但弱引用里面的对象还存在,并不是马上释放弱引用里面的对象
对于一般的环状数据,可以通过gc.collect(),手动清楚但一般也没必要,因为Python也会定期清楚环状数据
8.24 让类支持比较操作
我们想使用标准的比较操作苻(>,<)等在类实例之间进行比较,但是又不想编写大量的特殊方法
其实这个装饰器就帮你写了这么几句话
当创建类实唎时我们想返回一个缓存引用,让其指向上一个用同样参数(如果有的话)创建出的类实例
创建一个笁厂函数方便的创建弱引用类,缓存
整个类这一章节结束了除了那个遍历的树结构通过yield与列表進行弹栈与压栈比较难,另外的相对还是比较容易理解的
下一个章节,将是元编程这是一个难度不小的骨头,希望能够再10天能够啃完
格式:DOC ? 页数:27页 ? 上传日期: 01:54:27 ? 浏览次数:140 ? ? 760积分 ? ? 用稻壳阅读器打开
全文阅读已结束如果下载本文需要使用
1、试编写求两个无符号双字长数の和的程序两数分别在MEM1和MEM2单元中,和放在SUM单元
设计的两个数分别是:61234。运行结果显示知:SUM单元0B存入了最后结果可得:61234=9DFE8AC4。
2、试编写程序比较AX,BXCX中带符号数的大小,并将最大的数放在AX 中
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。