深入理解Python面向对象的三大特性
在面向对象程序设计中,对象可以看做是数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。编写代码时,我们可以将所有功能都写在一个文件里,这样也是可行的,但是这样不利于代码的维护,你总不希望维护代码前,还需要从头至尾的通读一遍吧,就好像一间杂乱无章的房子,你想找一件想要的东西,但是需要地毯式的搜索一遍,甚至多遍才能找到。很明显,这样做的话,很浪费我们的时间。
多态:顾名思义就是多种形态,即便不知道变量所引用的对象类型是什么,依旧可以对它操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。
例如 符号'+',在对数值操作和字符串操作所表现出的不同行为
数值操作
1 intsum=1+2
2 print(intsum)
输出结果: 3
字符串操作:
strSum='hello'+'Python'
print(strSum)
输出结果:helloPython
很明显,符号'+'对数值和字符串表现出了两种行为,一种是数值的相加,一种是字符串的拼接。
唯一能毁掉多态的就是使用函数显示的检查类型。比如type,isinstance以及issubclass函数,在不知道对象是什么类型,但是又想对对象做点什么
的时候,就可以使用多态,但要避免使用毁掉多态的方式。
使用案例说明这一点。
假设,我们平台有个支付功能,用户将商品放入购物车后计算出总价后点击支付按钮即可完成支付。此时使用一个元祖即可实现。
1 ('SPAM',100)
但客户提出一个新的要求,对商品添加一个拍卖功能,谁出价最高,商品归谁。显然之前简单的计算商品价格且将总价放入元祖里已经不能满足当前的需求,因为元祖是不可变的。
此时就需要不断的获取最新价格。直到竞拍价格达到客户满意为止。为了实现这个功能,代码每次询问价格的时候,对象都需要检查当前的价格。
1 def getPrice(object):
2 if isinstance(object,tuple):
3 return object[1]
4 else:
5 return magic_network_method(object)
但此时,调皮的程序员,想要换另外一种方式表示商品价格。比如字典。没关系,我们继续更新代码。
1 def getPrice(object):
2 if isinstance(object,tuple):
3 return object[1]
4 elif isinstance(object,dict):
5 return int(object['price'])
6 else:
7 return magic_network_method(object)
但是如果有人希望为存储在其他键下面的价格增加新的字典类型时,我们又需要更新代码,很明显,这是一件很繁琐的工作。如果我们能让对象自己操作,每个新的对象类型都可以检索和计算自己的价格并返回结果,且只需要向它询问价格即可。这时候,多态就可以帮我们解决这个问题。
多态和方法:
程序接收一个对象,完全不了解该对象内部的实现方式,它可能有一种或多种形态(实现方式),但是我们仅需要询问价格即可。
1 object.getPrice()
封装:
回顾多态的概念,多态是指让用户对于不知道是什么类(对象类型)的对象进行方法调用。例如
1 def Add(x,y):
2 print( x+y)
3 Add(1,2)
4 Add('hello ','world')
输出结果:
3
hello world
当我们的Add方法写好后,调用者只要知道传入几个参数,但并不需要知道该方法的实现细节(即便是简单的print (x+y))也不需要关心参数类型是什么(第一个是两个数值,第二个是两个字符串),因为他仅仅关心的是输出的结果3或hello world.其实封装跟多态类似,但又有所不同。封装仅不需要关心对象是如何构建的而可以直接使用。
继承:先看如下代码
1 class Person:
2 def __init__(self,name,age,address):
3 self.name=name
4 self.age=age
5 self.address=address
6 def say(self):
7 print('你好,我叫 %s我今年%s岁 我来自%s 职业不详 '%(self.name,self.age,self.address))
8 person=Person(name='张三',age=18,address='beijing')
9 person.say()
10
11 class Student(Person):
12 def __init__(self,name,age,address,job):
13 self.job=job
14 Person.__init__(self,name=name,age=age,address=address)
15 def say(self):
16 print('你好,我叫 %s我今年%s岁 我来自%s 我是%s,我正在学习Python教程'%(self.name,self.age,self.address,self.job))
17 men=Student(name='李四',age='18',address='河北',job='学生')
18 men.say()
19 class Teacher(Person):
20 def __init__(self,name,age,address,job):
21 self.job=job
22 Person.__init__(self,name=name,age=age,address=address)
23 def say(self):
24 print('你好,我叫 %s我今年%s岁 我来自%s 我是%s,我正在教授Python教程'%(self.name,self.age,self.address,self.job))
25 teacher=Teacher(name='王五',age='28',address='河北',job='教师')
26 teacher.say()
显示结果:
1 你好,我叫 张三我今年18岁 我来自beijing 职业不详
2 你好,我叫 李四我今年18岁 我来自河北 我是学生,我正在学习Python教程
3 你好,我叫 王五我今年28岁 我来自河北 我是教师,我正在教授Python教程
从代码中可以看出,我们先写了一个Person类,又写了学生类和教师类且在括号中使用了之前定义的Person类。我们在学生类和教师类的实例化时(__init__)通过Person.__init__(name=name,age=age,address=address)即可在实例化学生类或教师类时同时实例化Person类中的属性,通过代码可以看出,我们只在Person中对name,age,address写了属性赋值代码,但在学生类和教师类实例化时依旧可以使用。这就是Person的继承关系。当一段代码或者函数被多处调用时,可以将该段代码或者函数抽象为一个对象,其他对象继承该对象后就可以像引用自己内部属性一样对父类的代码进行操作。