Python 3 - 面向对象

自从它存在以来,Python一直是面向对象的语言.因此,创建和使用类和对象非常简单.本章帮助您成为使用Python面向对象编程支持的专家.

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

但是,这里是面向对象编程(OOP)的一个小介绍.帮助你减去;

OOP术语概述

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

  • 类变量 : 由类的所有实例共享的变量.类变量在类中定义,但在类的任何方法之外.类变量的使用频率与实例变量不同.

  • 数据成员 : 包含与类及其对象关联的数据的类变量或实例变量.

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

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

  • 继承 : 将类的特征转移到从中派生的其他类.

  • 实例 : 某个班级的个别对象.例如,属于Circle类的对象obj是Circle类的实例.

  • Instantiation : 创建类的实例.

  • 方法 : 一种在类定义中定义的特殊函数.

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

  • 运算符重载 : 为特定运算符分配多个函数.

创建类

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 是一个类变量,其值在所有之间共享本课程中的a个实例.这可以从类内部或类外部作为 Employee.empCount 访问.

  • 第一种方法 __ init __( )是一种特殊的方法,它被称为类构造函数或初始化方法,当你创建这个类的新实例时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)如果给定的子类 sub 确实是超类 sup 的子类,则布尔函数返回True.

  • isinstance(obj,Class)布尔函数返回True,如果 obj 是类类的实例或是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


基本重载方法

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

Sr.No.方法,描述&样品调用
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)

重载运算符

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

但是,您可以在类中定义 __ add __ 方法来执行向量加法,然后加号运算符将表现为per expectation :

示例

#!/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