python3 之 面向对象(类)、继承、派生和多态
类提供了一种 组合数据和功能 的方法。创建一个新类意味着:创建一个新 类型 的对象,从而允许创建一个该类型的新 实例。
每个类的实例可以拥有: 保存自己状态的属性。
一个类的实例也可以有改变自己状态的方法(定义在类中的)。
一、名称和对象
对象具有个性,多个名称(在多个作用域内)可以绑定到同一个对象,这在其他语言中成为别名。
在处理不可变的基本类型(数字、字符串、元祖)时,可以安全的忽略它。
但是,对可变对象如(列表、字典、集合等),python代码的语义会产生惊人的影像。
二、作用域和命名空间
在介绍类之前,我首先要告诉你一些Python的作用域规则。类定义对命名空间有一些巧妙的技巧,你需要知道作用域和命名空间如何工作才能完全理解正在发生的事情。顺便说一下,关于这个主题的知识对任何高级Python程序员都很有用。
让我们从一些定义开始:
- 命名空间(namespace):
- 一个从名字到对象的映射
- 当前,大部分命名空间都由python字典实现,一般情况下不会去关注它们(除了要面对性能问题时),而且也有可能在将来更改;
- 几个命名空间的例子:
- 存放内置函数的集合(包含abs()这样的函数,和内建的异常等)
- 模块中的全局名称
- 函数调用中的局部名称
- 从某种意义讲,对象的属性集合也是一种命名空间的形式
- 注意:不同命名空间中的名称之间绝对没有关系。例如:两个不同模块都可以定义一个my_max()函数,而不会产生混淆(模块的用户必须在其前面加上模块名称)。
- 属性:
- 任何跟在 “点” 之后的名称都称为:属性;
- 生存期:
- 不同时刻创建的命名空间拥有不同的生存期;
- 内置名称的命名空间是在python解释器启动时创建的,永远不会删除;
- 模块的全局命名空间,在模块定义被读入时创建,通常模块的命名空间会持续到解释器退出;
- 被解释器的顶层调用执行的语句,被认为是__main__模块调用的一部分(从一个脚本文件读取或交互式地读取),因此它们拥有自己的全局命名空间;
- 内置名称实际也存在于一个模块中,称作:builtins。
- 函数的本地命名空间:
- 一个函数的本地命名空间,在这个函数被调用时创建,并在函数返回或抛出一个不在函数内部处理的错误时被删除
- 每次,递归调用都会有它自己的本地命名空间
- 作用域:
- 指命名空间可直接访问的python程序的文本区域,这里的 ‘可直接访问’ 意味着:对名称的引用(非限定),会尝试在命名空间中查找名称;
- L:local,局部作用域,即函数中定义的变量;
- E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
- G:globa,全局变量,就是模块级别定义的变量;
- B:built-in,内建作用域,系统固定模块里面的变量,比如:int,bytearray等
-
搜索变量的优先级顺序:LEGB,即:局部作用域 > 外层嵌套作用域 > 当前模块的全局作用域 > python内置作用域
- 指命名空间可直接访问的python程序的文本区域,这里的 ‘可直接访问’ 意味着:对名称的引用(非限定),会尝试在命名空间中查找名称;
三、类定义
最简单的类定义看起来像这样:
1 class ClassName: 2 <statement-1> 3 . 4 . 5 . 6 <statement-N>
类定义与函数定义 (def
语句) 一样必须被执行才会起作用。 (你可以尝试将类定义放在 if
语句的一个分支或是函数的内部。)
在实践中,类定义内的语句通常都是函数定义,但也允许有其他语句,有时还很有用 ;
当进入类定义时,将创建一个新的命名空间,并将其用作局部作用域 --- 因此,所有对局部变量的赋值都是在这个新命名空间之内。 特别的,函数定义会绑定到这里的新函数名称。
当(从结尾处)正常离开类定义时,将创建一个 类对象。 这基本上是一个包围在类定义所创建命名空间内容周围的包装器。 原始的(在进入类定义之前起作用的)局部作用域将重新生效,类对象将在这里被绑定到类定义头所给出的类名称 (在这个示例中为 ClassName
)。
四、类对象
类对象支持两种操作:属性引用和实例化。
1、属性引用 :
使用 Python 中所有属性引用所使用的标准语法: obj.name
。 有效的属性名称是类对象被创建时存在于类命名空间中的所有名称。 因此,如果类定义是这样的:
1 class MyClass: 2 """A simple example class""" 3 i = 12345 4 5 def f(self): 6 return 'hello world'
那么 MyClass.i
和 MyClass.f
就是有效的属性引用,将分别返回一个整数和一个函数对象。 类属性也可以被赋值,因此可以通过赋值来更改 MyClass.i
的值。 __doc__
也是一个有效的属性,将返回所属类的文档字符串: "Asimple example class"
。
2、实例化:
类的 实例化 使用函数表示法。 可以把类对象视为是返回该类的一个新实例的不带参数的函数。 举例来说(假设使用上述的类):
1 x = MyClass() #创建类的新 实例 并将此对象分配给局部变量 x
3、__init__()初始化:
实例化操作(“调用”类对象)会创建一个空对象。 许多类喜欢创建带有特定初始状态的自定义实例。 为此类定义可能包含一个名为 __init__()
的特殊方法,就像这样:
1 def __init__(self): 2 self.data = []
当一个类定义了 __init__()
方法时,类的实例化操作会自动为新创建的类实例发起调用 __init__()
。 因此在这个示例中,可以通过以下语句获得一个经初始化的新实例:
1 x = MyClass()
当然,__init__()
方法还可以有额外参数以实现更高灵活性。 在这种情况下,提供给类实例化运算符的参数将被传递给 __init__()
。 例如,:
1 >>> class Complex: 2 ... def __init__(self, realpart, imagpart): 3 ... self.r = realpart 4 ... self.i = imagpart 5 ... 6 >>> x = Complex(3.0, -4.5) 7 >>> x.r, x.i 8 (3.0, -4.5)
五、类的继承与派生