从C++或者JAVA 语言入手学编程,好不容易搞懂了最基本的数据类型、赋值判断和循环,却又迎面撞上了OOP(object oriented programming)的大墙,一头扎进公有私有保护、多重继承、多态派生、纯函数、抽象类、友元函数等一堆专有名词的汪洋大海中找不到彼岸,于是就放弃了进阶之路

相比之下,Python是一门相对友好的语言,它在创立之初就鼓励命令交互式的轻量级编程

传统的命令式语言有无数重复性代码,虽然函数的诞生减缓了许多重复性,但随着计算机的发展,只有函数依然不够,需要把更加抽象的概念引入计算机才能缓解(而不是解决)这个问题,于是OOP应运而生

Python一步步攻城掠地飞速发展,从最基础的脚本程序,到后来可以编写系统程序、大型工程、数据科学运算、人工智能,早已脱离了当初的设计,因此一些其他语言的优秀设计之处依然需要引入

一、对象,你找到了吗

先来学习,面向对象编程中最基本的概念

1.1 生活类比

为了方便理解其中的抽象概念,打个比方感受一下
生物课上,学过"界门纲目科属种"的概念,核心思想是科学家们根据各种动植物、微生物的相似之处,将其分化为不同的类型方便研究。生活中也是如此,习惯对身边的事物进行分类:

  • 猫和狗都是动物
  • 直线和圆都是平面几何的图形
  • 《哈利波特》和《冰与火之歌》都是小说

自然,同一类事物便会有着相似的特性:

  • 动物会动
  • 平面图形有面积和周长
  • 小说有相应的作者和大致情节等各种元素

1.2 几个概念

那回到Python上又对应哪些内容呢?这里,先来看一段最基本的Python面向对象的应用代码


class Document():def __init__(self, title, author, context):print('init function called')self.title = titleself.author = authorself.__context = context # __开头的属性是私有属性def get_context_length(self):return len(self.__context)def intercept_context(self, length):self.__context = self.__context[:length]harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())harry_potter_book.intercept_context(10)print(harry_potter_book.get_context_length())print(harry_potter_book.__context)########## 输出 ##########init function called
Harry Potter
J. K. Rowling
77
10---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()22 print(harry_potter_book.get_context_length())23
---> 24 print(harry_potter_book.__context)AttributeError: 'Document' object has no attribute '__context'

参照着这段代码,先简单解释几个概念

一群有着相似性的事物的集合,这里对应Python的class

  • 对象

集合中的一个事物,这里对应由class生成的某一个object,比如代码中的harry_potter_book

  • 属性

对象的某个静态特征,比如上述代码中的titleauthor__context

  • 函数

对象的某个动态能力,比如上述代码中的intercept_context()函数

1.3 类的严谨定义

当然,这样的说法既不严谨,也不充分,但如果对面向对象编程完全不了解,它们可以让你迅速有一个直观的了解

还是通过刚刚那段代码,再给类下一个更为严谨的定义

1.3.1 什么是类

类,一群有着相同属性和函数的对象的集合

虽然有循环论证之嫌,但是反复强调还是希望能对面向对象的最基础的思想,有更真实的了解。清楚记住这一点后,接下来具体解读刚刚这段代码


class Document():def __init__(self, title, author, context):print('init function called')self.title = titleself.author = authorself.__context = context # __开头的属性是私有属性def get_context_length(self):return len(self.__context)def intercept_context(self, length):self.__context = self.__context[:length]harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())harry_potter_book.intercept_context(10)print(harry_potter_book.get_context_length())print(harry_potter_book.__context)########## 输出 ##########init function called
Harry Potter
J. K. Rowling
77
10---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()22 print(harry_potter_book.get_context_length())23
---> 24 print(harry_potter_book.__context)AttributeError: 'Document' object has no attribute '__context'

可以看到,class Document定义了Document类,再往下能看到它有三个函数,这三个函数即为 Document类的三个函数

1.3.2 init构造函数

其中,init表示构造函数,意即一个对象生成时会被自动调用的函数
可以看到harry_potter_book = Document(...)这一行代码被执行的时候,init function called字符串会被打印出来

1.3.3 普通函数

get_context_length()intercept_context()则为类的普通函数,调用它们来对对象的属性做一些事情

1.3.4 类的属性

class Document有三个属性,titleauthor__context分别表示标题、作者和内容,通过构造函数传入
这里代码很直观,可以看到intercept_context能修改对象harry_potter_book__context属性

  • 私有属性

这里唯一需要强调的一点是,如果一个属性以__(注意,两个 _)开头,默认这个属性是私有属性

私有属性,是指不希望在类的函数之外的地方被访问和修改的属性。所以,可以看到titleauthor能够很自由地被打印出来,但是print(harry_potter_book.__context)就会报错

二、更复杂的一些问题

掌握了最基础的概念,其实已经能做很多很多的事情了。不过,在工程实践中,随着复杂度继续提升,可能会遇到一些问题:

  • 如何在一个类中定义一些常量,每个对象都可以方便访问这些常量而不用重新构造?
  • 如果一个函数不涉及到访问修改这个类的属性,而放到类外面有点不恰当,怎么做才能更优雅呢?
  • 既然类是一群相似的对象的集合,那么可不可以是一群相似的类的集合呢?

前两个问题很好解决,不过,它们涉及到一些常用的代码规范,看下面一段代码示例


class Document():WELCOME_STR = 'Welcome! The context for this book is {}.'def __init__(self, title, author, context):print('init function called')self.title = titleself.author = authorself.__context = context# 类函数@classmethoddef create_empty_book(cls, title, author):return cls(title=title, author=author, context='nothing')# 成员函数def get_context_length(self):return len(self.__context)# 静态函数@staticmethoddef get_welcome(context):return Document.WELCOME_STR.format(context)empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove')print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing'))########## 输出 ##########init function called
7
Welcome! The context for this book is indeed nothing.

2.1 常量的定义

第一个问题,在Python的类里,只需要和函数并列地声明并赋值就可以实现这一点,例如这段代码中的WELCOME_STR
一种很常规的做法,是用全大写来表示常量,因此可以在类中使用self.WELCOME_STR,或者在类外使用Entity.WELCOME_STR来表达这个字符串

2.2 静态函数

而针对第二个问题,提出了类函数、成员函数和静态函数三个概念
它们其实很好理解,前两者产生的影响是动态的,能够访问或者修改对象的属性,而静态函数则与类没有什么关联,最明显的特征便是,静态函数的第一个参数没有任何特殊性

具体来看这几种函数。一般而言,静态函数可以用来做一些简单独立的任务,既方便测试,也能优化代码结构
静态函数还可以通过在函数前一行加上@staticmethod来表示,代码中也有相应的示例

2.3 类函数

而类函数的第一个参数一般为cls,表示必须传一个类进来
类函数最常用的功能是实现不同的init构造函数,比如上文代码中使用create_empty_book类函数来创造新的书籍对象,其context一定为nothing
这样的代码,比直接构造要清晰一些。类似的,类函数需要装饰器@classmethod来声明

2.4 成员函数

成员函数则是最正常的类的函数,它不需要任何装饰器声明,第一个参数self代表当前对象的引用,可以通过此函数来实现想要的查询/修改类的属性等功能

三、类的继承

3.1 多个类的集合

接下来,看一下上面所说的第三个问题,既然类是一群相似的对象的集合,那么可不可以是一群相似的类的集合呢?

答案是,当然可以
只要抽象得好,类可以描述成任何事物的集合。当然要小心、严谨地去定义它

3.2 父类和子类

类的继承,顾名思义指的是一个类既拥有另一个类的特征,也拥有不同于另一个类的独特特征
在这里的第一个类叫做子类,另一个叫做父类,特征其实就是类的属性和函数


class Entity():def __init__(self, object_type):print('parent class init called')self.object_type = object_typedef get_context_length(self):raise Exception('get_context_length not implemented')def print_title(self):print(self.title)class Document(Entity):def __init__(self, title, author, context):print('Document class init called')Entity.__init__(self, 'document')self.title = titleself.author = authorself.__context = contextdef get_context_length(self):return len(self.__context)class Video(Entity):def __init__(self, title, author, video_length):print('Video class init called')Entity.__init__(self, 'video')self.title = titleself.author = authorself.__video_length = video_lengthdef get_context_length(self):return self.__video_lengthharry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120)print(harry_potter_book.object_type)
print(harry_potter_movie.object_type)harry_potter_book.print_title()
harry_potter_movie.print_title()print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length())########## 输出 ##########Document class init called
parent class init called
Video class init called
parent class init called
document
video
Harry Potter(Book)
Harry Potter(Movie)
77
120

同样结合代码来学习这些概念。在这段代码中,Document和Video有相似的地方,都有相应的标题、作者和内容等属性。可以从中抽象出一个叫做Entity的类,来作为它俩的父类

3.3 构造函数

首先需要注意的是构造函数
每个类都有构造函数,继承类在生成对象的时候是不会自动调用父类的构造函数的,因此必须在init()函数中显式调用父类的构造函数,执行顺序是子类的构造函数->父类的构造函数

3.4 函数重写

其次需要注意父类get_context_length()函数
如果使用Entity直接生成对象,调用get_context_length()函数,就会raise error中断程序的执行。使用函数重写的方式,可以使子类必须重新写一遍get_context_length()函数来覆盖掉原有函数

3.5 父类函数继承

最后需要注意到print_title()函数
这个函数定义在父类中,但是子类的对象可以毫无阻力地使用它来打印title,这也就体现了继承的优势:减少重复的代码,降低系统的熵值(即复杂度)

3.6 抽象类

最后,再扩展一下抽象函数和抽象类,同样会用一段代码来辅助讲解


from abc import ABCMeta, abstractmethodclass Entity(metaclass=ABCMeta):@abstractmethoddef get_title(self):pass@abstractmethoddef set_title(self, title):passclass Document(Entity):def get_title(self):return self.titledef set_title(self, title):self.title = titledocument = Document()
document.set_title('Harry Potter')
print(document.get_title())entity = Entity()########## 输出 ##########Harry Potter---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-266b2aa47bad> in <module>()21 print(document.get_title())22
---> 23 entity = Entity()24 entity.set_title('Test')TypeError: Can't instantiate abstract class Entity with abstract methods get_title, set_title

可以发现,Entity本身是没有什么用,只需拿来定义Document和Video的一些基本元素就够了
不过,万一不小心生成Entity的对象该怎么办呢?为了防止这样的问题必须要介绍一下抽象类

抽象类是一种特殊的类,它生下来就是作为父类存在的,一旦对象化就会报错。同样,抽象函数定义在抽象类之中,子类必须重写该函数才能使用。相应的抽象函数则是使用装饰器@abstractmethod来表示

可以看到,代码中entity = Entity()直接报错,只有通过Document继承Entity才能正常使用

抽象类就是这么一种存在,是一种自上而下的设计风范,只需要用少量的代码描述清楚要做的事情,定义好接口,然后就可以交给不同开发人员去开发和对接

【Python核心】面向对象相关推荐

  1. 16.1、python初识面向对象(1)

    初识面向对象 楔子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的技能,比如人 ...

  2. Python之面向对象类和对象

    Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...

  3. 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录

    目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高 ...

  4. python核心编程第三版_Python之父:自学python,这3本书能节约你一大半时间编程...

    今天给大家推荐三本书,有两本是属于一个系列,即<Python核心编程>第二版和第三版,一本讲基础和一本讲进阶,非常适合Python的初学者和有一定基础的学习者.还有一本书适合所有想学Pyt ...

  5. 对《Python核心编程》中“第一个Python程序”的改进

    概述: 本程序主要是模仿<Python核心编程>中3.6节--第一个Python程序,并在其基础上做了一些小的改进,而改进的要求则是来源于第三章的课后练习题. 本篇博客的一个核心问题就是在 ...

  6. python3 socketserver_《Python核心编程(第3版)》——2.5 *SocketServer模块

    本节书摘来自异步社区<Python核心编程(第3版)>一书中的第2章,第2.5节,作者[美] Wesley Chun(卫斯理 春),孙波翔 李斌 李晗 译,更多章节内容可以访问云栖社区&q ...

  7. python人工智能_人工智能福利丨Python核心语法实战

    Python已正式跻身成熟语言行列,成为整个互联网的基础性语言之一,并以肉眼可见的速度,在全球攻城略地: --牢牢占据TIOBE世界编程语言排行榜第四名,且保持上升趋势 --国家级人工智能四大平台确立 ...

  8. python基础——面向对象的程序设计

    python基础--面向对象的程序设计 1 什么是面向对象的程序设计 面向过程的程序设计的核心是过程,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. 优 ...

  9. 《Python核心编程(第3版)》——1.3 正则表达式和Python语言

    本节书摘来自异步社区<Python核心编程(第3版)>一书中的第1章,第1.3节,作者[美] Wesley Chun(卫斯理 春),孙波翔 李斌 李晗 译,更多章节内容可以访问云栖社区&q ...

  10. python核心编程第三版_《Python核心编程(第3版)》

    <Python核心编程(第3版)>是经典畅销图书<Python核心编程(第二版)>的全新升级版本,本书适合具有一定经验的Python开发人员阅读,总共分为3部分.第1部分为讲解 ...

最新文章

  1. Hitcon 2016 Pwn赛题学习
  2. 人工智能伦理如何设定,从种群层面看人类的知识积累和进化
  3. Xtract 实现 VMware Vsphere 迁主机到 Nutanix cluster
  4. 论文浅尝 - ACL2020 | 利用常识知识图对会话流进行显式建模
  5. 谷歌要构建自己的区块链技术
  6. lucene创建索引的几种方式(一)
  7. html生成器_这些文案生成器,你知道几个?
  8. 使用 Blueprint 要注意 render_template 函数
  9. 全民一起玩python实战篇百度云_【全民一起玩python】下载 - 面包树
  10. 【利用FLASH制作交互式课件】
  11. 科研小白如何有效下载英文文献和英文书籍?
  12. 计算机电子表格操作步骤,Excel电子表格操作基本步骤.doc
  13. ubuntu更新时Not enough free disk space
  14. 荐书 | 22本颠覆我们认知的思维方式(上)
  15. 项目中有时候为什么加载不出来图片
  16. Codeforces #467 (Div. 2) B. Vile Grasshoppers 蚂蚱的题目
  17. 我为何一直强调外包公司别去
  18. 符合Chrome58的证书制作
  19. Android布局文件中的xmlns:tools作用以及用法
  20. Markdown 常用语法

热门文章

  1. Java实现抽奖幸运儿
  2. Unity VRPanorama 360 PRO Renderer 简单使用小记
  3. Oracle递归查询(查询当前记录所有父级或子级)
  4. CTF-Misc-[BJDCTF2020]你猜我是个啥
  5. DMP平台在贝壳的实践和应用
  6. 《Thenbsp;Road》影评
  7. 聚酯纤维,这种面料到底好不好?
  8. 塞上卧龙图 | 不论朋友还是兄弟,几杯都是情谊
  9. InkScape绘制LOGO教程 渐变色
  10. 随机森林算法-Deep Dive