python的OOP机制
python的OOP机制
在Python中,实际上一切都是对象,我们使用的内置数据类型,本质上也是类的实例化。例如:
>>> type("123")
<class 'str'>
而类本身也是对象,只不过是元类的对象而已。 例如:
>>> type(int)
<class 'type'>
>>> type(list)
<class 'type'>
从本质上讲,python的OOP机制主要依赖两个基础:1.函数的第一个参数;2.继承属性搜索。
OOP不仅是一门技术,更是一种经验。
因为OOP不是在所有场景下都优于POP(Procedure-Oriented Programmin)
python是一门一致性非常好的语言,大多数使用OOP的方式,都可以统一表达为:
object.attribute
这个表达式会在python中启动一次搜索,去搜索对象连接的类树,来寻找attribute首次出现的类。
搜索的顺序大致是从object开始,然后是该对象之上的所有类,自下至上,由左到右。
属性访问就是搜索类树,我们称这种搜索为“继承”。因为树中位置较低的对象继承了树中位置较高的对象所拥有的属性。当从下至上进行搜索时,连接至树中的对象就是树中所有父节点定义的所有属性的并集,直到树的根部。
图中的五个对象,C1,C2,C3是类的对象,l1和l2是实例对象。在python中类和通过类产生的实例是两种不同的对象类型。
我们来举一个例:
I2.w
这是一个 object.attribute 表达式,因此它会触发搜索类树。实际搜索顺序如下:
I2, C1, C2, C3
如果找到w,那么就停止搜索;如果搜索结束没找到w,就会引发一个错误。在图中,w属性只在C3中出现了。因此通过搜索将I2.w
解析为C3.w
,用OOP的术语来讲就是“I2从C3继承了属性w”。
类和实例
在python中,类和实例是两种不同的对象类型,但在类树中看它们几乎是完全等价的:两者的主要目的都是作为另一种命名空间。类和实例的主要差异在于,类是一种产生实例的工厂。
方法调用
前面我们介绍了python的OOP机制主要依赖两个基础之一:“继承属性搜索”,现在来看另一个基础:“函数的第一个参数”。前文所述的I2.w
是一个属性,现在假设w是C3的函数。那么其实际含义应该是“调用C3.w函数来处理I2”,python会自动将I2.w()函数调用映射为C3.w(I2),传入I2作为w函数的第一个参数。
事实上,每当我们以object.attribute来调用附属于类的函数时,总会隐含着这个类的实例。这个实例也是称其为面向对象模型的原因之一。当操作执行的时候,总是有个主体对象,否则这种调用是没有意义的。例如:有一个Employee的类,包含方法giveRaise,除非和加薪的员工结合在一起使用,否则调用giveRaise是没有意义的。
python把隐含的实例传入到方法中的第一个参数,习惯上我们把第一个参数命名为self(这只是个习惯,如果你曾经是C++程序员,那么将类中函数的第一个参数命名为this可能更符合你的习惯)。方法通过这个参数来处理调用的主体。方法能够通过实例(bob.giveRaise())或类(Employee.giveRaise(bob))进行调用。
python在运行bob.giveRaise()时,做了两件事情:
- 在bob所在的类树中通过继承搜索giveRaise()方法;
- 将bob传入giveRaise()方法的第一个参数self.
使用Employee.giveRaise(bob)来调用,相当于手动完成了上面的两件事情。
编写类树
在编写之前,先介绍一些东西。
- 每个class语句都会生成一个新的类对象;
- 每次类调用时,都会生成一个新的实例对象;
- 实例自动链接到创建它们的类;
- 类链接到父类的方式是,将父类列在class头部的括号内;括号中从左至右的顺序会决定树中的顺序。
现在,我们来建立最开始的那个类树。让我们暂时先忽略类中的属性,那么代码应该如下所示:
class C2:...class C3:...class C1(C2, C3):...I1 = C1()
I2 = C1()
其中C1继承自C2和C3,有两个父类。这通常被称为“多继承”,也就是父类有多个。如果父类只有一个那就是单继承。
由于继承是按照搜索来进行的,而这个搜索是按照某种特定顺序进行的,因此你要把属性附件到哪一个对象就显得非常重要。例如:C2和C3都有属性z,然后C1.z将会使用C2中的z,而不是C3中的z.
属性通常是在class语句的顶层语句块中通过赋值语句添加到类中。例如:
class C2:z = 1
属性通常是通过对self参数的赋值来附加给实例的,例如:
class C1(C2, C3):def setname(self, who):self.name = who
这个方式实际上是可以将属性附加给类的,但这需要编写额外的代码。例如:
class C1(C2, C3):def setname(self, who):self.name = whoC1.setname(C1, '赵四') # 给类附加属性class C(C1):...I = C() I1 = C1() I2 = C1()print(C.name) print(I1.name) print(I2.name)
代码执行结果如下所示:
赵四赵四赵四
附加在类上的属性,该类的子类和实例都会拥有该属性;而附加在实例上属性,只被该实例拥有。
和普通变量一样,类和实例属性不需要事先声明,而是在首次赋值后它的值才会存在。事实上,类树中所有对象都只不过是命名空间对象,我们可以使用恰当的名称来访问或修改其任何属性。例如,编写C1.setname(I1, 'jack')
和I1.setname('jack')
是等效操作。
运算符重载
在python中,直到调用setname之前,C1类都不会把name属性附加到实例上。因此,在调用I1.setname之前,使用I1.name会导致未定义名称的错误。如果需要name这个属性一定存在,通常会在构造时填充好这个属性。
class C2:...class C3:...class C1(C2, C3):def __init__(self, who): # 构造函数self.name = whodef setname(self, who):self.name = whoI1 = C1('jack') # 调用__inin__方法
I2 = C1('lucy') # 调用__inin__方法print(I1.name)
print(I2.name)
如果有__init__方法(编写这个方法,或者继承这个方法),那么每次从类产生实例时Python都会自动调用__init__方法。新实例会自动传入__init__的第一个参数self,而在类调用括号内的参数会成为__init__的第二及其之后的参数。其效果就是在创建实例的时候,初始化了实例。
由于__init__在实例化的时候自动调用,因此也被称为“构造函数”。构造函数是python中所谓的运算符重载这一大类方法中最常用的方法之一。
运算符重载方法也是可以被继承的,但是它们的名称开头和结束都带有双下划线(__),当能够支持这些操作的实例出现在对应的运算符旁时,python就会自动运行它们。运算符重载方法不是必须的,如果缺省则不支持对应的运算。如果没有__init__方法,类调用将返回一个空实例(实际上就是一个空的命名空间)。
OOP是关于代码重用
类所支持的代码重用方式是python中其它方式难以提供的,事实上,代码重用也是OOP最重要的目的。通过类,我们可以定制现有的代码来实现需求。类其实就是由函数和其它名称所构成的包,很像模块。但是类支持自动属性继承搜索,这样可以实现高层次的定制,而这是模块和函数做不到的。
多态和类
子类可以覆盖父类的函数,从而重新实现子类的行为。而实例对象会根据创建其的类来决定继承搜索从哪个层次开始,从而决定所使用的函数是哪一个。这就是多态的体现。(多态:运算的意义取决于运算的对象)
PS: 文中图片来自于《Python学习手册》的截图
python的OOP机制相关推荐
- 深入探讨Python的import机制:实现远程导入模块 | CSDN博文精选
来源 | Python编程时光(ID:Python-Time) 所谓的模块导入,是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用. 也许你看到这个标题,会说我怎么会发这么基础的文章? 与 ...
- 关于R和Python的安全机制
关于R和Python的安全机制 对于Python: >>> x = [1,2,3,4] >>> y = x >>> x[0] = 100 > ...
- python 释放变量所指向的内存_通俗易懂的Python垃圾回收机制及内存管理
Python垃圾回收机制及内存管理 内存管理: 先定义一个变量 name='wxl' 那么python会在内存中开辟一小块区域存放"wxl",此时变量的值是我们真正想要存储的,wx ...
- python是不是特别垃圾-python垃圾回收机制
python中有自动内存回收机制,一般情况不需要程序员来处理,面试时被大佬问到了,记录一下.没有画图,推荐读参考的第一篇博文 gc方式1:引用计数 若此对象无其他对象引用,则立马回收掉 优点:简单.实 ...
- python是不是特别垃圾-谈谈python垃圾回收机制
什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回收起来. 在坐的各位都没有女朋友对吧,那难以想象你们的房间会是一个什么样子,可能会有很多垃圾,很凌乱,自己也不收拾. ...
- python是不是特别垃圾-深度解析Python垃圾回收机制(超级详细)
我们知道,目前的计算机都采用的是图灵机架构,其本质就是用一条无限长的纸带,对应今天的存储器.随后在工程学的推演中,逐渐出现了寄存器.易失性存储器(内存)以及永久性存储器(硬盘)等产品.由于不同的存储器 ...
- python垃圾回收离职_谈谈python垃圾回收机制
什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回收起来. 在坐的各位都没有女朋友对吧,那难以想象你们的房间会是一个什么样子,可能会有很多垃圾,很凌乱,自己也不收拾. ...
- Python线程同步机制: Locks, RLocks, Semaphores, Condition
为什么80%的码农都做不了架构师?>>> 翻译自Laurent Luce的博客 原文名称:Python threads synchronization: Locks, RLoc ...
- Python虚拟机类机制之descriptor(三)
从slot到descriptor 在Python虚拟机类机制之填充tp_dict(二)这一章的末尾,我们介绍了slot,slot包含了很多关于一个操作的信息,但是很可惜,在tp_dict中,与__ge ...
- 一文搞懂 Python 的 import 机制
一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...
最新文章
- 数据科学干货分享来了!
- 脉冲神经网络_【强基固本】脉冲神经网络(SNN)
- 应用服务器与数据库之间是长连接,要接收多个 tcp 长连接不断发送的数据并存储,哪些数据库或数据存储方案比较合适?...
- drf 解析器,响应器,路由控制
- 何小鹏发文力挺李斌:2019年最惨的人
- 20145209 2016-2017-2 《Java程序设计》第4周学习总结
- 线性插值 多项式插值 样条插值 牛顿插值总结
- 对计算机科学与技术专业课程的认识,计算机科学与技术专业课程
- ntdll.dll 0xc0000005
- wpa_supplicant 框架
- 用QQ聊天记录生成一个词云
- 【戴师兄数分】excel基础操作——函数专题(个人笔记)
- html的nofollow、noindex标签
- 网络音乐是时候该聊聊“大数据”了
- pandas爬虫爬取网页表格
- 数据的相似性和相异性的度量
- 阿里云合作伙伴查询合作流程
- 目前月薪存一百万需要多久?
- 计算机机器人游戏教学计划,机器人教学计划.docx
- 淘宝卖家开店怎么做有效减少淘宝垃圾流量