📜  继承与多态

📅  最后修改于: 2021-01-04 04:43:34             🧑  作者: Mango


继承和多态性–这是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方法时,我们可能会更早地创建它。

对象。属性查找层次结构

  • 实例
  • 班级
  • 此类继承的任何类

继承实例

让我们来看一下继承示例-

继承实例

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

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

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

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

Python构造函数

输出

Python构造函数输出

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

如果一个子类尝试继承另一个子类的方法或数据,那么它将遇到一个错误,正如我们在Dog类尝试从该cat类调用swatstring()方法时所看到的那样,它将引发错误(例如AttributeError)。

多态(“许多形状”)

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

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

多态可以通过继承来实现,子类可以使用基类方法或对其进行重写。

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

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

多态性

输出

多态输出

因此,所有动物都表现出情感(show_affection),但它们的行为有所不同。因此,“ show_affection”行为是多态的,因为它的行为取决于动物。因此,抽象的“动物”概念实际上并不是“ 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__位于上层的父类中,这是因为子类中的dog或cat没有__init__方法。 Python使用继承属性查找在动物类中找到__init__。当我们创建子类时,首先将在dog类中查找__init__方法,然后找不到它,然后查看父类Animal并在那里找到并在那儿调用。因此,随着我们的类设计变得复杂,我们可能希望首先通过父类构造函数然后通过子类构造函数对其进行处理来初始化实例。

建设者

输出

构造器输出

在上面的示例中,所有动物都有一个名字,所有狗都有一个特定的品种。我们用super调用了父类构造函数。所以dog有自己的__init__,但是发生的第一件事是我们称之为super。 Super是内置函数,旨在将一个类与其父类或父类相关联。

在这种情况下,我们说要获得dog的超类,并将dog实例传递给我们在此处说构造函数__init__的任何方法。换句话说,我们用dog对象调用了父类Animal __init__。您可能会问为什么我们不只是对dog实例说Animal __init__,我们可以这样做,但是如果将来某个时候更改Animal类的名称。如果我们想重新安排班级层次结构,以便狗从另一个班级继承,该怎么办?在这种情况下使用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()方法。因此,下面示例中的方法解析顺序为

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语句创建。

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

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

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

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.
  • 静态方法无法修改对象状态或类状态。
  • 它们在可以访问哪些数据方面受到限制。

何时使用什么

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

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