相近对象,归为类
在人类认知中,会根据属性相近把东西归类,并且给类别命名。比如说,鸟类的共同属性是有羽毛,通过产卵生育后代。任何一只特别的鸟都在鸟类的原型基础上的。面向对象就是模拟了以上人类认知过程。在Python语言,为了听起来酷,我们把上面说的“东西”称为对象(object)。
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起。类是现实世界的抽象的实体以编程形式出现。实例是这些对象的具体化。可以类比一下,类是蓝图或者模型,用来产生真实的物体(实例)。类还可以派生出相似但有差异的子类。编程中类的概念就应用了很多这样的特征。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
下面是面向对象中的一些概念:
定义一个类
在 Python 中,类声明与函数声明很相似,头一行用一个相应的关键字class,class之后为类的名称并以冒号结尾,接下来是一个作为它的定义的代码体,如下所示:
class ClassName(): '类文档字符串信息' #类描述信息 class_suite #类结构体
基类是一个或多个用于继承的父类的集合;class_suite 类体由所有声明语句,类成员定义,数据属性和函数组成。类通常在一个模块的顶层进行定义,以便类实例能够在类所定义的源代码文件中的任何地方被创建。类的帮助信息可以通过ClassName.__doc__查看。
举例:创建一个员工类
#!/usr/bin/python # -*- coding: UTF-8 -*-
class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1
def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary
类属性
属性就是属于对象的数据或者函数元素,可以通过我们熟悉的句点属性标识法来访问。
类的数据属性(静态变量)
数据属性仅仅是所定义的类的变量。它们可以像任何其它变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其它什么地方被更新。
这种属性已为 OO 程序员所熟悉,即静态变量,或者是静态数据。它们表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。
静态成员通常仅用来跟踪与类相关的值。
>>> class c(object): foo = 100 >>> print c.foo 100 >>> c.foo+=1 >>> c.foo 101
二、由类实例化一个对象
要创建一个类的实例,你可以使用类的名称,并通过__init__方法接受参数。
"创建 Employee 类的第一个对象" emp1 = Employee("Zara", 2000) "创建 Employee 类的第二个对象" emp2 = Employee("Manni", 5000)
三、访问属性
您可以使用点(.)来访问对象的属性。使用如下类的名称访问类变量:
emp1.displayEmployee() emp2.displayEmployee() print "Total Employee %d" % Employee.empCount
举例:
#!/usr/bin/python # -*- coding: UTF-8 -*-
class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1
def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary "创建 Employee 类的第一个对象" emp1 = Employee("Zara", 2000) "创建 Employee 类的第二个对象" emp2 = Employee("Manni", 5000) emp1.displayEmployee() emp2.displayEmployee() print "Total Employee %d" % Employee.empCount
执行以上代码输出结果如下:
Name : Zara ,Salary: 2000 Name : Manni ,Salary: 5000 Total Employee 2
你可以添加,删除,修改类的属性,如下所示:
emp1.age = 7 # 添加一个 'age' 属性
emp1.age = 8 # 修改 'age' 属性
del emp1.age # 删除 'age' 属性
你也可以使用以下函数的方式来访问属性:
hasattr(emp1, 'age') # 如果存在 'age' 属性返回 True。
getattr(emp1, 'age') # 返回 'age' 属性的值
setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
delattr(empl, 'age') # 删除属性 'age'
四、python内置属性类
Python内置类属性调用实例如下:
#!/usr/bin/python # -*- coding: UTF-8 -*-
class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1
def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__
执行以上代码输出结果如下:
Employee.__doc__: 所有员工的基类 Employee.__name__: Employee Employee.__module__: __main__ Employee.__bases__: () Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}
下面正式介绍函数的三大特性:封装、继承、多态
五、类的封装
面向对象的程序设计中,某个类把所需要的数据(也可以说是类的属性)和对数据的操作(也可以说是类的行为)全部都封装在类中,分别称为类的成员变量和方法(或成员函数)。这种把成员变量和成员函数封装在一起的编程特性称为封装。
六、类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
需要注意的地方:
继承语法
class 派生类名(基类名)://... 基类名写作括号里,基本类是在类定义的时候,在元组之中指明的。
在python中继承中的一些特点:
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
语法:
派生类的声明,与他们的父类类似,继承的基类跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]): #如果多重(个)继承,不同父类用引号隔开即可 'Optional class documentation string' class_suite
# 子类也叫派生类,父类也叫作基类
# Python的类可以继承多个类,Java和C#中则只能继承一个类
实例:
#!/usr/bin/python # -*- coding: UTF-8 -*-
class Parent: # 定义父类
parentAttr = 100
def __init__(self): print "调用父类构造函数"
def parentMethod(self): print '调用父类方法'
def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "父类属性 :", Parent.parentAttr class Child(Parent): # 定义子类
def __init__(self): print "调用子类构造方法"
def childMethod(self): print '调用子类方法 child method' c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法
c.getAttr() # 再次调用父类的方法
以上代码执行结果如下:
调用子类构造方法 调用子类方法 child method 调用父类方法 父类属性 : 200
你可以继承多个类
class A: # 定义类 A
..... class B: # 定义类 B
..... class C(A, B): # 继承类 A 和 B
.....
扩展1:新式类和经典类
新式类的由来:随着Python版本的发展,python2.2版本以后引入的关于类的新概念,官方解释:为了统一类(class)和类型(type)。与之相对应的就是经典类。
在2.2之前,比如2.1版本中,类和类型是不同的,如a是ClassA的一个实例,那么a.__class__返回 ‘ class __main__.ClassA‘ ,type(a)返回总是<type 'instance'>。而引入新类后,比如ClassB是个新类,b是ClassB的实例,b.__class__和type(b)都是返回‘class '__main__.ClassB' ,这样就统一了。
引入新类后,还有其他的好处,比如更多的内置属性将会引入,描述符的引入,属性可以来计算等等。
为了向前兼容,默认情况下用户定义的类为经典类,新类需要继承自所有类的基类 object 或者继承自object的新类。【即只要是能向上追溯到继承自object类,就叫做新式类】
class CC: #经典类 def __init__( self ): pass class CCN(object): #新类 def __init__( self ): pass c1 = CC() c2 = CCN() c1.__class__ # 输出-> <class __main__.CC at 0x0137BF10> type(c1) # 输出-> <type 'instance'> c2.__class__ # 输出-><class '__main__.CCN'> type(c2) # 输出-><class '__main__.CCN'> dir(c1)
新式类对多重继承的影响:在多重继承中,如果不同父类中拥有相同的方法,在进行方法解析时采用的顺序(mro即method resolution order)是:
class D: def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
class D(object): def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
六、方法重写
Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法(就是子类中重新定义一个需要重写的在父类中的方法):
实例:
#!/usr/bin/python # -*- coding: UTF-8 -*-
class Parent: # 定义父类
def myMethod(self): print '调用父类方法'
class Child(Parent): # 定义子类
def myMethod(self): print '调用子类方法' c = Child() # 子类实例
c.myMethod() # 子类调用重写方法
执行以上代码输出结果如下:
调用子类方法 #这个结果是子类重写后的结果
到此,Python类的基本概念:封装、继承、多态就可理解为这么多,下一篇我们来继续深究Python面向对象编程。