程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

The most mysterious magic function in Python

編輯:Python

Hello everyone , I am a Jiejie, Today I will introduce you to a very mysterious magic method .

This method is very humble , Narrow use , I hardly ever noticed it , However , When it is found that it may be the above “ The laws of ” The only exception to , I think it's worth writing another article to examine it in detail .

The main concerns of this paper are :

(1) __missing__() Who is the Holy ?

(2) __missing__() What's special ? Good at “ Make a living ” Magic ?

(3) __missing__() Is it really an exception to the above findings ? If so , Why is there such an exception ?

1、 It's a little valuable __missing__()

When taking a value from a common dictionary , There may be key A situation that does not exist :

dd = {'name':'PythonCat'}
dd.get('age') # result :None
dd.get('age', 18) # result :18
dd['age'] # Report errors KeyError
dd.__getitem__('age') # Equate to dd['age']

about get() Method , It has a return value , And you can pass in a second parameter , As key Nonexistent return content , So it's also acceptable that . however , The other two ways of writing are wrong .

In order to solve the problem of the latter two ways of writing , You can use __missing__() Magic methods .

Now? , Suppose we have a claim like this : Take something from a dictionary key Corresponding value, If there is a value, return the value , If there is no value, insert key, And give it a default value ( For example, an empty list ).

If you use native dict, It's not very easy to implement , however ,Python Provides a very easy to use extension class collections.defaultdict:

As shown in the figure , When we take the nonexistent key when , There is no more report KeyError, It's stored in the dictionary by default .

Why? defaultdict This can be done ?

as a result of defaultdict Inherited the built-in type dict after , I've also defined one __missing__() Method , When __getitem__ When you take a value that doesn't exist , It will call the factory function passed in the input parameter ( The above example is to call list(), Create an empty list ).

As a typical example ,defaultdict Write... In the document comment :

In short ,__missing__() The main function of is by __getitem__ In the absence of key Called when the , So as to avoid KeyError.

Another typical use example is collections.Counter, So is it dict Subclasses of , In taking the statistics of key when , Return count 0:

2、 It's haunting __missing__()

It can be seen from the above that ,__missing__() stay __getitem__() Called when the value cannot be retrieved , however , I accidentally found a detail :__getitem__() When you can't get a value , You don't have to call __missing__().

This is because it is not a required property of the built-in type , It is not pre-defined in the dictionary base class .

If you go straight from dict Take the value of the attribute in the type , Reporting attribute does not exist :AttributeError: type object 'object' has no attribute '__missing__'.

Use dir() see , The attribute should not exist :

If from dict The parent class of is object View in , You'll find the same result .

What's going on here ? Why is it dict and object None of them __missing__ Attribute? ?

However , Check out the latest official documents ,object This attribute is clearly included in :

Source :https://docs.python.org/3/reference/datamodel.html?highlight=__missing__#object.__missing__

in other words , Theoretically object Class will be predefined __missing__, Its documentation proves that , But it's not really defined ! There is a deviation between the document and the reality !

In this way , When dict Subclasses of ( for example defaultdict and Counter) In defining __missing__ when , This magic method actually only belongs to this subclass , in other words , It's a magic method born of subclasses !

Accordingly , I have an immature guess :__getitem__() Will determine whether the current object is dict Subclasses of , And whether you have __missing__(), And then it's called ( If the parent class also has this method , You don't make a judgment first , It's called directly ).

I said this conjecture in the communication group , Some students will be there soon CPython Source code to find the validation :

And that's interesting , Magic methods that only exist on subclasses of built-in types , Throughout the Python The world , I'm afraid it's hard to find a second example .

I have a sudden Association : It's haunting __missing__(), It's like a guy who's good at playing “ Make a living ” The magician of , Let the audience see him through the glass first ( Official documents ), But when you open the door , He's not in it ( The built-in type ), Change the props again , He appeared intact again ( namely dict Subclasses of ).

3、 Enchanted __missing__()

__missing__() The magic of , Except that it changes itself “ Magic ” outside , It also needs a strong “ magic ” To drive .

In the last article , I found that the original magic methods were independent of each other , They are C The language interface may have the same core logic , But in Python Language interface , But there is no calling relationship :

This magic method “ Never see each other again ” The performance of the , It violates the general principles of code reuse , It's also the reason for some strange behavior of subclasses of built-in types .

official Python Would rather offer new UserString、UserList、UserDict Subclass , I don't want to reuse magic methods , The only reasonable explanation seems to be that the cost of making magic methods call each other is too high .

however , For special cases __missing__(),Python But they had to compromise , Have to pay this price !

__missing__() It's magic “ Two wait for a citizen ”, It doesn't have a separate call entry , Only passively by __getitem__() call , namely __missing__() Depend on __getitem__().

Different from those “ First class citizen ”, for example __init__()、__enter__()、__len__()、__eq__() wait , They are either triggered at a node in the object's life cycle or execution process , It's either triggered by a built-in function or operator , These are relatively independent events , Nothing to depend on .

__missing__() Depend on __getitem__(), In order to implement the method call ; and __getitem__() We have to rely on it __missing__(), In order to achieve full functionality .

To achieve this ,__getitem__() There's a back door in the interpreter code , from C Language interface back to Python Interface , To call the name “__missing__” The specific method of .

And that's what it really is “ magic ” 了 , So far, ,__missing__() It seems to be the only magic way to enjoy such treatment !

4、 Summary

Python The dictionary provides two built-in methods for taking values , namely __getitem__() and get(), When the value does not exist , Their processing strategies are different : The former will report a mistake KeyError, The latter will return None.

Why? Python There are two different ways ? Or you should ask , Why? Python To make these two methods do different things ?

There may be a very complicated one ( It can also be very simple ) The explanation of , This paper will not go into it for the time being .

But one thing is certain : Native dict The type is simple and rough KeyError There's something wrong with this .

In order to make dictionary types more powerful ( Or let it be __getitem__() make get() That kind of performance ),Python Let the subclass of the dictionary define __missing__(), for __getitem__() Find call .

This paper has sorted out __missing__() Implementation principle of , Thus, it is revealed that it is not an insignificant existence , On the contrary , It's the only one that breaks the barriers between magic methods , Support special cases called by other magic methods !

Python In order to maintain the independence of magic methods , He took great pains to introduce UserString、UserList、UserDict These derived classes , But for __missing__(), It chose to compromise .

This article reveals the mystery of this magic method , I don't know how you feel after reading it ? Feel free to leave a comment .


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved