Indentation of pformat() output
Asked Answered
O

3

7

I have a function that uses pformat() to convert a dictionary to a string (irrelevant: string will be later inserted with write() in a .py file).

So MY_DCT = {1: 11, 2: 22, 3: 33} would become a string like this:

MY_DCT = {
    1: 11,
    2: 22,
    3: 33}

There are 2 requirements for the function:

  1. Dict items must be displayed after the first line.
  2. Elements have to be indented by 4 whitespaces.

Here is the function:

import pprint    

def f(obj_name, obj_body_as_dct):

    body = '{\n' + pprint.pformat(obj_body_as_dct, indent=4, width=1)[1:]
    name_and_equal_sign = obj_name + ' = '

    return name_and_equal_sign + body + '\n\n'


d = {1: 11, 2: 22, 3: 33}

print(f('MY_DCT', d))

If indent=0 i get this string:

MY_DCT = {
1: 11,
2: 22,
3: 33}

If indent=4 i get this string:

MY_DCT = {
   1: 11,
    2: 22,
    3: 33}

I checked the parameters of pformat() but i couldn't figure out how to make the correct number of whitespaces appear on each line.

I know i can use replace(), +' ' etc., to fix the string, but i m wondering where does that extra whitespace come from and if i can get rid of it by setting correctly the parameters (if that is even possible, that is).

Note: If there is a better way to achieve the above, do let me know.

Octagonal answered 1/4, 2015 at 18:8 Comment(0)
O
5

The default value of indent in pformat is 1, so that keys appear one under the other.

For example, pformat(d, indent=0, width=1) would result in this string:

{1: 11,
2: 22,
3: 33}

indent=1:

{1: 11,
 2: 22,
 3: 33}

and indent=2:

{ 1: 11,
  2: 22,
  3: 33}

One less whitespace always on the first line.


Since the goal was to display dict elements after the first line, and have all elements indented by 4 spaces, adding a single whitespace before the first element and using indent=4 would work for some dicts (as suggested by @logic).

However dicts like d = {1: {'a': 1, 'b': 2}, 2: 22, 3: 33} would look rather ugly, since indent would affect the appearance of dicts with depth greater than 1 as well:

MY_DCT = {
    1: {   'a': 1,
           'b': 2},
    #    ^
    #    |
    # ugly
    2: 22,
    3: 33}

The most appealing solution (for the data i m working on) is keeping indent=1 and adding 3 whitespaces for first element, and 4 whitespaces for the rest.

def f(obj_name, given_dct):
    """
    Converts given dct (body) to a pretty formatted string.
    Resulting string used for file writing.

    Args:
        obj_name: (str) name of the dict
    Returns:
        (str)
    """

    string = pp.pformat(given_dct, width=1)[1:]

    new_str = ''
    for num, line in enumerate(string.split('\n')):
        if num == 0:
            # (pprint module always inserts one less whitespace for first line)
            # (indent=1 is default, giving everything one extra whitespace)
            new_str += ' '*4 + line + '\n'
        else:
            new_str += ' '*3 + line + '\n'

    return obj_name + ' = {\n' + new_str


s = f(obj_name='MY_DCT', given_dct=d)

Resulting in this string:

MY_DCT = {
    1: {'a': 'aa',
        'b': [1,
              2,
              3]},
    2: 22,
    3: 33}
Octagonal answered 6/4, 2015 at 10:20 Comment(0)
W
2

The parameters are not the issue with your printing. The problem occurs when you add newlines ('\n'). As you can see from this example:

import pprint    

def f(obj_name, obj_body_as_dct):

    body = '{' + pprint.pformat(obj_body_as_dct, indent=4, width=1, depth=1)[1:]
    name_and_equal_sign = obj_name + ' = ' + '\n'

    return '"""' + name_and_equal_sign + body + '"""' + '\n\n'


d = {1: 11, 2: 22, 3: 33}

print(f('MY_DCT', d))

This outputs:

"""MY_DCT = 
{   1: 11,
    2: 22,
    3: 33}"""

The newlines, and the addition of a '{' in the first row of printing is why only the first row of your output seems to be offset by a small space.

UPDATE #1:

After a bit of tinkering with these values, I was able to match your desired output:

import pprint    

def f(obj_name, obj_body_as_dct):

    body = pprint.pformat(obj_body_as_dct, indent=4, width=1, depth=1)[1:]
    name_and_equal_sign = obj_name 

    return '"""' + name_and_equal_sign + ' = ' + '{' + '\n ' + body +'"""'  + '\n\n'  
                                                        # ^ I added a space in this newline

d = {1: 11, 2: 22, 3: 33}

print(f('MY_DCT', d))

The above code outputs:

"""MY_DCT = { 
    1: 11,
    2: 22,
    3: 33}"""

So, in essence, you just needed one space in a newline character (shown in code).

UPDATE #2:

Following your suggestion in the comments:

Try this: print(pp.pformat({1:11, 2:22, 3:33}, indent=0, width=1)), with indent going from 0 to 2. You ll notice, it applies one less whitespace on the first line. It is the way pformat() is built. -@user 5061

I tried this:

import pprint as pp

for i in range(0,3):
    print(pp.pformat({1:11, 2:22, 3:33}, indent=i, width=1))

And it gave me this output:

{1: 11,     #Indent = 0 Each entry (except 1) is indented by 0 spaces, because the "{" operator is in the way. 
2: 22,
3: 33}
{1: 11,     #Indent = 1 Each entry is indented by 1 spaces
 2: 22,
 3: 33}
{ 1: 11,    #Indent = 2 Each entry is indented by 2 spaces
  2: 22,
  3: 33}

As you can see, the problem is still with this: "{"
If that wasn't there, then the indents would be even throughout. The function pformat() was coded to adjust for that character.

Whereof answered 1/4, 2015 at 18:25 Comment(2)
Try this: print(pp.pformat({1:11, 2:22, 3:33}, indent=0, width=1)), with indent going from 0 to 2. You ll notice, it applies one less whitespace on the first line. It is the way pformat() is built.Octagonal
@user5061 Did you try using my Python code though? I was able to fix your problem with just a space.Whereof
A
0

Logic's answer has you covered as to why you are having the indentation issue. As (IMO) a more concise approach however, you can make use of json. This output is assuming that by elements you mean dictionary elements:

import json
obj_name = 'MY_DCT = '
obj_body_as_dct = {1: 11, 2: 22, 3: 33}

stringy = "\"\"\"" + obj_name + "{" \
          + str(json.dumps(obj_body_as_dct, indent=4, separators=(',', ': ')))[1:] + "\"\"\""
print(stringy)

Outputs:

"""MY_DCT = {
    "1": 11,
    "2": 22,
    "3": 33
}"""
Angelynanger answered 1/4, 2015 at 18:31 Comment(5)
The string will be inserted in a .py file. This output would be invalid syntax.Octagonal
@user5061 Your desired output is exactly as shown in your first display? I can edit if so, and place in a string.Angelynanger
Your output is not the same as the one i have in my question. If you copy that output, and insert it in a py file, you would get a syntax error.Octagonal
@user5061 I formatted my code to get you a string ;) (got you covered)Whereof
@user5061 I understand what you mean, you wanted to put it literally in a .py file. Edited to do so, please see updated answerAngelynanger

© 2022 - 2025 — McMap. All rights reserved.