#目录

前言

我们定义一个类是希望能够把类当成模块来使用,并把类嵌入到我们的应用代码中,与其他的数据类型、逻辑执行流结合使用。一般来说我们可以使用两种方法在代码中利用类,那就是组合和派生。

组合

组合: 就是将不同的类混合并加入到其他类中,来 增加类的功能 / 提高代码的重用性 / 易于维护(对类的修改会直接反应到整个应用中) 。我们可以实例化一个更大的对象,同时还可以添加一些实例属性和实例方法的实现来丰富实例对象的功能。

In [1]: class NewAddrBookEntry(object):               # 定义一个类
...: """New address book entry class."""
...: def __init__(self, name, phone): # 定义构造器
...: self.name = Name(name) # 创建 Name 对象
...: self.phone = Phone(phone) # 创建 Phone 对象
...: print "Created the instance for ", self.name

上述的 class NewAddrBookEntry 有它自身和其他类 Name/Phone 组合而成, 可以实现一些更为复杂的功能。

派生

当我们希望较小的类是较大的类的组件时,组合是一个很好的处理方式。但当我们希望 相同的类却具有一些不同的功能时 派生就是最好的处理方式。这也是面向对象编程最强大的功能之一 —— 使用一个已经定义好的类,扩展它的功能或者对其进行修改生成一个新的类,但却不会对原来的类造成影响。

子类会从基类继承他们的任何属性(数据和方法),这种派生是可以继承多代的,且可以同时继承多个基类。

语法

class SubClassName(ParentClass1[, ParentClass2, ...]):
class_suite

EXAMPLE

In [8]: class Parent(object):
...: def __init__(self):
...: print "created an instance of: ", self.__class__.__name__
...:
...: In [9]: class Child(Parent):
...: pass
...: In [10]: c = Child()
created an instance of: Child In [14]: p = Parent()
created an instance of: Parent

类 Child 的实例对象 c 并没有定义 __init__() 构造器,但仍然执行了 print 语句,这是因为 Child 从 Parent 继承了其构造器。

通过继承来覆盖(重载)方法

当我们派生一个子类,但同时希望相同的方法能在子类实现不同的功能,这时我们需要使用方法的 重载。使子类的方法能够覆盖父类的同名方法。

In [17]: class Parent(object):
...: def func(self):
...: print "This is Parent."
...: In [18]: class Child(Parent):
...: def func(self):
...: print "This is Child."
...:
...: In [19]: p = Parent() In [20]: p.func()
This is Parent. In [21]: c = Child() In [22]: c.func()
This is Child.

这里子类 Child 重载了父类 Parent 的 func() 方法,实现了不同的功能。

但仍然有些场合需要我们即能使用子类的重载方法的同时,也要求我们重现父类方法的功能。那么我们可以调用那个被我们覆盖的父类方法吗?

答案是肯定的。

In [23]: Parent.func(c)
This is Parent.

我们可以通过 ParentClassName.functionName(object) 的方式来重现父类所被覆盖的方法。当然我们还有其他的方式可以实现这个效果,EG. 在子类的重载方法里显式的调用父类的同名方法:

In [24]: class Child(Parent):
...: def func(self):
...: print "This is Child."
...: Parent.func(self) In [25]: c = Child() In [26]: c.func()
This is Child.
This is Parent.

两种方式本质上是相同的,都是通过 父类名结合句点表达式 来实现对父类方法的调用,而且需要注意的是,在调用的时候必须传递一个实例对象给 func(),否则会触发参数不匹配的语法错误。

还有一个更好的实现方式就是子类使用 super() 内置函数:

In [27]: class Child(Parent):
...: def func(self):
...: print "This is Child."
...: super(Child, self).func()
...: In [28]: c = Child() In [29]: c.func()
This is Child.
This is Parent.

super() 内置函数不仅能自动的找到父类方法,并且还是自动的为父类方法传入 self 参数来实现实例方法的绑定。

最常用的重载场景(实例方法的重载)

最常用的重载场景莫过于 重载父类的构造器 了。

在上述的例子可以看出,当我们在子类中没有重载构造器的时候,会自动的调用父类的构造器。这很明显是不符合我们的需求的,因为我们常常需要在子类中定义一些新的成员属性。但是问题是:当我们为了初始化子类中新的成员属性时,不可避免的需要重复的编写初始化从父类中继承而来的属性的代码。 这也不符合代码重用的原则,所以我们一般会采用 重载构造器(init()) + 重现父类构造器(super()) 的方式来解决这个问题。

In [32]: class Parent(object):
...: def __init__(self, name, age):
...: self.name = name
...: self.age = age
...: print "This is Parent."
...: In [33]: class Child(Parent):
...: def __init__(self, name, age, sex): # 初始化子类实例对象的属性
...: super(Child, self).__init__(name, age) # 初始化父类实例对象的属性
...: self.sex = sex
...: print "This is Child."
...: In [35]: p = Parent("fanguiju", "24")
This is Parent. In [36]: c = Child("fanguiju", "24", "man")
This is Parent.
This is Child. In [37]: c.name
Out[37]: 'fanguiju' In [38]: c.age
Out[38]: '24' In [39]: c.sex
Out[39]: 'man'

一般而言,我们会在子类的构造器中首先调用父类的构造器,当然这并不是强制的。只是为了我们能够在执行子类构造器的代码之前首先完成对父类属性的初始化,防止在调用从父类继承而来的属性时仍未初始化的问题出现。

使用 super() 内置函数的漂亮之处在于,我们不需要明确的给出父类的名字,交由解析器去自动的找到该子类的父类,并自动的传入 self 参数来完成绑定。这样能够让代码具有更高的灵活性,我们只需要改变子类的定义语句,就可以改变类的继承关系。

从标准类中派生(类方法的重载)

不可变数据类型的派生:定义一个精度为 2 的浮点数据类型

派生不可变标准类,经常需要重载类方法,而类方法的重载一般是重载 __new__(),也就是所谓的 真·构造器

class RoundFloat(float):
def __new__(cls, value):
return float.__new__(cls, round(value, 2)) # 将类对象 float 传入参数 cls In [44]: RoundFloat(1.1111111)
Out[44]: 1.11

真·构造器会自动的将类对象 RoundFloat 传入 cls 参数,类似于构造器__init__(self)

可变数据类型的派生:定义一个有序的字典数据类型

可变数据类型的派生可能不需要使用 构造器 或者 真·构造器 也能够实现。

class SortedDict(dict):
def keys(self):
return sorted(super(SortedDict, self).keys()) In [47]: for x in SortedDict((("name", "fanguiju"), ("age", 24), ("sex", "man"))):
...: print x
...:
age
name
sex

通过 SortedDict 生成的字典按照字母的顺序排序。

需要注意的是: 在 Python 2.2 之后将类和类型合并了,所以所有的数据类型都是一个类,反之我们定义了一个类也相当于定义了一个新的类型。

Python 进阶_OOP 面向对象编程_组合与继承的更多相关文章

  1. Python 进阶_OOP 面向对象编程_类和继承

    目录 目录 类 最简单的类 类方法 构造器 __init__ 创建一个类 实例化一个对象 调用实例的方法和属性 创建子类 使用 super 来调用父类的构造器 实例化子类对象 调用子类的属性和方法 类 ...

  2. Python 进阶_OOP 面向对象编程_实例属性和方法

    目录 目录 构造器和解构器 构造器 __init__ 真构造器 __new__ 解构器 __del__ 实例方法 Python 中的 抽象方法 实例属性 查看实例属性 实例属性和类属性的区别 访问不可 ...

  3. Python 进阶_OOP 面向对象编程_类属性和方法

    目录 目录 类属性 调用类属性 查看类属性 特殊的类属性 类方法 真构造器 __new__ 类属性 在理解类属性之前要先搞清楚 实例属性 和 函数属性 之间的区别: 1. 实例属性:指的是实例化类对象 ...

  4. Python 进阶_OOP 面向对象编程_静态方法和类方法

    目录 目录 静态方法 类方法 使用函数修饰符来声明静态方法和类方法 静态方法 静态方法仅是类中的函数, 不需要绑定实例, 也就是说静态方法的定义不需要传入 self 参数. 静态方法不属于类的某一个实 ...

  5. Python 进阶_OOP 面向对象编程_self 的实例绑定

    目录 目录 self 和绑定 调用非绑定的方法 self 和绑定 在 Python 中 self 变量是特殊的, 其用于在实例方法中引用该方法所绑定的实例, 换句话说就是 Python 在实例化对象时 ...

  6. Python进阶之面向对象编程

    面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...

  7. Python进阶之面向对象编程(二)

    Python面向对象编程(二) .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB& ...

  8. Python进阶之面向对象编程概述

    Python面向对象编程(一) .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB& ...

  9. python进阶(5):组合,继承

    前两天我们认识了面向对象也对面向对象有了初步的认识今天我们先会说一点组合的进阶,今天重点是继承. 一.组合 组合只有一个例子因为组合只作为上一章的补充内容 #老师 课程 生日 class Course ...

随机推荐

  1. Hibernate入门2

    实体类的编写规则 要求实体类的属性是私有的 要求实体类中的私有属性有公开的get和set方法(设置器和访问器) 要求实体类有一个属性作为唯一值(一般使用id值) 实体类属性建议不使用基本数据类型,使用 ...

  2. 在vue中设计一个客户签名的功能

    直接贴代码: <template> <div class="hello"> <p>签字:</p> <canvas id=&qu ...

  3. 常见前端面试题http部分

    1.常见http状态码 100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息 200 OK 正常返回信息 20 ...

  4. Linux 脚本运维总结之Shell script

    1. 本地变量和环境变量 变量类型 定义形式 声明位置 显示命令 作用域 本地变量 VARNAME=value 命令行或shell脚本 set (显示所有变量) 本用户,本进程 环境变量 export ...

  5. IT路上可能遇到的小需求资源汇总

    jar文件打包为window service tomcat打包为window service springboot的jar包打包为window service

  6. UVA10779 Collectors Problem 【迁移自洛谷博客】

    这是一道不错的练最大流建模的基础题. 这种题目审题是关键. Bob's friends will only exchange stickers with Bob, and they will give ...

  7. Linux中检查内存使用情况的命令

    Linux操作系统包含大量工具,所有这些工具都可以帮助您管理系统.从简单的文件和目录工具到非常复杂的安全命令,在Linux上没有太多不能做的事情.而且,虽然普通桌面用户可能不需要在命令行熟悉这些工具, ...

  8. python基础:4.请至少列举5个 PEP8 规范(越多越好)。

    1.变量命名规则: 不能与关键字重名,必须以数字字母下划线组成,且不能以数字开头 2.导包规则: # 推荐这样写 import random import sys # 不推荐这样写 import ra ...

  9. JDBC简单总结

    几种常用数据库的JDBC URL 对于 Oracle 数据库连接,采用如下形式: jdbc:oracle:thin:@localhost:1521:sid 对于 SQLServer 数据库连接,采用如 ...

  10. JavaSE---JDK提供的命令行工具---javap

    1.javap 1.1 javap是JDK自带的反解析工具: 1.2 作用:就是根据class字节码文件,反解析出    当前类   对应的code区(汇编指令).本地变量表.异常表和代码行偏移量映射 ...