Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't re-assignment to __builtins__.dict affect creation of new dictionary objects?

A classmate asked a question about overwriting the built-in class dict, and after some poking around I became even more uncertain.

Take the built-in dict. I can assign this variable:

>>> dict=5
>>> dict
5

Now I've lost access to dict (would this be shadowing like in C++ or is it different?) but I still have access to the class via builtins.dict. But I can overwrite that as well:

>>> __builtins__.dict = 6
>>> __builtins__.dict
6

But even doing this doesn't break the class itself:

>>> stillDict = {'key': 'value'}
>>> stillDict
{'key': 'value'}

So why does the class still "work" after I've shadowed it? How does the interpreter know I'm making a dictionary with this assignment, and how is the dictionary constructed since it obviously doesn't actually require __builtins__.dict?

edit Taking this a bit further, from simeon's answer which says it's because I'm creating a dictionary literal ...

before overwriting, I can do this:

>>> a = dict()
>>> a.items
<built-in method items of dict object at 0x0000000002C97C08>

after overwriting dict and __builtins__.dict, I can do this:

>>> b = {}
>>> b.items
<built-in method items of dict object at 0x000000000288FC88>

Which leads to a follow-on ... Both of these are still "dict objects", is the dict class just using a constructor to make a dict object? Why do I still have access to the built-in methods once I've shadowed the class?

like image 293
Daniel B. Avatar asked Mar 19 '16 23:03

Daniel B.


1 Answers

{'key': 'value'} is a dictionary literal so that continues to have the behaviour of producing a dictionary. Python doesn't need to look up what dict means - it skips this step and directly generates byte code to construct a dictionary:

>>> def f(): {'a': 3}
>>> import dis
>>> dis.dis(f)
  1           0 BUILD_MAP                1
              3 LOAD_CONST               1 (3)
              6 LOAD_CONST               2 ('a')
              9 STORE_MAP
             10 POP_TOP
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE

In the byte code it continues to use BUILD_MAP as before (i.e., it builds a map / dictionary based on the code you've written).

The meaning of dict has changed as you mentioned.


Regarding the follow-up question: you haven't shadowed the dictionary class / type - you've only changed the meaning of what dict means. You cannot take the dictionary type away and Python produces it when using a dictionary literal (e.g., {}).

Once you have an object of type dict you have access to its methods (like items()) - it's just that you've constructed it using syntax (which you can't influence) rather than a call to dict() (which you can influence).

like image 170
Simeon Visser Avatar answered Sep 29 '22 18:09

Simeon Visser