继承与多态
继承和多态——这是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中,类的构造函数用于创建对象(实例),并为属性赋值。
子类的构造函数总是调用父类的构造函数来初始化父类中的属性值,然后开始为其属性赋值。
输出
在上面的示例中,我们看到我们放入父类中的命令属性或方法,以便所有子类或子类将从父类继承该属性。
如果一个子类尝试从另一个子类继承方法或数据,那么它将出现一个错误,正如我们所看到的,当 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”特性。
输出
实施例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.
- 静态方法既不能修改对象状态也不能修改类状态。
- 他们可以访问的数据受到限制。
什么时候用什么
我们一般使用类方法来创建工厂方法。工厂方法针对不同的用例返回类对象(类似于构造函数)。
我们通常使用静态方法来创建实用函数。