python之继承、多态、抽象类、新式类和经典类

一、上节补充

1、静态属性
静态属性 : 类的属性,所有的对象共享这个变量
  如果用对象名去修改类的静态属性:在对象的空间中又创建了一个属性,而不能修改类中属性的值
  操作静态属性应该用类名来操作

例1:请你写一个类,能够统计一共实例化了多少个对象?

复制代码
class Foo:
    count = 0
    def __init__(self):
        Foo.count += 1

f1 = Foo()
print(f1.count)       # 1
f2 = Foo()
f3 = Foo()
f4 = Foo()
f5 = Foo()
print(f1.count)       # 5
print(f5.count)       # 5
print(Foo.count)    # 5
复制代码

结论:当类中的属性发生改变的时候,对象中没有同名的属性、方法的时候,对象使用属性名会跟着类中的变量走

 

例二:(类的静态属性为可变数据类型,对象也能引用并修改)

复制代码
class Foo:
    count = [0]

f1 = Foo()
f1.count[0] += 1   # 静态属性的修改  1
print(f1.count[0]) # 对象引用类的静态属性 1
print(Foo.count[0]) # 类修改后 也是 1
f1.count = [2]    # 对象新增 2
print(f1.count)   # 2
print(Foo.count)  # 类不变 1
复制代码

结论:
1,类的静态属性是可变数据类型时,对象可以引用并修改
2,只要对象的某个属性被直接赋值,那么一定是对象的命名空间发生变化
3,只要是静态变量,就用类名操作

 

二、继承(提高代码的重用性,规范性)

(面向对象的三大特性:继承、多态、封装,这里先说一下继承)

(C语言没有面向对象,而java和C#只有单继承没有多继承(但是有接口),
python和C++有单继承也有多继承(python原本并没有接口,但是有些人为python开发了接口模块,所以在python中,
需要用接口的时候要安装特定的模块))


2-1单继承:
1,语法:
class A:
  pass

class B(A):
  pass

这就是单继承:
  A就叫:父类/超类/基类
  B就叫:子类/派生类


2,继承与重用:子类可以使用父类中的变量和方法
下面看个例子了解一下:
我们都知道猫有自己的名字和吃的粮食,还有猫会喝水吃东西,抓老鼠等,
狗也有自己的名字和吃的粮食,狗也会喝水吃东西,狗还能看家等。
因此构建出来的类如下:

复制代码
class Cat():
    def __init__(self,name,food):
        self.name = name
        self.food = food

    def eat(self):
        print('吃猫粮')

    def drink(self):
        print('喝水')
        
    def catch_mouse(self):
        print('抓老鼠')



class Dog():
    def __init__(self,name,food):
        self.name = name
        self.food = food

    def eat(self):
        print('吃狗粮')

    def drink(self):
        print('喝水')
    
    def look_after_house(self):
        print('看家')

xiaomao = Cat('啊猫','猫粮')
print(xiaomao.name)         #啊猫
xiaomao.eat()                     #吃猫粮
xiaomao.catch_mouse()   #抓老鼠

xiaogou = Dog('啊狗','狗粮')
print(xiaogou.name)       #啊狗
xiaogou.eat()                  #吃狗粮
xiaogou.look_after_house()   #看家
复制代码

这么一看是不是感觉代码有很多重复的地方,比如初始化名字和食物、吃东西和喝水的方法,
那么怎么样可以节省代码呢?这就要用到继承。
我们先定义一个公共的类Animal,用来存储猫和狗相同的方法和属性。猫类和狗类分别继承Animal类,
就可以继承Animal的属性和方法了。

复制代码
class Animal:
    def __init__(self,name,food):
        self.name = name
        self.food = food

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

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


class Cat(Animal):

    def catch_mouse(self):
        print('抓老鼠')


class Dog(Animal):

    def look_after_house(self):
        print('看家')

xiaomao = Cat('啊猫','猫粮')
print(xiaomao.name)         #啊猫
xiaomao.eat()                     #吃猫粮
xiaomao.catch_mouse()   #抓老鼠

xiaogou = Dog('啊狗','狗粮')
print(xiaogou.name)       #啊狗
xiaogou.eat()                  #吃狗粮
xiaogou.look_after_house()   #看家
复制代码

这样一来是不是就简化了很多,而且再来一个猪类,只要它也是有这些公共方法,它也能继承Animal类,
一下子就可以节省了很多代码。

其中:
父类/超类/基类 :Animal
子类/派生类 :Cat、Dog

 

3,继承与派生:
(1)继承:提高代码的重用性,规范代码
(2)派生:子类在父类的基础上又新创建了自己需要的方法和属性
(3)父类有的而子类没有:子类对象直接调用 就会直接执行父类的方法
(4)父类有的而子类也有:1、子类对象调用 直接执行子类中的方法
            2、想在子类中使用父类的名字:父类名、super()去调用

 例如:

复制代码
class Animal:
    def __init__(self, name, food):
        self.name = name
        self.food = food

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

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


class Cat(Animal):  # Animal的派生类
    def __init__(self, name, food, eye_color):
        self.eye_color = eye_color  # 派生属性
        super().__init__(name, food)
        # Animal.__init__(self,name,food) 跟上一句super()都是调用父类的方法
        # 不同的是super()不需要传self,而直接用父类名.方法(),需要传self

    def catch_mouse(self):  # 派生方法
        print('抓老鼠')

    def eat(self):   # 不仅执行了父类中的基础功能,还完成了特殊的功能
        Animal.eat(self)
        # super().eat()
        self.weight = 10


class Dog(Animal):
    def look_after_house(self):
        print('看家')

    def eat(self):
        # Animal.eat(self)
        super().eat()
        self.drink()  # 吃完东西调用父类喝水的方法


xiaomao = Cat('阿猫', '猫粮', '绿色')  
print(xiaomao.eye_color)  # 绿色
print(xiaomao.food)  #猫粮
xiaomao.catch_mouse() #抓老鼠
xiaomao.eat()  #吃猫粮
print(xiaomao.weight)  # 10

xiaogou = Dog('啊狗', '狗粮') 
xiaogou.eat()  #吃狗粮  喝水
复制代码

 

2-2多继承:
1,语法:
class A:
  pass

class B:
  pass

class C(A,B):
  pass

C既继承了A,又继承了B


2,实例:

复制代码
# 天鹅:飞 游泳 走路
# 老虎:走路 游泳
# 鹦鹉:飞 说话 走路

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

class FlyAnimal(Animal):  #会飞的动物类
    def fly(self):
        print('%s在飞' % self.name)

class WalkAnimal(Animal):  #会走路的动物类
    def walk(self): 
        print('%s在走路'%self.name)

class SwimAnimal(Animal):  #会游泳的动物类
    def swim(self):
        print('%s在游泳'%self.name)

class Tiger(SwimAnimal,WalkAnimal):  # 老虎
    pass

class Swan(SwimAnimal,WalkAnimal,FlyAnimal):  # 天鹅
    pass

class Parrot(FlyAnimal,WalkAnimal):  # 鹦鹉
    def talk(self):
        print('%s说话了'%self.name)

swan = Swan('天鹅')
swan.fly()  # 天鹅在飞
swan.walk()  # 天鹅在走路
复制代码

 

三、抽象类

1、python3中的抽象类

抽象类是一个规范,它基本不会实现什么具体的功能,只能被继承,不能被实例化

abc:Abstract Base Classes

作用:在代码中定义和使用抽象基类进行API检查


1、抽象类的作用:规范编程模式
  多人开发、复杂的需求、后期的扩展
  是一种用来帮助我们完成规范化的手段

2、如何定义抽象类
  1,from abc import ABCMeta,abstractmethod
  2,在这个类创建的时候指定 metaclass = ABCMeta
  3,在你希望子类要实现的方法的上一行加上一个 @abstractmethod装饰器

3、使用抽象类
  1,继承这个类
  2,必须实现这个类中被@abstractmethod装饰器装饰的方法(被装饰的方法也叫虚函数)

4、实例

只适用于python3的写法

复制代码
# 支付功能

from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):  # 模板的功能
    @abstractmethod                # abstractmethod是一个装饰器,装饰器怎么用?放在函数或者类的上一行
    def pay(self): pass

# 这样就构建了一个抽象类Payment,并声明了子类必须要实现的方法是 pay(),若子类没有定义pay(),则实例化时会报错


class Alipay(Payment):  # 继承了抽象类,就必须实现抽象类中被@abstractmethod装饰器装饰的方法 pay()
    def pay(self, money):
        print('使用支付宝支付了%s元' % money)


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


class My_pay(Payment):  # 这里没有定义pay()方法,那么在实例化的时候机会报错
    def fuqian(self,money):
        print('你支付了%s元' % money)


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


# p = Payment()  # 报错 抽象类不能被实例化

a = Alipay()
# a.pay(100)
pay(a,100)  # 使用支付宝支付了100元

we = Wechatpay()
# we.pay(200)
pay(we,200)  # 使用微信支付了200元

my = My_pay()  # 报错:类中没有定义抽象类的pay方法
pay(my,300)
复制代码

 

 5、如果你的规范中还包括属性,那么你可以使用 @abstractproperty来定义

复制代码
from abc import ABCMeta, abstractproperty, abstractmethod


class Base(metaclass=ABCMeta):
    @abstractproperty
    def value(self):
        pass

    @abstractmethod
    def pay(self):
        pass


class Zhifubao(Base):
    @property
    def value(self):
        return 'value'

    def pay(self):
        return "pay"


z = Zhifubao()
print(z.value)
复制代码

6、类似的还有 类方法@abstractclassmethod,静态方法 @abstractstaticmethod

 

2、python2中的抽象类 

2-1、python2中抽象类的写法

只适用于python2的写法

复制代码
from abc import ABCMeta, abstractmethod


class Payment(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def pay(self, money):
        pass


class Alipay(Payment):  # 继承了抽象类,就必须实现抽象类中被@abstractmethod装饰器装饰的方法 pay()
    def pay(self, money):
        print('使用支付宝支付了%s元' % money)
复制代码

 

2-2、使用six模块,适用于python2和3

复制代码
import six
from abc import ABCMeta, abstractmethod


@six.add_metaclass(ABCMeta)
class Payment(object):
    # __metaclass__ = ABCMeta

    @abstractmethod
    def pay(self, money):  # 虚函数
        pass


class Alipay(Payment):  # 继承了抽象类,就必须实现抽象类中被@abstractmethod装饰器装饰的方法 pay()
    def pay(self, money):
        print('使用支付宝支付了%s元' % money)
复制代码

 

四、多态

Python中多态是指一类事物有多种形态。比如动物有多种形态,人,狗,猫,等等。

复制代码
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')
复制代码

 

Python的多态性是指:让不同类型的实例有相同的调用方法。比如人,猫,狗,等等只要是继承了animal就可以直接调用它的talk()方法。

复制代码
peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()
复制代码

 

使用多态的好处

1.增加了程序的灵活性
  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

2.增加了程序额可扩展性
  通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

 

五、新式类

1、新式类和经典类

复制代码
"""
继承了object的类就是新式类
在py3中默认都继承了object因此所有的类都是新式类
在py2中既有新式类又有经典类
"""


# python3.x: 在python3.x版本中所有的类都是新式类
# 所有的新式类都有一个默认的父类 : object
class Person1: pass
class Person2(): pass
class Person3(object): pass

# __bases__方法是查看某个类继承的所有父类
print(Person1.__bases__)  # (<class 'object'>,)
print(Person2.__bases__)  # (<class 'object'>,)
print(Person3.__bases__)  # (<class 'object'>,)


# python2.7: 经典类和新式类并存
class Student: pass  # 经典类
class Student(object): pass  # 新式类

class Person1: pass  # 经典类
class Person2(): pass  # 经典类
class Person3(object): pass  # 新式类
复制代码

 

2、多继承的顺序(在新式类和经典类之间的区别)

新式类

  • 所有的多继承关系寻找方法的顺序 :遵循广度优先算法
  • 继承object
  • 类名.mro() # (返回一个类的寻找顺序(继承顺序)的列表)
  • super : super不是单纯的找父类,而是遵循mro顺序的

 

经典类

  • 不主动继承object
  • 经典类在找父类中方法的过程中遵循:深度优先算法
  • 不提供mro方法和super
  • 要看经典类的mro,需要使用 inspect.getmro
复制代码
import inspect


class A:
    def func(self):
        print("A")


class B(A):
    def func(self):
        A.func(self)
        print("B")


class C(A):
    def func(self):
        A.func(self)
        print("C")


class D(B, C):
    def func(self):
        B.func(self)
        print("D")


print(inspect.getmro(D))  # 经典类查看mro的方法
D().func()

class A(object):
    def func(self):
        print("A")


class B(A):
    def func(self):
        super().func()
        print("B")


class C(A):
    def func(self):
        super().func()
        print("C")


class D(B, C):
    def func(self):
        super().func()
        print("D")


print(D.mro())  # 新式类可以直接使用 mro 方法查看mro
D().func()
复制代码

 

注意点:

新式类的super方法,在python3和python2中稍微有点区别

复制代码
super() 方法的语法:
    super(type[, object-or-type])
        type -- 类。
        object-or-type -- 类,一般是 self

Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

# python3写法
class A:
    def show(self):
        print("A")


class B(A):
    def show(self):
        super().show()
        print("B")


B().show()


# python2写法(实际上这种写法python3也支持)
class A(object):  # Python2.x 记得继承 object,否则经典类不支持super
    def show(self):
        print("A")


class B(A):
    def show(self):
        super(B, self).show()
        print("B")


B().show()
复制代码

 

3、新式类的方法解析顺序

MRO(Method Resolution Order):方法解析顺序

MOR(方法解析顺序)

  • 经典类:深度优先 DFS python3以前
  • 新式类:广度优先 python2.2
  • 新式类:广度优先的C3算法 python2.3及以后

 

MRO作用是什么
为了解决多重继承的二义性的问题(二义性:父类存在同名函数的时候会产生二义性),意思就是当一个类(假设是A),同时继承了多个父类(假设是B和C),

且这些父类都拥有相同的方法(比如是 def play 方法),那么在当前类(A)使用supper().play 调用的是哪个父类的方法呢,为了解决此类问题,就出现了MRO,

为python调用一个类或者实例方法的时候提供查找顺序。

这个新式类的C3算法,可以参考我另一篇文章:python之MRO和垃圾回收机制

 

posted @   我用python写Bug  阅读(944)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示