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