Python面向对象编程(三)——— 面向对象三大特性(封装、继承、多态)

一、封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。在面向对象编程中它的概念就是将 属性和方法 放到 类内部,通过 对象 访问属性或者方法,隐藏功能的实现细节,当然还可以设置访问权限。

下面我们看一个例子:

class Student(object):  # 定义类
    def __init__(self,name,age,height):  # 封装属性
        self.name = name      
        self.age = age
        self.height = height
    
    def print_age(self):  # 封装方法
        return("{}的年龄是:{}岁".format(self.name,self.age))
        
    def print_height(self): # 封装方法
        return("{}的身高是:{}cm".format(self.name,self.height))

student = Student("小明",21,180)  # 类的实例化
print(student.print_age()) # 调用方法
print(student.print_height()) # 调用方法

小明的年龄是:21岁
小明的身高是:180cm

由上面的例子我们可以知道,当我们需要打印某个人的信息时,只需要录入其信息,然后通过方法调用即可打印,而不需要知道其内部是如何实现的,这就是封装的概念。

二、继承

继承的概念是指:子类拥有父类的所有属性和方法,当然子类可以提供自己的属性和方法。当我们定义一个类的时候,可以从某个现有的类继承,新的类被称为子类,而被继承的类称为基类、父类或超类。

2.1 单继承

只继承一个类的话称为单继承,下面看一下例子:

class Person(object): # 定义父类
    def run(self):
        return("我可以跑步")
    
    def sing(self):
        return("我可以唱歌")

class Man(Person):   # 定义子类,继承父类
    def repair(self):
        return("我可以修理电脑")
        
man = Man() # 类实例化对象
print(man.run()) # 调用父类方法
print(man.sing()) # 调用父类方法
print(man.repair()) # 调用子类自己的方法
print(man.name) # 调用父类的属性

我可以跑步
我可以唱歌
我可以修理电脑

由上面的例子我们可以看到,子类可以调用父类的方法,也可以访问父类的属性。

2.2 多继承

多继承的概念就是子类可以继承多个父类,并且具有所有父类的属性与方法,下面我们看一个例子

class Singer(object):
    def fun1(self):
        return("我是一个歌手")

class Dancer(object):
    def fun2(self):
        return("我是一个舞者")
        
class Man(Singer,Dancer):
    def fun3(self):
        return("我既是一个歌手又是一个舞者")

man = Man()
print(man.fun1())
print(man.fun2())
print(man.fun3())

我是一个歌手
我是一个舞者
我既是一个歌手又是一个舞者

我们可以看到子类可以调用父类的两个方法

2.3 继承中的注意事项

2.3.1 继承的传递性

假设我们现在有三个类:A、B、C,A是B的父类,B是C的父类,那么C不仅有B的方法与属性还具有A的方法与属性,这个概念就叫做继承的传递性

class Person(object): # 定义父类的父类
    def run(self):
        return("我可以跑步")

class Male(Person): # 定义父类
    def repair(self):
        return("我可以修理汽车")

class Boy(Male): # 定义子类
    def play(self):
        return("我可以玩耍")
        
        
boy = Boy()
print(boy.run())
print(boy.repair())
print(boy.play())

我可以跑步
我可以修理汽车
我可以玩耍

2.3.2 父类方法与子类重名

只要子类与父类的方法名相同,调用方法,子类就会覆盖父类的方法(重写父类方法),在运行中,只会调用在子类中重写的方法而不会调用父类的方法。注意重写后父类方法并不会改变,我们看一下下面的例子:

class Person(object): # 定义父类
    def run(self):
        return("我可以跑步")

class Man(Person): # 定义子类
    def run(self):
        return("我可以跑得非常快!!!")
    



man = Man()

print(man.run()) # 调用的是子类的方法

print(Person.run(man)) # 查看一下父类的方法

我可以跑得非常快!!!
我可以跑步

我们可以看到子类方法把父类方法覆盖,而且父类方法并没有被修改。

2.3.3 父类与子类都使用__init__()方法

1、先看一下父类使用子类不用

class Person(object): # 定义父类
    def __init__(self,age=20):
        self.age = age
    
class Man(Person): # 定义子类
    pass
        
man = Man() # 类的实例化

print(man.age) # 调用父类绑定的属性

20

2、看一下都用的情况
Python面向对象编程(三)——— 面向对象三大特性(封装、继承、多态)_第1张图片
我们可以发现只打印出了子类的属性,但是并没有打印出父类的,因为父类的__init__()方法被子类所屏蔽

2.3.4 多继承中寻找方法的方式

这里要提到两个概念经典类与新式类 ,经典类是 “深度优先” 的方式去查找,而新式类是"广度优先"的方式去查找。在Python2中有经典类与新式类的区分,而在Python3中所有的类都是新式类,我们这里只介绍新式类。看一下下面例子:

class A1(object): 
    def fun1(self):           
        print('A1_fun1') 

class A2(object):
    def fun1(self): 
        print('A2_fun1') 

    def fun2(self): 
        print('A2_fun2') 

class B1(A1,A2): 
     def fun2(self):
        print("B1_fun2")

class B2(A1,A2): 
     pass
     

class C(B1,B2): 
     pass 

c=C() 
c.fun1() 
c.fun2()  

# 实例c调用fun1()时,搜索顺序是 c->C->B1->B2->A1
# 实例c调用fun2()时,搜索顺序是 c->C->B1

A1_fun1
B1_fun2

在这里的广度优先遍历就和咱们数据结构中的一样,下面是一个示意图:
Python面向对象编程(三)——— 面向对象三大特性(封装、继承、多态)_第2张图片
从第一行开始,一行一行的遍历,直到找到方法为止。

三、多态

3.1 多态的概念

多态按字面的意思就是“多种状态” ,用通俗一点的说法来说:多态就是指不同对象调用同一个方法功能的表现形式不一样,例如:不同的两个对象,列表的加法和整数的加法,同样是加法,实现的功能是不一样的,下面我们看一个例子:

class A(object):  # 父类
    def printFun(self):
        return("我是小A")

class B(A):      # 子类
    def printFun(self):
        return("我是小B")

class C(A):    # 子类
    def printFun(self):
        return("我是小C")

b = B()
c = C()
print(b.printFun())
print(c.printFun())

我是小B
我是小C

由上面的代码我们可以看到,B类与C类都继承于A类,但是在调用相同的printFun()
函数时,结果却不相同。

3.2 Python中多态的不同之处

在Python中,崇尚鸭子类型,在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。鸭子类型可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”我们看一下下面的例子:

class A(object):           # 父类
    def printFun(self):
        return("我是小A")

class B(A):                 # 子类
    def printFun(self):
        return("我是小B")

class C(A):                # 子类
    def printFun(self):
        return("我是小C")
    
class D:         
    pass
    
def fun(name):
    return(name.printFun())


b = B()
c = C()
d = D()

print(fun(b))
print(fun(c))
print(fun(d))

Python面向对象编程(三)——— 面向对象三大特性(封装、继承、多态)_第3张图片我们看到上面的例子,因为b子类对象和c子类对象都有printFun()函数,所以它们可以打印。而d对象没有该方法,所以它打印时会报错,反而言之,如果它有该方法,即使它不继承于A类,它也可以打印。

我们要理解鸭子类型,需要和Java语言所对比。在Java中函数传入的参数必须要指定类型,以上面的例子来讲,如果它的参数类型是一个A类,那么在传入参数时,只能传入A类以及它的子类对象,这样才能运行下面的方法。而在Python中则不是,不管传入的参数类型是否是该类或者该类的子类,它都只关注于该类下是否有该方法,有该方法就可以执行。

你可能感兴趣的:(python)