当前位置:网站首页 > 技术博客 > 正文

python面向对象编程指南




本文内容主要为廖雪峰老师网上教程学习笔记,原文

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. 特殊情况

 
  1. 严重错误
    下面的写法有严重的错误
 
 

设置__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

如果有多个构造函数需要调用, 必须第二种

  • 上一篇: 温度set
  • 下一篇: http请求头信息有哪些
  • 版权声明


    相关文章:

  • 温度set2025-02-05 15:30:05
  • jm更新时进度条不动2025-02-05 15:30:05
  • csrf攻击解决方案2025-02-05 15:30:05
  • qt命令行编译2025-02-05 15:30:05
  • 字典树的实现方式2025-02-05 15:30:05
  • http请求头信息有哪些2025-02-05 15:30:05
  • 同步开关电源和异步2025-02-05 15:30:05
  • java学会增删改查达到什么水平2025-02-05 15:30:05
  • 内连接,左连接,右连接关键字2025-02-05 15:30:05
  • dcnn与cnn的区别2025-02-05 15:30:05