Python面向对象编程
面向对象的编程产生的历史原因:由于面向过程编程在构造系统时,无法解决重用,维护,扩展的问题,而且逻辑过于复杂,代码晦涩难懂,因此,人们开始想能不能让计算机直接模拟现实的环境,以人类解决问题的方法,思路,习惯和步骤来设计相应的应用程序。于是,面向对象的编程思想就产生了本章中,我们主要介绍面向对象编程(OOP, object-orientedprogramming)的编程思想。首先介绍面向对象编程的一些基本概念、思想、特点以及使用的主要目的。然后探讨类的创建以及使用细则和方法,类属性、对象的初始化等等。
1. 面向过程和面向对象编程的基本概念
核心思想:面向对象的编程的主要思想是把构成问题的各个事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为。对象作为程序的基本单位,将程序和数据封装其中,以提高程序的重用性,灵活性和可扩展性。类是创建对象的模板,一个类可以创建多个对象。对象是类的实例化。
面向过程的编程:函数式编程。C程序等。根据业务逻辑从上到下写代码
面向对象的编程:C++. Java, Python等。对函数进行分类和封装,让开发“更快更好更强
类和对象:是面向对象中的两个重要概念
类:是对食物的抽象,比如:动物
对象:是类的一个实例,比如:猫,狗等
例如:动物可以对猫的特征和行为进行抽象,然后可以实例化为一台真实的动物实体出来。
猫(对象) 动物(类) 狗(对象)
设计思想:现实世界中,任何一个操作或者是业务逻辑的实现都需要一个实体来完成,也就是说,实体就是动作的支配者,没有实体,就肯定没有动作发生!
现在让我们思考下,关于喝水这个动作有哪些动词?
拿杯子、接水、抬手、张嘴
有动词就一定有实现这个动作的实体!
所谓的模拟现实世界,就是使计算机的编程语言在解决相关业务逻辑的方式,与真实的业务逻辑的发生保持一致!需要使每一个动作的背后都一个完成这个动作的实体!
因为任何功能的实现都是依赖于一个具体的实体的“动作|操作|行动”,可以看作是一个又一个的实体在发挥其各自的“能力”并在内部进行协调有序的调用过程!
当采用面向对象的思想解决问题时,可分为下面几步:
1分析哪些动作是由哪些实体发出的;
2定义这些实体,为其增加相应的属性和功能;
3让实体去执行相应的功能或动作。
使用OOP编程的主要目的:
面向对象是为了解决系统的可维护性,可扩展性,可重用性,我们再进一步思考,面向对象为什么能解决系统的可维护性,可扩展性,可重用性?
面向对象产生的历史原因有下面两点:
1、 计算机是帮助人们解决问题的,然而计算机终究是个机器,他只会按照人所写的代码,一步一步的执行下去,最终得到了结果,因此无论程序多么的复杂,计算机总是能轻松应付,结构化编程,就是按照计算机的思维写出的代码,但是人看到这么复杂的逻辑,就无法维护和扩展了。
2、 结构化设计是以功能为目标来设计构造应用系统,这种做法导致我们设计程序时,不得不将客体所构成的现实世界映射到由功能模块组成的解空间中,这种转换过程,背离了人们观察和解决问题的基本思路。
可见结构化设计在设计系统的时候,无法解决重用、维护、扩展的问题,而且会导致逻辑过于复杂,代码晦涩难懂。于是人们就想,能不能让计算机直接模拟现实的环境,用人类解决问题的思路,习惯,步骤来设计相应的应用程序?这样的程序,人们在读它的时候,会更容易理解,也不需要再把现实世界和程序世界之间来回做转换。
与此同时,人们发现,在现实世界中存在的客体是问题域中的主角,所谓客体是指客观存在的对象实体和主观抽象的概念,这种客体具有属性和行为,而客体是稳定的,行为不稳定的,同时客体之间具有各种联系,因此面向客体编程,比面向行为编程,系统会更稳定,在面对频繁的需求更改时,改变的往往是行为,而客体一般不需要改变,所以我们就把行为封装起来,这样改变时候只需要改变行为即可,主架构则保持了稳定。
于是面向对象就产生了。
然而人们追求的系统可维护性,可扩展性,可重用性又是怎么在面向对象中体现出来的呢?
首先看看面向对象的三大特征:
封装:找到变化并且把它封装起来,你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分,这是所有设计模式的基础,就是封装变化,因此封装的作用,就解决了程序的可扩展性。
继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,因此也解决了系统的重用性和扩展性,但是继承破坏了封装,因为他是对子类开放的,修改父类会导致所有子类的改变,因此继承一定程度上又破坏了系统的可扩展性,所以继承需要慎用,只有明确的IS-A关系才能使用,同时继承在在程序开发过程中重构得到的,而不是程序设计之初就使用继承,很多面向对象开发者滥用继承,结果造成后期的代码解决不了需求的变化了。因此优先使用组合,而不是继承,是面向对象开发中一个重要的经验。
多态:接口的多种不同的实现方式即为多态。接口是对行为的抽象,刚才在封装提到,找到变化部分并封装起来,但是封装起来后,怎么适应接下来的变化?这正是接口的作用,接口的主要目的是为不相关的类提供通用的处理服务,我们可以想象一下。比如鸟会飞,但是超人也会飞,通过飞这个接口,我们可以让鸟和超人,都实现这个接口,这就实现了系统的可维护性,可扩展性。
因此面向对象能实现人们追求的系统可维护性,可扩展性,可重用性。面向对象是一种编程思想,起初,“面向对象”是专指在程序设计中采用封装、继承、多态等设计方法,但面向对象的思想已经涉及到软件开发的各个方面,比如现在细分为了面向对象的分析(OOA),面向对象的设计(OOD),面向对象的编程实现(OOP)
2类
2.1类的声明和创建
Python 类使用class关键字来创建。简单的类声明可以是关键字后紧跟类名。类声明与函数声明很相似,头一行用一个相应的关键字,接下来是一个作为它的定义代码体,如下所示:
ClassClassName (bases)
Class documentation string # 类文档字符串
class_suite #类体
1使用关键字Class定义一个类,并且类名的首字母要大写;
2当程序员需要创建的类型不能用简单类型表示时就需要创建类;
3类把需要的变量和函数组合在一起,这种包含也称之为“封装”。
Class 类名:
成员变量
成员函数
对于Python来说,声明与定义类没什么区别,因为他们是同时进行的,定义(类体)紧跟在声明(含class关键字的投行【header line】)和可选的文档字符串后面。同时,所有的方法必须同时被定义。
例1:
ClassRen:
name = “人
high = “cm”
wight = “kg”
def play(self)
print “LOL”
A= Ren ()
A.name= “浪辉”
A.high= :170cm”
A.weight= “100kg”
A.home= “新乡”
类的方法中至少有一个参数self
定一个类:
ClassRen:
name = “人
high = “cm”
wight = “kg”
def play(self)
print “LOL”
name,high,weight为成员变量。Play为成员函数(方法)。
2.2对象的创建:
创建对象的过程称之为实例化;当一个对象被创建后,包含三个方面的特性:对象的句柄、属性和方法。
句柄用于区分不同的对象
对象的属性和方法与类中的成员变量和成员函数对应
例:平时我们打开一个文件
Input= file (LOL, ‘r’)
这个过程既是一个实例化过程,file就是一个类,只不过是系统定义的,不是我们自己定义的
A= Ren ()
即实例化一个对象,A就有属性和方法即name等等,我们也可对属性进行重新赋值即:
A.name= “浪辉”
A.high= :170cm”
A.weight= “100kg”
每个对象又有不同的属性,类中没有时,可在对象中添加属性即
A.home= “新乡”
if __name__ == ‘__main__’: 当这段程序直接实例化这个对象时可以执行,当被当做一模块模块被其它程序调用时无法被执行,这点在定义函数部分介绍有。
2.3 Python对象的体系结构
经典对象:内部由属性和方法组成,属性可以是数据类型也可以是函数类型,如属性为函数则该属性就是一个方法。
类的内部方法-Python类当中提供了一些内部方法,方法名由前后两个下划线加字母组成。例如Python的构造函数__init__
新型对象:定义新型对象,必须继承object方法,class_name(object) 新型对象提供了对类方法和静态方法的支持。
2.3类的属性
例2:
Class Digits ():
A=10
S = Digits ()
B = Digits ()
#第一种情况
Print S.A, B.A Digits.A
#第二种情况
S.A +=2
Print S.A, B.A Digits.A
#第三种情况
Print S.A, B.A Digits.A
#结果
情形1的结果是:10 10 10;
情形2的结果是:12 10 10;
情形3的结果是:12 13 13;
首先为什么会有这个问题呢?
因为aaa属性被称为类属性,既然是类属性,那么根据从C++/Java这种静态语言使用的经验来判断,类属性应该是为其实例所共享的。很自然的,既然是共享关系,那么从类的层次改变aaa的值,自然其实例的aaa的值也要跟着变化了。
可是情形3的情况却说明,上面的说法是错的。
错哪里呢?
Python属于动态强类型的语言,在很多地方和静态语言不同,因此,不能把静态语言的规则套到动态语言上来。其中,类属性就是一个很好的例子。
对于属性,我们通常采用类.属性或实例.属性的形式调用。
对于属性的设置我们通常采用类.属性= 值或实例.属性= 值的形式
上例中S.aaa += 2等价于S.aaa = obj1.aaa + 2,这句话包含了属性获取及属性设置两个操作
即Python中属性的获取和设置的机制与静态语言是不同的,正是背后机制的不同,导致了Python中类属性不一定是为其实例所共享的,即Python属性遵循三个规则:
1. Python中属性的获取是按照从下到上的顺序来查找属性;
2. Python中的类和实例是两个完全独立的对象;
3. Python中的属性设置是针对对象本身进行的;
另外,类由属性和方法组成,类的属性是对数据的封装,类的方法则是对类的行为的封装。类的属性按使用范围分为共有属性和私有属性,类的属性范围取决于属性的名称
共有属性:所有共有属性就是类中和类外调用的属性
私有属性:不能被类以外的函数调用(可以通过instance._classname__attribute方式访问,但只用于调试程序。定义方式以“__”双下划线开始的成员变量就是私有属性,否则是公有属性)
内置属性:由系统在定义类的时候默认添加的,由前后两个下划线组成__doc__等等
例2:
ClassRen:
name = “人
high = “cm”
wight = “kg”
__money = “doller”
def play(self) #self名字随意设定
print “LOL”
print self.__money
print self.name
A= Ren ()
A.name= “浪辉”
A.high= :170cm”
A.weight= “100kg”
A.home= “新乡”
Ren.high
High,weight,name为公有属性,__money为私有属性,只能在类内部通过内部方法使用。这样实现对数据的隐藏,便于封装。
另外,可通过A.__Ren__money的方法访问私有属性,但是一般不建议使用,通常在测试的时候用。
一些特殊属性即内置属性:
C.__name__:类C的名字(字符串)
C.__doc__:类C的文档字符串
C.__bases__:类C的所有父类构成的元组
C.__dict__:类C的属性
2.3类的方法
例3:
ClassRen:
name = “人
high = “cm”
wight = “kg”
__money = “doller”
def play(self) #self名字随意设定
print “LOL”
print self.__money
print self.name
print self.__say()
def __say(self):
print“I love xxx”
def School (self)
print“湖大皇子”
b = classmethod(School)
def Student()
print“大鹏”
c = staticmethod(Student)
A= Ren ()
A.name= “浪辉”
A.high= “170cm”
A.weight= “100kg”
A.home= “新乡”
Ren.high
公有方法:类的方法共有方法不能被类直接调用,需要实例化对象去调用
私有方法:不能被外部的类和方法调用,私有方法的定义和私有属性的定义都是一样的,在方法的前面加上”__“双下划线就可以了;
类方法:被classmethod()函数处理过的函数,能被类所调用,也能被对象所调用(是继承的关系);
静态方法:相当与”全局函数“,可以被类直接调用,可以被所有实例化对象共享,通过staticmethod()定义静态方法,不需要”self“语句
上例中的def play(self)为公有方法,可在类中被调用或被实例化调用,但是不能被类直接调用。def __say(self) 为私有方法,只能在类中被调用。def School (self)是类方法,可被类直接调用,也可被对象调用。def Student()为静态方法,用法同类方法一样。
类方法和静态方法使用上是相同的,但是原理上是有区别的。
1静态方法不需要加self
2静态方法是调用时会预先将类中用到属性和方法进行加载,而类方法则是随调随用。因此类方法相比静态方法不占资源,但是速度不及静态方法。
3静态方法调用类中的属性时需要 类名.属性
另外,Python有一个装饰器功能,即在一个方法前加上”@classmethod“或”@staticmethod“可快速将此方法设置为类方法或静态方法。
参考资料:
Python核心编程
https://www.cnblogs.com/seesea125/archive/2012/04/03/2431176.html
https://www.cnblogs.com/zln1021/p/6070591.html
https://blog.csdn.net/jianyuerensheng/article/details/51602015
http://python.jobbole.com/85100/