> This article from the “Python Why? ” series , Please check [ All articles ](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUyOTk2MTcwNg==&action=getalbum&album_id=1338406397180084225&subscene=0&scenenote=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzUyOTk2MTcwNg%3D%3D%26mid%3D2247485945%26idx%3D1%26sn%3D02f1ac9a690f57accefeed7a1ea1247b%26chksm%3Dfa584e7ccd2fc76af5d45ebbc43c1e4d379a0fcfeee8ce70111a8a293c4b7efc8ac7a82dfd6a%26xtrack%3D1%26scene%3D0%26subscene%3D91%26sessionid%3D1596284425%26clicktime%3D1596284604%26enterid%3D1596284604%26ascene%3D7%26devicetype%3Dandroid-28%26version%3D2700103f%26nettype%3DWIFI%26abtest_cookie%3DAAACAA%253D%253D%26lang%3Dzh_CN%26exportkey%3DAa1rI96xIRQ8cDJChmQS9BU%253D%26pass_ticket%3DP%252BUyocSqsqUN5JuCQOyjZNpQH%252Fwm0bsN6NchdKKM9CFDDEu0ZPKsRpo8Utu4BBRc%26wx_header%3D1#wechat_redirect) not long ago ,`Python Cat ` I recommend a book to you 《 Flowing Python》([ Click to jump to read ](https://mp.weixin.qq.com/s/A4_DD2fvceNk1apn9MQcXA)), That article has a lot of “ Words of praise ”, It seems to be rather vague …… however ,《 Flowing Python》 A book is worth reading over and over again , You can learn from the old . Recently, I found a strange knowledge point in the book , So I'm going to talk about this topic ——** Subclassing built-in types can cause problems ?!**  ## 1、 What are the built-in types ? Before the official start , First of all, we need to popularize science :** Which are Python The built-in type of ?** According to the classification of official documents , Built in type (Built-in Types) It mainly includes the following contents :  Detailed documents :https://docs.python.org/3/library/stdtypes.html among , There is a well-known [ Number type ](https://mp.weixin.qq.com/s/0XpPaH53II5yO9Lfh80ZOw)、 Sequence type 、 Text type 、 And so on , And, of course, we've introduced [ Brin type ](https://mp.weixin.qq.com/s/JVhXjQKcd8uds8yTUumZJw)、[... thing ](https://mp.weixin.qq.com/s/SOSN_p74eDHv3tJnSJIZfg) wait . In so much content , This article focuses only on what is done ` Callable objects `(callable) The built-in type of , That is, with built-in functions (built-in function) On the surface, similar ones :**int、str、list、tuple、range、set、dict……** These types (type) It can be simply understood as a class in other languages (class), however Python There is no customary nomenclature for the hump , So it's easy to get misunderstood . stay Python 2.2 After that , These built-in types can be subclassed (subclassing), That is, it can be inherited (inherit). ## 2、 Subclassing of built-in types As we all know , For a common object x,Python To find its length, we need to use the common built-in function len(x), It is not like Java Object oriented languages like this , The latter objects usually have their own x.length() Method .(PS: The analysis of these two design styles , Recommended reading [ This article ](https://mp.weixin.qq.com/s/pKQT5wvyaSNFvnJexiCC8w)) Now , Suppose we want to define a list class , Hope it has its own length() Method , At the same time, keep all the features of a normal list . The experimental code is as follows ( Just for demonstration ): ```python # Define a list Subclasses of class MyList(list): def length(self): return len(self) ``` We make MyList This custom class inherits list, At the same time, a new definition of length() Method . In this way ,MyList Just have append()、pop() Method, etc. , At the same time, I have length() Method . ```python # Add two elements ss = MyList() ss.append("Python") ss.append(" Cat ") print(ss.length()) # Output :2 ``` The other built-in types mentioned earlier , It can also be subclassed in this way , It shouldn't be hard to understand . By the way ,** What are the benefits of subclassing built-in types / What about the usage scenarios ?** There's an intuitive example , When we are in a custom class , When you need to use a list object frequently ( Add... To it / Delete the element 、 Pass on as a whole ……), In this case, if our class inherits from list, You can write directly self.append()、self.pop(), Or will self Pass on as an object , So you don't have to define an extra list object , It will be concise in writing . There are other benefits / Use scenarios ? Welcome to leave a message to discuss ~~ ## 3、 Built in subclasses of “ The problem is ” Finally, it will enter the formal theme of this article :) generally , In our textbook cognition ,** Methods in subclasses override methods with the same name in the parent class , That is to say , The query priority of subclass method is higher than that of parent method .** Here's an example , The parent class Cat, Subclass PythonCat, All have one say() Method , The function is to say the current object inner_voice: ```python # Python A cat is a cat class Cat(): def say(self): return self.inner_voice() def inner_voice(self): return " meow " class PythonCat(Cat): def inner_voice(self): return " Meow meow " ``` When we create subclasses PythonCat The object of , its say() Methods will take precedence over the ones defined by themselves inner_voice() Method , instead of Cat Parent inner_voice() Method : ```python my_cat = PythonCat() # The following results are in line with expectations print(my_cat.inner_voice()) # Output : Meow meow print(my_cat.say()) # Output : Meow meow ``` This is the Convention of programming language , It's a basic principle , Students who have studied the fundamentals of object-oriented programming should know . However , When Python When implementing inheritance ,** It seems incomplete ** Will operate according to the above rules . It's divided into two situations : - In accordance with common sense : For use Python Implemented classes , They will follow “ The subclass precedes the parent ” The principle of - Against common sense : To be practical, it is to use C Implemented classes ( namely str、list、dict Wait, these built-in types ), When explicitly calling subclass methods , Will follow “ The subclass precedes the parent ” The principle of ; however ,** When there is an implicit call ,** They seem to follow “ The parent class precedes the child class ” The principle of , That is, the usual inheritance rules will fail here Contrast PythonCat Example , Equivalent to saying , Direct call my_cat.inner_voice() When , Will get the right “ Meow meow ” It turns out , But it's calling my_cat.say() When , You get more than you expect “ meow ” It turns out . Here is 《 Flowing Python》 The example given in (12.1 Chapter ): ```python class DoppelDict(dict): def __setitem__(self, key, value): super().__setitem__(key, [value] * 2) dd = DoppelDict(one=1) # {'one': 1} dd['two'] = 2 # {'one': 1, 'two': [2, 2]} dd.update(three=3) # {'three': 3, 'one': 1, 'two': [2, 2]} ```  In this case ,dd['two'] Will call subclasses directly \_\_setitem\_\_() Method , So the results are in line with expectations . If other tests are as expected , The end result will be {'three': [3, 3], 'one': [1, 1], 'two': [2, 2]}. However , Initialization and update() Inherited directly from the parent class \_\_init\_\_() and \_\_update\_\_(), And then they ** Implicitly ** call _\_setitem\_\_() Method , There is no method to call subclasses at this time , Instead, it calls the method of the parent class , The result is beyond expectation ! official Python This double rule approach , It's a little against common sense , If you don't pay attention to , It's easy to step on the pit if it's not done well . So , Why is this exception ? ## 4、 The true face of the built-in method We learned that built-in types don't implicitly call subclass overlay methods , Then , Namely `Python Cat ` To get to the bottom of it : Why doesn't it call ? 《[ Flowing Python](https://mp.weixin.qq.com/s/A4_DD2fvceNk1apn9MQcXA)》 There is no further questioning in the book , But , I tried to make a wild guess ( It should be verified from the source code ):** All the methods of built-in type are using C Language realizes , In fact, they don't call each other , So there is no query priority problem when calling .** That is to say , Ahead “\_\_init\_\_() and \_\_update\_\_() Will implicitly call _\_setitem\_\_() Method ” This statement is not accurate ! These magic methods are actually independent of each other !\_\_init\_\_() Have their own setitem Realize , It doesn't call the parent class \_\_setitem\_\_(), With Subclasses of course \_\_setitem\_\_() It doesn't matter much . To understand logically , Dictionary \_\_init\_\_() Method contains \_\_setitem\_\_() The function of , So we thought the former would call the latter ,** This is the embodiment of habitual thinking ,** However, the actual call relationship may be like this :  The method on the left opens the door to the language interface and enters the world on the right , To fulfill all its missions there , It does not return to the original interface to query the next instruction ( There is no red line path in the graph ). The reason for not turning back is simple , namely C Code calls between languages are more efficient , The implementation path is shorter , The implementation process is simpler . Empathy ,dict Type get() Method and \_\_getitem\_\_() There's no call relationship , If the subclass only covers \_\_getitem\_\_() And then , When a subclass calls get() When the method is used , It will actually use the parent class get() Method .(PS: On this point ,《 Flowing Python》 And PyPy The description of the document is not accurate , They mistook get() Method will call \_\_getitem\_\_()) That is to say ,Python The built-in method itself does not have a call relationship , Even though they're at the bottom C Language implementation , There may be common logic or methods that can be reused . I think of it. “[Python Why? ](https://github.com/chinesehuazhou/python-whydo)” The series has been analyzed 《[Python Why can support arbitrary truth value judgment ?](https://mp.weixin.qq.com/s/g6jZX0IdH9xpM7BMV3-ToQ)》. Before we write `if xxx` When , It seems to implicitly call \_\_bool\_\_() and \_\_len\_\_() Magic method , But in fact, the program is based on POP_JUMP_IF_FALSE Instructions , It goes straight into pure C Logic of code , There's no call to these magic methods ! therefore , Realizing C After the special methods implemented are independent of each other , Let's go back to subclassing built-in types , There will be new discoveries :  Parent \_\_init\_\_() Magic method will break the language interface to achieve its own mission , However, it is similar to subclasses of \_\_setitem\_\_() There is no pathway , That is, the red line path in the figure is not reachable . Special methods go their own way , thus , We'll come to a different conclusion from the previous one :** Actually Python Strictly followed “ Subclass methods precede parent methods ” The principle of succession , It doesn't destroy common sense !** Finally, it is worth mentioning that ,\_\_missing\_\_() It's a special case .《 Flowing Python》 Just a simple and vague sentence , Not too much unfolding . After preliminary experiments , I found that when subclasses define this method ,get() Read nonexistent key When , Normal return None; however \_\_getitem\_\_() and dd['xxx'] Read nonexistent key When , Will be defined by subclass \_\_missing\_\_() Processing .  I haven't had time to analyze , Please leave me a message if you know the answer . ## 5、 Best practices for subclassing built-in types To sum up , There is no problem with subclassing built-in types , It's just that we don't recognize the special method (C The method of language implementation ) The true face of , Will lead to the deviation of the result . So , This calls up a new question :** If you have to inherit the built-in type , What is the best way to practice ?** First , If after inheriting the built-in type , It doesn't rewrite (overwrite) Its special method , There's no problem with subclassing . secondly , If you want to override a particular method after inheritance , Remember to rewrite all the methods you want to change , for example , If you want to change get() Method , It's going to be rewritten get() Method , If you want to change \_\_getitem\_\_() Method , It's going to be rewritten …… however , If we just want to rewrite some logic ( namely C The language part ), If all the special methods used to use the logic have changed , For example, rewrite \_\_setitem\_\_() The logic of , At the same time, initialize and update() Wait for the operation to change , So what to do ? We know that there is no reuse between special methods , That is to say, simply define new \_\_setitem\_\_() It's not enough , So , How can it affect multiple methods at the same time ? PyPy This unofficial Python The version found this problem , Its approach is to make a call to a particular method of a built-in type , Establish a path of connection between them . official Python Of course, I'm aware of this problem , But it doesn't change the nature of the built-in type , It's about offering new solutions :UserString、UserList、UserDict……  Except for the name , They are basically equivalent to built-in types . The basic logic of these classes is to use Python Realized , It is equivalent to putting the preceding paragraph C Some of the logic of the language interface has moved to Python Interface , Set up a call chain on the left side , In this way , It solves the reuse problem of some special methods . In contrast to the previous example , After adopting a new way of inheritance , The result is as expected : ```python from collections import UserDict class DoppelDict(UserDict): def __setitem__(self, key, value): super().__setitem__(key, [value] * 2) dd = DoppelDict(one=1) # {'one': [1, 1]} dd['two'] = 2 # {'one': [1, 1], 'two': [2, 2]} dd.update(three=3) # {'one': [1, 1], 'two': [2, 2], 'three': [3, 3]} ``` Obviously ,** If you want to inherit str/list/dict And then , The best practice is to inherit `collections` The classes provided by the library .** ## 6、 Summary Write so much , It's time to do ending 了 ~~ In the previous article in this series ,Python Cat from the query order and execution speed of two aspects , Analysis of the “[ Why is the built-in function / Built in types are not everything ](https://mp.weixin.qq.com/s/YtfPlE9JAIS3tpLBGFo5ag)”, This article follows it in the same vein , It also reveals some mysterious and seemingly defective behavior of built-in types . Although this article is from 《 Flowing Python》 The inspiration from the book , But beyond the surface of language , We asked one more question “ Why? ”, Thus further analysis of the phenomenon behind the principle . In short ,** Special methods for building in types are created by C Language independent implementation of , They are Python There is no call relationship in the language interface , So when subclassing an intrinsic type , A particular method that is overridden only affects the method itself , It doesn't affect the effect of other special methods .** If we have a misconception about the relationship between particular methods , You might think that Python Destroyed “ Subclass methods precede parent methods ” The basic principle of inheritance is .( I'm sorry 《 Flowing Python》 and PyPy They all have this wrong perception ) In order to meet the general expectations of built-in types ,Python In the standard library UserString、UserList、UserDict These extension suite classes , It is convenient for programmers to inherit these basic data types . At the end : This article belongs to “[Python Why? ](https://github.com/chinesehuazhou/python-whydo)” series (Python Cat products ), The series focuses on Python The grammar of 、 Design and development , One by one “ Why? ” This is the starting point , Try to show Python The charm of . If you have other topics of interest , Welcome to fill in 《[Python Why are 100000 of them ?](https://mp.weixin.qq.com/s/jobdpO7BWWON0ruLNpn31Q) 》 In the questionnaire in .  Public number 【**Python Cat **】, This is a series of excellent articles , Yes Python Why series 、 Meow philosophy Cat Series 、Python Advanced Series 、 Good book recommendation series 、 Technical writing 、 High quality English recommendation and translation, etc , Welcome off