alvarosg alvarosg - 24 days ago 6
Python Question

What is this strange behavior in exception handling?

I am using python 2.7, but trying to make a code that checks if an object is a subclass of basestring compatible with python 3+ as well. I tried to follow the approach suggested here and found in the process a behavior that I do not understand

If I do:

def foo():
try: basestring
except NameError:
print "a"
foo()


nothing happens.

If I slightly modify that code just inside the except:

def foo():
try: basestring
except NameError:
print "a"
basestring=str
foo()


Then "a" is printed.

I do not understand how adding something to the except block, can affect the triggering of the exception.

I checked the same code outside a function:

try:
basestring
except NameError:
print("a")
basestring=str


but nothing gets printed in that case.

wim wim
Answer

In the first case it's easy, the name basestring is resolved on __builtins__.basestring. There is no Exception raised by the try block, so the behaviour should be as expected.

In the second case, it's tricky. Using the name basestring inside the function makes that name a local variable of the function. Note that the names which are local to a function are determined at function definition time! Python already knows when it is executing the first line of the function that the name basestring is a local variable of the function:

>>> def foo():
...     basestring
...     potato
...     errorerrorerror
...     
>>> print foo.func_code.co_names
('basestring', 'potato', 'errorerrorerror')
>>> print foo.func_code.co_varnames
()

Calling foo() would NameError out on the line potato. Compare and contrast with bar() below, which would NameError out on the line basestring:

>>> def bar():
...     basestring
...     potato
...     errorerrorerror
...     basestring = "D'Addario EXL160 Medium"
...     
>>> print bar.func_code.co_names
('potato', 'errorerrorerror')
>>> print bar.func_code.co_varnames
('basestring',)

Then the exception which is raised is due to the name being used before it has been bound to an object, which is an error in Python. This is an error at runtime, not at function definition time.

The third case is analagous to the first case - the concept of 'local variable' doesn't apply at the global scope.