467,136 Members | 1,281 Online
Bytes | Developer Community
Ask Question

Home New Posts Topics Members FAQ

Post your question to a community of 467,136 developers. It's quick & easy.

why UnboundLocalError?

I'm trying to define a function that prints fields of given widths
with specified alignments; to do so, I wrote some helper functions
nested inside of the print function itself. I'm getting an
UnboundLocalError, and after reading the Naming and binding section in
the Python docs, I don't see why.

Here's the error:
fieldprint([5, 4], 'rl', ['Ae', 'Lau'])

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "fieldprint.py", line 35, in fieldprint
str += cutbits()
File "fieldprint.py", line 11, in cutbits
for i in range(0, len(fields)):
UnboundLocalError: local variable 'fields' referenced before assignment

This is the code:
def fieldprint(widths,align,fields):

def measure():
totallen = 0
for i in range(0, len(fields)):
totallen += len(fields[i])
return totallen

def cutbits():
cutbit = []
for i in range(0, len(fields)):
if len(fields[i]) >= widths[i]:
cutbit.append(fields[i][:widths[i]])
fields = fields[widths[i]:]
elif len(fields[i]) > 0:
leftover = widths[i] - len(fields[i])
if align[i] == 'r':
cutbit.append(' '*leftover + fields[i])
elif align[i] == 'l':
cutbit.append(fields[i] + ' '*leftover)
else:
raise 'Unsupported alignment option'
fields[i] = ''
else:
cutbit.append(' '*widths[i])
return cutbit.join('')

if len(widths) != len(fields) or len(widths)!=len(align) or
len(align)!=len(fields):
raise 'Argument mismatch'

str = ''
while measure()!=0:
str += cutbits()

What's causing the error?

Thanks,
Alex
--
ChapterZero: http://tangentspace.net/cz/
Jul 21 '05 #1
  • viewed: 8987
Share:
6 Replies
Alex Gittens wrote:
I'm trying to define a function that prints fields of given widths
with specified alignments; to do so, I wrote some helper functions
nested inside of the print function itself. I'm getting an
UnboundLocalError, and after reading the Naming and binding section in
the Python docs, I don't see why.

Here's the error:
fieldprint([5, 4], 'rl', ['Ae', 'Lau'])


Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "fieldprint.py", line 35, in fieldprint
str += cutbits()
File "fieldprint.py", line 11, in cutbits
for i in range(0, len(fields)):
UnboundLocalError: local variable 'fields' referenced before assignment

This is the code:
def fieldprint(widths,align,fields):
[snip]
def cutbits():
cutbit = []
for i in range(0, len(fields)):
if len(fields[i]) >= widths[i]:
cutbit.append(fields[i][:widths[i]])
fields = fields[widths[i]:]

What's causing the error?


While I don't know the "correct" fix, the immediate cause is that you
are assigning to "fields" in the final line above, and there is no
"global fields" statement, so the compiler thinks it must be a local,
but you access it (as the error says) before it is assigned-to locally.

"A" fix would be to use a different name locally, and if you really want
it to reference the externally defined "fields", just do something like
"lfields = fields" at the top of cutbits() (where "lfields" means
"local fields", though you can pick any name you like).

-Peter
Jul 21 '05 #2
On Fri, 08 Jul 2005 21:21:36 -0500, Alex Gittens wrote:
str = ''
while measure()!=0:
str += cutbits()
It is considered poor practice to use the names of built-ins like str
or list as variable names.
What's causing the error?


Did you actually copy all of the code? It seems to me that once your code
has assembled the string, it doesn't actually do anything with it. Not
even return it.

Also, you seem to be calling a join method on lists. Lists don't have join
methods.

The rest of your code is very hard for me to follow, especially as you
have not put any comments in and seem to spend a lot of time re-inventing
the wheel. I would do something like this to handle fixed-width printing.

(Untested.)

def fieldprint(widths,alignments,fields):
"""Given a list of widths, alignments and fields (text) representing
a single row of text formatted in columns, returns an aligned version.
Text is never truncated to fit, and is padded with spaces.

Examples:
fieldprint([5, 5, 5], "lcr", ["cat", "12345", "12345"])
=> "cat 1234512345"
fieldprint([5, 5, 5], "rcl", ["cat", "dog", "12345"])
=> " cat dog 12345"
fieldprint([5, 5, 5], "rcl", ["cat", "dog", "mice"])
=> " cat dog mice "

"""

# helper function
def align(width, alignment, text):
"""Give a width, an alignment, and some text, returns a string
containing that text aligned correctly in columns.
"""
if alignment in "Ll":
# left-justify
return text.ljust(width)
elif alignment in "Rr":
# right-justify
return text.rjust(width)
elif alignment in "Cc":
# centered
return text.center(width)
else:
raise ValueError("Unsupported alignment '%s'" % alignment)

if not len(widths) == len(alignments) == len(fields):
raise 'Argument mismatch'
columns = zip(widths, alignments, fields)
# builds a list of tuple of (width, alignment, field)
L = [] # hold each formatted column as it is built
for column in columns:
L.append(align(*column))
# equivalent to:
# L.append(align(column[0], column[1], column[2]))
return ''.join(L)
Hope this is helpful to you.
--
Steven.

Jul 21 '05 #3
On Fri, 8 Jul 2005 21:21:36 -0500, Alex Gittens <sw******@gmail.com> wrote:
I'm trying to define a function that prints fields of given widths
with specified alignments; to do so, I wrote some helper functions
nested inside of the print function itself. I'm getting an
UnboundLocalError, and after reading the Naming and binding section in
the Python docs, I don't see why.

Here's the error:
fieldprint([5, 4], 'rl', ['Ae', 'Lau'])Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "fieldprint.py", line 35, in fieldprint
str +=3D cutbits()
File "fieldprint.py", line 11, in cutbits
for i in range(0, len(fields)):
UnboundLocalError: local variable 'fields' referenced before assignment

This is the code:
def fieldprint(widths,align,fields):

def measure():
totallen =3D 0
for i in range(0, len(fields)):
totallen +=3D len(fields[i])
return totallen

def cutbits():
cutbit =3D []
for i in range(0, len(fields)):
if len(fields[i]) >=3D widths[i]:
cutbit.append(fields[i][:widths[i]])
fields =3D fields[widths[i]:] fields[i] = fields[i][widths[i]:] # did you mean this, analogous to [1] below?
elif len(fields[i]) > 0:
leftover =3D widths[i] - len(fields[i])
if align[i] =3D=3D 'r':
cutbit.append(' '*leftover + fields=
[i])
elif align[i] =3D=3D 'l':
cutbit.append(fields[i] + ' '*lefto=
ver)
else:
raise 'Unsupported alignment option=
'
fields[i] =3D '' ^^^^^^^^^-- [1] else:
cutbit.append(' '*widths[i])
return cutbit.join('')

if len(widths) !=3D len(fields) or len(widths)!=3Dlen(align) or
len(align)!=3Dlen(fields):
raise 'Argument mismatch'

str =3D ''
while measure()!=3D0:
str +=3D cutbits()

What's causing the error?

Maybe the missing [i]'s ?

It's not clear what you are trying to do with a field string that's
wider than the specified width. Do you want to keep printing lines that
have all blank fields except for where there is left-over too-wide remnants? E.g.,
if the fields were ['left','left12345','right','12345right'] and the widths were [5,5,6,6] and align 'llrr'

should the printout be (first two lines below just for ruler reference)
1234512345123456123456
LLLLLLLLLLRRRRRRRRRRRR
left left1 right12345r
2345 ight

or what? I think you can get the above with more concise code :-)
but a minor mod to yours seems to do it:
def fieldprint(widths,align,fields): ... def measure():
... totallen = 0
... for i in range(0, len(fields)):
... totallen += len(fields[i])
... return totallen
... def cutbits():
... cutbit = []
... for i in range(0, len(fields)):
... if len(fields[i]) >= widths[i]:
... cutbit.append(fields[i][:widths[i]])
... #fields = fields[widths[i]:]
... fields[i] = fields[i][widths[i]:] # did you mean this, analogous to [1] below?
... elif len(fields[i]) > 0:
... leftover = widths[i] - len(fields[i])
... if align[i] == 'r':
... cutbit.append(' '*leftover + fields[i])
... elif align[i] == 'l':
... cutbit.append(fields[i] + ' '*leftover)
... else:
... raise 'Unsupported alignment option'
... fields[i] = '' # <-- [1]
... else:
... cutbit.append(' '*widths[i])
... # XXX return cutbit.join('')
... return ''.join(cutbit)
... if len(widths) != len(fields) or len(widths)!=len(align) or len(align)!=len(fields):
... raise 'Argument mismatch'
... # str = ''
... result_lines = []
... while measure()!=0:
... result_lines.append(cutbits())
... #str += cutbits()
... return '\n'.join(result_lines)
... fieldprint([5,5,6,6], 'llrr', ['left', 'left12345', 'right', '12345right']) 'left left1 right12345r\n 2345 ight' print fieldprint([5,5,6,6], 'llrr', ['left', 'left12345', 'right', '12345right'])

left left1 right12345r
2345 ight

Note that
for i in xrange(len(items)):
item = items[i]
# mess with item
just to walk through items one item at a time is more concisely written
for item in items:
# mess with item
and if you really need the index i as well,
for i, item in enumerate(items):
# use item and i here

Also, if you are going to raise 'Argument mismatch' it would be better to do it at the beginning
and use a standard exception. String exceptions are frowned upon these days, so probably raise
ValueError or TypeError with the string as an argument instead, unless you have a more informative thing to do.
BTW, measure above can be defined with current builtins as (untested)
def measure(): return sum([len(field) for field in fields])
I'll leave a few things ...

HTH

Regards,
Bengt Richter
Jul 21 '05 #4
Alex Gittens wrote:
I'm getting an UnboundLocalError def fieldprint(widths,align,fields): [...]
def cutbits(): [...]
fields = fields[widths[i]:]


There's your problem. You are assigning 'fields' a completely new
value. Python doesn't allow you to rebind a variable from an outer
scope in an inner scope (except for the special case where you
explicitly use the 'global' directive, which is no use for the nested
scopes you are using here).

So when you assign an identifier in a function Python assumes that you
want that identifier to be a completely new local variable, *not* a
reference to the variable in the outer scope. By writing 'fields= ...'
in cutbits you are telling Python that fields is now a local variable
to cutbits. So when the function is entered, fields is a new variable
with no value yet, and when you first try to read it without writing to
it first you'll get an error.

What you probably want to do is keep 'fields' pointing to the same
list, but just change the contents of the list. So replace the assign
operation with a slicing one:

del fields[:widths[i]]

--
Andrew Clover
mailto:an*@doxdesk.com
http://www.doxdesk.com/

Jul 21 '05 #5
On 9 Jul 2005 05:26:46 -0700, an********@doxdesk.com wrote:
Alex Gittens wrote:
I'm getting an UnboundLocalError

def fieldprint(widths,align,fields): [...]
def cutbits(): [...]
fields = fields[widths[i]:]


There's your problem. You are assigning 'fields' a completely new
value. Python doesn't allow you to rebind a variable from an outer
scope in an inner scope (except for the special case where you
explicitly use the 'global' directive, which is no use for the nested
scopes you are using here).

So when you assign an identifier in a function Python assumes that you
want that identifier to be a completely new local variable, *not* a
reference to the variable in the outer scope. By writing 'fields= ...'
in cutbits you are telling Python that fields is now a local variable
to cutbits. So when the function is entered, fields is a new variable
with no value yet, and when you first try to read it without writing to
it first you'll get an error.

What you probably want to do is keep 'fields' pointing to the same
list, but just change the contents of the list. So replace the assign
operation with a slicing one:

del fields[:widths[i]]


Except the OP probably had two errors in that line, and doesn't want to slice
out fields from the field list, but rather slice characters from the i-th field,
and strings are immutable, so he can't do
del fields[i][:widths[i]] # slice deletion illegal if fields[i] is a string
and so
fields[i] = fields[i][:widths[i]]
would be the way to go (see my other post).

Regards,
Bengt Richter
Jul 21 '05 #6
On Sat, 09 Jul 2005 06:17:20 GMT, bo**@oz.net (Bengt Richter) wrote:
On Fri, 8 Jul 2005 21:21:36 -0500, Alex Gittens <sw******@gmail.com> wrote:
I'm trying to define a function that prints fields of given widths
with specified alignments; to do so, I wrote some helper functions
nested inside of the print function itself. I'm getting an
UnboundLocalError, and after reading the Naming and binding section in
the Python docs, I don't see why.

It's not clear what you are trying to do with a field string that's
wider than the specified width. Do you want to keep printing lines that
have all blank fields except for where there is left-over too-wide remnants? E.g.,
if the fields were ['left','left12345','right','12345right'] and the widths were [5,5,6,6] and align 'llrr'

should the printout be (first two lines below just for ruler reference)
1234512345123456123456
LLLLLLLLLLRRRRRRRRRRRR
left left1 right12345r
2345 ight

or what? I think you can get the above with more concise code :-)
but a minor mod to yours seems to do it:

[...]

Not very tested, but you may enjoy puzzling out a more concise version
(and writing a proper test for it, since it might have bugs ;-)
def fieldprint(widths, align, fields): ... if len(widths) != len(fields) or len(widths)!=len(align):
... raise ValueError, 'fieldprint argument mismatch'
... align = [getattr(str, j+'just') for j in align]
... results = []
... while ''.join(fields):
... for i, (fld, wid, alg) in enumerate(zip(fields, widths, align)):
... results.append(alg(fld[:wid], wid))
... fields[i] = fld[wid:]
... results.append('\n')
... return ''.join(results)
... print fieldprint([5,5,6,6], 'llrr', ['left', 'left12345', 'right', '12345right'])

left left1 right12345r
2345 ight

BTW, since this destroys the fields list, you might want to operate on a copy
(e.g., remaining_fields = fields[:]) internally, or pass a copy to the routine.
Of course, you could put "print" instead of "return" in fieldprint and just call it
instead of printing what it returns as above, but you didn't show that part in your
original example. BTW2, note that "str" above is the built in string type, and it's
not a good idea to use a built in name for a variable the way your original code did.

Regards,
Bengt Richter
Jul 21 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by Brad Clements | last post: by
2 posts views Thread by silverburgh.meryl@gmail.com | last post: by
8 posts views Thread by David Bear | last post: by
15 posts views Thread by Paddy | last post: by
9 posts views Thread by Camellia | last post: by
2 posts views Thread by Konstantinos Pachopoulos | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.