Python Warts

misfeatures and other problems
PERL| PHP| PYTHON| RUBY
Answers: (all questions)
Answer 1: (question)
Question:
#!/usr/bin/python

i=1
def f():
	print "i=%s" % i
	i = i + 1 

f()
Answer:
This program fails due to a scoping error:
Traceback (most recent call last):
  File "function_scope.py", line 8, in ?
    f()
  File "function_scope.py", line 5, in f
    print "i=%s" % i
UnboundLocalError: local variable 'i' referenced before assignment
Python has an unusual scoping system, where functions are pre-scanned for assignments before they are executed. Any variable which gets assigned is assumed to be local unless explicitly declared global.

This has two undesirable effects:
  • It confuses users, since the same sort of code would work in almost any other language. It's not intuitive to need to define parent variables as "global" in order to assign a value to them.
  • It forces the use of kludges in order to access parent variables which are not global. Basically, you must put the parent data into a list or a hash, in order to trick the python interpreter into thinking you are not assigning a value to it.
Answer 2: (question)
Question:
#!/usr/bin/python

def outer():
	i=1
	def inner():
		print "i=%s" % str(i)
		i += 1
	
	inner()
	inner()

outer()
Answer:
This program fails due to a scoping error:
Traceback (most recent call last):
  File "q/function_scope-2.py", line 12, in ?
    outer()
  File "q/function_scope-2.py", line 9, in outer
    inner()
  File "q/function_scope-2.py", line 6, in inner
    print "i=%s" % str(i)
  UnboundLocalError: local variable 'i' referenced before assignment
Python has an unusual scoping system, where functions are pre-scanned for assignments before they are executed. Any variable which gets assigned is assumed to be local unless explicitly declared global.

This issue is further complicated by Python's lack of a way to access parent variables which are not global. In this case, there are three nested scopes: global, outer, and inner. But inner has no direct way to access outer's data for assignment. The only way to assign data from inner to outer is to wrap the data in containers so Python's optimizer will not detect any assignment, and will not create local variables to override the parent data.

This version of the code works:
 #!/usr/bin/python

def outer():
	i=[1]
	def inner():
		print "i=%s" % str(i[0])
		i[0] += 1
	
	inner()
	inner()

outer()
Answer 3: (question)
Question:
#!/usr/bin/python

def add_evil(data=[]):
	data.append(666)
	return data

print add_evil()
print add_evil(["hi there"])
print add_evil()
print add_evil()
Answer:
The output is:
[666]
['hi there', 666]
[666, 666]
[666, 666, 666]
Python has a bizarre way of handling default parameters. It binds the default value once, when the function is defined, instead of doing it each time the function is called. So, if a default value is mutable, its value may be different on successive calls.

Using mutable default values is not recommended.
Answer 4: (question)
Question:
#!/usr/bin/python

a = [1,2,3]
b = a

a += [4]
b = b + [5]

print "a=%s" % a
print "b=%s" % b
Answer:
Output:
a=[1, 2, 3, 4]
b=[1, 2, 3, 4, 5]
The += operator does not do the same thing as writing foo = foo + value.

For non-mutable objects, the effect is almost always the same, but mutable objects behave differently. The += operator is designed to change an object in-place, where the longer method will produce a new object and re-bind the name to the new object. Taking away the syntactic sugar, the example could be written more explicitly as:
a = [1,2,3]
b = a

a.__iadd__([4])
b = b.__add__([5])
Answer 5: (question)
Question:
#!/usr/bin/python

a = ([1,2,3], [7,8,9])

try:
	a[0] += [4,5,6]
except Exception, e:
	print "Error: %s" % e

print "a=%s" % str(a)
Answer:
Output:
Error: object doesn't support item assignment
a=([1, 2, 3, 4, 5, 6], [7, 8, 9])
Python manages to modify the [1,2,3] list in-place, but then it fails after making the changes, because tuples are not mutable.
PERL| PHP| PYTHON| RUBY
Last modified: March 30, 2006 @ 7:04 MST
Copyright (C) 1996-2024 Selene ToyKeeper