Python - 面向对象

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

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

然而,这里是面向对象编程(OOP)的小介绍你速度与减号;

OOP术语概述

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

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

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

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

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

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

  • 实例 : 某个班级的个别对象.例如,属于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 是一个类变量,其值在此类的所有实例之间共享.这可以从类内部或类外部作为 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/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.

您可以使用以下函数 :

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

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

  • setattr(obj,name,value) : 设置属性.如果属性不存在,那么它将被创建.

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

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 __ : 班级名称.

  • __ module __ : 定义类的模块名称.此属性在交互模式下为"__main__".

  • __ bases __ : 一个可能为空的元组,包含基类,按它们在基类列表中出现的顺序排列.

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

#!/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继承其父类的属性,您可以使用这些属性,就好像它们是在子类中定义的一样.子类也可以覆盖父类的数据成员和方法.

语法

派生类的声明与其父类非常相似;但是,在类名和减号之后给出了要继承的基类列表;

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

您可以使用issubclas s()或isinstance()函数检查两个类和实例的关系.

  • issubclass( sub,sup)如果给定的子类 sub 确实是超类 sup 的子类,则布尔函数返回true.

  • isinstance(obj,Class)布尔函数返回true,如果 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

基本重载方法

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

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

Data Hid ing

对象的属性在类定义之外可能可见也可能不可见.您需要使用双下划线前缀命名属性,然后这些属性不会被外人直接看到.

示例

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