当前位置: 首页 > 文档资料 > Python 面向对象 >

继承与多态(Inheritance and Ploymorphism)

优质
小牛编辑
131浏览
2023-12-01

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

继承 (Inheritance)

面向对象编程的一个主要优点是重用。 继承是实现同样的机制之一。 继承允许程序员首先创建通用类或基类,然后将其扩展到更专业的类。 它允许程序员编写更好的代码。

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

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

创建派生类的语法是 -

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

继承属性

现在看下面的例子 -

继承属性

输出 (Output)

继承属性输出

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

Object.Attribute查找层次结构

  • 实例
  • class
  • 此类继承的任何类

继承示例

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

继承示例

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

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

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

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

Python构造函数

输出 (Output)

Python构造函数输出

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

如果一个子类尝试从另一个子类继承方法或数据,那么它将通过一个错误,就像我们看到当Dog类尝试从该cat类调用swatstring()方法时,它会抛出一个错误(就像我们的情况下的AttributeError)。

Polymorphism (“MANY SHAPES”)

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

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

多态可以通过继承来执行,子类使用基类方法或覆盖它们。

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

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

多态性

输出 (Output)

多态性输出

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

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

多态

Overriding

在Python中,当子类包含覆盖超类方法的方法时,您还可以通过调用调用超类方法

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

例子 (Example)

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并在那里找到并在那里调用它。 因此,当我们的类设计变得复杂时,我们可能希望初始化一个实例,首先通过父类构造函数然后通过子类构造函数处理它。

构造函数

输出 (Output)

构造函数输出

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

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

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

结论 (Conclusion)

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

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

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

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

  • 我们可能想要在父级和我们自己的类中初始化。

多重继承和查找树

正如其名称所示,多重继承是Python,当一个类继承自多个类时。

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

Python Multiple Inheritance Syntax

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

以下是一个例子 -

>>> 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”特性。

输出 (Output)

Python mro功能输出

例子3 (Example 3)

让我们再看一个“钻石形状”多重继承的例子。

钻石形状多重继承

上图将被视为含糊不清。 从我们之前的例子中了解“方法解析顺序”.ie mro将是D→B→A→C→A,但事实并非如此。 在从C获得第二个A时,Python将忽略之前的A.因此在这种情况下mro将是D→B→C→A。

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

方法解决顺序

输出 (Output)

方法解决顺序输出

理解上述输出的简单规则是 - 如果在方法分辨率顺序中出现相同的类,则将从方法分辨率顺序中删除此类的早期外观。

总之 -

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

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

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

装饰器,静态和类方法

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

虽然方法的工作方式与函数完全相同,但方法第一个参数是实例对象的一点除外。

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

  • Simple method - 在类之外定义。 此函数可以通过提供实例参数来访问类属性:

def outside_func(():
  • Instance method -

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

   @classmethod
def cfunc(cls,)
  • Static method - 没有关于该类的任何信息

      @staticmethod
def sfoo()

直到现在我们已经看到了实例方法,现在是时候了解其他两种方法,

类方法 (Class Method)

@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)参数,但它可以自由地接受任意数量的其他参数。

syntax

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

什么时候用

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

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