This seems to work, even though str is a built in function and shouldn't be used as a variable.
Yes, that is true. Python doesn't stop you from shooting yourself in the foot. It's up to you as the developer to make sure your not overwriting builtin names.
What is actually happening here? My guess is str will no longer be usable as a function, but only in the scope of the blah() function he's written. Is that correct? This won't redefine str globally, right?
Your are partially correct here as well. If the value of str is overwritten local, then only the current scope is affected. The global value of str remains unchanged. However, if str is over written in the global scope, then it affects all sub-scopes. The reason behind this is how the Python interpreter compiles values at run-time. This behavior can be observed using a simple example:
>>> def foo():
... str = 0
... return str
...
>>> foo()
0
>>> str(0)
'0'
>>>
The first example works because str is only overwritten in the scope of foo(). This second example fails however because str is overwritten globally:
>>> str = 0
>>> def foo():
... return str(0)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
TypeError: 'int' object is not callable
>>>
You can always import builtins(__builtins__ in Python 2) though, and reset the value of str to its original meaning:
>>> str = 0
>>> str
0
>>> import __builtins__
>>> str = __builtins__.str
>>> str
<type 'str'>
>>> str(0)
'0'
>>>
Also, as @Brad Solomon stated, you can simply use del str to recover the builtin str value:
>>> str = 0
>>> str
0
>>> del str
>>> str
<class 'str'>
>>>