《大话数据结构》的作者程杰在博客园也有博客,网址是:http://cj723.cnblogs.com/
面向对象编程(OOP)
1、程序 = 指令 + 数据
- 代码可以选择以指令为核心或以数据为核心进行编写
2、两种范型
以指令为核心:围绕“正在发生什么”进行编写
- 面向过程编程:程序具有一系列线性步骤;主题思想是代码作用于数据
以数据为核心:围绕“将影响谁”进行编写
- 面向对像编程(OOP):围绕数据及为数据严格定义的接口来组织程序,用数据控制对代码的访问
面向对象的核心概念
1、所有编程语言的最终目的都是提供一种抽象方法
在机器模型("解空间"或“方案空间”)与实际解决的问题模型("问题空间")之间,程序员必须建立一种联系
- 面向过程:程序 = 算法+数据结构
- 面向对象:将问题空间中的元素以及它们在解空间中的表示物抽象为对象,并允许通过问题来描述问题而不是方案
- 可以把实例想象成一种新型变量,它保存着数据,但可以对自身的数据执行操作
2、类型由状态集合(数据)和转换这些状态的操作集合组成
类抽象
- 类:定义了被多个同一类型对象共享的结构和行为(数据和代码)
- 类的数据和代码:即类的成员
- 数据:成员变量或实例变量
- 成员方法:简称为方法,是操作数据的代码,用于定义如何使用成员变量;因此一个类的行为和接口是通过方法来定义的
- 方法和变量:
- 私有:内部使用
- 公有:外部可见
类是抽象的,实例是具体的,先去设计类,才能实例化
面向对象的程序设计方法
1、所有东西都是对象
2、程序是一大堆对象的组合
- 通过消息传递,各对象知道自己该做什么
- 消息:即调用请求,它调用的是从属于目标对象的一个方法
3、每个对象都有自己的存储空间,并可容纳其他对象
- 通过封装现有对象,可以制作成新型对象
4、每个对象都属于某一类型
- 类型,也即类
- 对象是类的实例
- 类的一个重要特性为“能发什么样的消息给它”
5、同一个类的所有对象都能接收相同的消息
对某个对象的方法的调用就是给它发消息,
对象的接口
1、定义一个类后,可以根据需要实例化出多个对象
2、如何利用对象完成真正有用的工作?
- 必须有一种方法能向对象发出请求
- 每个对象仅能接受特定的请求
- 能向对象发送的请求由其“接口”进行定义
- 对象的“类型”或“类”则规定了它的接口形式
类:将同一种具体事物的共同特性抽象出来的表现
状态和转换这些状态的操作(方法)
数据:即状态
变量:就是类属性
方法:即转换状态的操作
函数:操作变量引用的数据的代码
方法是类的组成部分,由类定义的,属性也是在类中定义的,但数据本身是类被实例化对象时被赋值的,
类定义的有数据结构和方法,这些方法能够对数据结构中的变量做操作,而变量还没有值,只在被实例化后才有值。
想调用一个方法,不能用类来调用,而应该通过实例来调用,因为方法是用来操作数据的,只有实例中才有数据,方法只有在类实例化出来之后,由对象或实例来进行调用,这种方式叫做绑定
类间关系
1、依赖("uses-a")
一个类的方法操纵另一个类的对象
2、聚合("has-a")
类A的对象包含类B的对象
3、继承("is-a")
描述特殊与一般关系
依赖关系:list.pop()方法操作了l1 = ['a','b','c']列表中的c元素,而c属于str中的对象,因此如果python不支持字符串的话,那么在列表中就没办法使用字符串当元素了。列表可以依赖于字符串的。列表也依赖数值,因为列表也包含数值。
聚合:l1 = ['a','b','c'] 比如类l1的对象包含了类a的对象,就是聚合,l1是列表类的对象,a是字串类的对象
继承:
面向对象编程的原则
面向对象的模型机制有3种原则:封装、继承及多态
1、封装(Encapsulation)
隐藏实现方案细节
将代码及其处理的数据绑定在一起的一种编程机制,用于保证程序和数据不受外部干扰且不会被误用
2、 继承(Inheritance)
- 一个对象获得另一个对象属性的过程;用于实现按层分类的概念
- 一个深度继承的子类继承了类层次中它的每个祖先的所有属性
- 超类、基类、父类
- 子类、派生类
3、多态性(Polymorphusm)
- 允许一个接口被多个通用的类动作使用的特性,具体使用哪个动作与应用的场合相关
- “一个接口,多个方法”
- 用于为一组相关的动作设计一个通用的接口,以降低程序的复杂性
对某个类而言,当传递给它的是不同数据,它就可以完成不同运算,为了实现同一个目的定义同一个接口,但却又多种不同的实现,能够自动判断调用哪种方法实现的机制就叫做多态。
python类和实例
1、类是一种数据结构,可用于创建实例
一般情况下,类封装了数据和可用于该数据的方法
2、python类是一个可调用对象,即类对象
3、python2.2之后,类是一种自定义类型,而实例则是声明某个我自定义类型的变量
4、实例初始化
- 通过调用类来创建实例
instance = ClassName(args...)
- 类在实例化时可以使用__init__和__del__两个特殊的方法
在内存中定义一个实例,实例运行起来要在内存中存放,所以在内存中存放的类代码,把类实例化就成了对象,这个对象是这个类的具体实现,设计汽车要有概念,概念应该由存放的地方,
类的存储就叫类对象,而把类对象调用之后,实例化出来的结果叫实例对象
python中创建类
1、python使用 class 关键字创建类,语法格式如下:
class ClassName(bases):
‘class documentation string’
class_suite
- 超类是一个或多个用于继承的父类的集合
- 类体可以包含:声明语句、类成员定义、数据属性、方法
- 注意:
- 如果不存在继承关系,ClassName后面的"(bases)"可以不提供
- 类文档为可选
2、class语句的一般形式:
class ClassName(base):
data = value //定义数据属性
def method(self,...): //定义方法属性
self.member = value //定义实例属性
data和method都是类成员,data是属性成员,method是方法成员,
def method(self,...): 方法就是函数,但是方法首个参数self必须要有,而且得为self,因为所有得方法调用都应该通过实例来调用,意味着这个方法的主要目的是对实例自身来进行操作的,如下图:
两个列表l1和l2,如果用l1.pop(),一定是对l1列表做操作,而不是对l2做操作,因为.pop()方法一定是对调用它的对象自身做操作。l1和l2都是列表,为什么l1调用方法时操作的不是列表l2呢,
方法是列表的方法,不是这两个特定列表实例的方法,为什么调用方法时不是对列表做操作,而是对特定实例做操作?就是靠self,它明确说明这个操作一定是对自身这个实例所表示的属性做操作
例子
- python中,class语句类似def,是可执行代码;直到运行class语句后类才会存在
- class语句内,任何赋值语句都会创建类属性
- 每个实例对象都会继承类的属性并获得自己的名称空间
python类方法及调用
1、实例(对象)通常包含属性
- 可调用的属性:方法
- object。method()
- 数据属性
2、在OOP中,实例就像是带有“数据”的记录,而类是处理这些记录的“程序”
- 通过实例调用方法相当于调用所属类的方法来处理当前实例
类似 instance.method(args...) 会被自动转换为class.method(instance,args...)
如前面的例子,x.display()会被自动转换为firstClass.display(x),即调用类的方法来处理实例x
- 因此,类的每个方法必须具有self参数,它隐含当前实例之意
- 在方法内对self属性做赋值运算会产生每个实例自己的属性
- python规定,没有实例,方法不允许被调用,此即为“绑定”
python类和实例的属性
1、class语句中的赋值语句会创建类属性,如前面的例子中的spam
2、在类方法中对传给方法的特殊参数self进行赋值会创建实例属性
python构造器
1、创建实例时,python会自动调用类中的__init__方法,以隐性地为实例提供属性
__init__方法被称为构造器
如果类中没有定义__init__方法,实例创建之初仅是一个简单的名称空间
实例属性最便捷的方式就是重载构造器
__varname__():会被python解释器自动调用的
a + b = a.__add__(b)
析构器:就是销毁一个实例
第二个例子
类的特殊属性
1、可以使用类的__dict__字典属性或python内置的dir()函数来获取类的属性
实例属性
1、实例仅拥有数据属性(严格意义上来说,方法是类属性)
- 通常通过构造器“__init__”为实例提供属性
- 这些数据属性独立于其他实例或类
- 实例释放时,其属性也将被清除
2、内建函数dir()或实例的特殊属性__dict__可用于查看实例属性
3、实例的特殊属性
python类方法中的可用变量
1、方法的可用变量
- 实例变量:指定变量名称及实例自身进行引用
- self.变量名
- 局部变量:方法内部创建的变量,可直接使用
- 类变量(也称静态变量):通过指定变量名与类名进行引用
- 类名.变量名
- 全局变量:直接使用
定义一个类,类内部有成员变量,有一个构造器方法,目的是构造一个数据
对类实例化,第一个实例i1中有一个属性d1,d1的值应该指向类的值,insdata由于是自己定义的,所以是指向自己的值
第二个实例实例化i2是d1的值也是指向类的值,insdata也是自己定义的,
每个实例中其实就是两个变量,如果第一个实例i1的d1要修改,是不支持原处修改的,就意味着i1.d1改后是指向另外一个位置的,
但i2.d1是不受影响的。如果把c1的属性修改了即c1.d1=‘new value’,那么i2.d1也跟着修改了
类的继承
1、继承描述了基类的属性如何“遗传”给派生类
- 子类可以继承它的基类的任何属性,包括数据属性和方法
- 一个未指定基类的类,其默认有一个名为object的基类
- python允许多重继承
2、创建子类
创建子类时,只需要在类名后跟一个或从其中派生的父类
class SubClassName(ParentClass1[,ParentClass2,...]) //可以有多个父类
'optional class documentation string'
class_suite
python类继承的例子
子类可以继承它的基类的任何属性,包括属性和方法
子类通过父类继承构造器
python类的继承和属性搜索
1、python中几乎所有的属性的获取都可以使用“object.attribute”的格式
不过,此表达式会在python中启动搜索------搜索联系的树
2、class语句会产生一个类对象,对class的调用会创建实例,实例自动连结至创建了此实例的类
类连结至其超类的方式
- 将超类列在类头部的括号中,其从左至右的顺序会决定书中的次序
- 由下至上,由左至右
继承方法专用化
1、继承会先在子类寻找变量名,然后才查找超类,因此,子类可以对超类的属性重新定义来取代继承而来的行为
- 子类可以完全取代从超类继承而来的属性
- 也可以通过已覆盖的方法回调超类来扩展超类的方法
类、实例和其他对象的内建函数
1、issubclass()
布尔函数,判断一个类是否由另一个类派生,
语法:issubclass(sub,sup)
2、isinstance()
布尔函数,判断一个对象是否式给定类的实例,
语法:isinstance(obj1,class_obj2)
3、hasattr()
布尔函数,判断一个对象是否拥有指定的属性,
语法:hasattr(obj,'attr')
同类的函数还有getattr()、setattr()和delattr()
4、super()
在子类中找出其父类以便于调用其属性
一般情况下仅能采用非绑定方式调用祖先类方法
而supper()可用于传入实例或类型对象,
语法:supper(type[,obj])
运算符重载
1、运算符重载是指在方法中拦截内置的操作---当类的实例出现在内置操作中,python会自动调用自定义的方法,并且返回自定义方法的操作结果
- 运算符重载让类拦截常规的python运算
- 类可重载所有python表达式运算符
- 类也可重载打印、函数调用、属性点号运算等内置运算
- 重载使类实例的行为像内置类型
- 重载通过提供特殊名称的类方法实现
2、运算符重载并非必须,并且通常也不是默认的
什么是运算符重载:每一种类型都有其内置的独特的有解释器自动装载的自动调用的属性
基于特殊的方法定制类
除了__init__和__del__之外,python类支持使用许多的特殊方法
- 特殊方法都以双下划线开头和结尾,有些特殊的方法由默认行为,没有默认行为的是为了留到需要的时候再实现
- 这些特殊方法是python中用来扩充类的强大工具,它们可以实现
- 模拟标准类型
- 重载操作符
- 特殊方法允许类通过重载标准操作符+,*,甚至包括分段下标及映射操作[]来模拟标准类型
常见的运算符重载方法
可调用对象: 之所以是可调用对象,因为它们都有共同的调用接口:__call_()
- 函数
- 内置函数
- 自定义函数
- def
- lambda
- 类
- 类方法(特殊的函数)
函数的属性:
- __doc__
- __name__
- __dict__
- __code__
- __globals__
方法的属性:
- __doc__
- __name__
- __class__:方法所属的类
- __func__:实现该方法函数对象
- __self__:调用此方法的实例
内置函数:
- __doc__
- __name__
- __self__
类:
- __doc__
- __name__
- __bases__
- __dict__
- __modules__:定义当前类的模块名称
实例:
- __class__
- __dict__
对象都有特殊方法:
- __init__
- __new__
- __del__
- __dir__()
- __add__()
- __ge__() a >= b 相当于 a.__ge__(b)