Skip to content

Commit 7fabe93

Browse files
committed
p3
1 parent 6ba24b9 commit 7fabe93

File tree

1 file changed

+31
-97
lines changed

1 file changed

+31
-97
lines changed

209.md

Lines changed: 31 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Python 3中也有异常:
164164

165165
还有一种可能存在,就是重写之后,如果要在子类中继承父类中相应部分,怎么办?
166166

167-
##super函数
167+
##调用覆盖的方法
168168

169169
承接前面的问题和程序,可以对子类`Gril`做出这样的修改。
170170

@@ -176,9 +176,9 @@ Python 3中也有异常:
176176
def get_name(self):
177177
return self.name
178178

179-
请读者注意观察`Girl`的初始化方法,与前面的有所不同。为了能够继续继承父类的初始化方法,以类方法的方式将父类的初始化函数再次调用`Person.__init__(self, name)`,同时,在子类的`__init__()`的参数中,要增加相应的参数`name`。这样就回答了前面的问题。
179+
请读者注意观察`Girl`的初始化方法,与前面的有所不同。为了能够使用父类的初始化方法,以类方法的方式调用`Person.__init__(self, name)`。另外,在子类的`__init__()`的参数中,要增加相应的参数`name`。这样就回答了前面的问题。
180180

181-
在实例化子类的时候,就以下面的方式进行
181+
实例化子类,以下面的方式运行程序
182182

183183
if __name__ == "__main__":
184184
cang = Girl("canglaoshi")
@@ -194,108 +194,40 @@ Python 3中也有异常:
194194
{'height': 160}
195195
{'breast': 90}
196196

197-
对于初始化函数的继承,跟一般方法的继承,还有点不同。可以看下面的例子:
197+
就这样,使用类方法的方式,将父类中被覆盖的方法再次在子类中实现。
198198

199-
#!/usr/bin/env python
200-
# coding=utf-8
201-
202-
__metaclass__ = type
203-
204-
class Person:
205-
def __init__(self):
206-
self.height = 160
207-
208-
def about(self, name):
209-
print "{} is about {}".format(name, self.height)
210-
211-
class Girl(Person):
212-
def __init__(self):
213-
self.breast = 90
214-
215-
def about(self, name):
216-
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
217-
218-
if __name__ == "__main__":
219-
cang = Girl()
220-
cang.about("canglaoshi")
221-
222-
在上面这段程序中,类Girl继承了类Person。在类Girl中,初始化设置了`self.breast = 90`,由于继承了Person,按照前面的经验,Person的初始化函数中的`self.height = 160`也应该被Girl所继承过来。然后在重写的about方法中,就是用`self.height`
223-
224-
实例化类Girl,并执行`cang.about("canglaoshi")`,试图打印出一句话`canglaoshi is a hot girl, she is about 160, and her bereast is 90`。保存程序,运行之:
225-
226-
$ python 20903.py
227-
Traceback (most recent call last):
228-
File "20903.py", line 22, in <module>
229-
cang.about("canglaoshi")
230-
File "20903.py", line 18, in about
231-
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
232-
AttributeError: 'Girl' object has no attribute 'height'
233-
234-
报错!
235-
236-
程序员有一句名言:不求最好,但求报错。报错不是坏事,是我们长经验的时候,是在告诉我们,那么做不对。
237-
238-
重要的是看报错信息。就是我们要打印的那句话出问题了,报错信息显示`self.height`是不存在的。也就是说类Girl没有从Person中继承过来这个属性。
239-
240-
原因是什么?仔细观察类Girl,会发现,除了刚才强调的about方法重写了,`__init__`方法,也被重写了。不要认为它的名字模样奇怪,就不把它看做类中的方法(函数),它跟类Person中的`__init__`重名了,也同样是重写了那个初始化函数。
241-
242-
这就提出了一个问题。因为在子类中重写了某个方法之后,父类中同样的方法被遮盖了。那么如何再把父类的该方法调出来使用呢?纵然被遮盖了,应该还是存在的,不要浪费了呀。
243-
244-
python中有这样一种方法,这种方式是被提倡的方法:super函数。
245-
246-
#!/usr/bin/env python
247-
# coding=utf-8
248-
249-
__metaclass__ = type
250-
251-
class Person:
252-
def __init__(self):
253-
self.height = 160
254-
255-
def about(self, name):
256-
print "{} is about {}".format(name, self.height)
199+
但上述方式有一个问题,如果父类的名称因为某种目前你无法预料的原因修改了,子类中该父类的的名称也要修改,有如果程序比较复杂或者忘记了,就会出现异常。于是乎,就有了更巧妙的方法——`super`。再重写子类。
257200

258201
class Girl(Person):
259-
def __init__(self):
260-
super(Girl, self).__init__()
261-
self.breast = 90
262-
263-
def about(self, name):
264-
print "{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)
265-
super(Girl, self).about(name)
266-
267-
if __name__ == "__main__":
268-
cang = Girl()
269-
cang.about("canglaoshi")
270-
271-
在子类中,`__init__`方法重写了,为了调用父类同方法,使用`super(Girl, self).__init__()`的方式。super函数的参数,第一个是当前子类的类名字,第二个是self,然后是点号,点号后面是所要调用的父类的方法。同样在子类重写的about方法中,也可以调用父类的about方法。
202+
def __init__(self, name):
203+
#Person.__init__(self, name)
204+
super(Girl, self).__init__(name)
205+
self.real_name = "Aoi sola"
272206

273-
执行结果:
207+
def get_name(self):
208+
return self.name
274209

275-
$ python 20903.py
276-
canglaoshi is a hot girl, she is about 160, and her breast is 90
277-
canglaoshi is about 160
210+
仅仅修改一处,将`Person.__init__(self, name)`去掉,修改为`super(Girl, self).__init__(name)`。实行程序后,显示的结果与以前一样。
278211

279-
最后要提醒注意:super函数仅仅适用于新式类。当然,你一定是使用的新式类。“喜新厌旧”是程序员的嗜好
212+
关于`super`,有人做了非常深入的研究,推荐读者阅读[《Python’s super() considered super! 》](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/),文中已经探究了`super`的工作过程。读者如果要深入了解,可以阅读这篇文章
280213

214+
###多重继承
281215

282-
##多重继承
216+
前面所说的继承,父类都只有一个。但,继承可以来自多个“父”,这就是多重继承。
283217

284-
所谓多重继承,就是指某一个类的父类,不止一个,而是多个。比如:
218+
所谓多重继承,就是指某一个子类的父类,不止一个,而是多个。比如:
285219

286220
#!/usr/bin/env python
287221
# coding=utf-8
288222

289-
__metaclass__ = type
290-
291-
class Person:
223+
class Person(object): #Python 3: class Person:
292224
def eye(self):
293-
print "two eyes"
225+
print "two eyes" #Python 3: print("two eyes"),下同,从略
294226

295227
def breast(self, n):
296228
print "The breast is: ",n
297229

298-
class Girl:
230+
class Girl(object): #Python 3: class Gril:
299231
age = 28
300232
def color(self):
301233
print "The girl is white"
@@ -310,32 +242,32 @@ python中有这样一种方法,这种方式是被提倡的方法:super函数
310242
kong.color()
311243
print kong.age
312244

313-
在这个程序中,前面有两个类:Person和Girl,然后第三个类HotGirl继承了这两个类,注意观察继承方法,就是在类的名字后面的括号中把所继承的两个类的名字写上。但是第三个类中什么方法也没有。
245+
在这个程序中,前面有两个类`Person``Girl`,然后第三个类`HotGirl`继承了这两个类,注意观察继承方法,就是在类的名字后面的括号中把所继承的两个类的名字写上。但是第三个类中什么方法也没有。
314246

315-
然后实例化类HotGirl,既然继承了上面的两个类,那么那两个类的方法就都能够拿过来使用。保存程序,运行一下看看
247+
然后实例化类`HotGirl`,既然继承了上面的两个类,那么那两个类的方法就都能够拿过来使用。保存程序,运行一下看看
316248

317249
$ python 20902.py
318250
two eyes
319251
The breast is: 90
320252
The girl is white
321253
28
322254

323-
值得注意的是,这次在类Girl中,有一个`age = 28`,在对HotGirl实例化之后,因为继承的原因,这个类属性也被继承到HotGirl中,因此通过实例属性`kong.age`一样能够得到该数据。
255+
值得注意的是,这次在类`Girl`,有一个`age = 28`,在对HotGirl实例化之后,因为继承的原因,这个类属性也被继承到`HotGirl`,因此通过实例属性`kong.age`一样能够得到该数据。
324256

325257
由上述两个实例,已经清楚看到了继承的特点,即将父类的方法和属性全部承接到子类中;如果子类重写了父类的方法,就使用子类的该方法,父类的被遮盖。
326258

327-
##多重继承的顺序
259+
多重继承的顺序很必要了解。
328260

329-
多重继承的顺序很必要了解。比如,如果一个子类继承了两个父类,并且两个父类有同样的方法或者属性,那么在实例化子类后,调用那个方法或属性,是属于哪个父类的呢?造一个没有实际意义,纯粹为了解决这个问题的程序:
261+
比如,如果一个子类继承了两个父类,并且两个父类有同样的方法或者属性,那么在实例化子类后,调用那个方法或属性,是属于哪个父类的呢?造一个没有实际意义,纯粹为了解决这个问题的程序:
330262

331263
#!/usr/bin/env python
332264
# coding=utf-8
333265

334-
class K1(object):
266+
class K1(object): #Python 3: class K1:
335267
def foo(self):
336-
print "K1-foo"
268+
print "K1-foo" #Python 3: print("K1-foo"),下同,从略
337269

338-
class K2(object):
270+
class K2(object): #Python 3: class K2:
339271
def foo(self):
340272
print "K2-foo"
341273
def bar(self):
@@ -364,14 +296,16 @@ python中有这样一种方法,这种方式是被提倡的方法:super函数
364296
K1-foo
365297
J2-bar
366298

367-
代码中的`print C.__mro__`是要打印出类的继承顺序。从上面清晰看出来了。如果要执行foo()方法,首先看J1,没有,看J2,还没有,看J1里面的K1,有了,即C==>J1==>J2==>K1;bar()也是按照这个顺序,在J2中就找到了一个
299+
代码中的`print C.__mro__`是要打印出类的继承顺序。从上面清晰看出来了。如果要执行`foo()`方法,首先看`J1`,没有,`J2`,还没有,`J1`里面的`K1`,有了,即C==>J1==>J2==>K1;`bar()`也是按照这个顺序,`J2`中就找到了一个
368300

369301
这种对继承属性和方法搜索的顺序称之为“广度优先”。
370302

371-
新式类用以及python3.x中都是按照此顺序原则搜寻属性和方法的
303+
Python 2的新式类,以及Python 3中都是按照此顺序原则搜寻属性和方法的
372304

373305
但是,在旧式类中,是按照“深度优先”的顺序的。因为后面读者也基本不用旧式类,所以不举例。如果读者愿意,可以自己模仿上面代码,探索旧式类的“深度优先”含义。
374306

307+
导致新式类和Python 3中继承顺序较旧式类有所变化,其原因是mro(Method Resolution Order)算法,读者对此若有兴趣,可以到网上搜索关于这个算法的内容进行了解。
308+
375309

376310
------
377311

0 commit comments

Comments
 (0)