资讯专栏INFORMATION COLUMN

【数据科学系统学习】Python # 编程基础[三]

molyzzx / 2397人阅读

摘要:新的称为子类,而被继承的称为基类父类或超类。继承最大的好处是子类获得了父类的全部功能。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。

在上一篇中我们介绍了模块和数据结构,这一篇将介绍面向对象编程。


面向对象编程

面向对象编程——Object Oriented Programming,简称 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。它将数据与功能进行组合,并将其包装在被称作“对象”的东西内。

在Python中,所有数据类型都可以视为对象,也可以自定义对象。一个类(Class)能够创建一种新的类型(Type),其中对象(Object)就是类的实例(Instance)。

可以这样来类比:你可以拥有类型 int 的变量,也就是说存储整数的变量是 int 类的实例(对象)。

面向对象的设计思想是抽象出Class,根据Class创建Instance

给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。

数据封装继承多态是面向对象的三大特点。

类和实例

面向对象最重要的概念就是类(Class)和实例(Instance)。

类:是抽象的模板,比如 Student 类。

实例:是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

在 Python 中,定义类是通过class关键字。class后面紧接着是类名,类名通常是大写开头的单词。紧接着是object,表示该类是从哪个类继承下来的。通常,如果没有合适的继承类,就使用object 类,这是所有类最终都会继承的类。

创建实例是通过类名加()实现的:

变量bart指向的就是一个Student的实例,后面的0x1032d3470是内存地址,每个object的地址都不一样,而Student本身则是一个类。

可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性。

在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把namescore等属性绑上去:

注意_init__前后分别有两个下划线。

__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python 解释器自己会把实例变量传进去。

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时不用传递该参数。除此之外,类的方法和普通函数没有什么区别,仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

数据封装

在上面的Student类中,每个实例就拥有各自的namescore这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩:

既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。、

这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:

要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入:

这样,我们从外部看Student类,就只需要知道创建实例需要给出namescore,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

和静态语言不同,Python 允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。

继承和多态

在 OOP 程序设计中,当我们定义一个class的时候,可以从某个现有的class继承。新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

继承最大的好处是:子类获得了父类的全部功能。

也可以对子类增加一些方法。

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()

这样,我们就获得了继承的另一个好处:多态

当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和 Python 自带的数据类型,比如strlistdict没什么两样。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。

新增一个Animal的子类,不必对run_twice()做任何修改。实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在AnimalDogCat还是Tortoise对象上,由运行时该对象的确切类型决定。

这就是多态真正的威力,调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:允许新增 Animal 子类;

对修改封闭:不需要修改依赖 Animal 类型的 run_twice( ) 等函数。

任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:

对于静态语言(例如 Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于 Python 这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

异常

在程序运行过程中,总会遇到各种各样的错误。

程序编写有问题造成的:比如本来应该输出整数结果输出了字符串。这种错误我们通常称之为 bug,bug 是必须修复的。

用户输入造成的:比如让用户输入 email 地址,结果得到一个空字符串。这种错误可以通过检查用户输入来做相应的处理。

完全无法在程序运行过程中预测的:比如写入文件的时候,磁盘满了,写不进去了;或者从网络抓取数据,网络突然断掉了。这类错误也称为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。

Python 内置了一套异常处理机制,来帮助我们进行错误处理。

调试:跟踪程序的执行,查看变量的值是否正确。Python 的 pdb 可以让我们以单步方式执行代码。

编写测试:有了良好的测试,就可以在程序修改后反复运行,确保程序输出符合我们编写的测试。

错误处理
高级语言通常都内置了一套try...except...finally...的错误处理机制

try的机制:

try:
    print("try...")
    r = 10 / 0
    print("result:", r)
except ZeroDivisionError as e:
    print("except:", e)
finally:
    print("finally...")
print("END")

当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。

可以有多个except来捕获不同类型的错误。如果没有错误发生,可以在except语句块后面加一个else,当没有错误发生时,会自动执行else语句。

可以跨越多层调用,比如函数main()调用foo()foo()调用bar(),结果bar()出错了,这时,只要main()捕获到了,就可以处理。也就是说,不需要在每个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。

Python 所有的错误都是从BaseException类派生的,点击可查看常见的错误类型和继承关系。

如果错误没有被捕获,它就会一直往上抛,最后被 Python 解释器捕获,打印一个错误信息,然后程序退出。

错误信息第 1 行:

Traceback (most recent call last):

告诉我们这是错误的跟踪信息。

依次往下看,根据错误类型,判断最后的错误源头。

出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。

Python 内置的logging模块可以非常容易地记录错误信息。同样是出错,但程序打印完错误信息后会继续执行,并正常退出。通过配置,logging还可以把错误记录到日志文件里,方便事后排查。

更多

更多关于 Python 的学习可阅读Python 官方文档,Python 标准库文档,Python的第三方库。

进一步探索标准库的一个好方法是阅读由 Doug Hellmann 撰写的优秀的 Python Module of the Week 系列。


参考链接:
简明Python教程(电子书可阅读)
廖雪峰Python教程

如有不足,欢迎指正。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42187.html

相关文章

  • 数据学系学习Python # 编程基础[一]

    摘要:在定义函数时给定的名称称作形参,在调用函数时你所提供给函数的值称作实参。调用函数要调用一个函数,需要知道函数的名称和参数。默认参数值可以有效帮助解决这一情况。是默认参数定义默认参数要牢记一点默认参数必须指向不变对象。 关于数据科学在做什么,我们已经在前两篇文章中进行了总结,即专题概述和描述性统计分析。要进行数据科学的探索,需要一个好工具,就是Python。从本篇开始,将总结学习Pyth...

    luckyyulin 评论0 收藏0
  • 数据学系学习Python # 数据分析基本操作[] matplotlib

    摘要:有一些表示常见图形的对象称为块,完整的集合位于。中的绘图函数在中,有行标签列标签分组信息。密度图通过计算可能会产生观测数据的连续概率分布的估计而产生的。在探索式数据分析工作中,同时观察一组变量的散布图是很有意义的。 我们在上一篇介绍了 pandas,本篇介绍 matplotlib。 绘图和可视化 一个用于创建出版质量图表的桌面绘图包。 Matplotlib API入门 Figure ...

    BDEEFE 评论0 收藏0
  • 数据学系学习Python # 数据分析基本操作[一] numpy

    摘要:提供了使我们能够快速便捷地处理结构化数据的大量数据结构和函数。结构化数据,例如多维数据矩阵表格行数据,其中各列可能是不同的类型字符串数值日期等。基础数组和矢量计算高性能科学计算和数据分析的基础包。 本篇内容为整理《利用Python进行数据分析》,博主使用代码为 Python3,部分内容和书本有出入。 利用 Python 进行科学计算的实用指南。本书重点介绍了用于高效解决各种数据分析问...

    wushuiyong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<