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()时,做了两件事情:

  1. 在bob所在的类树中通过继承搜索giveRaise()方法;
  2. 将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机制相关推荐

  1. 深入探讨Python的import机制:实现远程导入模块 | CSDN博文精选

    来源 | Python编程时光(ID:Python-Time) 所谓的模块导入,是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用. 也许你看到这个标题,会说我怎么会发这么基础的文章? 与 ...

  2. 关于R和Python的安全机制

    关于R和Python的安全机制 对于Python: >>> x = [1,2,3,4] >>> y = x >>> x[0] = 100 > ...

  3. python 释放变量所指向的内存_通俗易懂的Python垃圾回收机制及内存管理

    Python垃圾回收机制及内存管理 内存管理: 先定义一个变量 name='wxl' 那么python会在内存中开辟一小块区域存放"wxl",此时变量的值是我们真正想要存储的,wx ...

  4. python是不是特别垃圾-python垃圾回收机制

    python中有自动内存回收机制,一般情况不需要程序员来处理,面试时被大佬问到了,记录一下.没有画图,推荐读参考的第一篇博文 gc方式1:引用计数 若此对象无其他对象引用,则立马回收掉 优点:简单.实 ...

  5. python是不是特别垃圾-谈谈python垃圾回收机制

    什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回收起来. 在坐的各位都没有女朋友对吧,那难以想象你们的房间会是一个什么样子,可能会有很多垃圾,很凌乱,自己也不收拾. ...

  6. python是不是特别垃圾-深度解析Python垃圾回收机制(超级详细)

    我们知道,目前的计算机都采用的是图灵机架构,其本质就是用一条无限长的纸带,对应今天的存储器.随后在工程学的推演中,逐渐出现了寄存器.易失性存储器(内存)以及永久性存储器(硬盘)等产品.由于不同的存储器 ...

  7. python垃圾回收离职_谈谈python垃圾回收机制

    什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回收起来. 在坐的各位都没有女朋友对吧,那难以想象你们的房间会是一个什么样子,可能会有很多垃圾,很凌乱,自己也不收拾. ...

  8. Python线程同步机制: Locks, RLocks, Semaphores, Condition

    为什么80%的码农都做不了架构师?>>>    翻译自Laurent Luce的博客 原文名称:Python threads synchronization: Locks, RLoc ...

  9. Python虚拟机类机制之descriptor(三)

    从slot到descriptor 在Python虚拟机类机制之填充tp_dict(二)这一章的末尾,我们介绍了slot,slot包含了很多关于一个操作的信息,但是很可惜,在tp_dict中,与__ge ...

  10. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

最新文章

  1. 数据科学干货分享来了!
  2. 脉冲神经网络_【强基固本】脉冲神经网络(SNN)
  3. 应用服务器与数据库之间是长连接,要接收多个 tcp 长连接不断发送的数据并存储,哪些数据库或数据存储方案比较合适?...
  4. drf 解析器,响应器,路由控制
  5. 何小鹏发文力挺李斌:2019年最惨的人
  6. 20145209 2016-2017-2 《Java程序设计》第4周学习总结
  7. 线性插值 多项式插值 样条插值 牛顿插值总结
  8. 对计算机科学与技术专业课程的认识,计算机科学与技术专业课程
  9. ntdll.dll 0xc0000005
  10. wpa_supplicant 框架
  11. 用QQ聊天记录生成一个词云
  12. 【戴师兄数分】excel基础操作——函数专题(个人笔记)
  13. html的nofollow、noindex标签
  14. 网络音乐是时候该聊聊“大数据”了
  15. pandas爬虫爬取网页表格
  16. 数据的相似性和相异性的度量
  17. 阿里云合作伙伴查询合作流程
  18. 目前月薪存一百万需要多久?
  19. 计算机机器人游戏教学计划,机器人教学计划.docx
  20. 淘宝卖家开店怎么做有效减少淘宝垃圾流量

热门文章

  1. Keras.metrics中的accuracy总结
  2. 面试时候被问到为什么离职 离职原因 该如何回答?
  3. 客房管理系统C语言——课程设计实习
  4. python中双重循环_python中双循环
  5. python中hist的用法总结
  6. asp.net使用MailMessage发送邮件的方法
  7. BZOJ5294 BJOI2018 二进制 线段树
  8. 《构建之法》CH5~6读书笔记 PB16110698 第九周(~5.15)
  9. Servlet三大作用域
  10. 料酒是什么,怎么用?