联博开奖:Python 简明教程 --- 21,Python 继续与多态

admin 3个月前 (07-05) 科技 35 0

微信民众号:码农充电站pro
个人主页:https://codeshellme.github.io

程序不是年轻的专利,然则,它属于年轻。

目录

我们已经知道封装继续多态 是面向工具的三大特征,面向工具语言都市提供这些机制。

1,封装

在这一节先容类的私有属性和方式的时刻,我们已经讲到过封装

封装就是在设计一个类的时刻,只允许使用者接见他需要的方式,将庞大的,没有必要让使用者知道的方式隐藏起来。这样,使用者只需关注他需要的器械,为其屏障了庞大性。

私有性就是实现封装的一种手段,这样,类的设计者就可以控制类中的哪些属性和方式可以被使用者接见到。一样平常,类中的属性,和一些庞大的方式都不会露出给使用者。

由于前边的章节先容过封装,这里就不再举例说明了。

2,继续

通过继续的机制,可使得子类轻松的拥有父类中的属性和方式继续也是一种代码复用的方式。

Python 支持类的继续,继续的类叫做子类或者派生类被继续的类叫做父类基类

继续的语法如下:

class 子类名(父类名):
    pass

子类名后边的括号中,写入要继续的父类。

object

在Python 的继续系统中,object 是最顶层类,它是所有类的父类。在界说一个类时,若是没有继续任何类,会默认继续object 类。如下两种界说方式是等价的:

# 没有显示继续任何类,默认继续 object
class A1:
    pass

# 显示继续 object
class A2(object):
    pass

每个类中都有一个mro 方式,该方式可以打印类的继续关系(顺序)。我们来查看A1A2 的继续关系:

>>> A1.mro()
[<class '__main__.A1'>, <class 'object'>]
>>>
>>> A2.mro()
[<class '__main__.A2'>, <class 'object'>]

可见这两个类都继续了 object 类。

继续中的__init__ 方式

当一个子类继续一个父类时,若是子类中没有界说__init__,在建立子类的工具时,会挪用父类的__init__ 方式,如下:

#! /usr/bin/env python3

class A(object):

    def __init__(self):
        print('A.__init__')

class B(A):
    pass

以上代码中,B 继续了AA 中有__init__ 方式,B 中没有__init__ 方式,建立类B 的工具b

>>> b = B()
A.__init__

可见A 中的__init__ 被执行了。

方式笼罩

若是类B 中也界说了__init__ 方式,那么,就只会执行B 中的__init__ 方式,而不会执行A 中的__init__ 方式:

#! /usr/bin/env python3

class A(object):

    def __init__(self):
        print('A.__init__')

class B(A):

    def __init__(self):
        print('B.__init__')

此时建立B 的工具b

>>> b = B()
B.__init__

可见,此时只执行了B 中的__init__ 方式。这实在是方式笼罩的缘故原由,由于子类中的__init__父类中的__init__ 的参数列表一样,此时,子类中的方式笼罩了父类中的方式,以是建立工具b 时,只会执行B 中的__init__ 方式。

当发生继续关系(即一个子类继续一个父类)时,若是子类中的一个方式与父类中的一个方式一模一样(即方式名相同,参数列表也相同),这种情形就是方式笼罩(子类中的方式会笼罩父类中的方式)。

方式重载

方式名参数列表都一样时会发生方式笼罩;当方式名一样,参数列表纷歧样时,会发生方式重载

在单个类中,代码如下:

#! /usr/bin/env python3

class A(object):

    def __init__(self):
        print('A.__init__')

    def test(self):
        print('test...')

    def test(self, i):
        print('test... i:%s' % i)

A 中的两个test 方式,方式名相同,参数列表差别。

实在这种情形在JavaC++ 是允许的,就是方式重载。而在Python 中,虽然在类中这样写不会报错,但实际上,下面的test(self, i) 已经把上面的test(self) 给笼罩掉了。建立出来的工具只能挪用test(self, i),而test(self) 是不存在的。

示例:

>>> a = A()       # 建立 A 的工具 a
A.__init__
>>>
>>> a.test(123)   # 可以挪用 test(self, i) 方式
test... i:123
>>>
>>> a.test()      # 挪用 test(self) 发生异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test() missing 1 required positional argument: 'i'

在继续关系中,代码如下:

#! /usr/bin/env python3

class A(object):

    def __init__(self):
        print('A.__init__')

    def test(self):
        print('test...')

class B(A):

    def __init__(self):
        print('B.__init__')

    def test(self, i):
        print('test... i:%s' % i)

上面代码中B 继续了ABA 中都有一个名为test 的方式,然则参数列表差别。

这种情形跟在单个类中的情形是一样的,在类B 中,test(self, i) 会笼罩A 中的test(self),类B 的工具只能挪用test(self, i),而不能挪用test(self)

示例:

>>> b = B()        # 建立 B 的工具
B.__init__
>>> 
>>> b.test(123)    # 可以挪用 test(self, i)  方式
test... i:123
>>>
>>> b.test()       # 挪用 test(self) 方式,出现异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test() missing 1 required positional argument: 'i'

super() 方式

super() 方式用于挪用父类中的方式。

示例代码:

#! /usr/bin/env python3

class A(object):

    def __init__(self):
        print('A.__init__')

    def test(self):
        print('class_A test...')

class B(A):

    def __init__(self):
        print('B.__init__')
        super().__init__()     # 挪用父类中的组织方式

    def test(self, i):
        print('class_B test... i:%s' % i)
        super().test()         # 挪用父类中的 test 方式

演示:

>>> b = B()          # 建立 B 的工具
B.__init__           # 挪用 B 的组织方式
A.__init__           # 挪用 A 的组织方式
>>>
>>> b.test(123)      # 挪用 B 中的 test 方式 
class_B test... i:123
class_A test...      # 执行 A 中的 test 方式 

is-a 关系

一个子类的工具,同时也是一个父类的工具,这叫做is-a 关系。然则一个父类的工具,纷歧定是一个子类的工具。

这很好明白,就像,猫一定是动物,但动物纷歧定是猫。

我们可以使用isinstance() 函数来判断一个工具是否是一个类的实例。

好比我们有如下两个类,Cat 继续了 Animal

#! /usr/bin/env python3

class Animal(object):
    pass

class Cat(Animal):
    pass

来看下工具和类之间的从属关系:

>>> a = Animal()           # 建立 Animal 的工具
>>> c = Cat()              # 建立 Cat 的工具
>>> 
>>> isinstance(a, Animal)  # a 一定是 Animal 的实例
True
>>> isinstance(c, Cat)     # c 一定是 Cat 的实例
True
>>> 
>>> isinstance(c, Animal)  # Cat 继续了 Animal,以是 c 也是 Animal 的实例
True
>>> isinstance(a, Cat)     # 但 a 不是 Cat 的实例
False

3,多继续

多继续就是一个子类同时继续多个父类,这样,这个子类就同时拥有了多个父类的特征。

C++ 语言中允许多继续,但由于多继续会使得类的继续关系变得庞大。因此,到了Java 中,就克制了多继续的方式,取而代之的是,在Java 中允许同时继续多个接口

Python 中也允许多继续,语法如下:

# 括号中可以写多个父类
class 子类名(父类1, 父类2, ...):
    pass

我们组织一个如下的继续关系:

代码如下:

#! /usr/bin/env python3

class A(object):
    def test(self):
        print('class_A test...')

class B(A):
    def test(self):
        print('class_B test...') 

class C(A):
    def test(self):
        print('class_C test...') 

class D(B, C):
    pass

ABC 中都有test() 方式,D 中没有test() 方式。

使用D 类中的mro()方式查看继续关系:

>>> D.mro()
[<class 'Test.D'>, <class 'Test.B'>, <class 'Test.C'>, <class 'Test.A'>, <class 'object'>]

建立D 的工具:

>>> d = D()

若是类D 中有test() 方式,那么d.test() 肯定会挪用D 中的test() 方式,这种情形很简单,不用多说。

当类D 中没有test() 方式时,而它继续的父类 BC 中都有 test() 方式,此时会挪用哪个test() 呢?

>>> d.test()
class_B test...

可以看到d.test() 挪用了类B 中的 test() 方式。

实际上这种情形下,Python 注释器会凭据D.mro() 的输出效果来依次查找test() 方式,即查找顺序是D->B->C->A->object

以是d.test() 挪用了类B 中的 test() 方式。

建议:

由于多继续会使类的继续关系变得庞大,以是并不提倡过多的使用多继续

4,多态

多态从字面上明白就是一个事物可以出现多种状态。继续是多态的基础。

在上面的例子中,类D 的工具d 挪用test() 方式时,沿着继续链D.mro())查找合适的test() 方式的历程,就是多态的显示历程。

好比,我们有以下几个类:

  • Animal:有一个speak() 方式
  • Cat:继续Animal 类,有自己的speak() 方式
  • Dog:继续Animal 类,有自己的speak() 方式
  • Duck:继续Animal 类,有自己的speak() 方式

CatDogDuck 都属于动物,因此都继续Animal,代码如下:

#! /usr/bin/env python3

class Animal(object):
    def speak(self):
        print('动物会语言...')

class Cat(Animal):
    def speak(self):
        print('喵喵...') 

class Dog(Animal):
    def speak(self):
        print('汪汪...') 

class Duck(Animal):
    def speak(self):
        print('嘎嘎...') 

def animal_speak(animal):
    animal.speak()

我们还界说了一个animal_speak 函数,它接受一个参数animal,在函数内,挪用了speak() 方式。

实际上,这种情形下,我们挪用animal_speak 函数时,可以为它通报Animal 类型的工具,以及任何的Animal 子类的工具。

通报Animal 的工具时,挪用了Animal 类中的 speak()

>>> animal_speak(Animal())
动物会语言...

通报Cat 的工具时,挪用了Cat 类中的 speak()

>>> animal_speak(Cat())
喵喵...

通报Dog 的工具时,挪用了Dog 类中的 speak()

>>> animal_speak(Dog())
汪汪...

通报Duck 的工具时,挪用了Duck 类中的 speak()

>>> animal_speak(Duck())
嘎嘎...

可以看到,我们可以给animal_speak() 函数通报多种差别类型的工具,为animal_speak() 函数通报差别类型的参数,输出了差别的效果,这就是多态

5,鸭子类型

静态类型语言中,有严酷的类型判断,上面的animal_speak() 函数的参数只能通报Animal 及其子类的工具。

而Python 属于动态类型语言,不会举行严酷的类型判断。

因此,我们不仅可以为animal_speak() 函数通报Animal 及其子类的工具,还可以通报其它与Animal 类毫不相关的类的工具,只要该类中有speak() 方式就行。

这种特征,在Python 中被叫做鸭子类型,意思就是,只要一个事物走起来像鸭子,叫起来像鸭子,那么它就是鸭子,纵然它不是真正的鸭子

从代码上来说,只要一个类中有speak() 方式,那么就可以将该类的工具通报给animal_speak() 函数。

好比,有一个鼓类Drum,其中有一个函数speak()

class Drum(object):
    def speak(self):
        print('咚咚...')

那么,类Drum 的工具也可以通报给animal_speak() 函数,纵然DrumAnimal 类毫不相关:

>>> animal_speak(Drum())
咚咚...

从另一个角度来思量,实际上Python 函数中的参数,并没有标明参数的类型。在animal_speak() 函数中,我们只是将参数叫做了animal 而已,因此我们就以为animal_speak() 函数应该接受Animal 类及其子类的工具,实在这仅仅只是我们以为的而已。

计算机并不知道animal 的寄义,若是我们将原来的animal_speak() 函数:

def animal_speak(animal):
    animal.speak()

改写成:

def animal_speak(a):
    a.speak()

实际上,我们知道,这两个函数并没有任何区别。因此,参数a可以是随便的类型,只要a 中有speak() 方式就行。这就是Python 能够显示出鸭子特征的缘故原由。

(完。)

推荐阅读:

Python 简明教程 --- 16,Python 高阶函数

Python 简明教程 --- 17,Python 模块与包

Python 简明教程 --- 18,Python 面向工具

Python 简明教程 --- 19,Python 类与工具

Python 简明教程 --- 20,Python 类中的属性与方式

迎接关注作者民众号,获取更多手艺干货。

,

Allbet官网

欢迎进入Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

Allbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:联博开奖:Python 简明教程 --- 21,Python 继续与多态

网友评论

  • (*)

最新评论

站点信息

  • 文章总数:521
  • 页面总数:0
  • 分类总数:8
  • 标签总数:945
  • 评论总数:144
  • 浏览总数:3159