本文内容主要为廖雪峰老师网上教程学习笔记,原文
link,有些细节(自己没太看懂)的补充。
极客时间景霄老师 《python 核心技术与实战》课程学习笔记
Define : 把对象作为程序的基本单元,对象既有数据又有操作数据的函数。
面向过程的程序设计:把程序看作一系列命令的集合,一组函数的顺序执行。
例如处理一个学生的成绩:
首先,表示一个学生的成绩
之后处理学生的成绩
面向对象的程序设计: 把计算机程序看作一组对象的集合,每个对象都可以接收其他对象发过来的消息,并进行处理。
考虑的不是程序的执行流程, 而是处理的对象 student,这个对象包含什么(属性),对这个对象的操作(print)
面向对象设计思想来自自然界,主要概念是:类(Class)和实例(Instance)
Class 是一种抽象的概念,例如 鸟类 ; Instance 是根据类抽象的模版创建的具体的,比如 大雁 就是鸟类中具体的实例
1.类的定义
Student 是类名— 通常大写开头
object 是表示继承的类,如果没有,一般使用 object
继承和不继承object的区别—详见参考
在 python 3 中,括号没不加 object,也会默认 继承 object 类
2.创建实例
3.绑定属性
可以在实例中绑定属性
由于类的作用就是一个模版,对于一些共有的属性可以写入类之中。 例如定义一个鸟的类,共性就是会飞,这个属性就可以写进,而这个 通过 init 实现
第一个参数永远是 self,self 是创建的实例本身—详见参考
观察上面的测试代码,self 代表的是 类的实例,并且直接指向 Test 类
self 之后加入其他的参数,在定义类的时候需要传入,self 不需要传入
由于 Student 实例 本身就包含了 name 和 score 等数据,我们访问这些数据,可以直接在类的内部定义访问数据的函数
只需要调用,不需要知道内部的实现细节,数据和逻辑就被封装起来了
1. 限制访问
观察上面的程序,可以发现内部调用 print_score 不影响, 外部调用报错
2. 外部获取属性— get
观察上面代码,实际上就是内部调用之后 返回导出
3. 外部修改属性 — set
观察上面的代码,如果数据传入 超过范围,就会报错
4. 特殊情况
- 严重错误
下面的写法有严重的错误
设置__name 的时候,虽然没有报错,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量。内部原有的并没有改变。
如果编写的类是另一个现有类的特殊版本,可使用继承
定义某个新的 class 的时候,可以从现有的 class 继承,新的 class 称为子类 subclass;被继承的class 称为 父类或者超类(base class,Super class)
子类继承了父类所有的属性和方法,同时定义自己的属性和方法
1,首先 dog 继承了 Animal的 所有功能,如下面的代码
2,其次,dog 类还可以加入自定义的功能
对于父类属性的继承
5,使用实例 作为 属性
对实例 属性调用的时候,先查找属性 battery, 之后对储存在改属性中的 Battery 实调用方法
首先,当我们定义一个类的时候,实际上就定义了一种数据类型
使用 isinstance 进行判断
但是,一个变量可能对应多种数据类型
观察上面的代码可以发现 :c不仅仅是Dog,c还是Animal! 当我们创建一个 dog 的时候,也是在创建一个 animal。所以 子类实例的数据类型可以是父类,但是反过来不行
多态有什么好处呢?
对于上面的代码,传入一个 animal的实例的时候
传入一个 Dog 实例的时候
之后我们再定义一个类型(小动物)
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了
动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
给实例绑定属性的方法是通过 self 变量
对于类本身绑定一个属性
定义一个类属性之后,属性归类所有,类的所有实例都可以访问
实例属性 的名字和类属性的名字相同,实例属性将会屏蔽掉类属性
改变类属性
只允许对类的实例添加固定的属性
由于’score’没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
上文中,提到限制属性的修改
但是上述的调用方法略显复杂
希望有一个既能检查参数,又可以使用类似属性的方法来访问类的变量
使用 @property 装饰器,把一个 Method 变成属性调用,如下面的代码所示
定义只读属性 只定义 getter 方法, 不定义 setter 方法
一个子类可以同时获得多个父类的所有功能
Mixin
观察上面的继承, 主线都是单一继承, mammal 继承 Animal, Dog 继承 Mammal;
如果需要混入额外的功能 , 比如 混入 RUnable(), 就进行同时继承
在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
可见 d 调用的 是 超类 B 的方法,这是因为 Python会按照特定的顺序遍历继承图 — 方法解析顺序(Meathod Resolution Order )
每个类 都有一个 mro 属性, 按照 MRO的顺序列出 每个超类,一直向上追溯,直到 object)
python 3 中唯一支持新式 类
上图的 拓扑结构到 C 之前,可以使用 下面代码表示
如果加上 C 会出现错误
C 类的创建具有 二义性的继承关系
具体分析:
1,C 类的线性化 结果记作 L[C] = [C1,C2,…CN] , 其中 C1 称为 L[C] 的头,其余元素 [C2,…,CN] 称为尾 这里的尾部定义和常识不同,后面会用到,!!
2,如果一个类 C 继承自基类 B1、B2、……、BN
merge 输入一组列表,输出是一个列表:
- 检查第一个列表的头元素(如 L[B1] 的头),记作 H。
- 若 H未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 H,继续该步骤。
- 重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。
例如 上面图中的 A
计算 L[C] 的 线性化结果
L[C] 最后没办法输出, 所以无法构建一个没有二义性的继承关系
1. str
首先,我们定义一个类,
返回 类的实例
发现是,实例类的指向以及地址,不是我们太希望看到的内容
加入 str Method,再进行 print
但是直接输出变量还是原来的样子
观察上面的代码,我们可以发现,直接显示,还是显示的是地址这一类的
这是因为我们直接显示调用的不是 str, 而是 repr()
str: 返回用户看到的字符串
repr(): 返回开发者看到的字符串
所以我们可以再定义一个 repr
但是由于和 str 是一样的
测试一下
2. iter
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象.
Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
3. getitem 类实现循环切片等
Fib实例虽然可以用于循环,但是不能够像列表一样按照下表进行选取等
使用 getitem method 访问数据中任意一项
使用 getitem Method 对数据可以切片处理
测试
但是没有对step参数作处理:
也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。
通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
测试
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, ‘score’)来尝试获得属性
5. call : 直接实例调用
测试对实例调用
对实例进行直接调用就好比对一个函数进行调用一样
call()还可以定义参数
测试一下
6. 其他定制类
https://docs.python.org/3/reference/datamodel.html#special-method-names
抽象类是一种特殊的类, 只能作为父类存在, 不能对象化。 定义在抽象类中的抽象函数,子类中必须重写函数才能够使用
直接 实例化 entity 会报错,只有 通过 Document 继承, 才能使用
这种类 对应于软件工程中的 ---- 定义接口的概念。
实际开发中, 开发文档中会定义不同模块的大致功能和接口, 定义好借口, 交付给不同的开发人员去开发和对接。
搜索引擎由 搜索器,索引器, 检索器, 和用户接口四个部分组成
- 搜索器 : 爬虫 ,在互联网上爬取个累网站的内容, 交付给索引器
- 索引器: 对内容进行处理,形成索引(index),储存在内部的数据库等待检索
- 用户接口: 网页和 APP 前端界面
- 爬虫部分跳过, 默认语料存在本地
每一个引擎都应该实现 process_corpus() – 语料处理 , search() 两个函数,对应于索引器 和 检索器
上面的代码 基本实现了一个搜索引擎的基类
运行结果
上面 Simple的 问题:
每次检索需要大量的时间, 需要遍历所有的文件 O(N) 的时间复杂度
索引需要占用大量的空间 , 空间复杂度是 O(N)
这里只能 query 一个词,不嫩处理分散的词
根据 Zipf law ,进行语料分词,把语料看成一个个的词汇,储存词汇的 set.
实验结果
加入了 静态函数 @staticmethod, 因为函数不涉及对象的私有变量,相同的输入得到相同的输出, 方便其他的 类进行调用。
serach 找的时候 同样把 query 打碎成 词set, query 的词 set 和 语料库的 对比
上面的代码 每次还是要遍历所有的 ID,全部遍历代价太大
BOW 模型忽略了单词的顺序, 这有时候也很重要
倒叙索引 :
保留 word --> id 的 字典
结果
随着访问量越来越多,服务器会不够用, 大量重复性的搜索占据很大的流量,解决的一个办法就是加入缓存
LUR 缓存: 符合自然的局部性原理, 保留最近使用过的对象, 淘汰很久没有用的
has() 判断是否在缓存里面, 在的话 get() 返回结果,不在的话, 送入后台计算结果,放入缓存
多重继承两个类, 有两种初始化方法
代码中的第一种, 最顶层的父类 必须是 object
如果有多个构造函数需要调用, 必须第二种
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/5986.html