接下来就是面向对象的三大特征
封装 、 继承 、 多态
一.封装
上节课讲过了封装,这里简单的复习下
1.含义
封装是对全局作用域中其它区域隐藏多余信息的原则。
2.实例
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
代码如下:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问
实例变量.__name
和实例变量.__score
了。
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name
和get_score
这样的方法:
class Student(object):
#这里的代码和上面一样,定义两个私有属性__name和__score
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法:
class Student(object):
#代码同上
...
def set_score(self, score):
self.__score = score
需要注意的是,在Python中,变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量
,所以,不能用__name__
、__score__
这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
二.继承
1.什么是继承
我们不想把同一段代码写好几次,之前使用的函数避免了这种情况。但现在又有个更微妙的问题。如果已经有了一个类,又想建立一个非常类似的类,只是添加几个方法。
比如有人类,我们又想在人类的基础上建立学生类、医生类,教师类。因为他们都具有共同的属性和方法,比如都有 姓名 、年龄 、性别 等共同属性,还有吃饭、睡觉等共同方法。我们就可以写一个人类作为父类,包括姓名、年龄、性别等属性和吃饭睡觉等方法。然后再写多个子类继承父类的这些属性和方法。
但需要注意的是,父类的私有属性和方法不会被子类继承
话不多说,直接上代码:
#父类
class Person():
def __init__(self,name=None,age=None,sex=None):
self.name=name
self.age=age
self.sex=sex
def eat(self):
print("%s正在吃饭"%self.name)
#学生子类:继承人类父类的属性
class Student(Person):
#子类的初始化参数要和父类的一样,否则没有办法给父类传参,会报错
def __init__(self,name=None,age=None,sex=None,score=None):
# self.name=name
# self.age=age
# self.sex=sex
#上面三行的代码等价于下面一行的代码,都是给父类的属性传参
Person.__init__(self,name,age,sex)
#还可以这样写
#super().__init__(name,age,sex)
self.score=score
#这个可以是子类独有的方法,不会影响到父类
def study(self):
self.eat()
print("%s在学习,考了%d分"%(self.name,self.score))
#实例化一个学生对象,然后可以调用子类的方法,也可以直接调用父类的方法
stu=Student("汤姆",20,"男",80)
stu.study()
以上代码运行结果为:
汤姆正在吃饭
汤姆在学习,考了80分
2.有了继承,我们可以实现子类对父类方法的重写
子类继承父类时,子类的方法签名和父类一样,此时子类重写了父类的方法,当生成子类对象时,调用的是子类重写的方法。
下面上代码:
#父类
class Person():
def __init__(self,name=None,age=None,sex=None):
self.name=name
self.age=age
self.sex=sex
def eat(self):
print("%s正在吃饭"%self.name)
#学生子类:继承人类父类的属性
class Student(Person):
def __init__(self,name=None,age=None,sex=None,score=None):
# self.name=name
# self.age=age
# self.sex=sex
#Person.__init__(self,name,age,sex)
super().__init__(name,age,sex)
self.score=score
def study(self):
self.eat()
print("%s在学习,考了%d分"%(self.name,self.score))
#方法的重写,调用的时候调用子类的方法
#可以对自定义的方法进行重写
def eat(self):
print("%d的%s正在吃饭,他是%s的"%(self.age,self.name,self.sex))
#也可以对自带的object类的方法进行重写。
def __str__(self):
return "姓名:{0},年龄:{1},性别:{2},成绩:{3}".format(self.name,self.age,self.sex,self.score)
def __lt__(self,other):
""" if isinstance(other,Student):
return self.age<other.age
else:
return False """
if self.name==other.name:
return self.age<other.age
else:
return self.name<other.name
#实例化
stu=Student("汤姆",20,"男",80)
stu.study()
stu.eat()
list1=[]
stu1=Student("杰克",20,"男",90)
stu2=Student("杰森",21,"男",20)
stu3=Student("杰森",12,"女",50)
list1.append(stu)
list1.append(stu1)
list1.append(stu2)
list1.append(stu3)
for student in list1:
print(student)
list1.sort()
for student in list1:
print(student)
以上代码输出为:
20的汤姆正在吃饭,他是男的
汤姆在学习,考了80分
20的汤姆正在吃饭,他是男的
姓名:汤姆,年龄:20,性别:男,成绩:80
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:汤姆,年龄:20,性别:男,成绩:80
如上,我们对自定义的eat()
方法进行了重写,也对
3.多继承
一个类可以继承多个父类。
class A:
def __init__(self,a=None):
self.a=a
def test(self):
print("A...test")
class B:
def __init__(self,b=None):
self.b=b
def test(self):
print("B...test")
class C(B,A):
def __init__(self,a):
A.__init__(self,a)
def t(self):
A.test(self)#调用A的test()
super().test()#这个调用的也是B的test
print("C....t")
c=C("aa")
#默认调用的是父类B的test方法,因为在class C(B,A),B在A前面
c.test()
c.t()
以上实例输出结果为:
B...test
A...test
B...test
C....t
class C(B,A),当有AB均有相同方法,而子类又重写时,调用子类的方法,如果子类没有方法,则调用在继承时写在前面的父类的方法(这里也就是B)。
三.多态
1.什么是多态?
当子类和父类都存在相同的方法时,我们说,子类的方法覆盖了父类的方法,在代码运行的时候,总是会调用子类的方法。这样,我们就获得了继承的另一个好处:多态。
2.多态的实例
简单工厂模式
就是典型的多态体现:
让用户输入要选择的汉堡,他不需要知道内部是如何制作的,只要得到一个选择的汉堡实例对象就可以
#创建汉堡的父类,并根据父类创建几个子类
class Hamburger:
def make(self):
print("您没有正确选择要制作的汉堡,请重新输入")
class FishHamburger(Hamburger):
def make(self):
print("您的鱼肉汉堡已经制作好了")
class BeafHamburger(Hamburger):
def make(self):
print("您的牛肉汉堡已经制作好了")
class ChickenHamburger(Hamburger):
def make(self):
print("您的鸡肉汉堡已经制作好了")
#工厂类,用来判断用户输入的值并创建相应的对象
class HamburgerFactory:
@classmethod
def getinput(cls,temp):
if temp=="1":
ch=FishHamburger()
elif temp=="2":
ch=BeafHamburger()
elif temp=="3":
ch=ChickenHamburger()
else:
ch=Hamburger()
return ch
#主方法,通过用户输入的值调用工厂的类方法
while True:
temp=input("请输入您要制作汉堡的序号,1.鱼肉汉堡,2.牛肉汉堡,3.鸡肉汉堡")
if temp=="1" or temp=="2" or temp=="3":
ch=HamburgerFactory.getinput(temp)
ch.make()
break
else:
ch=Hamburger()
ch.make()
continue