Python是一种面向对象的编程语言,不过,Python和C++一样,还支持面向过程的程序设计。在Python中完全可以使用函数、模块等方式来完成工作。但是,当使用Python编写一个较为庞大的项目时,则应该考虑使用面向对象的方法,以便更好地对项目进行管理。
6.1 面向对象编程概述
面向对象程序设计是与面向过程程序设计不同的另一种编程架构。
6.1.1 Python中的面向对象思想
面向对象程序设计强调对象的“抽象”“封装”和“多态”。面向对象程序设计的基本思想是将任何事物都当作对象,是其所属对象类的一个实例。对于复杂的对象,则将其划为为简单的对象,由这些简单的对象以某种方式组合而形成的复杂的对象。每个对象都有其相对应的对象类,属于同一对象类的对象具有相同的属性和方法。
对象以对象类的形式将其内部的数据或者方法进行封装。对象与对象之间只能相互传递数据,而不能访问其他对象的内部,对象的内部对其他对象而言是不可见的。不同的对象类之间可以通过继承的方式来拥有其他对象的属性和方法,从而形成父子关系。
面向对象程序设计的基本过程如下:
1.确定对象及属性和方法
2.分析对象之间的联系,确定其通信机制
3.将具有共同特征的对象抽象为对象类
4.设计、实现类,并确定类相互间的继承关系
5.创建对象实例,实现对象间的相互联系。
例如,可以将人看作一个对象类。每一个具体的人,如张三,则是一个对象实例。每个人都具有姓名、性别、年龄和身高等特征,可以将这些特征抽象为对象类的属性。
Python完全采用了面向对象程序设计的思想。在Python中,可以使用类建立一个对象模型,以及对象所拥有的的属性和方法。该模型能够较好地反映事物的本质,以及相互之间的关系,其本质更接近人类认知事物所采用的的计算模型。
Python是真正面向对象的脚本语言,虽然其与C++的类机制有所区别,但Python的内置数据类型(如字符串、列表、字典等)都不是类,但却具有一些和类相似的语法。例如,通过“.”操作符来使用内置数据类型的某些方法。
6.1.2 类和对象
类是面向对象程序设计的基础。类具有抽象性、封装性、继承性和多态性。
- 类的抽象性:类是对具有共同属性和方法的一类对象的描述。
- 类的封装性:类将属性和方法封装,外部 是不可见的,只有通过类提供的接口才能与属于类的实例对象进行信息交换,
- 类的继承性:类可以由已有的类派生。派生出来的类具有父类的方法和属性。
- 类的多态性:类可以根据不同的参数调用不同的方法,同一个方法可以处理不同类型的参数。实际上,Python的内部已经很好地实现了多态。在Python中,使用类不需要考虑太多类型之间数据的处理关系。
每个类都有自己的属性和方法。类的属性实际上就是类内部的变量,而类的方法则是在类内部定义的函数。对象是具体的事物,是类的实例化结果。类与对象的关系如图所示:
6.2 在Python中定义和使用类
6.2.1 类的定义
在Python中,类的定义使用关键字class。同时使用缩进表示缩进的语句属于该类。定义形式如下:
class <类名>:
<语句1>
<语句2>
...
<语句n>
与函数的定义相同,在使用类之前必须先定义类。类的定义一般放在脚本的头部。也可以在if语句的分支或者函数的定义中定义类。下面为human类示例:
class human: #定义human类
age = 0 #定义age属性
sex = ''
height = 0
weight = 0
name = ''
类还可以通过继承的方式进行定义。通过类继承来定义类的基本形式如下:
class <类名>(父类名):
<语句1>
<语句2>
...
<语句n>
示例:
class student(human): #通过继承human类定义student类
school = '' #定义新属性school
number = 0 #定义新属性number
grade = 0
上述通过human类继承而来的student类自动继承了human类中的属性,同时又为student类定义了其它的属性。
类定义后就产生了一个名字空间,与函数类似。在类内部使用的属性相当于函数中的变量名,还可以在类的外部继续使用。类的内部与函数的内部一样,相当于一个局部作用域。不同类的内部也可可以使用相同的属性名。
6.2.2 类的使用
类在定义后必须先实例化才能使用。类的实例化与函数调用类似,只要使用类名加圆括号的形式就可以实例化一个类。
类实例化后会生成一个对象。一个类可以实例化生成多个对象,对象与对象之间并不会相互影响。类实例化后可以使用属性和方法。下面代码首先定义了一个book类,然后将其实例化。
class book: #定义book类
author = '' #定义author属性
name = '' #定义name属性
page = 0 #定义page属性
price = '' #定义price属性
press = '' #
a = book() #book类实例化
>>> a #查看对象a
<class _main_.book at 0x01120A0>
>>> a.author #访问author属性
0
>>> a.author = 'Tom' #设置author属性
>>> a.page = 300 #设置page属性
>>> a.price = 25 #设置price属性
>>> a.author #重新访问author属性
'Tom'
>>>a.page
300
b = book() #将book类实例化生成b对象
>>>b.author #访问b对象的author属性
''
b.author = 'Jack' #设置b对象的author属性
>>>b.author #访问b对象的author属性
'Jack'
>>>a.author #a对象的author属性也没有改变
'Tom'
上述例子只定义类的属性,并在类实例化后重新设置其属性。从代码中可以看出,类的实例化相当于只调用一个函数,这个函数就是类。函数返回一个类的实例对象,返回后的对象就具有类所定义的属性。上述例子生成了book类的两个实例对象,设置其中一个对象的属性,并不影响到另一个对象的属性。
在Python中需要注意的是,虽然类首先需要实例化,然后才能使用其属性,但实际上,当创建一个类以后,就可以通过类名访问其属性。如果直接使用类名修改其属性,那么将影响已经通过该类实例化的其他对象。演示代码如下:
class A: #定义A
name = 'A' #定义属性name,将其赋值为'A'
num = 2 #定义属性num,将其赋值为2
>>>A.name #直接使用类名访问的属性
'A'
>>>A.num #直接使用类名访问类的属性
2
a = A() #生成A对象
>>>a.name #查看a对象的name属性
'A'
b = A() #生成b对象
>>>b.name #查看b对象的name属性
'A'
A.name = 'B' #使用类名修改name属性
>>>a.name #a对象的name属性被修改
'B'
>>>b.name #b对象的name属性被修改
'B'
6.3 类的属性和方法
每个类都具有自己的属性和方法。属性和方法是面向对象程序设计所独有的概念。属性是类所封装的数据,而方法则是类对数据进行的操作。
6.3.1 类的属性
类的属性实际上是类内部的变量,在上一章节使用类的属性是类的共有属性,在类的外部就可以设置其属性的值,在某些情况下,可能不希望在类的外部对其属性进行操作,此时就可以使用类的私有属性。
数据保护是面向对象程序设计所特有的,在面向过程程序设计中并没有数据保护的概念。类中的私有属性是不能在类的外部进行操作的,这便起到了对属性的保护作用。与C++不同,Python在类的内部声明一个私有成员时不需要使用private关键字,而是通过类中的属性的命名形式来表示类属性是共有的还是私有的。
在Python中,如果类中的属性是以双下划线开始的,则该属性为类的私有属性,不能在类的外部使用或者访问。下面是一个私有属性的命名形式:
_priavte_attrs #以双下划线开始
如果在类内部的方法中使用类的私有属性,则应该用下面的方式调用
self._priavte_attrs #应该在私有属性名前加上“self.”
下面的代码用来对6.2节的book类进行修改,将其部分属性改为私有属性。
class book: #定义book类
_author = '' #类的私有属性_author
_name = ''
_page = 0
price = 0 #类的共有属性price
_press = ''
a = book() #实例化book类生成a对象
>>>a._author #试图访问a对象的_author私有属性,结果导致错误
>>>a.price = 20 #修改price属性
>>>a.price #price属性改变了
20
可以看到,在定义类的时候,凡是以双下划线开始的属性不能在类的外部访问,当然也不能修改。如果要修改类的私有属性值或者获取其值,则可以通过使用类提供的方法来完成。
6.3.2 类的方法
类的方法实际上就是在类的内部使用def关键字定义的函数。定义类的方法与定义一个函数基本相同,在类的方法中也同样要使用缩进。
1.定义类的方法
在类的内部使用def关键字可以为类定义一个方法。与函数定义不同的是,类的方法必须包括参数self,且self必须为第一个参数。下面的代码用来为6.3.1节中的book类添加show()和setname()方法。
class book: #定义book类
_author = '' #类的私有属性_author
_name = ''
_page = 0
price = 0 #类的共有属性price
_press = ''
def show(self): #定义类的show()方法,其参数必须为self
print(self._author) #输出类的私有属性_author
print(self._name) #输出类的私有属性_name
def setname(self,name): #定义类的setname()方法,其有两个参数
self._name = name #设置类的_name私有属性
>>>a = book() #生成类的实例a
>>>a.show() #调用show()方法,输出为空,因为其私有属性都为空
>>>a.setname('Tom') #调用setname()方法,但只向其传递一个参数
>>>a.show() #调用show()方法
Tom
与类的属性相同,类的方法也是可以类私有的,类的私有方法不能在类的外部调用。和类的私有属性命名相同,类的私有方法名也要以双下划线开始。
类的私有方法只能在类的内部调用,而不能在类的外部调用。另外,在类的内部调用其私有方法时,要使用“self.私有方法名”的形式。下面的代码用来为book类添加一个名为check的私有方法,并且修改show()方法。
class book: #定义book类
_author = '' #类的私有属性_author
_name = ''
_page = 0
price = 0 #类的共有属性price
_press = ''
def _check(self,item): #定义_check()私有方法
if item == '': #判断item是否为空
return 0 #为空则返回0
else:
return 1 #否则返回1
def show(self): #修改show()方法
if self._check(self._author): #判断_author私有属性是否为空
print(self._author) #不为空则输出其值
else:
print('No value') #否则输出no value
if self._check(self._name): #判断私有属性_check是否为空
print(self._name) #不为空则输出其值
else:
print('No value') #否则输出no value
def setname(self,name): #定义setname()方法
self._name = name
a = book()
>>>a.show() #调用show()方法
No value
No value
>>>a.setname('Tom') #调用setname()方法
>>>a.show() #重新调用show()方法,看到输出值已经改变
No value
Tom
>>>a._check() #调用类的私有方法,导致出错
2.类的专有方法
类中有一些以双下划线开始并且以双下划线结束的方法,我们称之为类的专有方法(Special Method)。专有方法是针对类的特殊操作的一些方法,例如,在类实例化时将调用_init_()方法。部分类的专有方法如图表6-1所示。
以下代码修改book类,使用_init_()方法为对象的属性赋初值。
class book: #定义book类
_author = '' #类的私有属性_author
_name = ''
_page = 0
price = 0 #类的共有属性price
_press = ''
def _check(self,item): #定义_check()私有方法
if item == '': #判断item是否为空
return 0 #为空则返回0
else:
return 1 #否则返回1
def show(self): #修改show()方法
if self._check(self._author): #判断_author私有属性是否为空
print(self._author) #不为空则输出其值
else:
print('No value') #否则输出no value
if self._check(self._name): #判断私有属性_check是否为空
print(self._name) #不为空则输出其值
else:
print('No value') #否则输出no value
def setname(self,name): #定义setname()方法
self._name = name
def _init_(self,author,name): #使用_init_()方法为_author以及_name属性赋初值
self._author = author
self._name = name
>>>a = book('Tom','A Wonderful Book') #实例化,为_author以及_name属性赋初值
>>>a.show() #调用show()方法
Tom
A Wonderful Book
>>>a.setname('About Jack') #重新设置_name私有属性
>>>a.show() #调用show()方法
Tom
About Jack
6.4 类的继承
一个新类可以通过继承来获得已有类的属性及方法等。通过继承而来的类也可以自己定义新的属性和方法。
6.4.1 使用继承
在本章前面介绍类的定义时已经提到如何通过继承来获得一个新类。新类可以继承父类的共有属性和共有方法,但是不能继承父类的私有属性和私有方法。如下所示的代码通过继承book类来创建一个名为student的类。
class book: #定义book类
_author = '' #类的私有属性_author
_name = ''
_page = 0
price = 0 #类的共有属性price
_press = ''
def _check(self,item): #定义_check()私有方法
if item == '': #判断item是否为空
return 0 #为空则返回0
else:
return 1 #否则返回1
def show(self): #修改show()方法
if self._check(self._author): #判断_author私有属性是否为空
print(self._author) #不为空则输出其值
else:
print('No value') #否则输出no value
if self._check(self._name): #判断私有属性_check是否为空
print(self._name) #不为空则输出其值
else:
print('No value') #否则输出no value
def setname(self,name): #定义setname()方法
self._name = name
def _init_(self,author,name): #使用_init_()方法为_author以及_name属性赋初值
self._author = author
self._name = name
class student(book): #通过继承创建student类
class = ''
_grade = ''
_sname =''
def showinfo(self): #定义一个新的showfo()方法
self.show() #此处仅调用book类的show()方法
>>>b = student('Jack','Big Book') #student类继承了book类的_init_()方法
>>>b.showinfo() #调用student类的showinfo()方法
Jack
Big Book
>>>b.show() #调用book类的show()方法
Jack
Big Book
如果在定义类的时候试图使用父类的私有属性或者私有方法,将会导致错误,代码如下:
6.4.2 Python的多重继承
多重继承是指所创建的类同时拥有几个类的属性和方法。多重继承与单重继承不同的是,在类名后面的圆括号中可以包含多个父类名,父类名之间以逗号隔开。通过多重继承创建一个新类的一般形式如下:
class 新类名(父类1,父类2,...,父类n):
<语句1>
<语句2>
...
<语句n>
使用多重继承需要注意圆括号中父类名字的顺序。如果父类中有相同的方法名,而在类中使用时未指定父类名,则Python解释器将按从左至右的顺序搜索。
以下代码首先定义了两个类,然后通过多重继承创建一个新类
class A: #定义A类
name = 'A' #定义其name属性
_num = 1 #定义其_num属性
def show(self): #定义show()方法
print(self.name)
print(self._num)
def setnum(self,num): #定义setnum()方法以设置_num 属性
self.num = num
class B: #定义B类
nameb = 'B' #定义nameb属性
_numb = 2 #定义_numb属性
def show(self): #定义show()方法
print(self.nameb)
print(self._numb)
def setname(self,name): #定义setname()方法
self.nameb = name
class C(A,B): #通过多重继承创建C类
def showall(self): #定义showall()方法
print(self.name)
print(self.nameb)
>>>c = C() #实例化C类生成c对象
>>>c.showall() #调用showall()方法
A
B
>>>c.show() #调用show()方法,此处调用的是A类的show()方法
A
1
>>>c.setnum(3)
>>>c.show()
A
3
>>>c.setname('D') #调用setname()方法,即B类的setnum()方法
>>>c.showall()
A
D
如果需要在C类中使用B类的show()方法,则可以按如下代码修改C类。
class C(A,B): #通过多重继承创建C类
def showall(self):
print(self.name)
print(self.nameb)
show = B.show #这里表明show()方法为B类的方法
>>>c = C()
>>>c.show() #调用show()方法,此处调用的是c类的show()方法
B
2
6.5 在类中重载方法和运算符
前面介绍了,当 继承某几个类时,当前定义的类也继承父类的方法。重载是指重新定义父类中的方法。在Python中,不仅可以重载方法,而且还可以重载运算符,如+ - * / (加减乘除)等,以适用于所创建的类进行相应的运算。
6.5.1 方法重载
通过继承而创建的类,其父类方法不一定能满足类的需要。新类实际上是修改部分功能,为了避免重新命名函数,可以使用方法重载来解决。或者,新类需要重新初始化,此时就可以通过重载_init_()方法来实现。
方法的重载实际上是在类中使用def关键字重新定义父类中已有的方法。如果重载父类中的方法,但又需要在类中先使用父类的该方法,则可以使用父类+“.”+方法名的形式调用。例如,在重载_init_()方法时,如果需要使用父类的_init_()方法,则可以在_init_前加上父类名来调用该方法。
下面的代码首先定义了一个父类,然后通过继承创建一个新类,并且重载父类的方法。
class human:
_age = 0
_sex = ''
_height = 0
_weight = 0
name = ''
def _init_(self,age,sex,height,weight): #重载_init_()方法
self._age = age #初始化_age属性
self._sex = sex #初始化化_height属性
self._height = height
self._weight = weight
def setname(self,name): #定义setname()方法
self.name = name
def show(self):
print(self.name)
print(self._age)
print(self._sex)
print(self._height)
print(self.weight)
class student(human): #通过继承human类生成student类
_classes = 0
_grade = 0
_num = 0
def _init_(self,classes,grade,num,age,sex,height,weight):#重载_init_()方法
self._classes = classes
self._grade = grade
self._num = num
human._init_(self,age,sex,height,weight) #调用human类的_init_()方法,初始化
#human属性
def show(self):
human.show(self)
print(self._classes)
print(self._grade)
print(self._num)
>>>a = student(12,3,20070305,19,'male',175,65) #实例化生成a对象
>>>a.setname('Tom') #调用show()方法,即用重载后的
#show()方法输出属性
Tom
19
male
175
65
12
3
20070305
6.5.2 运算符重载
由于在Python中运算符都有其相对应的函数,因此,运算符重载不需要像在C++中那样使用operator关键字。在类中,运算符对应一些专有方法。因此,运算符的重载实际上是对运算符对应的专有方法的重载。部分运算符和类的专有方法名对照如表6-2所示。
实例代码如下:
class MyList: #定义MyList类
_mylist = [] #定义_mylist属性
def _init_(self,*args): #重载_init_()方法
self.list = [] #此处相当于_mylist初始化,以避免多个实例对象数据混合
for arg in args:
self._mylist.append(arg)
def _add_(self,n): #重载“+”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] + n
def _sub_(self,n): #重载“-”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] - n
def _mul_(self,n): #重载“*”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] * n
def _div_(self,n): #重载“/”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] / n
def _mod_(self,n): #重载“%”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] % n
def _pow_(self,n): #重载“**”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] ** n
def _len_(self): #重载len_len_()方法
return len(self._mylist)
def show(self): #定义show()方法
print(self._mylist)
>>>l = Mylist(1,2,3,4,5) #实例化生成l对象
>>>l.show() #调用show()方法
[1,2,3,4,5]
>>>l + 5
>>>l.show()
[6,7,8,9,10]
>>>l - 3 #此处调用_sub_()方法
>>>l.show() #调用show()方法
[3,4,5,6,7]
>>>l * 6 #此处调用_mul_()方法
>>>l.show #调用show()方法
[18,24,30,36,42]
>>>l / 3 #此处调用_div_()方法
>>>l.show()
[6,8,10,12,14]
>>>l % 3 #此处调用_mod_()方法
>>>l.show()
[0,2,1,0,2]
>>>l ** 3 #此处调用_pow_()方法
>>>l.show()
[0,8,1,0,8]
>>>len(l) ##此处调用_len_()方法
5
>>>b = MyList(2,3,4,5,6,7,8,9)
>>>len(b) #此处将调用_len_()方法
7
>>>b.show()
[2,3,4,5,6,7,8,9]
>>>b - 5 #此处调用_sub_()方法
>>>b.show()
[-3,-2,0,1,2,3,4]
>>>l.show() #调用l的show()方法,比较l和b的值
[0,8,1,0,8]
6.6 在模块中定义类
类与函数一样,也可以写入模块中。在其他的脚本中可以通过导入模块名来使用模块中已定义的类。模块中类的使用方式与模块中的函数类似。实际上,可以将模块中的类当作函数来使用。将6.5节中定义的MyList类整理下,保存在MyList.py脚本中。代码如下:
class MyList: #定义MyList类
_mylist = [] #定义_mylist属性
def _init_(self,*args): #重载_init_()方法
self.list = [] #此处相当于_mylist初始化,以避免多个实例对象数据混合
for arg in args:
self._mylist.append(arg)
def _add_(self,n): #重载“+”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] + n
def _sub_(self,n): #重载“-”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] - n
def _mul_(self,n): #重载“*”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] * n
def _div_(self,n): #重载“/”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] / n
def _mod_(self,n): #重载“%”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] % n
def _pow_(self,n): #重载“**”运算符
for i in range(0,len(self._mylist)):
self._mylist[i] = self.mylist[i] ** n
def _len_(self): #重载len_len_()方法
return len(self._mylist)
def show(self): #定义show()方法
print(self._mylist)
然后编写一个UseMyList.py脚本,使用MyList.py脚本中的MyList类。其代码如下:
引用说明
《Python数据分析从入门到精通》 张啸宇 李静 编著