继承
是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,或者又可以称为基类或者超类,新建的类称为派生类或子类。
继承分为单继承和多继承
class A: #父类(基类或者超类)
pass
class B: #父类
pass
class A_son(A,B): #子类(派生类)
pass
一个类可以被多个类继承,且可以继承多个父类(仅仅在python中)。
print(A_son.__bases__)
__bases__
可以查看父类。在Python3 中所有的类,都有父类,没有声明继承父类的类,都默认继承object
类。
继承与抽象
继承是基于抽象的结果,通过编程语言去实现它,肯定是先经理抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧。通过抽象,才能得到类。
面试题
class Animal:
def __init__(self):
print('执行Animal.__init__')
self.func()
def eat(self):
print('%s eating'%self.name)
def drink(self):
print('%s drinking'%self.name)
def func(self):
print('Animal.func')
class Dog(Animal):
def guard(self):
print('guarding')
def func(self):
print('Dog.func')
dog = Dog()
输出:
可以看到,当子类中有相应的方法的时候,会优先调用自己的方法。当自己没有这个方法的时候,会在父类中找,找到之后,调用父类中的方法。
派生
派生属性
父类中没有的属性,在子类中出现,叫做派生属性。
class Animal():
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
Animal.__init__(self,name,aggr,hp) #self也要传过来。
self.kind = kind #派生属性
def bite(self,person):
person.hp -= self.aggr
class Person(Animal):
def __init__(self,name,aggr,hp,sex):
Animal.__init__(self,name,aggr,hp)
self.sex = sex #派生属性
self.money = 0 #派生属性
派生方法
父类中没有的方法,在子类中出现,叫做派生方法。
class Animal():
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
Animal.__init__(self,name,aggr,hp) #self也要传过来。
self.kind = kind #派生属性
def bite(self,person): #派生方法
person.hp -= self.aggr
如果在子类中还要用到父类中的方法,需要单独调用父类的方法:
父类名.方法名
需要自己传self参数,或者super().方法名
不需要自己传self
class Animal:
def __init__(self, name, aggr, hp):
self.name = name
self.aggr = aggr
self.hp = hp
def eat(self):
print('吃东西')
self.hp += 100
class Dog(Animal):
def __init__(self, name, aggr, hp, kind):
super().__init__(name, aggr, hp) # 只在新式类中有,python3中所有类都是新式类
self.kind = kind # 派生属性
def eat(self):
print('大黄吃粑粑')
huang = Dog('大黄', 200, 500, 'tugou')
print(huang.name)
huang.eat()
super(Dog, jin).eat() #不仅可以在方法中调用,还可以在函数外调用。方法是super(父类,实例化名称).方法()
输出:
class A:
def func(self):
print('A')
class B:
def func(self):
print('B')
class C:
def func(self):
print('C')
class D(A,B,C):
pass
# def func(self):
# print('D')
# pass
d = D()
d.func()
输出:
当多继承的时候,如果子类中没有该方法,就从离他最近的父类中找,就如上述代码中的A
下面是更复杂的一种:
class A:
def func(self):
print('A')
class B(A):
# def func(self):
# print('B')
pass
class C:
def func(self):
print('C')
# pass
class D(C):
# def func(self):
# print('D')
pass
class E(B,D):
# def func(self):
# print('E')
pass
e = E()
e.func()
输出:
class A:
def func(self):
print('A')
class B(A):
# def func(self):
# print('B')
pass
class C(A):
# def func(self):
# print('C')
pass
class D(B):
# def func(self):
# print('D')
pass
class E(C):
# def func(self):
# print('E')
pass
class F(D,E):
# def func(self):
# print('F')
pass
F = F()
F.func()
继承顺序:

print(类名.mro())
方法可以把继承顺序显示出来。
所以,在新式类中的继承顺序为“广度优先”。
Python2.7中,新式类和经典类共存,新式类要继承object
类。
Python3 中,只有新式类,默认继承object
经典类中,进行深度优先
在新式类中,进行广度优先。
经典类和新式类,,mro()
方法只有新式类中才有,super()
只在Python3中使用。
super
super()
的本质不是单纯的找父类,而是根据调用者的节点位置的广度优先顺序来。
class A(object):
def func(self):
print('A')
class B(A):
def func(self):
super().func()
print('B')
# pass
class C(A):
def func(self):
super().func()
print('C')
# pass
class D(B):
def func(self):
super().func()
print('D')
# pass
class E(C):
def func(self):
super().func()
print('E')
# pass
class F(D,E):
def func(self):
super().func()
print('F')
# pass
F = F()
F.func()
输出:
多态
一类事物有多种状态。做一件事情根据实际情况开设计实际的方法。
Python天生支持多态。
在其他语言中,我们需要通过如下的代码实现多态。(下面的例子在别的语言中可能出错,没有按规范写)
class Payment:
pass;
class Alipay(Payment):
def pay(self,money):
print('已经用支付宝支付了%s元' % money)
class Applepay():
def pay(self,money):
print('已经用applepay支付了%s元' % money)
def pay(Payment pay_obj,int money):
pay_obj.pay(money)
pay()
然而在Python代码中,我们可以直接通过如下代码:
class Alipay():
def pay(self, money):
print('已经用alipay支付了%s元' % money)
class Applepay():
def pay(self, money):
print('已经用applepay支付了%s元' % money)
def pay(pay_obj, money): # 统一支付入口 归一化设计
pay_obj.pay(money)
ap = Applepay()
ali = Alipay()
pay(ap, 13)
pay(ali, 13)
Python语言不崇尚根据继承所得来的相似。例如List
,Tuple
这两个类型的数据,两者的很多方法是一样的,但不是通过继承的方式来约束其相似的方法,而是自己实现自己的代码。
这种实现方法的方式称为“鸭子类型”。那么也就是说,如果两个类刚好相似,并不是一个父类的子类的兄弟关系,那么就是鸭子类型。这种编程方式的优点是松耦合,每个相似的类之间都没有影响。
封装
广义上面向对象的封装:代码的保护,面向对象的思想本身就是一种封装,之让用实例化的对象来操作类中属性或者方法。
狭义上面向对象的封装:将属性和方法都藏起来,不让你看见。
class Person:
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性
def get_pwd(self):
return self.__passwd
alex = Person('zhangsan','1234')
print(alex.get_pwd())
print(alex.__passwd)
输出:
私有属性不可以通过实例化名称.属性
的方式得到值。
带有私有属性、方法的代码:
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性
def __get_pwd(self): # 私有方法
return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名
def login(self): # 正常的方法调用私有的方法
self.__get_pwd()
alex = Person('zhangsan','1234')
print(alex._Person__passwd) # _类名__属性名
print(alex.get_pwd())
输出:
当定义了私有属性的时候,可以通过_类名__属性名
的方式调用私有属性,但不赞成这样去使用。私有方法的调用跟私有属性的规则相似,不能通过实例化名称.方法
的方式得到值。
类中定义方法和属性的时候,在前面加上__
(双下划线),会变成私有的方法或者属性。
所有的私有方法或属性,都不能再类的外部使用,且只能在类的内部声明。如果在外部声明一个私有类,则可以在外部使用,即在外部声明的类似与私有属性相似的属性,不是私有属性。
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性
def __get_pwd(self): # 私有方法
return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名
def login(self): # 正常的方法调用私有的方法
self.__get_pwd()
alex = Person('zhangsan','1234')
alex.__weight = 130
print(alex.__weight)
输出:
在上述代码中说定义的alex.__weight = 130
并不是一个私有属性。
在Python类中声明的属性,相比于其他语言声明的属性访问限制如下表。Python中没有protect类型的数据。
python | 其他 |
---|---|
self.属性 | public |
self.__属性 | private |
访问修饰符public、private、protect、default范围:
同一个类中 | 同一个包 | 不同包的子类 | 不同包且非子类 | |
---|---|---|---|---|
private | 是 | |||
protect | 是 | 是 | 是 | |
public | 是 | 是 | 是 | 是 |
private:一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
protect:介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法 及子类访问,即使子类在不同的包中也可以访问。
public:访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包访问。