4.2 Python的类
面向对象的编程强调数据封装、继承等对象特性,即对象=数据+方法(数据处理程序)。
在面向过程的编程中,def语句是定义函数体。
Python类中方法的定义也使用了def语句,所不同的是参数表不同,方法定义形式参数表中第一个参数永远是“self”关键字。
面向对象编程的基础是类的编程,下面介绍一些关于类的概念。
类(Class):用来描述具有相同的属性和方法的对象集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类变量:类变量在整个实例化的对象中是公用的,它定义在类中且在方法体之外。
数据成员:即类变量或者实例变量,用于处理类及其实例对象的相关数据。
方法:类中定义的数据处理或操作,定义和使用类似于函数。
方法重写:如果从父类继承的方法中不能满足子类的需求,就可以对其进行改写,这个过程叫作方法的覆盖(override),也称为方法的重写。
实例变量:定义在方法中的变量,只作用于当前实例的类。
继承:即一个派生类(derived class)继承基类(base class)的方法。继承也允许把一个派生类的对象作为一个基类的对象对待。
实例化:创建一个类的实例,类的具体对象。类是抽象的模板,实例化是根据类创建出来的不同名字的具体“对象”。每个对象都拥有相同的方法,但各自的数据可能不同。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
下面介绍Python中类的定义和使用。
1.创建类
使用class语句创建一个新类,class后面为类的名称并以冒号“:”结尾。
class ClassName: '类的帮助信息’ #类文档字符串 class_suite #类体
类的帮助信息可以通过“ClassName.__doc__”查看。class_suite由类成员、方法、数据属性组成。
以下是一个简单的例子,示例4-9的部分代码。
class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’): #类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): #类对象销毁 class_name = self.__class__.__name__ print(class_name+ " 销毁")
__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法。当创建了这个类的实例时,就会调用该方法。
self代表类的实例。虽然在调用时不必传入相应的参数,但在定义类的方法时它是必须有的。
2.实例化类对象
在其他编程语言中,实例化类一般用关键字new,但是在Python中没有这个关键字,因此其类的实例化的方式类似于函数调用。
以下使用类的名称(dog)来实例化,并通过__init__()方法接收参数。示例4-9的部分代码。
#实例化第一个类MyDoga MyDoga=dog() #使用默认参数 MyDoga.Run() #实例化第一个类MyDoga MyDogb=dog(’旺财’) #使用自定义参数 MyDogb.CalltheDog() MyDogb.SetName(’小哈’) MyDogb.CalltheDog()
见示例4-9。
#类演示代码 class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’): #类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): #类对象销毁 class_name = self.__class__.__name__ print(class_name+ " 销毁") #实例化第一个类MyDoga MyDoga=dog() #使用默认参数 MyDoga.Run() #实例化第一个类MyDoga MyDogb=dog(’旺财’) #使用自定义参数 MyDogb.CalltheDog() MyDogb.SetName(’小哈’) MyDogb.CalltheDog()
程序运行结果:
你的小狗叫小虎。 小虎正在跑。 你的小狗叫旺财。 你呼唤"旺财",旺财向你跑来。 你的小狗改名叫小哈。 你呼唤"小哈",小哈向你跑来。
3.访问属性
可以使用点号“.”来访问对象的属性。
在上例中,可以通过SetName()方法来修改dog的名字,也可以直接修改类属性。
见示例4-10。
class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’): #类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): #类对象销毁 class_name = self.__class__.__name__ print(class_name+ " 销毁") #实例化第一个类MyDoga MyDogc=dog(’小黑’) #使用自定义参数 MyDogc.CalltheDog() MyDogc.name="旺旺财" MyDogc.CalltheDog()
程序运行结果:
你的小狗叫小黑。 你呼唤"小黑",小黑向你跑来。 你呼唤"旺旺财",旺旺财向你跑来。
4.类的内置属性
Python类的内置属性的种类及含义见表4-1。
表4-1 Python类的内置属性
见示例4-11。
class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’): #类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): #类对象销毁 class_name = self.__class__.__name__ print(class_name+ " 销毁") print('dog.__name__ : ', dog.__name__) print('dog.__doc__ : ', dog.__doc__) print('dog.__bases__ : ', dog.__bases__) print('dog.__dict__ : ', dog.__dict__) print('dog.__module__ : ', dog.__module__) print('dog.__class__ : ', dog.__class__)
程序运行结果:
dog.__name__ : dog dog.__doc__ : 这是一个dog类的定义。 dog.__bases__ : (<class 'object'>,) dog.__dict__ : {'__module__': '__main__', '__doc__': ’这是一个dog类的 定义。', '__init__': <function dog.__init__ at 0x0000000009C30048>, 'SetName': <function dog.SetName at 0x0000000009C30A60>, 'Run': <function dog.Run at 0x0000000009C30B70>, 'CalltheDog': <function dog.CalltheDog at 0x0000000009C301E0>, '__del__': <function dog.__del__ at 0x0000000009C30D08>, '__dict__': <attribute '__dict__' of 'dog' objects>, '__weakref__': <attribute '__weakref__' of 'dog' objects>} dog.__module__ : __main__ dog.__class__ : <class 'type'>dog.__module__ : __main__
5.类对象销毁
Python使用了引用计数来跟踪和回收类对象。当类对象(类实例)被创建时,就已经创建了一个引用计数。当类对象不再使用,即这个对象的引用计数变为0时,它就要被销毁。但是销毁不是立即操作的,而是由Python解释器在适当的时机进行,并回收类对象占用的内存空间。用户也可以在程序中主动销毁类对象。
Python类有析构函数“__del__”。它在对象销毁时被调用,并且当对象不再被使用时,用户可以使用del语句销毁类对象。
del MyDoga del MyDogb del MyDogc
见示例4-12。
class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’):#类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): #类对象销毁 class_name = self.__class__.__name__ print(class_name+ " 销毁") #实例化第1个类MyDoga MyDoga=dog() #使用默认参数 MyDoga.CalltheDog() #实例化第2个类MyDoga MyDogb=dog(’旺财’) #使用自定义参数 MyDogb.CalltheDog() #实例化第3个类MyDoga MyDogc=dog(’小黑’) #使用自定义参数 MyDogc.CalltheDog() del MyDoga del MyDogb del MyDogc MyDogc.CalltheDog() #类示例销毁,再执行此句会出错
程序运行结果:
你的小狗叫小虎。 你呼唤"小虎",小虎向你跑来。 你的小狗叫旺财。 你呼唤"旺财",旺财向你跑来。 你的小狗叫小黑。 你呼唤"小黑",小黑向你跑来。 dog销毁 dog销毁 dog销毁 MyDogc.CalltheDog() NameError: name 'MyDogc' is not defined
6.类的继承
面向对象的编程的主要好处之一是代码的重用,而实现重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法:
class派生类名(基类名) ...
在Python中继承的一些特点:
(1)如果在子类中需要直接使用父类的构造方法,就需要显式调用或者不重写父类的构造方法。
(2)在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。而在类中调用普通方法时不需要带上self参数。
(3)Python会先在派生类中查找对应类型的方法,如果找不到,就会到基类中逐个查找。
如果在继承元组中罗列了一个以上的类,那么它就被称作多重继承。
派生类的声明与它们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName(ParentClass1[, ParentClass2, ...]): ...
7.方法重写
如果父类方法的功能不能满足需求,就可以在子类重写父类的方法。下面给出有关类继承和方法重新的演示代码,见示例4-13。
class dog(object): #定义小狗类 '这是一个dog类的定义。' def __init__(self, dogname=’小虎’): #类初始化 self.name=dogname print('\n你的小狗叫’+self.name+'。') def SetName(self, dogname): self.name=dogname print(’你的小狗改名叫’+self.name+'。') def Run(self): print(self.name+’正在跑。') def CalltheDog(self): print(’你呼唤\"'+self.name+'\", '+self.name+’向你跑来。\n') def __del__(self): class_name = self.__class__.__name__ print(class_name+ " 销毁") class newdog(dog): '这是新dog类’ def Eat(self): #定义子类新方法 print(self.name+’正在吃东西。') def CalltheDog(self): #重构基类方法 print(’你呼唤\"'+self.name+'\", '+self.name+’抬头看你。\n') #实例化第一个类MyDoga MyDoga=newdog() #使用默认参数 MyDoga.Run() MyDoga.Eat() print('----------------') MyDoga.name=’宝贝’ MyDoga.Run() MyDoga.Eat() MyDoga.CalltheDog() del MyDoga
程序运行结果:
你的小狗叫小虎。 小虎正在跑。 小虎正在吃东西。 ---------------- 宝贝正在跑。 宝贝正在吃东西。 你呼唤"宝贝",宝贝抬头看你。 newdog销毁
8.类基础方法重载
类能重载的基础方法的种类及功能,见表4-2。
表4-2 类基础方法重载
Python除了支持基础方法重载,同样支持运算符重载,见表4-3。
表4-3 常用运算符重载方法
9.类的super()方法
在类的继承中,如果重定义某个方法,子类方法就会覆盖父类的同名方法。但如果我们希望能同时使用父类(基类)的同名方法,就可以通过super()方法来实现。
super()方法是用于调用父类(基类)的一个方法。
super()方法是用来解决多重继承问题的。在使用单继承时,可以直接用类名调用父类方法,这时如果使用多继承就会涉及查找顺序(MRO)、重复调用(钻石继承)等问题。
super()方法的语法:
super(type[, object-or-type])
super()方法的参数见表4-4。
表4-4 super()方法的参数
事实上,对于定义的每一个类,Python都会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序。一个类的MRO列表就是合并所有父类的MRO列表,并遵循以下三条原则。
(1)子类永远在父类前面。
(2)如果有多个父类,就会根据它们在列表中的顺序被检查。
(3)如果下一个类存在两个合法的选择,就选择第一个父类。
super()方法的使用,见示例4-14。
class A(object): def __init__(self): print("init A Class") super(A, self).__init__() class B(object): def __init__(self): print("init B Class") super(B, self).__init__() class C(object): def __init__(self): print("init C Class") super(C, self).__init__() class D(A, B, C): def __init__(self): print("init D class") super(D, self).__init__() class E(D): def __init__(self): print("init E class") super(E, self).__init__() class F(E): def __init__(self): print("init F class") super(F, self).__init__() F = F()
程序运行结果:
init F class init E class init D class init A Class init B Class init C Class
10.类的属性与方法
类的私有属性如下:
__private_attrs:以两个连接的下画线开头,声明该属性为私有,不能在类的外部使用或直接访问。在类的内部使用时为self.__private_attrs。
类的方法:在类的内部使用def关键字可以为类定义一个方法。与一般函数定义不同,类方法必须包含参数self,且为第一个参数。
类的私有方法如下:
__private_method:以两个连接的下画线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用时为self.__private_methods(self. __私有方法)。
Python不允许类实例访问私有属性,但可以使用object._className__attrName(对象名._类名__私有属性名)访问私有属性。见示例4-16的print(t._testPrivate__data)。
单下画线、双下画线、头尾双下画线的说明(foo为名字,可以是变量名或方法名):
_foo:以单下画线开头表示的是protected类型的变量,即保护类型,只允许其本身与子类进行访问,不能用于from module import *。
__foo:以两个连接的下画线开头表示的是私有类型private的变量,只允许这个类进行访问。
__foo__:其定义的是特殊方法,一般是系统定义名字,类似于__init__()。
见示例4-15。
class testPrivate: def __init__(self): self.__data = [] def add(self, item): self.__data.append(item) def printData(self): print(self.__data) t = testPrivate() t.add('dancingrain') t.add('hello') t.printData() print(t.__data)
程序运行结果:
['dancingrain', 'hello'] print(t.__data) AttributeError: 'testPrivate' object has no attribute '__data'
在程序中,如果直接访问私有变量就会出现错误提示“AttributeError: 'testPrivate' object has no attribute '__data'”。
但是,这并不意味着我们就不能从外部访问这个变量了。前面说过Python在类的内部用_classname__foo替换了__foo,因此,我们可以在类的外面使用_testPrivate__data访问__data。代码见示例4-16。
class testPrivate: def __init__(self): self.__data = [] def add(self, item): self.__data.append(item) def printData(self): print(self.__data) t = testPrivate() t.add('testPrivate Var') t.add('hello') t.printData() print(t._testPrivate__data)
程序运行结果:
['testPrivate Var', 'hello'] ['testPrivate Var', 'hello']
上面结果表明,我们获取到了私有变量的值。
使用私有变量是为了防止其他程序员或用户误改关键数据。如果一定要修改内部私有数据,就使用类内部方法间接修改。