why UnboundLocalError?

Bengt Richter bokr at oz.net
Sat Jul 9 02:17:20 EDT 2005


On Fri, 8 Jul 2005 21:21:36 -0500, Alex Gittens <swiftset at 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



More information about the Python-list mailing list