把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
面向对象思想有三大要素:封装、继承和多态
封装:封装是一个概念,它的含义是把方法、属性、事件集中到一个统一的类中,并对使用者屏蔽其中的细节问题。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
继承:如果两个类存在继承关系,则子类会自动继承父类的方法和变量,在子类中可以调用父类的方法和变量,如果想要在子类里面做一系列事情,应该放在父类无参构造器里面。在java中,只允许单继承,也就是说一个类最多只能显示地继承于一个父类。但是一个类却可以被多个类继承,也就是说一个类可以拥有多个子类。
多态:多态性是指相同的操作可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
类和对象
简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
定义类
在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
class Student(object):
# __init__是一个特殊方法用于在创建对象时进行初始化操作
# 通过这个方法我们可以为学生对象绑定name和age两个属性
def __init__(self,name,age):
self.name=name
self.age=age
def study(self,course_name):
print('%s正在学习%s' %(self.name,course_name))
# PEP 8要求标识符的名字用全小写多个单词用下划线连接
# 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识)
def wacht_av(self):
if self.age < 18:
print('%s只能观看《熊出没》' %self.name)
else:
print('%s正在观看岛国爱情动作片.' % self.name)
说明:写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。
创建和使用对象
当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。
def main():
stu1=Student('张苏',38)
stu1.study('python程序设计')
stu1.wacht_av()
stu2=Student('张三',15)
stu2.study('思想品德')
stu2.wacht_av()
if __name__ == '__main__':
main()
继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
class 猫:
def 喵喵叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
class 狗:
def 汪汪叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
class 动物:
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):
def 喵喵叫(self):
print '喵喵叫'
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):
def 汪汪叫(self):
print '喵喵叫'
多态
不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
1、多态 可以 增加代码的灵活度
2、以 继承 和 重写父类方法 为前提
3、是调用方法的技巧,不会影响到类的内部设计
多态案例演练
需求:
1、在Dog
类中封装方法game
普通狗只能是简单的玩耍
2、定义XiaoTianDog
继承自Dog
,并且重写game
方法
哮天犬需要在天上玩耍
3、定义Person
类,并且封装一个和狗玩的方法
在方法内部,直接让狗对象调用game
方法
class Dog(object):
def __init__(self,name):
self.name=name
def game(self):
print("%s 蹦蹦跳跳愉快的玩耍...")
class XiaoTianDog(Dog):
def game(self):
print("% 飞到天上去玩耍...")
class Person(object):
def __init__(self.name):
self.name=name
def game_with_dog(self,dog)
print("%s 和 %s 快乐的玩耍" %(self.name,dog.name))
#1. 创建一个狗对象
wangcai=XiaoTianDog("飞天旺财")
#2. 创建一个小明对象
xiaoming=Persion("小明")
#3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)
访问可见性问题
对于上面的代码,有C++、Java、C#等编程经验的程序员可能会问,我们给Student对象绑定的name和age属性到底具有怎样的访问权限(也称为可见性)。因为在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo)
if __name__ == "__main__":
main()
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)
if __name__ == "__main__":
main()
强化练习
练习1:设计一个Circle(圆)类
包括圆心位置,半径、颜色等属性。编写构造方法和其他方法,计算周长和面积
from math import pi
class Circle:
def __init__(self,color,point,radius):
self.color=color
self.point=point
self.radius=radius
def area(self):
return pi*self.radius*self.radius
def perimeter(self):
return 2*pi*self.radius
circle=Circle(color="red",point="18",radius=10)
area1=circle.area()
per1=circle.perimeter()
color1=circle.color
point1=circle.point
print(area1,per1,color1,point1)
练习2:定义一个类描述数字时钟
from time import sleep
class Clock(object):
def __init__(self,hour=0,minute=0,second=0):
self.hour=hour
self.minute=minute
self.second=second
def run(self):
self.second+=1
if self.second==60:
self.second=0
self.minute+=1
if self.minute==60:
self.minute=0
self.hour+=1
if self.hour == 24:
self.hour=0
def __str__(self):
return '%02d:%02d:%02d' %(self.hour,self.minute,self.second)
def main():
clock=Clock(23,59,58)
while True:
print(clock)
sleep(1)
clock.run()
if __name__ == '__main__':
main()
练习3:定义和使用矩形类
class Rect(object):
def __init__(self,width=0,height=0):
self.__width=width
self.__height=height
def perimeter(self):
return (self.__height+self.__width)*2
def area(self):
return (self.__width*self.__height)
def __str__(self):
return '矩形[%f,%f]' %(self.__width,self.__height)
def __del__(self):
print('销毁矩形对象')
if __name__ == '__main__':
rect1=Rect()
print(rect1)
print(rect1.perimeter())
print(rect1.area())
rect2=Rect(4,5)
print(rect2)
print(rect2.perimeter())
print(rect2.area())