【Python之旅】第四篇(三):Python面向对象编程详解
终于是来到了Python的面向对象编程,以前是没有接触过其它的面向对象编程的语言,因此学习这一部分是相当带劲的,这里也总结一下。
1.面向对象编程的相关名词及解释
世界万物,皆可分类,一切皆为对象。
所谓的面向对象编程,指的是一种编程的思想,通过对具体代码实现过程(面向过程编程)的不断抽象,以形成一个个的类别,以提高我们进行大型程序编写的效率(面向对象的具体实现需要面向过程,大型程序也可以用面向过程来编写,只是比较麻烦)。对于面向对象编程的相关名词和解释如下:
对象 :类的实体\一个叫Rain的好色的男人
类:人\动物\机器
方法:人会走,会思考\狗会叫,会咬人\定义一个类的各个功能
消息传递:狗叫了,人听见了,就叫通信
继承:狗都四条腿走路
封装:人不能引用狗的特性,比如四条腿走路
多态性:一个叫的功能,可能是低吼,也可是大声叫
抽象性:简化复杂的现实问题的途径,它可以为具体问题找到最恰当的类定义
对“类”这个名词举个简单的例子:
杯子:类
这个杯子:对象(实体),属于“杯子”这个类
对“方法”这个名词举个例子:动态方法(动态属性)与静态方法(静态方法可以理解为类的属性)。
2.类、对象、方法
关于三者的关系,用下面的一张图片可以非常清晰明确的说明:
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() #使用类的方法 执行情况如下: [email protected]:/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' )
执行情况如下: [email protected]:/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' )
执行情况如下: [email protected]:/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 执行情况如下: [email protected]:/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 执行情况如下: [email protected]:/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() 执行情况如下: [email protected]:/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() #在类外通过普通方式调用类的私有属性 执行情况如下: [email protected]:/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() 执行情况如下: [email protected]:/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' ) #子类继承父类后便可以直接调用父类的方法
#在有多个需要同时执行相同功能的子类情况下,使用类的继承可以节省代码空间
执行情况如下: [email protected]:/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
执行情况如下: [email protected]:/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 is ShitTshirt , I am from US
Welcome onboard! ************************************************************ Hi, my name is Alex , I am from FR
I am teaching C++, i am making 5000 per month !
S1.name: WangFanHao S2.name: ShitTshirt T1.name: Alex |
通过上面的例子便可以很好地理解类的继承的作用了,即如果需要在多个子类中执行相同的代码功能时,就可以通过类的继承来节省代码空间。
8.类的属性修改:setattr(),delattr()与getattr()
以上面程序为例,做如下的演示:
首先在交互器中导入该类:
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
|
>>> import class_continue8
Hi, my name is WangFanHao , I am from JP
Get the fuck off... Oldboy Linux edu. ************************************************************ Hi, my name is ShitTshirt , I am from US
Welcome onboard! ************************************************************ Hi, my name is Alex , I am from FR
I am teaching C++, i am making 5000 per month !
S1.name: WangFanHao S2.name: ShitTshirt T1.name: Alex >>> import tab
>>> class_continue8. class_continue8.S1 class_continue8.__hash__( class_continue8.S2 class_continue8.__init__( class_continue8.SchoolMember class_continue8.__name__ class_continue8.Student class_continue8.__new__( class_continue8.T1 class_continue8.__package__ class_continue8.Teacher class_continue8.__reduce__( class_continue8.__class__( class_continue8.__reduce_ex__( class_continue8.__delattr__( class_continue8.__repr__( class_continue8.__dict__ class_continue8.__setattr__( class_continue8.__doc__ class_continue8.__sizeof__( class_continue8.__file__ class_continue8.__str__( class_continue8.__format__( class_continue8.__subclasshook__( class_continue8.__getattribute__( |
setattrr():在类中添加属性
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
|
1 .子类有的属性,属于该子类的对象没有定义,会继承该属性;如果有定义,则不会继承子类的该属性
>>> class_continue8.Student.age #原来Student类中并没有age属性 Traceback (most recent call last): File "<stdin>" , line 1 , in <module>
AttributeError: class Student has no attribute 'age'
>>> setattr(class_continue8.S) class_continue8.S1 class_continue8.SchoolMember class_continue8.S2 class_continue8.Student >>> setattr(class_continue8.Student, 'age' , 29 ) #在Student类中添加age属性
>>> class_continue8.Student.age #再次查看age属性 29 >>> class_continue8.S1.age #S1与S2作为Student类的实例化对象,也会有age属性 29 >>> class_continue8.S2.age 29 >>> setattr(class_continue8.S1, 'age' , 26 )
>>> class_continue8.S1.age #此时S1对象有定义,因此不会继承所属子类的该属性 26 2 .子类的对象有的属性,但在其所属子类没有定义,则该子类不会反继承该属性
>>> setattr(class_continue8.S1, 'tuition' , 5000 ) #如果仅仅是在对象中添加tuition属性
>>> class_continue8.Student.age 29 >>> class_continue8.Student.tuition #则在S1对象所属的类Student中并不会有tuition属性 Traceback (most recent call last): File "<stdin>" , line 1 , in <module>
AttributeError: class Student has no attribute 'tuition'
>>> class_continue8.S1.tuition 5000 >>> class_continue8.S2.tuition #因此S2对象也不会有该属性 Traceback (most recent call last): File "<stdin>" , line 1 , in <module>
AttributeError: Student instance has no attribute 'tuition'
3 .父类有的属性,子类中没有定义,则子类会继承该属性;如果子类中有定义,则不会继承
>>> setattr(class_continue8.SchoolMember, 'name' , 'Python' ) #在子类在没有定义
>>> class_continue8.SchoolMember.name 'Python' >>> class_continue8.Student.name #因此子类会继承该属性 'Python' >>> setattr(class_continue8.SchoolMember, 'age' , 25 ) #在父类中定义子类有定义的属性
>>> class_continue8.SchoolMember.age 25 >>> class_continue8.Student.age #子类已经定义了age属性,因此不会再继承父类的该属性 29 |
从上面的例子可以看出,对于父类、子类、对象,属性的优先级以本地定义的为主,如果本地没有定义,则继承上一级的属性,如果有定义,则使用本地的。
delattr():删除类中的属性
1
2
3
4
5
6
7
|
>>> class_continue8.SchoolMember.name 'Python' >>> delattr(class_continue8.SchoolMember, 'name' )
>>> class_continue8.SchoolMember.name Traceback (most recent call last): File "<stdin>" , line 1 , in <module>
AttributeError: class SchoolMember has no attribute 'name'
|
getattr()的主要用法如下:
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
|
程序代码如下: class person:
def tell(self, name):
print 'hi my name is' , name
def study(sefl):
print 'I am studying Py right now.'
class student(person):
def study(sefl):
print 'I am studying Py right now.'
p = person() var s = [ 'tell' , 'study' ]
v1 = var s[ 0 ]
getattr(p, v1)( 'Oldboy' )
执行情况如下: [email protected]:/mnt/hgfs/Python/day4$ python cla_getattr_with_no_arg10.py hi my name is Oldboy
|
对于该程序,如果直接使用p.v1('name')来访问类中的方法是不行的,会有如下的提示错误:
1
2
3
4
5
|
[email protected]:/mnt/hgfs/Python/day4$ python cla_getattr_with_no_arg10.py Traceback (most recent call last): File "cla_getattr_with_no_arg10.py" , line 21 , in <module>
p.v1( 'Oldboy' )
AttributeError: person instance has no attribute 'v1'
|
因此getattr()的主要用途就在于有一个类有多个方法时,需要对该类的所有方法都执行一遍,那么可以将类中的方法都写入一个列表中,再通过getattr()和循环的方法来完成,这样就可以节省很多代码空间。
本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1699864,如需转载请自行联系原作者