Python3 面向对象


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

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

但是,这里仅介绍一下面向对象编程(OOP),以帮助你:

OOP术语概述


  • :对象的用户定义原型,该原型定义了代表该类任何对象的一组属性。属性是数据成员(类变量和实例变量)和方法,可通过点表示法进行访问。

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

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

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

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

  • 继承:将一个类的特性转移到从该类派生的其他类上。

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

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

  • 方法:在类定义中定义的一种特殊功能。

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

  • 运算符重载:将多个功能分配给特定的操作员。

创建类


The class 语句创建一个新的类定义。类的名称紧随关键字之后 class 接着是冒号,如下所示:

class ClassName:
    'Optional class documentation string'
    class_suite
  • 该类具有一个文档字符串,可以通过以下方式访问ClassName .__ doc__.

  • 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/python3

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.salary = 7000  # Add an 'salary' attribute.
emp1.name = 'xyz'  # Modify 'age' attribute.
del emp1.salary  # Delete 'age' attribute.

你可以使用以下功能来代替使用普通语句访问属性:

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

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

  • setattr(obj,name,value):设置属性。如果属性不存在,则将创建它。

  • delattr(obj,name):删除属性。

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

内置类属性


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

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

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

  • __name__: 班级名称。

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

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

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

#!/usr/bin/python3

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)

emp1 = Employee("Zara", 2000)
emp2 = Employee("Manni", 5000)
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__: (<class 'object'>,)
Employee.__dict__: {
    'displayCount': <function Employee.displayCount at 0x0160D2B8>,
    '__module__': '__main__', '__doc__': 'Common base class for all employees',
    'empCount': 2, '__init__':
    <function Employee.__init__ at 0x0124F810>, 'displayEmployee':
    <function Employee.displayEmployee at 0x0160D300>,
    '__weakref__':
    <attribute '__weakref__' of 'Employee' objects>, '__dict__':
    <attribute '__dict__' of 'Employee' objects>
}

销毁对象(垃圾收集)


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/python3

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

执行以上代码后,将产生以下结果:

140338326963984 140338326963984 140338326963984
Point destroyed

注意:理想情况下,你应该在一个单独的文件中定义类,然后使用以下命令将它们导入到主程序文件中: import 语句。

在上面的示例中,假设Point类的定义包含在 point.py 并且其中没有其他可执行代码。

#!/usr/bin/python3
import point

p1 = point.Point()

继承


你可以从一个现有的类派生一个类,而不是从头开始创建一个类,方法是在新类名后的括号中列出父类。

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

语法

派生类的声明与父类很相似;但是,在类名称后给出了要继承的基类的列表:

class SubClassName (ParentClass1[, ParentClass2, ...]):
    'Optional class documentation string'
    class_suite
#!/usr/bin/python3

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 calss B
.....

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

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

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

  • isinstance(obj,Class)boolean function returns True, if obj 是类的一个实例 Class 或是Class子类的实例

覆盖方法


你始终可以覆盖父类方法。覆盖父方法的一个原因是,你可能需要子类中的特殊功能或其他功能。

#!/usr/bin/python3

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 = className(param)

2

__del __(self)

析构函数,删除一个对象

例子: del obj

3

__repr __(self)

可评估的字符串表示形式

例子: repr(obj)

4

__str __(self)

可打印的字符串表示

例子: str(obj)

5

__cmp__(self,x)

Object comparison

例子: cmp(obj,x)

重载运算符


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

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

#!/usr/bin/python3

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/python3

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