python3 调用父类构造方法, 以及调用普通方法的思考_阿常呓语的专栏 - CSDN 博客_python3 调用父类

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

python 调用 父类构造方法, 以及调用普通方法的思考

文章目录

最近看一些 代码, 产生 一些困惑 , 关于 python 中的调用父类的方法以及 构造方法, 有的方法 直接用类去调用.

我不是很理解 这种写法,

一段代码引发的思考


下面是我看 WSGIServer 代码的时候, 有一点疑惑 .

server_bind() 这个实例方法 的调用 . 为啥要 用类名去调用一个 实例方法呢 ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class TCPServer:
    def server_bind(self):
        print("TCPServer server_bind() is  calling.")
        pass


class HTTPServer(TCPServer):
    allow_reuse_address = 1  # Seems to make sense in testing environment

    def server_bind(self):
        """Override server_bind to store the server name."""
        TCPServer.server_bind(self)


class WSGIServer(HTTPServer):
    """BaseHTTPServer that implements the Python WSGI protocol"""

    application = None

    def server_bind(self):
        """Override server_bind to store the server name."""
        HTTPServer.server_bind(self)
        self.setup_environ()

    def setup_environ(self):
        # Set up base environment
        env = self.base_environ = {}
        # env['SERVER_NAME'] = self.server_name
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        # env['SERVER_PORT'] = str(self.server_port)
        env['REMOTE_HOST'] = ''
        env['CONTENT_LENGTH'] = ''
        env['SCRIPT_NAME'] = ''

    def get_app(self):
        return self.application

    def set_app(self, application):
        self.application = application

这个继承关系 : WSGIServer –> HTTPServer –> TCPServer

在 WSGIServer 里面 , server_bind 这个方法里面, HTTPServer.server_bind(self) 为啥要这样调用 server_bind 呢? 为啥不使用 super 关键字去调用呢?

为啥要使用 类名去调用一个 实例方法呢? 我陷入 了沉思 . 实例方法 不是要给实例对象来调用的吗, 不明白前辈们 当时写代码的想法. 带着好奇 我查了 一下资料 .

super 关键字


看着一段简单的代码 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person:

    def __init__(self):
        print('Person  __init__() is calling')
        self.name = 'person'
        pass

    def get_name(self):
        print(f"name:{self.name!r}")

class Frank(Person):

    def __init__(self):
        print('Frank  __init__() is calling')

        self.myname = 'frank'
        pass

    def get_myname(self):
        print(f"myname:{self.myname!r}")


if __name__ == '__main__':
    frank = Frank()
    frank.get_name()
    frank.get_myname()

    pass

继承关系 中, 如果一个方法在子类的实例中被调用,或者一个属性在子类的实例中被访问,但是该方法或属性在子类中并不存在,那么就会自动的去其父类中进行查找

这里 就会报错

AttributeError: ‘Frank’ object has no attribute ‘name’ Frank init() is calling

这里在调用 的时候 get_name 调用父类的方法 , 但是父类 self.name 并没有在子类进行初始化, 所有 就报错了 , 解决这个问题, 只要在初始化的时候, 加上父类的初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Frank(Person):

    def __init__(self):
        print('Frank  __init__() is calling')
        # 父类初始化
        Person.__init__(self)
        self.myname = 'frank'
        pass
    def get_myname(self):
        print(f"myname:{self.myname!r}")

这样 就可以正常 出结果:

Frank init() is calling Person init() is calling name:‘person’ myname:‘frank’

还有一种写法 : 使用关键字 super

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Frank(Person):

    def __init__(self):
        print('Frank  __init__() is calling')
        # super关键字
        super(Frank, self).__init__()
        self.myname = 'frank'
        pass

    def get_myname(self):
        print(f"myname:{self.myname!r}")

也可以这样写 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Frank(Person):

    def __init__(self):
        print('Frank  __init__() is calling')
        # super(Frank, self).__init__()
        super().__init__()
        self.myname = 'frank'
        pass

    def get_myname(self):
        print(f"myname:{self.myname!r}")


if __name__ == '__main__':
    frank = Frank()
    frank.get_name()
    frank.get_myname()

以上写法 都可以在子类初始化的时候, 父类也就 初始化了. 看起来 结果 都一样 .

但是 super 和 直接用类 调用 init() 方法 到底有什么区别 呢 ?

总结了一下 有三种写法 :

1
2
3
4
5
6
7
8
9
class Frank(Person):

    def __init__(self):
        print('Frank  __init__() is calling')
        # Person.__init__(self)
        # super().__init__()
        # super(Frank, self).__init__()
        super().__init__()
        self.myname = 'frank'

以上者三种写法

Person.init(self) 调用未绑定的超类构造方法

super.init() 这样调用

从这里来看几乎看不到区别

下面的一段代码来自 python cookbook

调用父类方法 https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Base:
    def __init__(self):
        print('Base.__init__')

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

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

class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('C.__init__')
1
2
3
4
5
6
>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__

从这里看 Base init 执行 了两次.

如果 这样写 , 用 super 关键字 , 这样只会执行一次.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')

class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')

class C(A,B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print('C.__init__')
1
2
3
4
5
c  = C()
Base.__init__
B.__init__
A.__init__
C.__init__

这就是区别.

这就是 python 中的多重继承的问题

Python 会计算出一个所谓的方法解析顺序 (MRO) 列表 ,super() 已经帮助我们 处理了这个关系, 就不会调用 两次__init__() 方法.

如果是在单继承 就不会出现这个问题. 所以 super 就是为了解决这个问题而生的. 所以 是没有问题. python cook book 作者也推荐 使用 super .

super 两种写法的区别是什么?


还有一个问题 : super 两种写法 到底有什么区别 ?

super(Frank, self).init()

super().init()

其实 super 关键字 PEP 3135 – New Super New Super

给出了 说明:

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS81MTM0MTA2NzhjZjY0Mjc3NDM5ZWI2MWQ1NmQzNzMzNy94bWxub3RlLzQ2RTc0MjAxNDVCMDQ1Mzk4NkMxRkE1M0ZCMjBDOTlFLzQ0Nzk4?x-oss-process=image/format,png

​ 这个是两种写法, 现在已经不需要在 super() 里面传入任何关键字了. 直接使用第一种写法 就可以了. super().foo(1,2) 这个是比较新的写法 . 在 supe() 里面传入类名, 是旧的写法.

回到最初的问题


文章开头的疑问

server_bind() 为啥 要用类名调用一个实例方法呢?

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS81MTM0MTA2NzhjZjY0Mjc3NDM5ZWI2MWQ1NmQzNzMzNy94bWxub3RlLzRDMDU2OEUwQjU5MzRCN0E4OTdDQ0Q0ODU0QjFCQ0IyLzQ0Nzky?x-oss-process=image/format,png

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ub3RlLnlvdWRhby5jb20veXdzL3B1YmxpYy9yZXNvdXJjZS81MTM0MTA2NzhjZjY0Mjc3NDM5ZWI2MWQ1NmQzNzMzNy94bWxub3RlLzM1NEE0Rjc0NzhGOTQ1M0RBQjQyNzMyMzMyQUVBRUVBLzQ0Nzk0?x-oss-process=image/format,png

作者这样 写法 首先不是错误, 因为 本身就是单继承, 不会出现方法被调用多次的情况 . 所以应该没有什么问题 .

但是作者为啥这样写, 本人猜测 这些代码是 很久以前写的,

那个时候 super 支持并不是特别好, 所以就使用了比较原始的写法 , 但是看到这样的代码, 也不要感觉奇怪. 只是以后 我们在写法 的时候 , 要新的方式写就可以了 . 接受别以前的代码, 自己写代码用最新的方式就好了.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class WSGIServer(HTTPServer):
    """BaseHTTPServer that implements the Python WSGI protocol"""

    application = None

    def server_bind(self):
        """Override server_bind to store the server name."""
        # HTTPServer.server_bind(self)
        # 这样写可以了. 
        super().server_bind()
        self.setup_environ()

参考文档


python 子类调用父类的方法 http://www.crazyant.net/1303.html

PEP 3135 – New Super https://www.python.org/dev/peps/pep-3135/

8.7 调用父类方法 https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html

分享快乐, 留住感动. ‘2019-09-03 19:33:38’ –frank