继承与多态


继承和多态——这是Python中非常重要的概念。如果你想学习,你必须更好地理解它。

遗产

面向对象编程的主要优点之一是重用。继承是实现这一目标的机制之一。继承允许程序员首先创建通用类或基类,然后将其扩展为更专门的类。它可以让程序员写出更好的代码。

使用继承,您可以使用或继承基类中可用的所有数据字段和方法。稍后您可以添加自己的方法和数据字段,因此继承提供了一种组织代码的方法,而不是从头开始重写。

在面向对象的术语中,当类 X 扩展类 Y 时,Y 称为超类/父类/基类,X 称为子类/子类/派生类。这里需要注意的一点是,只有非私有的数据字段和方法可以被子类访问。私有数据字段和方法只能在类内部访问。

创建派生类的语法是 -

class BaseClass:
   Body of base class
class DerivedClass(BaseClass):
   Body of derived class

继承属性

现在看下面的例子 -

继承属性

输出

继承属性输出

我们首先创建一个名为 Date 的类,并将对象作为参数传递,这里的 object 是 Python 提供的内置类。后来我们创建了另一个名为 time 的类,并调用 Date 类作为参数。通过这个调用,我们可以访问 Time 类中 Date 类的所有数据和属性。因此,当我们尝试从我们之前创建的 Time 类对象 tm 获取 get_date 方法时。

对象.属性查找层次结构

  • 实例
  • 班上
  • 该类继承自的任何类

继承示例

让我们看一下继承示例的闭包 -

继承示例

让我们创建几个类来参与示例 -

  • Animal - 类模拟动物
  • 猫 - 动物的子类
  • 狗 - 动物的子类

在Python中,类的构造函数用于创建对象(实例),并为属性赋值。

子类的构造函数总是调用父类的构造函数来初始化父类中的属性值,然后开始为其属性赋值。

Python 构造函数

输出

Python 构造函数输出

在上面的示例中,我们看到我们放入父类中的命令属性或方法,以便所有子类或子类将从父类继承该属性。

如果一个子类尝试从另一个子类继承方法或数据,那么它将出现一个错误,正如我们所看到的,当 Dog 类尝试从该 cat 类调用 swatstring() 方法时,它会抛出一个错误(如我们例子中的 AttributeError )。

多态性(“多种形状”)

多态性是 Python 中类定义的一个重要功能,当您在类或子类中具有通用命名的方法时,可以使用多态性。这允许函数在不同时间使用不同类型的实体。因此,它提供了灵活性和松散耦合,以便随着时间的推移可以扩展代码并轻松维护。

这允许函数使用任何这些多态类的对象,而无需了解类之间的区别。

多态性可以通过继承来实现,子类可以使用基类方法或重写它们。

让我们通过前面的继承示例来理解多态性的概念,并在两个子类中添加一个名为 show_affection 的通用方法 -

从示例中我们可以看到,它指的是一种设计,其中不同类型的对象可以以相同的方式处理,或者更具体地说,两个或多个具有相同名称或公共接口的方法的类,因为相同的方法(下面示例中的show_affection)使用任一类型的对象进行调用。

多态性

输出

多态性输出

所以,所有动物都会表现出情感(show_affection),但它们的表现方式有所不同。因此,“show_affection”Behave是多态的,因为它根据动物的不同而表现不同。所以,抽象的“动物”概念实际上并不是“show_affection”,而是特定的动物(如狗和猫)有“show_affection”动作的具体实现。

Python 本身具有多态类。例如,len() 函数可以与多个对象一起使用,并且所有对象都根据输入参数返回正确的输出。

多态性

压倒一切

在Python中,当子类包含重写超类方法的方法时,还可以通过调用来调用超类方法

Super(Subclass, self).method 而不是 self.method。

例子

class Thought(object):
   def __init__(self):
      pass
   def message(self):
      print("Thought, always come and go")

class Advice(Thought):
   def __init__(self):
      super(Advice, self).__init__()
   def message(self):
      print('Warning: Risk is always involved when you are dealing with market!')

继承构造函数

如果我们从前面的继承示例中看到,__init__位于up中的父类中,因为子类dog或cat中没有__init__方法。Python 使用继承属性查找来查找动物类中的 __init__ 。当我们创建子类时,首先它会查找dog类中的__init__方法,然后没有找到,然后查找父类Animal并在那里找到并调用它。因此,当我们的类设计变得复杂时,我们可能希望初始化一个实例,首先通过父类构造函数处理它,然后通过子类构造函数处理它。

构造函数

输出

构造函数输出

在上面的例子中,所有动物都有一个名字,所有狗都有一个特定的品种。我们用 super 来调用父类的构造函数。所以狗有它自己的 __init__ 但首先发生的是我们调用 super。Super 是内置函数,旨在将类与其超类或其父类相关联。

在这种情况下,我们说获取狗的超类并将狗实例传递给我们在这里所说的构造函数__init__。换句话说,我们用狗对象调用父类 Animal __init__ 。您可能会问为什么我们不只对狗实例说 Animal __init__ ,我们可以这样做,但如果动物类的名称在将来的某个时候发生变化。如果我们想重新排列类层次结构,让狗从另一个类继承,该怎么办?在这种情况下使用 super 可以让我们保持模块化并且易于更改和维护。

因此,在这个示例中,我们能够将通用 __init__ 功能与更具体的功能结合起来。这使我们有机会将通用功能与特定功能分开,从而消除代码重复并以反映系统整体设计的方式将类相互关联。

结论

  • __init__ 与任何其他方法一样;它可以被继承

  • 如果一个类没有 __init__ 构造函数,Python 将检查其父类以查看是否可以找到一个。

  • 一旦找到,Python 就会调用它并停止查找

  • 我们可以使用super()函数来调用父类中的方法。

  • 我们可能希望在父类以及我们自己的类中进行初始化。

多重继承和查找树

顾名思义,Python 中的多重继承是指一个类继承多个类。

例如,孩子继承了父母双方(母亲和父亲)的人格特质。

Python 多重继承语法

为了使一个类继承多个父类,我们在定义派生类时将这些类的名称写在括号内。我们用逗号分隔这些名称。

下面是一个例子 -

>>> class Mother:
   pass

>>> class Father:
   pass

>>> class Child(Mother, Father):
   pass

>>> issubclass(Child, Mother) and issubclass(Child, Father)
True

多重继承是指从两个或两个以上的类继承的能力。当孩子从父母继承而父母从祖父母类继承时,就会出现复杂性。Python 会爬上继承树,寻找被请求从对象中读取的属性。它将检查实例中、类内、父类、最后祖父类中的内容。现在的问题是按什么顺序搜索类 - 呼吸优先或深度优先。默认情况下,Python 采用深度优先。

这就是为什么在下图中,Python 首先在类 A 中搜索 dothis() 方法。因此,下面示例中的方法解析顺序将是

Mro- D→B→A→C

看下面的多重继承图 -

多重继承

让我们通过一个例子来了解Python的“mro”特性。

输出

Python mro 特征输出

实施例3

我们再举一个“菱形”多重继承的例子。

菱形多重继承

上图将被认为是不明确的。从我们之前的例子中理解“方法解析顺序”。即mro将是D→B→A→C→A,但事实并非如此。从 C 中获取第二个 A 时,Python 将忽略前一个 A。因此在这种情况下 mro 将是 D→B→C→A。

让我们根据上图创建一个示例 -

方法解析顺序

输出

方法 分辨率 顺序 输出

理解上述输出的简单规则是 - 如果相同的类出现在方法解析顺序中,则该类的较早出现将从方法解析顺序中删除。

总之 -

  • 任何类都可以继承多个类

  • Python 在搜索继承类时通常使用“深度优先”顺序。

  • 但是,当两个类继承同一个类时,Python 会从 mro 中消除该类的第一次出现。

装饰器、静态和类方法

函数(或方法)由 def 语句创建。

尽管方法的工作方式与函数完全相同,但方法的第一个参数是实例对象。

我们可以根据方法的Behave方式对其进行分类,例如

  • 简单方法- 在类外部定义。该函数可以通过提供实例参数来访问类属性:

def outside_func(():
  • 实例方法-

def func(self,)
  • 类方法- 如果我们需要使用类属性

   @classmethod
def cfunc(cls,)
  • 静态方法- 没有有关该类的任何信息

      @staticmethod
def sfoo()

到目前为止我们已经看到了实例方法,现在是时候深入了解其他两种方法了,

类方法

@classmethod 装饰器是一个内置函数装饰器,它传递被调用的类或作为第一个参数调用的实例的类。该评估的结果会影响您的函数定义。

句法

class C(object):
   @classmethod
   def fun(cls, arg1, arg2, ...):
      ....
fun: function that needs to be converted into a class method
returns: a class method for function

他们可以访问此 cls 参数,但不能修改对象实例状态。那需要访问自我。

  • 它绑定到类而不是类的对象。

  • 类方法仍然可以修改适用于类的所有实例的类状态。

静态方法

静态方法既不接受 self 也不接受 cls(class) 参数,但可以自由接受任意数量的其他参数。

句法

class C(object):
   @staticmethod
   def fun(arg1, arg2, ...):
   ...
returns: a static method for function funself.
  • 静态方法既不能修改对象状态也不能修改类状态。
  • 他们可以访问的数据受到限制。

什么时候用什么

  • 我们一般使用类方法来创建工厂方法。工厂方法针对不同的用例返回类对象(类似于构造函数)。

  • 我们通常使用静态方法来创建实用函数。