Python was designed (was Re: Multi-threading in Python vs Java)

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Oct 17 22:57:48 EDT 2013


On Thu, 17 Oct 2013 18:59:07 -0700, Mark Janssen wrote:

>>>>> --> int="five"
>>>>> --> [int(i) for i in ["1","2","3"]]
>>>>>
>>>>> TypeError:  str is not callable
>>>>>
>>>>> Now how are you going to get the original int type back?


Trivially easy:

py> int
<type 'int'>
py> int = "five"  # Oops!
py> int(42.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'str' object is not callable
py> del int
py> int(42.5)  # Phew!
42



> Thank you for bringing this back up.  Was it you who suggested that
> built-in are re-assignable?  


It's not just a suggestion, it is a fact. The built-ins are just a 
namespace, like any other module, class, or other namespace.

(Of course, if you break something in the built-ins, the consequences are 
likely to be significantly more wide-ranging, but that's another issue.)

However, in the code shown above, you don't actually reassign a built-in. 
You merely shadow it within the current module. Do you understand the 
difference? In the above, the *builtin* int still exists, but your code 
can no longer get direct access to it because a *global* int gets in the 
way. Using Python 2.7:

py> import __builtin__ as builtins
py> builtins.int
<type 'int'>
py> int = "five"
py> int
'five'
py> builtins.int
<type 'int'>

so deleting the global "int" simply reveals the otherwise hidden 
builtin.int instead.

However, if you rebind the builtin, Python doesn't remember what the old 
value was (although in principle it could):

py> del int  # get rid of the global
py> int is builtins.int
True
py> builtins.int = "six"  # oh oh, this could be bad
py> int
'six'
py> del int
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'int' is not defined



In this case, deleting the builtin doesn't magically recover it, it just 
deletes it:

py> del builtins.int
py> int
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'int' is not defined


At this point, in general, you've buggered up the current Python 
environment and would normally need to restart the interpreter. But in 
this specific case, all is not quite so lost: we can recover from this if 
only we can find another reference to the int built-in type, and restore 
it to the builtins:


py> builtins.int = type(42)
py> int("23")
23


I see no reason why Python couldn't create a read-only "backup builtins" 
namespace, but on the other hand, why bother?


> Because this is a bad idea for the reasons I just showed.

"Breaking things" is always a bad idea. But re-binding is not necessarily 
a bad thing. Let's say I want to find out how often the built-in "len" 
function is called by some module:


py> count = 0
py> def mylen(x):
...     global count
...     count += 1
...     return _len(x)
...
py> _len = len  # save the real len
py> builtins.len = mylen  # monkey-patch the builtins
py> import mymodule
py> count
3

Now obviously this is a trivial example. But there are more interesting, 
and useful, reasons for monkey-patching builtins, usually for testing and 
debugging purposes. Such a technique should be used with caution, but it 
can be used.



-- 
Steven



More information about the Python-list mailing list