开发者

Automated alignment of "tabular" Python code [closed]

Closed. This question is seeking recommendations for books, tools, software libraries, and more. It does not meet Stack Overflow guidelines. It is not currently accepting answers. 开发者_运维百科

We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.

Closed 7 years ago.

Improve this question

I have some Python code that looks something like this:

rates = {3: [((17500, 99999), Decimal('23425.00'), Decimal('7234.24'))],
         4: [((    0,  3510), Decimal( '4563.00'), Decimal('5234.00')),
             (( 3510, 17500), Decimal('34578.00'), Decimal('3464.50')),
             ((17500, 99999), Decimal('18268.00'), Decimal('5734.66'))],
         5: [((17500, 99999), Decimal('83564.00'), Decimal('3475.60'))]}

Note that the decimal values are aligned around the decimal point, while the integers are right-aligned.

Is there some software that can perform this kind of complex alignment in an automated fashion? I'd be interested in anything that gets close, even if it can't match the above exactly.


(NOTE: I do not consider the following particularly sane.)

For the most part if you just typed out (most editors will help you align your dict and list items) your original code you should get something like this:

rates = {3: [((17500, 199999), Decimal('23425.00'), Decimal('7234.245'))],
         4: [((0, 3510), Decimal('4563.00'), Decimal('5234.00')),
             ((3510, 17500), Decimal('34578.00'), Decimal('464.50')),
             ((17500, 99999), Decimal('18268.00'), Decimal('5734.66'))],
         15: [((17500, 99999), Decimal('83564.00'), Decimal('3475.60'))]}

(I've made some values longer and some shorter to add a little more quirkiness.)

With the Tabular plugin for Vim, executing the following commands in order over the above code (you may want to visually block it) will format the above code in a way that matches your original question:

:Tab /^[^[(]*\zs[[(]/l0
:Tab /^[^(]*\zs(/l0
:Tab /(\zs\d\+\s*,/l0r1
:Tab /,\s*\zs\d\+)/l1r0
:Tab /['"]\d*\ze\.\d*['"]/l0r0

The operations are:

  1. Align the first [s and (s.
  2. Align the first (s, this fixes the misalignment from the first operation.
  3. Right-align the (17500,-like values on ,.
  4. Right-align the , 99999)-like values on ,.
  5. Align the '4563.00'-like values on ..

You could make a mapping for use in normal and visual mode:

noremap <leader>ff :Tab /^[^[(]*\zs[[(]/l0<CR>
                  \:Tab /^[^(]*\zs(/l0<CR>
                  \:Tab /(\zs\d\+\s*,/l0r1<CR>
                  \:Tab /,\s*\zs\d\+)/l1r0<CR>
                  \:Tab /['"]\d*\ze\.\d*['"]/l0r0<CR>

Final result:

rates = {3:  [((17500, 199999), Decimal('23425.00'), Decimal('7234.245'))],
         4:  [((    0,   3510), Decimal( '4563.00'), Decimal('5234.00')),
              (( 3510,  17500), Decimal('34578.00'), Decimal( '464.50')),
              ((17500,  99999), Decimal('18268.00'), Decimal('5734.66'))],
         15: [((17500,  99999), Decimal('83564.00'), Decimal('3475.60'))]}

Obviously the effectiveness of these operations depends on the structure and original formatting of the code but hopefully this inspires you.


While the Standard Library pprint module will add whitespace to make dictionaries and lists at least look reasonable on the screen, I know of nothing that will, for example, consider the periods in string constants to be significant and add enough whitespace to align them! Unless I am far wrong, you will likely always be doing this kind of alignment in Python by hand.

If you store these values in a separate table file that is plain text, then, of course, you will probably find several editors that would be willing to help you align the decimal points.


I ran into the same problem. Management wants a pretty report printed from data in some kind of tabular format.

I didn't want to just do a bunch of print statements with "magic" spaces to fix up the alignment, so I came up with this python function:

def column(filename, data, indent=0):
    """This function takes a list of lists and produces columized output"""
    # get the width of the columns
    width = []
    for mylist in data:
        for count, d in enumerate(mylist):
            if count > (len(width)-1):
                width.append(len(str(d)))
            elif len(str(d)) > width[count]:
                width[count] = len(str(d))
    # print the data
    for mylist in data:
        line = '{0:<{indent}}'.format('', indent=indent)    
        for count, d in enumerate(mylist):
            try:
                line = '%s%s' % (line, '{0:{w},} '.format(d, w=width[count]))
            except ValueError, e:
                line = '%s%s' % (line, '{0:{w}} '.format(d, w=width[count]))
        filename.write(line)
        filename.write('\n')

It's not perfect, and you'll need to adjust it to produce the output you want. Most specifically, it currently expects a list, because I wanted to pass in a specific order, and dictionaries are not ordered.


Here's a code that do what you want, AFAIU.

It's largely artificial, because it is adapted only to the shape of your precise dictionary.
However, I'm sure it's a basis that could be improved to take account of other specifications, for example several Decimal instances in each tuple.

from decimal import Decimal


rates = {3:  [((   500,   999), Decimal('23425.008'), Decimal('   4.24245'))],
         281: [((     0,    10), Decimal( '4563.00' ), Decimal('  34.00'   )),
              ((  3510,   500), Decimal('  578'    ), Decimal(' 464.503'  )),
              ((174500,    19), Decimal('   68.2'  ), Decimal('5734'      ))],
         54:  [(( 93500, 99999), Decimal(' 1564.44' ), Decimal('  75.60'   ))]}



def complex_display(di):
    K,I1,I2,D1B,D1P,D2B,D2P = [],[],[],[],[],[],[]

    for key,val in di.iteritems():
        K.append(len(str(key)))
        for (i,j),d1,d2 in val :
            I1.append(len(str(i)))
            I2.append(len(str(j)))
            d1b,d1p = str(d1).split('.') if '.' in str(d1) else (str(d1),'.')
            d2b,d2p = str(d2).split('.') if '.' in str(d2) else (str(d2),'.')
            D1B.append(len(d1b))
            D1P.append(len(d1p))
            D2B.append(len(d2b))
            D2P.append(len(d2p))

    k   = '%%%dd: [' % max(K)
    fv = "%%s((%%%ds, %%%ds), Decimal('%%%ds.%%-%ds'), Decimal('%%%ds.%%-%ds'))%%s" % (max(I1),max(I2),max(D1B),max(D1P),max(D2B),max(D2P))

    def produce(di):
        for key,val in sorted(di.iteritems()):
            for n,((i,j),d1,d2) in enumerate(val) :
                d1b,d1p = str(d1).split('.') if '.' in str(d1) else (str(d1)[0:-2],"")
                d2b,d2p = str(d2).split('.') if '.' in str(d2) else (str(d2)[0:-2],"")
                yield fv % ('      ' if n else k % key,i,j,d1b,d1p,d2b,d2p,']' if n+1==len(val) else '')

    return '\n'.join(produce(di))

result

  3: [((   500,   999), Decimal('23425.008'), Decimal('   4.24245'))]
 54: [(( 93500, 99999), Decimal(' 1564.44 '), Decimal('  75.60   '))]
281: [((     0,    10), Decimal(' 4563.00 '), Decimal('  34.00   '))
      ((  3510,   500), Decimal('    5.   '), Decimal(' 464.503  '))
      ((174500,    19), Decimal('   68.2  '), Decimal('  57.     '))]

There are not the two characters '{' and '}' , it is a lot of more complexity to add them for a faint result. I let you to complete the code to add them if you want

The result is sorted according the keys.


It's reasonably simple to write some code to pretty-print your code with whatever alignment you see fit; but you have to tell it what to look for.

When you write it out by hand, you're making a lot of style decisions without thinking much about it, but if you want a program to do the same formatting, you have to make these style decisions explicit. For example, you've decided to align your parentheses vertically, while aligning the list containing 9 elements as if it were actually 3 groups of 3, while completely ignoring the level to which each is nested.

Such an alignment layout really only makes sense with the list you've provided rather than in the general sense, so you're not going to find pre-fab code from someone else to do it like you did. But obviously that doesn't mean you can't do it yourself.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜