封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。在面向对象编程中它的概念就是将 属性和方法 放到 类内部,通过 对象 访问属性或者方法,隐藏功能的实现细节,当然还可以设置访问权限。
下面我们看一个例子:
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
由上面的例子我们可以知道,当我们需要打印某个人的信息时,只需要录入其信息,然后通过方法调用即可打印,而不需要知道其内部是如何实现的,这就是封装的概念。
继承的概念是指:子类拥有父类的所有属性和方法,当然子类可以提供自己的属性和方法。当我们定义一个类的时候,可以从某个现有的类继承,新的类被称为子类,而被继承的类称为基类、父类或超类。
只继承一个类的话称为单继承,下面看一下例子:
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) # 调用父类的属性
我可以跑步
我可以唱歌
我可以修理电脑
由上面的例子我们可以看到,子类可以调用父类的方法,也可以访问父类的属性。
多继承的概念就是子类可以继承多个父类,并且具有所有父类的属性与方法,下面我们看一个例子
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())
我是一个歌手
我是一个舞者
我既是一个歌手又是一个舞者
我们可以看到子类可以调用父类的两个方法
假设我们现在有三个类: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())
我可以跑步
我可以修理汽车
我可以玩耍
只要子类与父类的方法名相同,调用方法,子类就会覆盖父类的方法(重写父类方法),在运行中,只会调用在子类中重写的方法而不会调用父类的方法。注意重写后父类方法并不会改变,我们看一下下面的例子:
class Person(object): # 定义父类
def run(self):
return("我可以跑步")
class Man(Person): # 定义子类
def run(self):
return("我可以跑得非常快!!!")
man = Man()
print(man.run()) # 调用的是子类的方法
print(Person.run(man)) # 查看一下父类的方法
我可以跑得非常快!!!
我可以跑步
我们可以看到子类方法把父类方法覆盖,而且父类方法并没有被修改。
1、先看一下父类使用子类不用
class Person(object): # 定义父类
def __init__(self,age=20):
self.age = age
class Man(Person): # 定义子类
pass
man = Man() # 类的实例化
print(man.age) # 调用父类绑定的属性
20
2、看一下都用的情况
我们可以发现只打印出了子类的属性,但是并没有打印出父类的,因为父类的__init__()方法被子类所屏蔽
这里要提到两个概念经典类与新式类 ,经典类是 “深度优先” 的方式去查找,而新式类是"广度优先"的方式去查找。在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
在这里的广度优先遍历就和咱们数据结构中的一样,下面是一个示意图:
从第一行开始,一行一行的遍历,直到找到方法为止。
多态按字面的意思就是“多种状态” ,用通俗一点的说法来说:多态就是指不同对象调用同一个方法功能的表现形式不一样,例如:不同的两个对象,列表的加法和整数的加法,同样是加法,实现的功能是不一样的,下面我们看一个例子:
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()
函数时,结果却不相同。
在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))
我们看到上面的例子,因为b子类对象和c子类对象都有printFun()函数,所以它们可以打印。而d对象没有该方法,所以它打印时会报错,反而言之,如果它有该方法,即使它不继承于A类,它也可以打印。
我们要理解鸭子类型,需要和Java语言所对比。在Java中函数传入的参数必须要指定类型,以上面的例子来讲,如果它的参数类型是一个A类,那么在传入参数时,只能传入A类以及它的子类对象,这样才能运行下面的方法。而在Python中则不是,不管传入的参数类型是否是该类或者该类的子类,它都只关注于该类下是否有该方法,有该方法就可以执行。