• 張近東代表建議,用AI、區塊鏈技術打造數據安全“護城河” 2018-08-18
  • 清明忆先烈 习近平这样传承“红色基因” 2018-08-18
  • 第二届福建省大学生文化创新创意大赛落幕 2018-08-18
  • 乘客突发脑梗 611路司机忙送医一直等到家属到 2018-08-18
  • 中国新一代百亿亿次超级计算机“天河三号”原型机首次亮相[组图] 2018-08-17
  • “动静”结合丨带你感受不一样的实爆训练场 2018-08-17
  • 侠客岛:拿什么拯救你,不学普通话的香港年轻人 2018-08-17
  • 机构改革是一场政治考验 2018-08-17
  • 简讯:韩国前总统朴槿惠一审被判24年有期徒刑 2018-08-16
  • 人民网评:走向大洋,呼唤现代化的中国海军 2018-08-16
  • 北京成立学生申诉处理委员会-地方新闻-时政频道-中工网 2018-08-16
  • 虽胜不足喜!鲁能一隐忧不治愈,恐高开低走 2018-08-16
  • 小鱼儿戴胡可帽子似贵妇 临睡之前表白妈妈嘴超甜 2018-08-16
  • “北京8分钟”融入人工智能 通过互联网与国人互动 2018-08-16
  • 四部门调整完善新能源汽车推广应用财政补贴政策 2018-08-15
  • Python的类,继承和多态详解

    栏目: IT技术资讯 发布于: 2017-07-22 02:47:35

    类的定义

    假如要定义一个类Point,表示二维的坐标点:


    1

    2

    3

    4

    # point.py

    class?Point:

    ????def __init__(self,?x=0,?y=0):

    ????????self.x,?self.y?= x,?y

    最基本的就是__init__?方法,相当于 C++ / Java 的构造函数。带双下划线?__?的方法都是特殊方法,除了?__init__?还有很多,后面会有介绍。

    参数self?相当于 C++ 的?this,表示当前实例,所有方法都有这个参数,但是调用时并不需要指定。


    1

    2

    3

    4

    5

    6

    >>> from point import *

    >>> p?= Point(10,?10)??# __init__ 被调用

    >>> type(p)

    <class?'point.Point'>

    >>> p.x,?p.y

    (10,?10)

    几乎所有的特殊方法(包括__init__)都是隐式调用的(不直接调用)。

    对一切皆对象的 Python 来说,类自己当然也是对象:


    1

    2

    3

    4

    5

    6

    7

    >>> type(Point)

    <class?'type'>

    >>> dir(Point)

    ['__class__',?'__delattr__',?'__dict__',?...,?'__init__',?...]

    >>> Point.__class__

    <class?'type'>


    Point?是?type?的一个实例,这和?p?是?Point?的一个实例是一回事。

    现添加方法set


    1

    2

    3

    4

    class?Point:

    ????...

    ????def set(self,?x,?y):

    ????????self.x,?self.y?= x,?y


    1

    2

    3

    4

    >>> p?= Point(10,?10)

    >>> p.set(0,?0)

    >>> p.x,?p.y

    (0,?0)

    p.set(...)?其实只是一个语法糖,你也可以写成?Point.set(p, ...),这样就能明显看出p?就是?self?参数了:


    1

    2

    3

    >>> Point.set(p,?0,?0)

    >>> p.x,?p.y

    (0,?0)

    值得注意的是,self?并不是关键字,甚至可以用其它名字替代,比如?this


    1

    2

    3

    4

    class?Point:

    ????...

    ????def set(this,?x,?y):

    ????????this.x,?this.y?= x,?y

    与 C++ 不同的是,“成员变量”必须要加?self.?前缀,否则就变成类的属性(相当于 C++ 静态成员),而不是对象的属性了。

    访问控制

    Python 没有?public / protected / private?这样的访问控制,如果你非要表示“私有”,习惯是加双下划线前缀。


    1

    2

    3

    4

    5

    6

    7

    8

    9

    class?Point:

    ????def __init__(self,?x=0,?y=0):

    ????????self.__x,?self.__y?= x,?y


    ????def set(self,?x,?y):

    ????????self.__x,?self.__y?= x,?y


    ????def __f(self):

    ????????pass

    __x、__y?和?__f?就相当于私有了:


    1

    2

    3

    4

    5

    6

    7

    >>> p?= Point(10,?10)

    >>> p.__x

    ...

    AttributeError: 'Point'?object?has no attribute?'__x'

    >>> p.__f()

    ...

    AttributeError: 'Point'?object?has no attribute?'__f'

    _repr_

    尝试打印Point?实例:


    1

    2

    3

    4

    >>> p?= Point(10,?10)

    >>> p

    <point.Point object?at?0x000000000272AA20>


    通常,这并不是我们想要的输出,我们想要的是:


    1

    2

    >>> p

    Point(10,?10)

    添加特殊方法__repr__?即可实现:


    1

    2

    3

    class?Point:

    ????def __repr__(self):

    ????????return?'Point({}, {})'.format(self.__x,?self.__y)

    不难看出,交互模式在打印p?时其实是调用了?repr(p)


    1

    2

    >>> repr(p)

    'Point(10, 10)'

    _str_

    如果没有提供__str__,str()?缺省使用?repr()?的结果。
    这两者都是对象的字符串形式的表示,但还是有点差别的。简单来说,repr()?的结果面向的是解释器,通常都是合法的 Python 代码,比如?Point(10, 10);而str()?的结果面向用户,更简洁,比如?(10, 10)。

    按照这个原则,我们为Point?提供?__str__?的定义如下:


    1

    2

    3

    class?Point:

    ????def __str__(self):

    ????????return?'({}, {})'.format(self.__x,?self.__y)

    _add_

    两个坐标点相加是个很合理的需求。


    1

    2

    3

    4

    5

    6

    >>> p1?= Point(10,?10)

    >>> p2?= Point(10,?10)

    >>> p3?= p1?+ p2

    Traceback?(most recent call last):

    ??File?"<stdin>",?line?1,?in?<module>

    TypeError: unsupported operand type(s)?for?+: 'Point'?and?'Point'

    添加特殊方法__add__?即可做到:


    1

    2

    3

    class?Point:

    ????def __add__(self,?other):

    ????????return?Point(self.__x?+ other.__x,?self.__y?+ other.__y)


    1

    2

    3

    >>> p3?= p1?+ p2

    >>> p3

    Point(20,?20)

    这就像 C++ 里的操作符重载一样。
    Python 的内建类型,比如字符串、列表,都“重载”了?+?操作符。

    特殊方法还有很多,这里就不逐一介绍了。

    继承

    举一个教科书中最常见的例子。Circle?和?Rectangle?继承自?Shape,不同的图形,面积(area)计算方式不同。


    用法比较直接:


    1

    2

    3

    4

    5

    6

    7

    >>> from shape import *

    >>> circle?= Circle(3.0)

    >>> circle.area()

    28.274333882308138

    >>> rectangle?= Rectangle(2.0,?3.0)

    >>> rectangle.area()

    6.0

    如果Circle?没有定义自己的?area


    1

    2

    class?Circle(Shape):

    ????pass

    那么它将继承父类Shape?的?area


    1

    2

    >>> Shape.area is?Circle.area

    True

    一旦Circle?定义了自己的?area,从Shape?继承而来的那个?area?就被重写(overwrite)了:


    1

    2

    3

    >>> from shape import *

    >>> Shape.area is?Circle.area

    False

    通过类的字典更能明显地看清这一点:


    1

    2

    3

    4

    5

    >>> Shape.__dict__['area']

    <function?Shape.area at?0x0000000001FDB9D8>

    >>> Circle.__dict__['area']

    <function?Circle.area at?0x0000000001FDBB70>


    所以,子类重写父类的方法,其实只是把相同的属性名绑定到了不同的函数对象??杉?Python 是没有覆写(override)的概念的。

    同理,即使Shape?没有定义?area?也是可以的,Shape?作为“接口”,并不能得到语法的保证。

    甚至可以动态的添加方法:


    1

    2

    3

    4

    5

    6

    7

    class?Circle(Shape):

    ????...

    ????#??def area(self):

    ????????#??return math.pi * self.r * self.r


    # 为 Circle 添加 area 方法。

    Circle.area?= lambda self: math.pi *?self.r *?self.r

    动态语言一般都是这么灵活,Python 也不例外。

    Python 官方教程「9. Classes」第一句就是:

    Compared with other programming languages, Python’s class mechanism adds classes with a minimum of new syntax and semantics.

    Python 以最少的新的语法和语义实现了类机制,这一点确实让人惊叹,但是也让 C++ / Java 程序员感到颇为不适。

    多态

    如前所述,Python 没有覆写(override)的概念。严格来讲,Python 并不支持「多态」。

    为了解决继承结构中接口和实现的问题,或者说为了更好的用 Python 面向接口编程(设计模式所提倡的),我们需要人为的设一些规范。

    请考虑Shape.area()?除了简单的返回?0.0,有没有更好的实现?

    以内建???/span>asyncio?为例,AbstractEventLoop?原则上是一个接口,类似于 Java 中的接口或 C++ 中的纯虚类,但是 Python 并没有语法去保证这一点,为了尽量体现?AbstractEventLoop?是一个接口,首先在名字上标志它是抽象的(Abstract),然后让每个方法都抛出异常?NotImplementedError。


    1

    2

    3

    4

    class?AbstractEventLoop:

    ????def run_forever(self):

    ????????raise NotImplementedError

    ????...

    纵然如此,你是无法禁止用户实例化AbstractEventLoop?的:


    1

    2

    3

    4

    5

    loop?= asyncio.AbstractEventLoop()

    try:

    ????loop.run_forever()

    except NotImplementedError:

    ????pass

    C++ 可以通过纯虚函数或设构造函数为?protected?来避免接口被实例化,Java 就更不用说了,接口就是接口,有完整的语法支持。

    你也无法强制子类必须实现“接口”中定义的每一个方法,C++ 的纯虚函数可以强制这一点(Java 更不必说)。

    就算子类「自以为」实现了“接口”中的方法,也不能保证方法的名字没有写错,C++ 的?override?关键字可以保证这一点(Java 更不必说)。

    静态类型的缺失,让 Python 很难实现 C++ / Java 那样严格的多态检查机制。所以面向接口的编程,对 Python 来说,更多的要依靠程序员的素养。

    回到Shape?的例子,仿照?asyncio,我们把“接口”改成这样:


    1

    2

    3

    class?AbstractShape:

    ????def area(self):

    ????????raise NotImplementedError

    这样,它才更像一个接口。

    super

    有时候,需要在子类中调用父类的方法。

    比如图形都有颜色这个属性,所以不妨加一个参数color?到?__init__


    1

    2

    3

    class?AbstractShape:

    ????def __init__(self,?color):

    ????????self.color?= color

    那么子类的__init__()?势必也要跟着改动:


    1

    2

    3

    4

    class?Circle(AbstractShape):

    ????def __init__(self,?color,?r=0.0):

    ????????super().__init__(color)

    ????????self.r?= r

    通过super?把?color?传给父类的?__init__()。其实不用super?也行:


    1

    2

    3

    4

    class?Circle(AbstractShape):

    ????def __init__(self,?color,?r=0.0):

    ????????AbstractShape.__init__(self,?color)

    ????????self.r?= r

    但是super?是推荐的做法,因为它避免了硬编码,也能处理多继承的情况。


    信ID:vfsatedu

    你来过且关注

    ? ? ? ? ?是我的“荣新

    查看“阅读原文”这里有你想要的
    本站文章除注明转载外,均为本站原创或编译?;队魏涡问降淖?,但请务必注明出处。
    转载请注明:文章转载自 七星彩票平台
    本文标题:Python的类,继承和多态详解
    IT技术书籍推荐:
    算法导论(原书第3版)
    算法导论(原书第3版)
    Thomas H.Cormen (作者), Charles E.Leiserson (作者), Ronald L.Rivest (作者), Clifford Stein (作者), 殷建平 (译者), 徐云 (译者), 王刚 (译者), 等 (译者)
    《算法导论(原书第3版)》内容简介:在有关算法的书中,有一些叙述非常严谨,但不够全面;另一些涉及了大量的题材,但又缺乏严谨性?!端惴ǖ悸?原书第3版)》将严谨性和全面性融为一体,深入讨论各类算法,并着力使这些算法的设计和分析能为各个层次的读者接受。全书各章自成体系,可以作为独立的学习单元;算法以英语和伪代码的形式描述,具备初步程序设计经验的人就能看懂;说明和解释力求浅显易懂,不失深度和数学严谨性。全书选材经典、内容丰富、结构合理、逻辑清晰,对本科生的数据结构课程和研究生的算法课程都是非常实用的教材,在IT专业人员的职业生涯中,《算法导论(原书第3版)》也是一本案头必备的参考书或工程实践手册。