Python 面向对象


Python 从出现以来一直是一种面向对象的语言。因此,创建和使用类和对象非常容易。本章帮助你成为使用 Python 面向对象编程支持的专家。

如果你以前没有任何面向对象 (OO) 编程的经验,你可能需要查阅有关它的入门课程或至少某种教程,以便掌握基本概念。

不过,这里是面向对象编程(OOP)的小介绍,带你快速上手:

OOP 术语概述


  • Class :用户定义的对象原型,它定义了一组属性,这些属性表征类的任何对象。属性是通过点符号访问的数据成员(类变量和实例变量)和方法。

  • 类变量 :一个类的所有实例共享的变量。类变量在类内定义,但在类的任何方法之外。类变量不像实例变量那样频繁使用。

  • 数据成员 : 类变量或实例变量,保存与类及其对象相关的数据。

  • 函数重载 :将多个行为分配给特定功能。执行的操作因所涉及的对象或参数的类型而异。

  • 实例变量 : 定义在方法内部的变量,只属于类的当前实例。

  • 遗产 :将一个类的特性转移到从它派生的其他类。

  • Instance : 某一类的个体对象。例如,属于类 Circle 的对象 obj 是类 Circle 的一个实例。

  • 实例化 :创建一个类的实例。

  • Method :在类定义中定义的一种特殊函数。

  • Object :由其类定义的数据结构的唯一实例。一个对象包括数据成员(类变量和实例变量)和方法。

  • 运算符重载 :将多个函数分配给特定的运算符。

创建类


The class 语句创建一个新的类定义。类的名称紧跟关键字 class 后跟冒号如下:

class ClassName:
    'Optional class documentation string'
    class_suite
  • 该类有一个文档字符串,可以通过 类名.__doc__ .

  • The class_suite 由定义类成员、数据属性和函数的所有组件语句组成。

例子

下面是一个简单的 Python 类的例子:

class Employee:
    'Common base class for all employees'
    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
  • 变量 empCount 是一个类变量,其值在此类的所有实例之间共享。这可以访问为 Employee.empCount 从班内或班外。

  • 第一种方法 __在里面__() 是一种特殊方法,称为类构造函数或初始化方法,Python 在创建此类的新实例时会调用该方法。

  • 你声明其他类方法,如普通函数,但每个方法的第一个参数是 self . Python 添加了 self 为你列出清单的论据;调用方法时不需要包含它。

创建实例对象


要创建一个类的实例,你可以使用类名调用该类并传入它的任何参数 __init__ 方法接受。

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)

访问属性


你可以使用带有对象的点运算符访问对象的属性。类变量将使用类名访问,如下所示:

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

现在,把所有的概念放在一起:

#!/usr/bin/python

class Employee:
    'Common base class for all employees'
    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

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
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  # Add an 'age' attribute.
emp1.age = 8  # Modify 'age' attribute.
del emp1.age  # Delete 'age' attribute.

除了使用普通语句来访问属性,你还可以使用以下函数:

  • The getattr(obj, name[, default]) : 访问对象的属性。

  • The hasattr(obj,name) :检查一个属性是否存在。

  • The setattr(对象,名称,值) :设置一个属性。如果属性不存在,那么它将被创建。

  • The delattr(对象,名称) :删除一个属性。

hasattr(emp1, 'age')    # Returns true if 'age' attribute exists
getattr(emp1, 'age')    # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age')    # Delete attribute 'age'

内置类属性


每个 Python 类都遵循内置属性,并且可以像任何其他属性一样使用点运算符访问它们:

  • __dict__ : 包含类的命名空间的字典。

  • __doc__ :类文档字符串或无,如果未定义。

  • __name__ : 班级名称。

  • __模块__ : 定义类的模块名。此属性在交互模式下为“__main__”。

  • __基地__ :包含基类的可能为空的元组,按照它们在基类列表中出现的顺序排列。

对于上面的类,让我们尝试访问所有这些属性:

#!/usr/bin/python

class Employee:
    'Common base class for all employees'
    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__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2, 
'displayEmployee': <function displayEmployee at 0xb7c8441c>, 
'__doc__': 'Common base class for all employees', 
'__init__': <function __init__ at 0xb7c846bc>}

销毁对象(垃圾收集)


Python 会自动删除不需要的对象(内置类型或类实例)以释放内存空间。 Python 定期回收不再使用的内存块的过程称为垃圾收集。

Python 的垃圾收集器在程序执行期间运行,并在对象的引用计数达到零时触发。对象的引用计数随着指向它的别名数量的变化而变化。

对象的引用计数在被分配新名称或放置在容器(列表、元组或字典)中时会增加。对象的引用计数在删除时减少 del ,它的引用被重新分配,或者它的引用超出范围。当一个对象的引用计数达到零时,Python 会自动收集它。

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c = [b]     # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c[0] = -1   # Decrease ref. count  of <40> 

当垃圾收集器销毁孤立实例并回收其空间时,你通常不会注意到。但是一个类可以实现特殊的方法 __del__() ,称为析构函数,在实例即将被销毁时调用。此方法可用于清理实例使用的任何非内存资源。

例子

这个 __del__() 析构函数打印即将被销毁的实例的类名:

#!/usr/bin/python

class 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) # prints the ids of the obejcts
del pt1
del pt2
del pt3

执行上述代码时,会产生如下结果:

3083401324 3083401324 3083401324
Point destroyed

注意 : 理想情况下,你应该在单独的文件中定义你的类,然后你应该在你的主程序文件中使用 import 陈述。

类继承


你可以通过在新类名后面的括号中列出父类,从预先存在的类派生类来创建一个类,而不是从头开始。

子类继承其父类的属性,你可以像在子类中定义这些属性一样使用这些属性。子类也可以覆盖父类的数据成员和方法。

语法

派生类的声明很像它们的父类;但是,在类名之后给出了要继承的基类列表:

class SubClassName (ParentClass1[, ParentClass2, ...]):
    'Optional class documentation string'
    class_suite

例子

#!/usr/bin/python

class Parent:        # define parent class
    parentAttr = 100
    def __init__(self):
        print "Calling parent constructor"

    def parentMethod(self):
        print 'Calling parent method'

    def setAttr(self, attr):
        Parent.parentAttr = attr

    def getAttr(self):
        print "Parent attribute :", Parent.parentAttr

class Child(Parent): # define child class
    def __init__(self):
        print "Calling child constructor"

    def childMethod(self):
        print 'Calling child method'

c = Child()          # instance of child
c.childMethod()      # child calls its method
c.parentMethod()     # calls parent's method
c.setAttr(200)       # again call parent's method
c.getAttr()          # again call parent's method

执行上述代码时,会产生如下结果:

Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200

类似的,你可以从多个父类中驱动一个类,如下:

class A:        # define your class A
.....

class B:         # define your class B
.....

class C(A, B):   # subclass of A and B
.....

你可以使用 issubclass() 或 isinstance() 函数来检查两个类和实例的关系。

  • The issubclass(sub, sup) boolean function returns true if the given subclass sub 确实是超类的子类 sup .

  • The 实例(对象,类) boolean function returns true if obj 是类的一个实例 Class 或者是 Class 的子类的一个实例

覆盖方法


你始终可以覆盖你的父类方法。重写父类方法的一个原因是你可能希望子类中有特殊或不同的功能。

例子

#!/usr/bin/python

class Parent:        # define parent class
    def myMethod(self):
        print 'Calling parent method'

class Child(Parent): # define child class
    def myMethod(self):
        print 'Calling child method'

c = Child()          # instance of child
c.myMethod()         # child calls overridden method

执行上述代码时,会产生如下结果:

Calling child method

基础重载方法


下表列出了你可以在自己的类中覆盖的一些通用功能:

序号. 方法、描述和样品调用
1

__init__ ( self [,args...] )

构造函数(带有任何可选参数)

样品电话: obj = 类名(args)

2

__del__(自我)

析构函数,删除一个对象

样品电话: del obj

3

__repr__(自我)

可评估的字符串表示

样品电话: 代表(对象)

4

__str__( 自我 )

可打印的字符串表示

样品电话: str(obj)

5

__cmp__ ( 自我, x )

Object comparison

样品电话: cmp(obj, x)

重载运算符


假设你创建了一个 Vector 类来表示二维向量,当你使用加号运算符将它们相加时会发生什么? Python 很可能会对你大喊大叫。

但是,你可以定义 __add__ 类中的方法来执行向量加法,然后加号运算符将按预期运行:

例子

#!/usr/bin/python

class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __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

执行上述代码时,会产生如下结果:

Vector(7,8)

数据隐藏


对象的属性在类定义之外可能可见,也可能不可见。你需要使用双下划线前缀命名属性,然后这些属性对于外人来说是不直接可见的。

例子

#!/usr/bin/python

class JustCounter:
    __secretCount = 0
  
    def count(self):
        self.__secretCount += 1
        print self.__secretCount

counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount

执行上述代码时,会产生如下结果:

1
2
Traceback (most recent call last):
    File "test.py", line 12, in <module>
        print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

Python 通过在内部更改名称以包含类名来保护这些成员。你可以访问以下属性 object._className__attrName .如果你将最后一行替换如下,那么它适用于你:

.........................
print counter._JustCounter__secretCount

执行上述代码时,会产生如下结果:

1
2
2