本文由 简悦 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
给出了 说明:
这个是两种写法, 现在已经不需要在 super() 里面传入任何关键字了. 直接使用第一种写法 就可以了. super().foo(1,2) 这个是比较新的写法 . 在 supe() 里面传入类名, 是旧的写法.
回到最初的问题
文章开头的疑问
server_bind() 为啥 要用类名调用一个实例方法呢?
作者这样 写法 首先不是错误, 因为 本身就是单继承, 不会出现方法被调用多次的情况 . 所以应该没有什么问题 .
但是作者为啥这样写, 本人猜测 这些代码是 很久以前写的,
那个时候 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