目录
回顾
前面几天已经学到了Python的基础知识了,有那些知识我们按照面向过程的方法进行编程了。
但是python也给我们提供了面向对象编程的元素机制。面向过程和面向对象在于方法使用不在于编程语言特点。
从c转入java的也是拿着面向对象的语言写着面向过程的程序。如果你领悟了面向对象那么你即使使用了c也可以开发出面向对象的
程序,当然你要灵活使用指针来自己去造面向对象元素的轮子。
面向对象编程的元素
初阶面向对象
从C++学起再到java ,一直离不开面向对象.
基础阶段的面向对象编程元素 : 封装 , 继承 , 多态 。
可能大家对继承比较偏爱,这三大法宝里面其实 封装 是最难的,也是最灵活的,
因为有个粒度和度的把握,也是面向对象艺术编程的体现。
多态活化了对象,使面向对象多姿多彩。
反而是继承,初阶面向对象我们使用继承来扩展,疑问这是面向对象的精髓,恰恰误导了我们。
因为封装,提出了类的概念,但是类只是实现封装的一种机制而已不是唯一。
从类我们出现了继承的概念,那么继承和类是出现在一起的,类是为了去描述定义的,那么继承也是用它来进行概念的描述
更贴近它的用途。
封装最不好把控,可大可小,依据的角度也很多。所以好好领悟封装的奥义就把握了面向对象的命脉。
高阶面向对象
从初阶过来习惯了使用 继承来做扩展,但是庞大的继承层次反而消掉了灵活性。
到高阶将要学会多种设计思想并且使用它们指导面向对象开发。
第一个就是使用 组合来代替继承做扩展
第二个 面向接口编程
第三个 设计模式中的几个思想
第四个 AOP
从这里围绕着如何对程序的扩展做到 开闭 目的展开
至此需要知道,实现封装的手段很多,实现继承的手段也很多(了解下nodejs中的prototype)
那么python也有自己的类和自己的封装机制
有着C体系的面向对象基础学习python的这个是很简单的,触类旁通
Python的类 class
使用 class 关键字来定义一个类,非常的简单,注意范围仍然是 从 冒号开始的
定义对象
定义对象和我们的 C++使用类似,看样子是调用了构造函数,但是呢
python自己有自己的机制,后面我们会说道 __inti__才是 主因
目前我们就记住 创建对象使用 C++那种构造函数调用即可
空类具有的属性
前面说过 查看属性使用 dir()函数进行
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
空类具有的属性也就是这个对象具有的属性。
定义成员方法
C++和java中的成员方法是被封装于类中啊,没用static修饰的函数,每个成员方法内部有一个 this 的对象
其实呢这个this 是隐藏的,可以写可以不写,最终调用该方法的时候会将 发起消息的对象 传给this
这个就实现了当前对象调用方法执行的数据都是当前对象的。
我们如果再拓展下,如果将这个方法中的this 作为方法的一个参数,也是可以实现的
那么python就用了这种方式,但是python中不是this , 而是 self
还有一点,python的类里面可以定义成员方法也可以定义普通的函数的
所以python定义的成员方法就 很突出明显 ,只要在函数的参数中第一个加入一个 self
调用成员方法的对象会把当前调用的对象传递给self,当然调用的时候这个参数是可以忽略传值的。
但是注意: self 只是约定俗称的,并没有必须规定使用self,这里总是将第一个参数给了绑定对象的。
如果我们第一个参数使用 this 也是可以的。
如果我们的函数没有self这个参数,那么这个函数就是类的函数了。对象自己是无法进行调用的。
例如下面我们在 命令行中定义一个类
(命令行中定义的时候,注意缩进,第二行笔第一行多一个空格
如果是def内部,那么语句比 def 中的d 右移一个空格
空格不对会报语法错误的
)
下面这个类No 定义了 两个 属性,一个是成员方法,一个是类的方法
可以看到在外部对象是无法使用这个类的方法的.
python的构造函数
C++和Java中的构造函数定义是函数名字必须和类的名字相同
但是python呢,使用固有的函数名字 __inti__来表示一个构造函数,但是也需要和成员方法一样加入 self这个额外参数
但是外部调用的时候我们要用 C++创建对象的方式使用
self的使用
python对象的属性定义不像 C++和 Java , 需要在类中定义对象的成员属性。
python的成员属性定义在了 self 的属性中
然而 这种定义简直和 javascript 对象属性的定义一样,灵活,不拘一格。
只要给 self 增加属性就可以了,self的属性就是这个类创建出对象的属性
这个加大了对象创建的灵活度,属性增加很自由的。
对象所需要的属性我们在 构造函数 __init__的时候进行设置增加就可以了
相对的隐藏性
Java等语言中我们可以通过 public , private 等访问控制符来控制属性包括方法的访问
但是python 呢,我们只能做多相对的隐藏。我们可以 给一个对象里面设置 getter/setter 方法
但是,self里面的属性,对象使用的时候仍然是可以直接访问的,因为并没有 如同Java类似的权限进行限制
例如下面这个例子,我们使用的时候,直接可以访问
(这里使用的时候我们 使用了 import ,这个是模块的概念,Java里面有类似的包机制,我们目前认为
py文件要使用里面定义的东西的时候需要导入这个文件才可以.
这个例子 我们的 py 文件名是 No ,No.py
恰巧的是 文件名和类名是一样的,这个不是必然的要求)
我们可以看到 可以直接访问这个对象的属性的,前提你要是知道这个对象有这么个属性的话
我们如果要进行隐藏,也只能是 控制住这个属性不被外部知道,通过api方式控制住
外部使用者只能从api中知道可以使用的属性
python封装提供的技巧
虽然python的隐藏是相对的,但是python仍然提供了一个技巧可以去做一定程度上的隐藏
技巧: 类的成员命名以 双下划线开头的 python 会将他们隐藏起来
这就是为啥我们看到python中有好的 以 __开头的属性了
例如下面的例子中:
成员属性 __name是被隐藏起来了不能通过对象进行访问的的
只能通过 getname() 方法来进行的
但是 name2 属性就是可以在外界进行访问的
因此如果想和java 一样隐藏属性的话那么就要使用 __进行开头的命名
这样就可以做到表明的隐藏
注意这只是表面上的隐藏的,双下划线开头的只是一个技巧不是机制
如果要访问这种双下划线开头的属性有其他的方法的
可以使用 对象._类名双下滑下开头的变量名 就可以进行访问的只是一般不这么进行的而已
例如:
类的继承
没有如同java中优雅的 extend 来表示继承
python类的继承方式很简单,通过如下的方式进行继承
class 类名(父类名) :
如果需要使用父类的东西,就要使用 父类.属性
__init__方法
我们知道子类的构造函数定义时候必须要调用父类的构造函数
因此 对于 __init__方法来说,子类的需要调用父类的__init__
调用父类的 __init__方法的时候,注意 self也要传递进去
然后对于重写方法,只要覆盖定义一遍就行
例如下面的代码:这里用到了模块
模块目前你认为就是一个python的源文件就行
Man继承了Person,然后再初始化的时候调用了父类的__init__
因此Man也具有Person的getName,getAge 等方法
多继承
到目前为止我们发现python好多类的使用上是和C++类似的,C++可以继承多个类,Java只能继承一个类
那么Python呢,Python也是可以继承多个类的
class 类名(父类1,父类2,父类3,...) :
但是多继承带来的困扰就是 父类中 方法重名的问题如何进行辨析
当继承多个父类时,如果父类中有相同的方法,那么子类会优先使用最先被继承的方法
顶层父类
和java一样,python也有一个顶层的父类,称为 Object
例如上面定义的类 No ,类属性中有一个 __bases__属性,表示了其父类
我么再做一个 No的子类,看看这个__bases__属性
我们再做一个 No2,继承多个类,我们发现__bases__属性
super()函数
C体系中是super 关键字表示了父类
但是在python中要使用 super() 函数来表示父类
super()调用和直接使用父类名调用的区别
使用 父类名调用的时候,每次执行都会重新定义
但是super()只会定义一次
还有很多的区别需要我们发掘。
pass 关键字使用
如果一个类内部不想做任何定义,直接使用pass 就可以了
Python多态
C体系中的多态,总是通过如下的方式
父类 对象 = new 各种子类
然后通过后面的实际分配的子类对象的地址的数据从而达到多态
也就是前面的的对象调用统一的api方法时候,根据实际对象呈现不同的效果
但是Python中我们定义对象并不需要这个 类型的声明限制
因此 只能 靠着 api的呈现,由于没有 类型的约束限制,所以这种多态只要对象具有约定的方法就可以进行.
没有类型约束有坏处,我们编写的时候不能使用父类或者接口做统一的入参约束,
没有类型约束也有好处,定义对象自由,没有类型约束
但我们依然可以 像Java一样画葫芦 来进行 多态的实现
例如上面例子中给 people 类中增加 marrige成员方法,这样
man 和 women 都覆盖这个方法之后,他们各自 生产的对象 都有 marrige方法
进行调用时候就呈现出了 多态。
函数式接口的Python表达
Java8中出现的函数式接口给使用 Java 的 lambda表达式提供了实现机制
通过函数式接口我们可以 规范行为。