Python是一个功能非常强大的编程语言、玩法很多。但是在我们享受Python带来的编程乐趣之前,我们必须要掌握OOP编程技能,这样才能够更好的应用Python高级功能。OOP我曾经在《Python_基础知识储备》中提到过,这次我们再全面而详细的回炉一次。
在计算机领域中,对象是人们要进行研究的任何事物,它表示具体的事物,此外也表示抽象的规则、计划或事件。对象是数据和操作数据的方法的结合。
对象的状态(数据):一个对象用数据值来描述它的状态,也称为对象属性。
对象的行为(操作):对象的行为用于改变对象的状态,也称为对象方法。
对象唯一性(句柄):即对象名称,每个对象都有自身唯一的标识,通过这种标识,可找到相应的对象。在对象的整个生命期中,它的标识都不改变。
对象之间的通信:对象之间进行通信的结构叫做消息,当一个消息发送给某个对象时,消息应该包含下面几种信息:
1. 接受消息的对象名(目的对象名)。
2. 接收对象去执行某种操作的信息(调用方法名)。
3. 发送给该对象的参数,参数可以是该对象私有的变量名,或者是所有对象都知道的全局变量名。
4. 发送给该对象的消息名(源消息名)。
具有相同特性(数据元素)和行为(方法)的对象的抽象就是类,类本质是一种数据结构。对象的抽象是类,类的具体个性化就是对象。
类的属性:它是对象的状态的抽象,用数据结构来描述类的属性,也称为成员属性。
类的方法:它是对象的行为的抽象,用操作名和实现该操作的方法来描述,也称为成员方法。
类的结构:在客观世界中有若干类,这些类之间有一定的结构关系。通常有下面两种主要的结构关系:
1. 一般/具体结构称为分类结构,也可以说是is a关系。用来描述在继承中子类与父类的关系,即一个派生子类的实例化对象是其父类的一个”例子”。所以有”is a”的关系。
2. 整体/部分结构称为组装结构,它们之间的关系是has a关系。”组合”是实现继承的方式之一,在”组合”继承中,一个子类可以有多个父类,即一个子类”has a”一个父类。
面向对象(Object Oriented,OO),面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。在Python中一切皆对象,通过面向对象的思维,将现实世界的事物都抽象成为对象,将现实世界中的关系抽象成类、继承,帮助人们实现对现实世界的抽象与数字建模。通过面向对象的方法,更利于用人理解的方式对复杂系统进行分析、设计与编程。而且,面向对象能有效提高编程的效率,通过封装技术,消息机制可以像搭积木的一样快速开发出一个全新的系统。其中对象是类的集合,面向对象思维将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和可扩展性。
面向对象编程踏上了进化的阶梯,增强了结构化编程,实现了数据与动作的融合:数据层和逻辑层现在由一个可用以创建这些对象的简单抽象层来描述。
OO后来又扩展为OOA/OOD/OOP:
OOA(Object Oriented Analysis)面向对象分析:即根据抽象关键的问题域来分解系统。
OOD(Object Oriented Design)面向对象设计:是一种提供符号设计系统的面向对象的实现过程,它用非常接近实际领域术语的方法把系统构造成“现实世界”的对象。
OOP(Object Oriented Programming)面向对象编程:是一种程序设计范型,同时也是一种程序开发的方法,实现OOP的程序希望能够在程序中包含各种独立而又互相调用的对象,每一个对象又都应该能够接受数据、处理数据并将数据传达给其它对象,因此每个对象都可以被看作一个小型的”机器”,而整个程序系统就是由这些小机器相互协助、组合构建起来的。
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口(接口函数)和访问方法。在类的设计时,为数据提供了相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
封装的目的:就是把客观事物抽象并封装成类,封装好的每一个类都是独立的,而由类实例化创建的对象亦是如此。对象之间只能通过 消息 这种通信结构来实现通讯。这样做能够实现:类自己的数据和方法只让可信的类或者对象操作,对不可信操作的进行信息隐藏。为类和对象中的方法、属性提供了安全的保障。
封装的作用:一个类就是一个封装了数据以及操作这些数据的方法的逻辑实体。在一个对象内部,某些方法或属性可以是私有的(实例化对象将类中定义的私有属性和方法个性化),它们不能被外界访问。通过封装,为对象内部的数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。即隐藏类的功能实现的细节,使代码模块化(松耦合、高内聚)。
封装的意义:一般的,我们会认为程序=数据结构+算法,在OOP中,因为封装的存在,我们会将这条等式转化为程序=对象+消息。
#fileName =encapsulation.py
#coding = utf8
_metaclass_ = type #确定使用新式类;新式类必须继承Object基类,并且新式类提供了对类的方法和静态方法的支持。
class TestEncapsulation(object):
def __init__(self): #构造函数,在程序启动时自动调用,一般作为实例化对象的初始化
self.statement = "Start"
print(self.statement)
def accessibleMethod(self): #绑定方法,能在类外部被访问;绑定方法一定要有self形参,self表示类本身,用于传递当前类中的成员属性和方法,但是self不接收实参。
print("You can access this method!",end='')
print(self.statement)
print("the secert message is:")
self.__inaccessible()
def __inaccessible(self): #私有方法,不能在类外部被访问;方法名以'__'开头
print("You cannot access!")
@staticmethod
def staticMethod():
#self.accessibleMethod() #静态方法无法直接调用绑定方法。因为静态方法没有self形参
print("This is a static method")
def setStatement(self,statement): #访问器
self.Statement = statement
def getStatement(self): #访问器
return self.Statement
statement = property(fget = getStatement,fset = setStatement) #自动调用访问器设定属性值
#property(fget=None, fset=None, fdel=None, doc=None) --> fget is a function to be used for getting an attribute value, and likewise fset is a function for setting, and fdel a function for del'ing, anattribute. Typical use is to define a managed attribute x:
name = "Jmilk"
if __name__ == "__main__":
test = TestEncapsulation() #类的实例化调用
test.accessibleMethod()
test.staticMethod()
#test.__inaccessible() #类的私有方法的命名空间只在类的范围中,在类外不能调用
print(TestEncapsulation.name)
print(test.getStatement())
test.setStatement("Just test")
print(test.getStatement())
OUTPUT:
In [56]: %run encapsulation.py
Start
You can access this method!Start
the secert message is:
You cannot access!
This is a static method
Jmilk
Start
Just test
上面的例子是一个简单的封装,在Class中定义了各种不同类型的方法和属性,他们都有着属于自己的命名空间和访问限制。
公有属性:类内外都可以被访问
私有属性:只能被类内函数调用,以’__’开头。
内置属性:系统在定义类时默认添加,命名格式为’__X__’
继承令不同类之间拥有了一定的结构与关系。
类之间的关系:
1. 通过继承创建的新类称为”子类”或”派生类”
2. 被继承的类称为”基类”、”父类”或”超类”
通过继承得来的子类能够保留父类中所有需要的数据和行为,同时允许修改或者其他自定义的操作,这些都不会影响到父类。换句话说,继承的新类(子类),能够使用被继承类(父类)的所有功能,并且继承后的子类能够在无需重写这些属性和方法的情况下对父类的功能进行扩展。所以说,继承就是从一般(父类)到特殊(子类)的过程。所以继承最明显的好处在于代码的复用。
泛化表示所有的子类与其父类及祖先类有着一样的特点。
特化描述所有的子类的自定义,也就是,什么属性让它与其父类一同。
要实现继承,可以通过”继承”(Inheritance)和”组合”(Composition)来实现。其中”继承”又有实现继承和可视继承,”组合”又有接口继承和纯虚类。
实现继承:子类的属性和方法完全继承于父类。
可视继承:子类继承父类的外观和方法。
接口继承:子类的属性明和方法名继承于父类,但是具体的属性值和方法实现由子类重写。
#coding = utf8
#fileName=inherit.py
_metaclass_ = type #确定使用新式类
class Animal(object):
def __init__(self):
self.name = "Animal"
print(self.name)
def move(self,meters):
print("%s moved %sm." % (self.name,meters))
class GeneralAnimal(Animal): #实现继承,子类属性、方法完全继承于父类
pass
class Cat(Animal): #对父类的方法进行重写,属性、方法定义继承于父类,但是具体的属性值和方法实现由子类决定
def __init__(self): #重写父类的构造方法
self.name = 'Cat'
def move(self,meters): #重写父类的绑定方法
print("Cat never moves than 1m")
class RobotCat(Animal):
def __init__(self):
self.name = "RobotCat"
def move(self,meters):
print("RobotCat move flying.")
if __name__ == '__main__':
animal = Animal()
animal.move(10)
print("----------------")
generalanimal = GeneralAnimal() #实现继承子类实例化后的对象,与父类对象的区别只有对象句柄不一致
generalanimal.move(10)
print("----------------")
cat = Cat() #接口继承子类,重写了方法实现
cat.move(100)
print("----------------")
robotCat = RobotCat()
robotCat.move(1000)
OUTPUT:
In [21]: %run inherit.py
Animal
Animal moved 10m.
----------------
Animal
Animal moved 10m.
----------------
Cat never moves than 1m
----------------
RobotCat move flying.
Python支持多重继承,即’组合’继承。在Java或C#中使用接口的概念来实现多重继承的效果。而在Python中在定义类的时候,可以直接继承多个父类来实现多重继承。
#coding=utf8
#fileName = multipleInherit.py
_metaclass_=type # 确定使用新式类
class Animal:
def eat(self,food):
print("eat %s" %food)
def move(self,kilometers):
pass
class Robot:
def fly(self,kilometers):
print("flyed %skm." %kilometers)
class RobotCat(Animal,Robot): #继承多个父类
def __init__(self):
self.Name="Doraemon"
if __name__ == '__main__':
robot=RobotCat() #一只可以吃东西的会飞行的叫哆啦A梦的机器猫
print(robot.Name)
robot.eat("cookies") #从动物继承而来的eat
robot.fly(10000000) #从机器继承而来的fly
上面例子中由类RobotCat()实例化的对象robot,具有由两个父类组合而成的属性和方法。
注意:在实现多重继承时,如果继承的多个父类中均有名称相同的方法时,要注意继承的顺序。
_metaclass_=type # 确定使用新式类
class Animal:
def eat(self,food):
print("eat %s" %food)
def move(self,kilometers): #动物的move方法
pass
class Robot:
def move(self,kilometers): #机器的move方法
print("flyed %skm." %kilometers)
class RobotCat(Animal,Robot): #继承class Animal的eat()和move()方法,将class Robot的move()方法屏蔽了。所以这个顺序是我们所不希望的
#class RobotCat(Robot,Animal): #这个是正确的顺序,即能继承Animal的eat(),又能继承Robot的move()
def __init__(self):
self.Name="Doraemon"
robot=RobotCat()
print(robot.Name)
print("---------------")
robot.eat("cookies")
print("---------------")
robot.move(10000000)
OUTPUT:
In [31]: %run multipleInherit.py
Doraemon
---------------
eat cookies
---------------
多态的概念指出了不同的对象如何通过他们共同的属性和动作来操作以及访问,而不需考虑他们具体的类,多态表明了动态绑定(运行时)的存在,允许重载及运行时类型确定和验证。
多态就是指不同的类实例(对象)都拥有相同方法和属性,而这些相同的方法和属性在不同情形有着不同的表现形式。多态机制使不同的对象可以共享相同的外部接口(属性名、方法名)。这意味着,虽然针对不同对象的具体操作(方法实现)不同,但通过一个公共的类,它们可以通过相同的方式(属性名、变量名)予以调用。即在不同的对象中可以定义同名属性或方法,但是属性值或方法实现却是各不相同的。
多态的作用:实现了接口重用,保证了在类发生继承或派生时,我们仍然能够正确的调用这些相关类的实例化对象的属性和方法。
#coding=utf8
#fileName = polymorphic.py
from random import choice
_metaclass_=type
class calculator:
def count(self,args): #定义calculator类的成员函数count()
return 1
calc=calculator()
obj=choice(['hello,world',[1,2,3],calc]) #这里随机返回一个不确定类型的对象,并且每个对象中都重写了count()方法
print(type(obj))
print(obj.count('a'))
OUTPUT:
In [42]: %run polymorphic.py
<class '__main__.calculator'>
1
In [43]: %run polymorphic.py
<class 'str'>
0
In [44]: %run polymorphic.py
<class 'list'>
0
从结果中可以看出,我们在调用了不同的对象的相同的方法时,这个方法有着不一样的表现形式。
再举一个栗子:Duck Typing
_metaclass_=type
#在Duck类和Person类中都定义了相同的方法接口,和不同的方法实现
class Duck:
def quack(self):
print("Quaaaaaack!")
def feathers(self):
print("The duck has white and gray feathers.")
class Person:
def quack(self):
print("The person imitates a duck.")
def feathers(self):
print("The person takes a feather from the ground and shows it.")
def in_the_forest(obj): #这个方法实现了访问参数对象的方法
obj.quack()
obj.feathers()
def game():
donald = Duck()
john = Person()
print("------------")
in_the_forest(donald)
print("------------")
in_the_forest(john)
game()
OUTPUT:
In [50]: %run duck.py
------------
Quaaaaaack!
The duck has white and gray feathers.
------------
The person imitates a duck.
The person takes a feather from the ground and shows it.
在了解了OOP的基本知识后,就继续通过代码实现来加深理解吧。 : -)