继承、抽象类、多继承

# 继承的引子
# 假设我们现在有一个游戏,游戏中有这些具体的事物(对象 ): 梅西、奥巴马、史努比、史派克、小猪配齐、猪八戒。
# 采取面向对象设计的方法,是要对游戏中的具体对象,进行分门别类的抽象,抽取中相似对象的共同特征和行为来,作为一种规范,
# 一种模板来,梅西和奥巴马,它们的相似,具有人的特征,因此,我们把它们归为一类,即人类,就抽象出人类这一概念来了。
# 史努力和史派克 具有共同的特征和行为,我们把它们归为一类,并取名为狗类,完成具体到综合的第一次抽象,而小猪配齐和希猪八戒,
# 它们两个具体的事物,又具有共性的东西,因此将它们归为一类,并取名为猪类。
# 以上,通过对抽取相似具体事物的共同特征,归为一类,完成了事物的第一层抽象。
# 梅西、奥巴马、史努比、史派克、小猪配齐、猪八戒 ===》 人类 、狗类 、 猪类 # 基本的分类
# 人类 、狗类 、 猪类 ===》 经过再一次抽象,抽象出 动物类。
# 因此面向对象的过程,就是由具体 到 抽象 的过程。从宏观上,概念上 统领相似事物。抽象的层次,越高,能够描述和规范的事物就越多。
# # 猫类:
# 属性: 名字,品种,食物
# 方法: 叫,抓老鼠,吃,喝
# 狗类:
# 属性: 名字,品种,食物
# 方法:叫,看家,吃,喝
# 人类:
# 属性:名字,品种,食物
# 方法:说话,工作,吃,喝
# 虽然,我们从具体的事物,抽象出三个类别,但是我们发现,这三类之间,还有共同的代码,没有提高代码的重用性。
# 于是乎,我们可以将三个类别的共同代码,再抽取出来,建立一个类别,比如说叫动物类,它让猫类、狗类、人类隶属于这个类,
# 也就具有这个类的属性。
# 那么用puthon代码如何实现呢?

# 定义 一个 Animal() 的父类
class Animal: # 父类,超类,基类
def __init__(self,name,kind,food,language):
self.name = name
self.kind = kind
self.food = food
self.language = language

def yell(self):
print("%s叫" % self.language)

def eat(self):
print("吃%s" % self.food)

def drink(self):
print("喝水")

# 定义 类的 时候 ,继承某个类,它将继承父类的所有属性 和 方法,继承的写法,就是将类 要继承的父类,放在类名后面的小括号里。

class Cat(Animal): # 派生类
def catch_mouse(self):
print("抓老鼠")

class Dog(Animal): # 派生类

def __init__(self,name,kind,food,language,eye_color): # 子类重写父类的方法,并通过super()方法,调用父类的同名方法
self.eye_color = eye_color # 派生属性
super().__init__(name,kind,food,language) # self ,不用传,会自动传
def swim(self): # 派生方法
print("游泳")

def look_afte_home(self):
print("看家")

class Human(Animal): # 派生类
def __init__(self): # 这里子类对父类的方法,进行了重写,即覆盖了父类,此时父类的同名方法,对子类就不可见了。
self.walk_way = "直立行走"
def work(self):
print("工作")


# 在继承关系中,被继承的类,我们称之为超类/基类/父类, : 比如上面的 Animal类
# 继承的类,叫做子类/派生类: 比如上面的 Cat, Dog, Person


# 继承与重用:是同一个事物的两方面,类的继承就是了为提高代码的重用性,提高代码的重用性,除了函数,类就是别外一种的实现方式。
# 比如说:写了两个类或多个类,它们有共同的代码或逻辑,这时候就可以为它们创建一个父类,把共同的代码或逻辑放到父类里,这时通过继承
# 就实现了代码的重用性。
# 继承与重用,父类中的所有方法和属性都被子类中使用了。
阿猫 = Cat("阿猫","花猫","鱼","喵喵")
阿猫.yell()
阿猫.eat()
阿猫.drink()
print(阿猫.name)

yellDog = Dog("黄狗","土狗","骨头","汪汪","蓝色")
yellDog.eat()
yellDog.drink()
yellDog.yell()
print(yellDog.name)

xiaomin = Human() # 因为子类对父类的 __init__方法,进行了重写,除了self以外,没有参数,不需要手动传参。
print(xiaomin.walk_way)
yellDog.swim()

# 总结:当子类当中有要被调用的方法 和 属性的时候,子类的对象会直接选择子类中的方法、变量,父类中的同名方法不会被自动执行。
# 如果我们既想要执行子类中方法,也想要执行父类中的同名方法,那么需要在子类的方法中调用父类的方法:有两种方式:
# 父类名.方法名(...)
# super().方法名(...)

# 面试题:

class Foo:
def __init__(self): # 初始化对象实例的函数,此函数仅在调用 类名() 时执行一次
self.func() # 对象调用
def func(self):
print('in Foo')

class Son(Foo):
# def func(self):
# print("in Son")
pass

s1 = Son() # 创建了一个Son类的对象实例,并赋值给对象引用 s1。过程是,在内存中开辟一个对象空间,根据对象所属的直接类的类
#对象指针 找寻 __init__方法执行,子类没有,在父类中寻找,找到将对象传递参数self,执行 __init__函数体,执行self.func()语句,
# 对象执行一个方法,先在对象所属直接类中,找到,直接执行,没有找到,再到父类找,所以此处是打印 in Son

# class Foo:
# Country = 'China'
# def func(self):
# print(self.Country)
#
# class Son(Foo):
# Country = 'English'
# def func(self): # 走这个方法
# print(self.Country)
#
# s = Son()
# s.func()

# class Foo:
# Country = 'China'
# def func(self): # 走这个方法
# print(self.Country)
#
# class Son(Foo):
# Country = 'English'
#
# s = Son()
# s.func() # English

# class Foo:
# Country = 'China'
# def func(self):
# print(self.Country)
#
# class Son(Foo):pass
#
# s = Son()
# s.func() # 'China'
# 结论:始终注意,那个对象调用 ,self就是哪个对象

# 抽象类
# 工作中 公司使用抽象类 ,来规范 开发的规则。
# 源码 别人使用抽象类

from abc import ABCMeta,abstractmethod # 从 abc 这个外部模块中,导入 ABCMeta 类 和 abstracmethod这个用于装饰函数的装饰器

class Payment(metaclass=ABCMeta): # 给类的元类,传递一个关键字参数
@abstractmethod
def pay(self):pass # 抽象方法,仅有定义,没有方法的实现

@abstractmethod
def shouqian(self):pass # 假如,把这两行的注释打开,如果子类不实现这个方法,程序会报错
# a = Alipay()
# TypeError: Can't instantiate abstract class Alipay with abstract methods shouqian
def abstract(self):
print("我继承抽象类")

class Alipay(Payment):
def pay(self,money):
print("使用支付宝支会了%s元" % money)
def shouqian(self,money):
print("使用收款方式收到%s元" % money)
class Wechatpay(Payment):
def pay(self,money):
print("使用微信支付了%s元" % money)
def shouqian(self,money):
print("使用收款方式收到%s元" % money)
class Applepay(Payment):
def pay(self,money):
print("使用applepay支付了%s元" % money)
def shouqian(self,money):
print("使用收款方式收到%s元" % money)

# def pay(obj,money):
# obj.pay(money)

a = Alipay()
a.pay(100)
a.shouqian(2000)

a.abstract() # 抽象类,也可以有普通方法,子类继承
wechat = Wechatpay()
wechat.pay(260)
wechat.shouqian(489)

apple = Applepay()
apple.pay(159)
apple.shouqian(6000)

# 以上多个子类,继承同一个抽象类,子类必须去实现抽象类中用@abstractmethod装饰器装饰的方法,否则会报错。
# 实现了不同的对象,调用相同的方法

# 在上面的基础上,我们来定义 一个 统一的函数,也可以叫接口

def 付钱(obj,money):
obj.pay(money)
# 这样,func()就是一个统一的支付接口
付钱(wechat,1600)
付钱(a,3500)

def 收钱(obj,money):
obj.shouqian(money)
收钱(a,10000)
收钱(apple,30000)

# p = Payment() # 抽象类 , 不能实例化 TypeError: Can't instantiate abstract class Payment with abstract methods pay, shouqian


# 总结: 抽象类 是一种 规范
# 多人开发、复杂的需求、后期的扩展
# 手段,来帮助我们完成规范。

# 抽象类 如何创建
# 抽象类是一个规范,它基本上不会实现什么具体的功能,抽象类是不能被实例化的。
# 要想写一个抽象类的步骤:
# from abc import ABCMeta,abstractmethod
# 在创建这个类的时候,指定 metaclass = ABCMeta
# 在你希望子类实现的方法上加一个 @abstractmethond 装饰器
# 使用抽象类:
# 继承这个类
# 必须实现这个类中被 @abstractmehond 装饰器装饰的方法
原文地址:https://www.cnblogs.com/chris-jia/p/9557264.html