【Python之旅】第四篇(三):Python面向对象编程详解

简介:

终于是来到了Python的面向对象编程,以前是没有接触过其它的面向对象编程的语言,因此学习这一部分是相当带劲的,这里也总结一下。


1.面向对象编程的相关名词及解释

    世界万物,皆可分类,一切皆为对象。

    所谓的面向对象编程,指的是一种编程的思想,通过对具体代码实现过程(面向过程编程)的不断抽象,以形成一个个的类别,以提高我们进行大型程序编写的效率(面向对象的具体实现需要面向过程,大型程序也可以用面向过程来编写,只是比较麻烦)。对于面向对象编程的相关名词和解释如下:

对象 :类的实体\一个叫Rain的好色的男人

类:人\动物\机器

方法:人会走,会思考\狗会叫,会咬人\定义一个类的各个功能

消息传递:狗叫了,人听见了,就叫通信

继承:狗都四条腿走路

封装:人不能引用狗的特性,比如四条腿走路

多态性:一个叫的功能,可能是低吼,也可是大声叫

抽象性:简化复杂的现实问题的途径,它可以为具体问题找到最恰当的类定义

    对“类”这个名词举个简单的例子:

杯子:类

这个杯子:对象(实体),属于“杯子”这个类

    对“方法”这个名词举个例子:动态方法(动态属性)与静态方法(静态方法可以理解为类的属性)。


2.类、对象、方法

    关于三者的关系,用下面的一张图片可以非常清晰明确的说明:

wKiom1YNXzXhVqtqAAG2f59r2RI144.jpg


3.类的语法:第一个面向对象程序

1
2
3
4
5
6
7
8
9
10
11
程序代码如下:
class  dog:                    #定义一个类
     def name(self):       #定义类的方法
         print  "Hello master, my name is Python."
 
D = dog()    #类的实例化,产生类的对象,如果不实例化,将无法访问该类
D.name()     #使用类的方法
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python myclass4.py 
Hello master, my name  is  Python.

 

4.类的方法:有关self

    类的方法(类函数)与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,但是在调用这个方法的时候你不为这个参数赋值,Python会提供这个值。这个特别的变量指对象本身,按照惯例它的名称是self。

    如你有一个类称为MyClass和这个类的一个实例MyObject。当你调用这个对象的方法MyObject.method(arg1, arg2)的时候,这会由Python自动转为MyClass.method(MyObject, arg1, arg2)。

    类中如果有多个不同的方法,并且类中的变量需要相互访问,这时候就需要使用self即用类自身来作为中间变量以进行变量在不同类中的传递了,可以看下面的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
程序代码如下:
class  Dog:
     class_object =  'food'  #这是一个类变量
     def sayHi(self):
         print  'Hi master , I am your little dog, who do you want me to bite...'
         favorate_food =  'bone'
         self.FavorFood = favorate_food    #将favorate_food变为全局变量,让其它方法可以引用
     def eat(self, food_type):
         if  food_type == self.FavorFood:  #不同类方法之间的变量调用,需要通过类本身
             print  'I like it very much..thanks'
         else :
             print  'Do not give me this bull shit...'
 
d = Dog()
d.sayHi()    '' '如果不调用sayHi(),则无法将favorrate_food变成一个类变量,所谓类变量,
             即是类下定义的第一级变量 '' '
d.eat( 'bone'
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class7.py 
Hi master , I am your little dog, who  do  you want me to bite...
I like it very much..thanks


    当然这里实际上是把sayHi方法中的favorate_food变量变为变中的全局变量,需要注意的是,下面的方法是不可以的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
程序代码如下:
class  Dog:
     class_object =  'food' 
     def sayHi(self):
         print  'Hi master , I am your little dog, who do you want me to bite...'
         favorate_food =  'bone'
 
     def eat(self, food_type):
         if  food_type == self.sayHi().favorate_food: #这样使用是不行的
             print  'I like it very much..thanks'
         else :
             print  'Do not give me this bull shit...'
 
d = Dog()
d.sayHi()   
d.eat( 'bone'
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class7.py 
Hi master , I am your little dog, who  do  you want me to bite...
Hi master , I am your little dog, who  do  you want me to bite...
Traceback (most recent call last):
   File  "class7.py" , line  16 in  <module>
     d.eat( 'bone'
   File  "class7.py" , line  9 in  eat
     if  food_type == self.sayHi().favorate_food: 
AttributeError:  'NoneType'  object has no attribute  'favorate_food'

    上面程序不行是因为,self.sayHi()时,其实会执行sayHi()方法,因为它本来就是一个函数,并且没有定义返回值,因此会返回None值,导致程序执行出错。

    上面的例子意在说明,不同类方法之间调用变量时,需要将其变为全局变量,并且要通过self即类本身进行引用。即类下的各个方法(函数,name也认为是一个函数),不能独自相互通信,必须要通过类作为中间人来进行通信,self即代表self本身,通过self.name即可完成通信,所以在定义类下的方法时,必须要加一个参数,即self,来代表类本身,否则这个方法就不能被使用当然参数不一定要是self。


5.类的构造函数__init__()与解构函数__del__()

    关于两个函数的说明,可以看下面一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
程序代码如下:
class  Person:
     def __init__(self,name,age):                #构造函数,只要把类实例化,就会执行此函数
         print  'I am being called right now...'
         self.Name = name
         self.Age = age
 
     def sayHi(self):                            #类的普通方法
         print  'Hi my name is %s, i am %s years old'  % (self.Name,self.Age)
 
     def __del__(self):                          #解构函数,当实例在内存中释放时,才会执行此函数
         print  'I got killed just now...bye...' , self.Name
 
p = Person( 'Alex' , 29 )
p.sayHi()
#del p
print  '*' * 60
 
p2 = Person( 'Jack' , 40 )
p2.sayHi()
#del p2
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python person_class5.py 
I am being called right now...
Hi my name  is  Alex, i am  29  years old
************************************************************
I am being called right now...
Hi my name  is  Jack, i am  40  years old
I got killed just now...bye... Jack
I got killed just now...bye... Alex

    可以看到程序最后有两行相同内容的输出(除了名字不一样),是因为程序结束时,会把该程序在内存中占用的空间释放,于是执行两次解构函数(因为进行了两次类的实例化),才出现这样的情况为了验证这个结论,将程序代码修改为如下并执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
程序代码如下:
class  Person:
     def __init__(self,name,age):
         print  'I am being called right now...'
         self.Name = name
         self.Age = age
 
     def sayHi(self):
         print  'Hi my name is %s, i am %s years old'  % (self.Name,self.Age)
 
     def __del__(self):
         print  'I got killed just now...bye...' , self.Name
 
p = Person( 'Alex' , 29 )
p.sayHi()
del p    #其它不变,只是在做类的另一个实例前,删除该实例
print  '*' * 60
 
p2 = Person( 'Jack' , 40 )
p2.sayHi()
#del p2
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python person_class5.py 
I am being called right now...
Hi my name  is  Alex, i am  29  years old
I got killed just now...bye... Alex
************************************************************
I am being called right now...
Hi my name  is  Jack, i am  40  years old
I got killed just now...bye... Jack

    上面的程序代码及执行结果便可以说明,解构函数在类完成调用并且在内存中释放时才会执行。


6.类的公有属性与私有属性及其调用

    类的公有属性即是类下的普通方法,这些方法是可以直接被调用的;而类的私有属性只有在类中不同方法之间可以相互调用,一般情况下是不能在类外直接调用的。

    可以看下面一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
程序代码如下:
class  Person:
     def __init__(self,name,age):
         print  'I am being called right now...'
         self.Name = name
         self.Age = age
 
     def sayHi(self):
         print  'Hi my name is %s, i am %s years old'  % (self.Name,self.Age)
         self.__talk()    #可以引用类中的私有属性
 
     def __talk(self):    #私有属性的定义方法
         print  "I'm private..."
 
p=Person( 'Alex' , 29 )
p.sayHi()
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name  is  Alex, i am  29  years old
I'm  private ...

    如果将代码修改为如下,即在类外调用私有属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
程序代码如下:
class  Person:
     def __init__(self,name,age):
         print  'I am being called right now...'
         self.Name = name
         self.Age = age
 
     def sayHi(self):
         print  'Hi my name is %s, i am %s years old'  % (self.Name,self.Age)
         self.__talk()
 
     def __talk(self):
         print  "I'm private..."
 
p=Person( 'Alex' , 29 )
p.sayHi()
p.__talk()    #在类外通过普通方式调用类的私有属性
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name  is  Alex, i am  29  years old
I'm  private ...
Traceback (most recent call last):
   File  "private6.py" , line  17 in  <module>
     p.__talk()
AttributeError: Person instance has no attribute  '__talk'  #会出现找不到类方法的错误提示

    当然可以通过特殊的方法引用类中的私有属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
程序代码:
class  Person:
     def __init__(self,name,age):
         print  'I am being called right now...'
         self.Name = name
         self.Age = age
 
     def sayHi(self):
         print  'Hi my name is %s, i am %s years old'  % (self.Name,self.Age)
         self.__talk()
 
     def __talk(self):
         print  "I'm private..."
 
p=Person( 'Alex' , 29 )
p.sayHi()
 
print  '*' * 30
 
p._Person__talk()
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name  is  Alex, i am  29  years old
I'm  private ...
******************************
I'm  private ...


7.类的继承

    类有分父类和子类,在有多个子类的情况下,子类通过继承父类的属性或方法,就可以节省很多代码空间。可以先看下面一个没有参数传递的类的继承简单例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
程序代码如下:
class  person:                #父类
 
     def tell(self, name):
         print  'hi my name is' , name
 
class  student(person):       #子类,继承父类
 
     def study(sefl):
         print  'I am studying Py right now.'
 
s = student()
 
s.study()
s.tell( 'MengFanHao' ) #子类继承父类后便可以直接调用父类的方法
                      #在有多个需要同时执行相同功能的子类情况下,使用类的继承可以节省代码空间
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python cla_with_no_arg9.py 
I am studying Py right now.
hi my name  is  MengFanHao

    看过上面的例子后,再看下面一个含有参数传递的例子,通过该例子也可以很好地理解前面第2点中关于类、对象、方法的理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class  SchoolMember:        #父类,学校成员
     school_name =  'Oldboy Linux edu.'     #第一级变量,即类属性
     def __init__(self,name,gender,nationality =  'CN' ):    #构造函数
         self.name = name
         self.gender = gender
         self.nation = nationality
 
     def tell(self):    #普通的类方法
         print  'Hi, my name is %s , I am from %s'  % (self.name,self.nation)
 
class  Student(SchoolMember):    #子类,学生,继承父类学校成员的相关属性
     def __init__(self, Name, Gender, Class, Score, Nation =  'US' ): #子类下的方法
         SchoolMember.__init__(self, Name, Gender, Nation) #让父类使用子类传递过去的参数
         self.Class = Class
         self.Score = Score
 
     def payTuition(self, amount):    #子类下的方法
         if  amount <  6499 :
             print  'Get the fuck off...'
         else :
             print  'Welcome onboard!'
 
class  Teacher(SchoolMember):    #子类,老师,继承父类学校成员的相关属性
     def __init__(self, Name, Gender, Course, Salary, Nation =  'FR' ):
         SchoolMember.__init__(self, Name, Gender, Nation)
         self.course = Course
         self.Salary = Salary
 
     def teaching(self):
         print  'I am teaching %s, i am making %s per month !'  % (self.course, self.Salary)
 
S1 = Student( 'WangFanHao' 'Male' 'Python' 'C+' 'JP' ) #实例化一个子类对象,学生
S1.tell()    #直接继承父类中的tell()方法
S1.payTuition( 4999 )    #使用子类Student()自身中的类方法
print S1.school_name   #直接继承父类的一个属性   
 
print  '*' * 60
 
S2 = Student( 'ShitTshirt' 'Male' 'Linux' 'B' )
S2.tell()
S2.payTuition( 6500 )
#S2.age =  29
#print S2.age
 
print  '*' * 60
 
T1 = Teacher( 'Alex' 'Male' 'C++' 5000 )   #实例化一个子类对象,学生
T1.tell()            #直接继承父类中的tell()方法
T1.teaching()        #直接继承父类的一个属性
 
print  'S1.name:' , S1.name    #测试用,观察输出结果
print  'S2.name:' , S2.name
print  'T1.name:' , T1.name
 
执行情况如下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class_continue8.py
Hi, my name  is  WangFanHao , I am from JP
Get the fuck off...
Oldboy Linux edu.
************************************************************
Hi, my name