python面向对象编程(类和对象详解介绍 – Python高级开发教程)

上一章Python教程请查看:python异常处理和断言
自从Python存在以来,Python一直是一种面向对象的语言,但是在Python中创建和使用类和对象非常简单,本章将帮助你成为使用Python的面向对象编程支持的专家。
如果你以前没有任何面向对象(OO)编程的经验,那么你可能需要参考一门介绍性课程,或者至少是某种教程,以便掌握基本概念。
一、OOP术语概述OOP中常见的术语如下:

  • 类-一个用户定义的对象原型,它定义了一组属性来描述类的任何对象。属性是通过点表示法访问的数据成员(类变量和实例变量)和方法。
  • 类变量——一个类的所有实例共享的变量。类变量在类中定义,但在类的任何方法之外,类变量的使用频率不如实例变量。
  • 数据成员——保存与类及其对象相关的数据的类变量或实例变量。
  • 函数重载——指定一个特定函数的多个行为,执行的操作因涉及的对象或参数的类型而异。
  • 实例变量——定义在方法内部,只属于类当前实例的变量。
  • 继承——把一个类的特性转移到由它派生出来的其他类。
  • 一个类的个体对象,例如一个属于类圆的对象obj就是类圆的一个实例。
  • 实例化——类实例的创建。
  • 方法——类定义中定义的一种特殊函数。
  • 对象——由其类定义的数据结构的唯一实例,对象包括数据成员(类变量和实例变量)和方法。
  • 操作符重载——将一个以上的函数赋给一个特定的操作符。
二、创建类类语句创建一个新的类定义,类的名称紧跟在关键字类后面,后面跟一个冒号如下所示:
class ClassName: '可选类文档字符串' class_suite

该类有一个文档字符串,可以通过classname .__doc__访问该字符串。
class_suite由定义类成员、数据属性和函数的所有组件语句组成。
下面是一个简单的Python类的例子:
class Employee: '基类' empCount = 0def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1def displayCount(self): print "雇员数 %d" % Employee.empCountdef displayEmployee(self): print "Name : ", self.name,", Salary: ", self.salary

  • 变量empCount是一个类变量,它的值在该类的所有实例之间共享,这可以作为雇员访问,类内部或类外部的empCount。
  • 第一个方法是一个特殊的方法,称为类构造函数或初始化方法,当你创建该类的新实例时,Python会调用这个方法。
  • 除了每个方法的第一个参数是self之外,你可以声明其他类方法,如普通函数。Python将self参数添加到列表中; 在调用方法时不需要包含它。
三、创建实例对象要创建类的实例,你可以使用类名来调用类,并传入类的任意参数。
"这将创建Employee类的第一个对象" emp1 = Employee("Van Gogh", 2000) "第二个实例对象" emp2 = Employee("Oreja", 5000)

四、访问属性你可以使用带有object的点操作符来访问对象的属性,类变量将使用如下的类名来访问:
emp1.displayEmployee() emp2.displayEmployee() print "雇员数: %d" % Employee.empCount

现在把上面所有的概念放在一起:
#!/usr/bin/pythonclass Employee: '基类s' empCount = 0def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1def displayCount(self): print "雇员数 %d" % Employee.empCountdef displayEmployee(self): print "Name : ", self.name,", Salary: ", self.salary"创建第一个实例对象" emp1 = Employee("Van Gogh", 2000) "创建第二个实例对象" emp2 = Employee("Oreja", 5000) emp1.displayEmployee() emp2.displayEmployee() print "雇员数 %d" % Employee.empCount

你可以随时添加、删除或修改类和对象的属性
emp1.age = 7# 添加'age' 属性. emp1.age = 8# 修改'age' 属性. del emp1.age# 删除 'age' 属性.

你可以使用以下函数,而不是使用常规语句来访问属性:
  • getattr(obj, name[, default])——访问对象的属性。
  • hasattr(obj,name)——检查属性是否存在。
  • 用于设置属性的setattr(obj,name,value),如果属性不存在,那么将创建它。
  • delattr(obj, name)删除一个属性。
hasattr(emp1, 'age') getattr(emp1, 'age') setattr(emp1, 'age', 8) delattr(empl, 'age')

五、内置类属性每个Python类使内置属性后,他们可以使用点操作符访问?像任何其他属性:
(1)__dict__?字典包含类的名称空间。
(2)__doc__?类文档字符串或没有,如果定义。
(3)__name__?类名。
(4)__module__?模块名称的类定义,这个属性是“__main__”互动模式。
(5)__bases__?可能包含基类空元组,按照他们的出现在基类列表中。
上面的类?我们试图访问这些属性
#!/usr/bin/pythonclass Employee: '基类' empCount = 0def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1def displayCount(self): print "雇员数 %d" % Employee.empCountdef displayEmployee(self): print "Name : ", self.name,", Salary: ", self.salaryprint "Employee.__doc__:", Employee.__doc__ print "Employee.__name__:", Employee.__name__ print "Employee.__module__:", Employee.__module__ print "Employee.__bases__:", Employee.__bases__ print "Employee.__dict__:", Employee.__dict__

六、销毁对象(垃圾收集)Python自动删除不需要的对象(内置类型或类实例)以释放内存空间,Python定期回收不再使用的内存块的过程称为垃圾收集。
Python的垃圾收集器在程序执行期间运行,并在对象的引用计数为0时触发,当指向对象的别名数目改变时,该对象的引用计数也随之改变。
对象的引用计数在分配新名称或放在容器(列表、元组或字典)中时增加,当使用del删除对象、重新分配对象的引用或将其引用移出范围时,对象的引用计数将减少,当对象的引用计数为0时,Python自动收集它。
a = 40# 创建对象< 40> b = a# < 40>增加引用计数 c = [b]# < 40>增加引用计数del a# < 40>减少引用计数 b = 100# < 40>减少引用计数 c[0] = -1# < 40>减少引用计数

通常你不会注意到垃圾收集器何时销毁孤立实例并回收其空间,但是类可以实现称为析构函数的特殊方法,该方法在实例即将被销毁时调用,此方法可用于清理实例使用的任何非内存资源。
这个del__()析构函数打印一个即将被销毁的实例的类名
#!/usr/bin/pythonclass Point: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print class_name, "destroyed"pt1 = Point() pt2 = pt1 pt3 = pt1 print id(pt1), id(pt2), id(pt3) del pt1 del pt2 del pt3

注意理想情况下,应该在单独的文件中定义类,然后使用import语句将它们导入主程序文件。
七、类继承你可以创建一个类,而不是从零开始,方法是通过在新类名后面的括号中列出父类,从而从一个已存在的类派生该类。
子类继承父类的属性,你可以像在子类中定义属性一样使用这些属性,子类还可以覆盖来自父类的数据成员和方法。
派生类的声明很像它们的父类,但是在类名之后给出了要继承的基类的列表:
class SubClassName (ParentClass1[, ParentClass2, ...]): '可选类文档字符串' class_suite

python类继承例子如下:
#!/usr/bin/pythonclass Parent:# 定义父类 parentAttr = 100 def __init__(self): print "调用父类构造函数"def parentMethod(self): print '调用父类函数'def setAttr(self, attr): Parent.parentAttr = attrdef getAttr(self): print "父类属性 :", Parent.parentAttrclass Child(Parent): # 定义子类 def __init__(self): print "调用子类构造函数"def childMethod(self): print '调用子类函数'c = Child()# 子类实例 c.childMethod()# 调用子类方法 c.parentMethod()# 调用父类方法 c.setAttr(200) c.getAttr()

类似地,你可以从多个父类驱动一个类,如下所示
class A:# 定义类A .....class B:# 定义类B .....class C(A, B):# A和B的子类

你可以使用issubclass()或isinstance()函数来检查两个类和实例之间的关系。
如果给定的子类确实是超类sup的子类,则issubclass(sub, sup)布尔函数返回true。
如果obj是Class类的实例或者是Class子类的实例,则isinstance(obj, Class)布尔函数返回true
八、重写方法你总是可以覆盖父类方法,重写父方法的一个原因是,你可能希望子类具有特殊或不同的功能。
#!/usr/bin/pythonclass Parent:# 定义父类 def myMethod(self): print '调用父类函数'class Child(Parent): # 定义子类 def myMethod(self): print '调用子类函数'c = Child()# 子类实例 c.myMethod()

九、重载基本方法下表列出了一些可以在自己的类中覆盖的通用功能
编号 方法,说明和调用例子
1 __init__ ( self [,args… ] ) 构造函数(带有任何可选参数) 简单调用 :  obj = className(args)
2 __del__( self ) 析构函数,删除一个对象 简单调用 :  del obj
3 __repr__( self ) 评估字符串表示 简单调用 :  repr(obj)
4 __str__( self ) 可打印字符串表示 简单调用 :  str(obj)
5 __cmp__ ( self, x ) 对象比较 简单调用 :  cmp(obj, x)
十、重载操作符假设你创建了一个向量类来表示二维向量,那么当你使用加号操作符将它们相加时会发生什么?
但是你可以在类中定义__add__方法来执行向量加法,然后加号操作符将按照期望操作
#!/usr/bin/pythonclass Vector: def __init__(self, a, b): self.a = a self.b = bdef __str__(self): return 'Vector (%d, %d)' % (self.a, self.b)def __add__(self,other): return Vector(self.a + other.a, self.b + other.b)v1 = Vector(2,10) v2 = Vector(5,-2) print v1 + v2

十一、数据隐藏【python面向对象编程(类和对象详解介绍 – Python高级开发教程)】对象的属性在类定义之外可能可见,也可能不可见,你需要使用双下划线前缀来命名属性,这样外部人员就不能直接看到这些属性。
#!/usr/bin/pythonclass JustCounter: __secretCount = 0def count(self): self.__secretCount += 1 print self.__secretCountcounter = JustCounter() counter.count() counter.count() print counter.__secretCount

Python通过内部更改名称以包含类名来保护这些成员,你可以访问object. _classname_attrname等属性,如果你将最后一行替换为以下内容,那么它就适合你:
......................... print counter._JustCounter__secretCount

    推荐阅读