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

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


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

我不是很理解 这种写法,

一段代码引发的思考

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

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

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 关键字

看着一段简单的代码 :

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 并没有在子类进行初始化, 所有 就报错了 , 解决这个问题, 只要在初始化的时候, 加上父类的初始化

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

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}")

也可以这样写 :

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() 方法 到底有什么区别 呢 ?

总结了一下 有三种写法 :

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

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__')
>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__

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

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

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__')
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

给出了 说明:

image-20190901083516319

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

回到最初的问题

文章开头的疑问

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

image-20190901083826113

image-20190901083854294

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

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

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

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
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值