面向对象基础

1  面向对象基础
    1.1  面向对象和面向过程
        1.1.1  面向过程
        1.1.2  面向对象
        1.1.3  对比一下
    1.2  面向对象的实现
        1.2.1  类的定义
        1.2.2  构造函数
        1.2.3  方法
        1.2.4  self
        1.2.5  总结
        1.2.6  练习题
    1.3  面向对象和面向过程的比较
        1.3.1  大象装冰箱示例
        1.3.2  小猪佩奇大战奥特曼
    1.4  面向对象的三大特征
        1.4.1  封装
        1.4.2  继承
        1.4.3  多态
2  面向对象基础2
    2.1  类的成员简介
    2.2  变量
        2.2.1  实例变量
        2.2.2  类变量(静态变量)
        2.2.3  总结
    2.3  方法
        2.3.1  实例方法
        2.3.2  静态方法
        2.3.3  类方法
    2.4  属性方法
    2.5  私有成员
        2.5.1  私有变量
        2.5.2  私有方法
    2.6  类的使用            
导读目录

1 面向对象基础   

  1.1 面向对象和面向过程     

    1.1.1 面向过程

    过程:指的是解决问题的步骤,这种解决问题的思路就好比是工厂中的流水线。   

    优点:复杂的问题流程化,进而简单化。

    缺点:可扩展性差,比如,一个脚本就是干一件事情的。

    1.1.2 面向对象

    对象:指的是具有相同属性和动作的结合体叫对象。       

    优点:可扩展性强

    缺点:编程的复杂度高于面向过程

    1.1.3 对比一下   

# 问:把大象装进冰箱,总共分几步?

# 面向过程:面向的是事物发展的流程
答:三步啊,
第一步,把冰箱门打开。
第二步,把大象装进去。
第三步,把冰箱门关上。


#面向对象:操纵某个事物个体
首先,我们需要有两个对象--冰箱和大象。冰箱可以开门和关门,大象可以被放进冰箱。

冰箱开门。
大象走进冰箱。
最后冰箱门关上。
这就是面向对象的思维方式。

  1.2 面向对象的实现   

  怎么造一辆汽车?普通小轿车,跑车

  肯定是先由设计师来设计,在图纸上勾勒出汽车应该有的东西,以及这辆汽车应该有的功能【类】,然后再交给工厂去生产真正的汽车【属性】。不同类型的车,进行不同实例化。

    1.2.1 类的定义

# 定义
class Car:
    pass
# 实例化
c1 = Car()  # 造一辆车
c2 = Car()  # 再造一辆车

    1.2.2 构造函数

    在Python中我们是通过在类中定义__init__()“函数”,来进行初识化操作,在创建对象的时候就能给对象设置一些初始的属性信息。这个“函数”被称为构造函数(方法)。

class Car:
    def __init__(self, color, code, power):
        # self表示当前创建的这个对象,你创建的是谁self就指的谁
        self.color = color
        self.code = code
        self.power = power


c1 = Car('red', 'SNIS-517', '2.0T')
c2 = Car('green', 'SNIS-594', '3.0T')

    1.2.3 方法

    汽车不光有特征,它还应该有”动作” —— 车还会跑。

class Car:
    def __init__(self, color, code, power):
        # self表示当前创建的这个对象,你创建的是谁self就指的谁
        self.color = color
        self.code = code
        self.power = power

    def run(self, speed):
        print('车可以跑{}迈'.format(speed))


c1 = Car('red', 'SNIS-517', '2.0T')
c1.run(70)  # 此时,Python会自动把c1这个对象当成run()的第一个参数传进去

    1.2.4 self

    构造函数中:__init__中的self表示创建的当前对象。

    方法中:定义的函数中的self表示谁调用的方法,self就是谁。

    1.2.5 总结

    类:是对事物的总结,是一种抽象的概念。类用来描述对象。

    对象:是类实例化的结果,对象能执行哪些方法,都是由类来定义的。类中定义了什么,对象就拥有什么。

    1.2.6 练习题

from math import pi

class Circle:
    """
    定义了一个圆形类;
    提供计算面积(area)和周长(perimeter)的方法
    """
    def __init__(self, radius):
        self.radius = radius  # 圆的半径

    def area(self):
        return pi * self.radius * self.radius

    def perimeter(self):
        return 2 * pi * self.radius


circle = Circle(10)      # 实例化一个radius=10的圆
area1 = circle.area()        # 计算圆面积
per1 = circle.perimeter()  # 计算圆周长
print(area1, per1)           # 打印圆面积和周长

  1.3 面向对象和面向过程的比较

    1.3.1 大象装冰箱示例

## 面向过程

# 非函数版
print('开冰箱门')
print('把大象装进冰箱')
print('关冰箱门')

# 函数版
def open_door():
    print('开冰箱门')
def zhuang():
    print('把大象装进冰箱')
def close_door():
    print('关冰箱门')

open_door()
zhuang()
close_door()
--------------------------------------------------------------------------------------------
## 面向对象
class Elephant:
    def open_door(self):
        print('开冰箱门')

    def zhuang(self):
        print('走进冰箱')

    def close_door(self):
        print('把门带上')


dx = Elephant()
dx.open_door()
dx.zhuang()
dx.close_door()
View Code

    1.3.2 小猪佩奇大战奥特曼

# 面向过程
def da_out_man(name, age, jn):
    print('{}, 今年{}岁,正在使用{}技能对奥特曼疯狂输出...'.format(name, age, jn))

def da_bat_man(name, age, jn):
    print('{}, 今年{}岁,正在使用{}技能对蝙蝠侠疯狂输出...'.format(name, age, jn))

def da_spider_man(name, age, jn):
    print('{}, 今年{}岁,正在使用{}技能对蜘蛛侠疯狂输出...'.format(name, age, jn))

da_out_man('小猪佩奇', 18, '嘟嘟嘴')
da_bat_man('小猪佩奇', 18, '嘟嘟嘴')
da_spider_man('小猪佩奇', 18, '嘟嘟嘴')


# 面向对象
class Pig:
    def __init__(self, name, age, skill):
        self.name = name
        self.age = age
        self.skill = skill

    def da_out_man(self):
        print('{}, 今年{}岁,正在使用{}技能对奥特曼疯狂输出...'.format(self.name, self.age, self.skill))

    def da_bat_man(self):
        print('{}, 今年{}岁,正在使用{}技能对蝙蝠侠疯狂输出...'.format(self.name, self.age, self.skill))

    def da_spider_man(self):
        print('{}, 今年{}岁,正在使用{}技能对蜘蛛侠疯狂输出...'.format(self.name, self.age, self.skill))

pq = Pig('小猪佩奇', 18, '嘟嘟嘴')
pq.da_out_man()
pq.da_bat_man()
pq.da_spider_man()
View Code

  1.4 面向对象的三大特征

  面向对象有三大特性:封装、继承和多态。

    1.4.1 封装

# 广义上面向对象的封装:代码的保护,面向对象的思想本身就是一种封装
# 只让自己的对象能调用自己类中的方法
# 封装:属性和方法都藏起来不让用

    1.4.2 继承

       (1)抽象与继承

    抽象是指找出物体之间的共同点,把这些相同的部分抽象成一个划分标准。

      比如黑人和白人之间的共同点是都是人。

      抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度),看下图:

    继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

      (2)继承关系

# 继承:只是类与类之间的关系
# 作用:提高代码重用;规范类的行为
# 父类,基类,超类;     子类,派生类

# 一个类 可以被多个类继承
# 一个类 可以继承多个父类   ———— 【python里】

####################################
# 父类中没有的属性,在子类中出现,叫做派生属性
# 父类中没有的方法,在子类中出现,叫做派生方法

# 只要是子类的对象调用,子类中有的名字一定用子类的,子类中没有才找父类的,如果父类也没有报错;
# 如果父类和子类都有,一定用子类的
# 如果还想用父类的,需要单独调用(super)
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):
        Animal.__init__(self, name, aggr, hp)     ## 注意此处的self: 是Dog中的self,标是Dog调用的方法
        self.kind = kind
    def eat(self):
        print('dog eating')

jin = Dog('小金',100,500,'Teddy')
print(jin.name,jin.kind)    # 小金 Teddy
jin.eat()                   # dog eating     

      (3)super()方法:调用“父类”的方法

       super()的本质:不是直接找父类,而是根据调用者的节点位置,按照广度优先(一层一层)顺序来找下一个节点

       如下例子:按照深度优先遍历,运行结果为ADBC

语法:
# 在子类中使用时:
super().__init__(name, aggr, hp)    # 等价于Animal.__init__(self,name,aggr,hp) 
        # 注意使用super()时,不需要写self

# 对象使用时:
super(当前类,当前对象)    
class Animal:
    def __init__(self,name,aggr,hp):
        self.name = name
        self.aggr = aggr
        self.hp = hp
    def eat(self):
        print('父类Animal中的eat方法')
        self.hp += 100

class Dog(Animal):
    def __init__(self,name,aggr,hp,kind):
        super().__init__(name, aggr, hp)
        self.kind = kind
    def eat(self):
        print('子类Dog eating')

jin = Dog('小金',100,500,'Teddy')
print(jin.name,jin.aggr,jin.kind)
jin.eat()               # 先执行Dog自身的eat()方法
super(Dog,jin).eat()    

# 运行结果
小金 100 Teddy
子类Dog eating
父类Animal中的eat方法

   (4)多继承

     ① 多继承问题

# 在java角度上看是有区别的
    # java 本来就支持单继承,所以就有了抽象类
    # java 本来没有多继承,所以为了接口隔离原则,设计了接口Interface这个概念,支持了多继承
# 在python中
    # python既支持单继承也支持多继承,所以对于接口类和抽象类的区别就不那么明显了
    # 甚至在python中没有内置接口类

          ② 多继承顺序

    使用  子类.mro() 可以显示为该子类记录的继承顺序【子类永远在父类前面】。

class A(object):
    def func(self):
        print('A')
class B(A):
    def func(self):
        super(B, self).func()        # 按照广度优先,此时B的super()是C
        print('B')
class C(A):
    def func(self):
        super(C, self).func()        # C的super()是A
        print('C')
class D(B,C):
    def func(self):
        super().func()
        print('D')
d = D()
d.func()

print(D.mro())   # 显示记录的继承顺序    # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
View Code
    # 深度优先 —— 不同的“爷爷”    
    # 广度优先 —— 相同的“爷爷” 【python3只有新式类:广度优先】
    # 小乌龟问题 —— 先深度遍历后广度遍历

    ③ 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口

############### 借用abc模块来实现接口      ##########
# 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口
# tiger:走路 游泳
# swan: 走路 游泳 飞
# eagle: 走路 飞
from abc import abstractmethod,ABCMeta
class Swim_Animal(metaclass=ABCMeta):
    @abstractmethod
    def swim(self):
        pass
class Walk_Animal:
    @abstractmethod
    def walk(self):
        pass
class Fly_Animal:
    @abstractmethod
    def fly(self):
        pass

class Tiger(Walk_Animal,Swim_Animal):
    def swim(self):
        print('tiger can swim')
    def walk(self):
        print('tiger can walk')

class Eagle(Walk_Animal,Fly_Animal):
    def fly(self):
        print('eagle can fly')
    def walk(self):
        print('eagle can walk')

class Swan(Walk_Animal,Swim_Animal,Fly_Animal):
    def fly(self):
        print('Swan can fly')
    def walk(self):
        print('Swan can walk')
    def swim(self):
        print('Swan can swim')

###### 依赖倒置原则
# 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
# 即,要针对接口编程,而不是针对实现编程

    1.4.3 多态

    同一个对象,多种形态。

# 多态性:指在不考虑实例类型的情况下使用实例
    ## 即每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数
    ## 比如:老师.下课铃响了(),学生.下课铃响了()  老师执行的是下班操作,学生执行的是下课操作。虽然二者消息一样,但是执行的效果不同
# 同样都是调用eat()方法,但是执行结果不同
class Animal:
    def eat(self):
        print("动物就知道吃")

class Pig(Animal):
    def eat(self):
        print("猪在吃")
class Dog(Animal):
    def eat(self):
        print("狗在吃")
class Feeder:
    def feed_animal(self, ani):
        ani.eat()


p = Pig()
d = Dog()
f = Feeder()
f.feed_animal(p)
f.feed_animal(d)

2 面向对象基础2   

  2.1 类的成员简介

class Person:
    # 方法
    def __init__(self, name, age):
        # 属性
        self.name = name
        self.age = age
        
    # 方法
    def say(self):
        print('我是{self.name},我今年{self.age}岁!'.format(self=self)) 

    在上面的代码中,__init__和say都是属于类的成员方法,又称为实例方法

    self.name和self.age都是实例对象的属性,这些称为成员变量或实例变量

  2.2 变量

    2.2.1 实例变量

# 就是每个实例都应该拥有的变量
#比如,人的名字,人的年龄,每个人的信息都属于具体的人,这些都可以称为实例变量。

class Person:
    # 方法
    def __init__(self, name, age):
        # 属性
        self.name = name
        self.age = age

    # 方法
    def say(self):
        print('我是{self.name},我今年{self.age}岁!'.format(self=self))


p1 = Person('张三', 18)
p2 = Person('李四', 20)

print(p1.name, p1.age)  # 张三 18

print(p2.name, p2.age)  # 李四 20

print(id(p1.name), id(p2.name))  # 4567249208 4567250088
print(id(p1.age), id(p2.age))  # 4564114656 4564114720

    2.2.2 类变量(静态变量)

# 类变量就是这一类事物统一拥有的变量,比如,各位都是中国人,大家都拥有一个同一个祖国--中国。

class Person:
    # 类变量,所有实例对象都共享这个变量
    country = '中国'

    # 方法
    def __init__(self, name, age):
        # 属性
        self.name = name
        self.age = age

    # 方法
    def say(self):
        print('我是{self.name},我今年{self.age}岁!'.format(self=self))


p1 = Person('张三', 18)
p2 = Person('李四', 20)

print(p1.country, p2.country)  # 中国 中国
print(id(p1.country), id(p2.country))  # 4567249032 4567249032

    可以发现,实例对象p1和p2的country属性都是类中定义的那个,公用的。

    (1)当修改了类变量country,各实例对象中的country属性也跟着变了。

Person.country = 'China'
print(p1.country, p2.country)  # China China
print(id(p1.country), id(p2.country))  # 4551075464 4551075464

 

    (2)调用实例对象来“改变”country,发现只有该实例对象中的country属性变了;新实例化的属性还是使用类属性。

# p3.country = '大秦'的时候,并没有去改变类中的country,而是给自己添加了一个实例属性。
# 这个属性只有在p3这个实例对象中才是存在的,在p4中是不存在的。
class Person: # 类变量,所有实例对象都共享这个变量 country = '中国' # 方法 def __init__(self, name, age): # 属性 self.name = name self.age = age # 方法 def say(self): print('我是{self.name},我今年{self.age}岁!'.format(self=self)) p3 = Person('王五', 3000) p3.country = '大秦' print(p3.name) # 王五 print(p3.country) # 大秦 p4 = Person('赵六', 30) print(p4.name) # 赵六 print(p4.country) # 中国

  

    2.2.3 总结

    类变量,是定义在类中的变量,该类的实例对象都可以访问到该变量,但是实例对象只能查看不能修改想要修改类变量,只能通过类名来修改。

    实例变量,给实例对象用的。

    类变量,实例对象共享的。

# 通过类变量来记录创建的实例个数:
class Foo:
    count = 0
    
    def __init__(self):
        Foo.count += 1    # 注意这里调用的是  类名.变量    
        
print(Foo.count)  # 0
Foo()
Foo()
Foo()
print(Foo.count)  # 3

  2.3 方法

    2.3.1 实例方法

class Computer:

    # 实例方法
    def play(self):
        print('我的电脑可以玩游戏')


c1 = Computer()
c1.play()  # 实例对象直接调用实例方法

    2.3.2 静态方法

class Computer:

    # 实例方法
    def play(self):
        print('我的电脑可以玩游戏')

    @staticmethod
    def fire():
        # 方法不需要传入实例对象变量
        print('我的电脑非常牛B,可以煎鸡蛋')


c1 = Computer()
c1.play()  # 实例对象直接调用实例方法
c1.fire()

#  静态方法和静态变量一样,都可以使用类名直接访问。
Computer.fire()

    2.3.3 类方法

#类方法是由类调用的方法,他的第一个参数必须是类本身。类方法在被调用的时候,不需要调用者传递该参数,解释器会自动的把类当成第一个参数,类方法在定义的时候需要在类方法上添加@classmethod

class Computer:

    # 实例方法
    def play(self):
        print('我的电脑可以玩游戏')

    @staticmethod
    def fire():
        # 方法不需要传入实例对象变量
        print('我的电脑非常牛B,可以煎鸡蛋')

    @classmethod
    def calc(cls, a, b):
        print(cls)
        return a + b

c1 = Computer()
c1.play()  # 实例对象直接调用实例方法
c1.fire()

ret = Computer.calc(10, 20)  # <class '__main__.Computer'>
print(ret)  # 30

  2.4 属性方法

  属性方法:其实就是把类中的方法改造成属性的一种写法,该写法需要在方法上加上@property

  加了@property后,就伪装成属性了,不需要通过 方法名() 调用了,直接当做类属性调用

from datetime import datetime
from dateutil.relativedelta import relativedelta

class Person:

    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday

    @property
    def age(self):
        # 人的年龄是一个属性,但是需要由生日和当前时间计算得出
        # 所以我们把这个方法包装成属性
        ret = relativedelta(datetime.now(), datetime.strptime(self.birthday, '%Y-%m-%d'))
        return ret.years

p1 = Person('李国庆', '1990-10-01')
# 像访问实例属性一样
print(p1.age)   #28
class Goods:
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)  # 获取商品价格
obj.price = 200  # 修改商品原价
print(obj.price)  
del obj.price  # 删除商品原价
print(obj.price)  # AttributeError: 'Goods' object has no attribute 'original_price'

  2.5 私有成员

    2.5.1 私有变量

    定义一个Person类,有name属性和salary属性,薪资是保密,所以在salary的前面加上两个连续的下划线,把它变成私有的

# 下面的代码报错,因为我们无法直接访问实例对象的私有属性。
class Person:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary  # 薪资是秘密,设置为私有


p1 = Person('李狗剩', 200)
print(p1.name)
print(p1.__salary)  # AttributeError: 'Person' object has no attribute '__salary'

    私有属性不能直接访问,但是可以通过其他的实例方法来访问私有属性。这样做的好处是实现了私有属性只能查看不能修改。

class Person:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary  # 薪资是秘密,设置为私有

    def dazuiba(self):
        return self.__salary


p1 = Person('李狗剩', 200)
print(p1.name)
salary = p1.dazuiba()
print(salary)  # 200

    不仅仅实例属性可以私有,类属性也可以:

class Person:
    __secret = '人都是自私的'

    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary  # 薪资是秘密,设置为私有

    def dazuiba(self):
        return self.__salary


p1 = Person('李狗剩', 200)
print(p1.name)
salary = p1.dazuiba()
print(salary)  # 200
print(Person.__secret)  # AttributeError: type object 'Person' has no attribute '__secret'

    2.5.2 私有方法

    就是只能自己调用的方法,别人都不能随便调用。

# __play是一个私有方法,只能够在类的内部调用,类的外部无法调用。
class Person:

    def __init__(self):
        pass

    def __play(self):
        print('嘿嘿嘿')

    def work(self):
        print('工作使我快乐!')


p1 = Person()
p1.work()  # 工作使我快乐!
p1.__play()  # AttributeError: 'Person' object has no attribute '__play'

    但是可以在类的内部调用私有方法:

class Person:

    def __init__(self):
        pass

    def __play(self):
        print('嘿嘿嘿')

    def work(self):
        print('工作使我快乐!')

    def dazuiba(self):
        self.__play()


p1 = Person()
p1.work()
p1.dazuiba()  # 嘿嘿嘿

    在实际的应用场景中,我们通常会把那些不想暴露给实例对象的方法,定义为私有方法

    另外还需要注意的一点是,对于私有成员,子类是无法继承的

class A:
    __secret = '密码'

    def __init__(self, salary):
        self.__salary = salary

    def __play(self):
        print('嘿嘿嘿')

class B(A):

    def dazuiba(self):
        self.__play()


b1 = B(200)
print(b1.__salary)    # 'B' object has no attribute '__salary'
b1.__play()              # 'B' object has no attribute '__play'
b1.dazuiba()            # AttributeError: 'B' object has no attribute '_B__play'
print(b1.__secret)     # AttributeError: 'B' object has no attribute '__secret'
print(B.__secret)      # AttributeError: type object 'B' has no attribute '__secret'

  2.6 类的使用

  (1)依赖关系

class Elephant:
    def __init__(self, name):
        self.name = name

    def open(self, ref):
        print("⼤象要开门了。默念三声!开!")
        # 由外界传递进来⼀个冰箱, 让冰箱开门。这时,⼤象不用背着冰箱到处跑。
        # 类与类之间的关系也就不那么的紧密了,换句话说只要是有open_door()⽅法的对象都可以接收运⾏
        ref.open_door()

    def close(self, ref):
        print("⼤象要关门了。默念三声!关!")
        ref.close_door()

    def enter(self):
        print("钻进去")


class Refrigerator:

    def open_door(self):
        print("冰箱门被打开了")

    def close_door(self):
        print("冰箱门被关上了")


# 造冰箱
r = Refrigerator()
# 造⼤大象
el = Elephant("神奇的大象")
el.open(r)  # 注意:此时是把一个冰箱作为参数传递进去了。也就是说大象可以指定任何一个冰箱
el.enter()
el.close(r)

  (2)关联关系

class Boy:
    def __init__(self, name, girl_friend=None):
        self.name = name
        self.girl_friend = girl_friend

    def have_a_dinner(self):
        if self.girl_friend:
            print("{}和{}一起去吃晚餐".format(self.name, self.girl_friend.name))
        else:
            print("单身狗。吃什么饭!")

class Girl:
    def __init__(self, name):
        self.name = name


b = Boy('张大锤')
b.have_a_dinner()   #单身狗。吃什么饭!
g1 = Girl("如花")
b.girl_friend = g1

g2 = Girl("李小花")
bb = Boy("张二锤", g2)  

bb.have_a_dinner()       #张二锤和李小花一起去吃晚餐
bb.girl_friend = None
bb.have_a_dinner()       #单身狗。吃什么饭!

  (3)组合练习

# 创建一个老师类,生日类,课程类
class Teacher:
    def __init__(self,name,age,sex,birthday):
        self.name = name
        self.age = age
        self.sex = sex
        self.birthday = birthday
        self.course = Course(self,'python',6,200000)
class Birthday:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
class Course:
    language = 'Chinese'
    def __init__(self,teacher,cname,period,price):
        self.teacher = teacher
        self.cname =cname
        self.period = period
        self.price = price

bd = Birthday(1995,6,20)
ls = Teacher('egon',23,'不详',bd)
print(ls.name)                  # egon
print(ls.birthday.year)         # 1995
print(ls.course.teacher.name)   # egon

  (4)接口类:规范作用

# 接口类 : python原生不支持的,满足接口隔离原则,面向对象开发的思想、规范,一般用来实现功能不完全一样的类的继承(接口隔离原则)
# 抽象类 : python原生支持的
### 接口类:默认多继承,接口类中所有方法都必须不能实现
### 抽象类:不支持多继承,抽象类中方法可以有一些代码的实现
# 接口类和抽象类:本质上是做代码规范用的,希望在子类中实现和父类方法名字完全一样的方法
################### 规范类 —— 报错  ######################
# 建立了一个规范的类,规范下边的子类:接口类或抽象类都可以
from abc import abstractmethod,ABCMeta
class Payment(metaclass=ABCMeta):       # 元类,默认的元类 type
    @abstractmethod                     # 只需要实例化,不需要执行,便可发现抛出错误
    def pay(self,money):
        pass
        # raise NotImplemented            # 意思是:没有实现这个方法
#########################################

class Wechat(Payment):
    def pay(self,money):
        print('使用微信支付了%s元'%money)

class Alipay(Payment):
    def pay(self,money):
        print('使用支付宝支付了%s元'%money)

# 子类必须实现pay方法,否则实例化子类的时候就会报错
class Applepay(Payment):
    def fuqian(self,money):       # TypeError: Can't instantiate abstract class Applepay with abstract methods pay
        print('使用Applepay支付了%s元' % money)

def all_pay(pay_obj,money):  # 统一支付入口
    pay_obj.pay(money)

wechat = Wechat()
ali = Alipay()
apple = Applepay()

all_pay(wechat,100)
all_pay(ali,200)
all_pay(apple,300)

  (5)抽象类:规范作用

#  抽象类:不支持多继承,抽象类中方法可以有一些代码的实现 【规范】
#  一般情况下是单继承,能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
## 多继承的情况下,由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中
# 例子:一切皆文件

import abc
class All_file(metaclass=abc.ABCMeta):
    all_type = 'file'
    @abc.abstractmethod
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod
    def write(self):
        '子类必须定义写功能'
        pass

class Txt(All_file):    # 子类继承抽象类,但必须定义read和write方法
    def read(self):
        print('文本数据的读方法')
    def write(self):
        print('文本数据的写方法')

class Sata(All_file):
    def read(self):
        print('硬盘数据的读方法')
    def write(self):
        print('硬盘数据的写方法')

class Process(All_file):
    def read(self):
        print('进程数据的读方法')
    def write(self):
        print('进程数据的写方法')

t = Txt()
s = Sata()
p = Process()

# 这样大家都是被归一化了,也就是一切皆文件的思想
t.read()
s.write()
p.read()
p.write()

print(t.all_type)
print(s.all_type)
print(p.all_type)

 

原文地址:https://www.cnblogs.com/timetellu/p/10686485.html