How to use pprint to print an object using the built-in __str__(self) method?
Asked Answered
B

6

22

I have a Python script which processes a .txt file which contains report usage information. I'd like to find a way to cleanly print the attributes of an object using pprint's pprint(vars(object)) function.

The script reads the file and creates instances of a Report class. Here's the class.

class Report(object):
    def __init__(self, line, headers):
        self.date_added=get_column_by_header(line,headers,"Date Added")
        self.user=get_column_by_header(line,headers,"Login ID")
        self.report=get_column_by_header(line,headers,"Search/Report Description")
        self.price=get_column_by_header(line,headers,"Price")
        self.retail_price=get_column_by_header(line,headers,"Retail Price")

    def __str__(self):
        from pprint import pprint
        return str(pprint(vars(self)))

I'd like to be able to print instances of Report cleanly a-la-pprint.

for i,line in enumerate(open(path+file_1,'r')):
    line=line.strip().split("|")
    if i==0:
        headers=line

    if i==1:
        record=Report(line,headers)
        print record

When I call

print record

for a single instance of Report, this is what I get in the shell.

{'date_added': '1/3/2012 14:06',
'price': '0',
'report': 'some_report',
'retail_price': '0.25',
'user': 'some_username'}
 None

My question is two-fold.

First, is this a good / desired way to print an object's attributes cleanly? Is there a better way to do this with or without pprint?

Second, why does

None

print to the shell at the end? I'm confused where that's coming from.

Thanks for any tips.

Barkley answered 3/2, 2012 at 21:6 Comment(4)
side note in str when you include from pprint import pprint, if you're only using this method once or twice it's fine, but for a commonly used method this module is going to be imported (computationally intense) every time the method is called. I think it might be better to have your modules imported at the top. Also this improves overall performance in python scripts.Callas
@Callas -- not true about subsequent imports being computationally intensive. When you import, the mechanism looks in a dict in sys.modules to see if it's already loaded. If it is, nothing else is done. Only if it's never been imported does the import mechanism do anything else.Megganmeggi
@bgporter: What if you create another instance of the object?Impede
See the docs on import: docs.python.org/release/2.5.2/ref/import.html "The system maintains a table of modules that have been or are being initialized, indexed by module name. This table is accessible as sys.modules. When a module name is found in this table, step (1) is finished. If not, a search for a module definition is started."Megganmeggi
V
7

pprint is just another form of print. When you say pprint(vars(self)) it prints vars into stdout and returns none because it is a void function. So when you cast it to a string it turns None (returned by pprint) into a string which is then printed from the initial print statement. I would suggest changing your print to pprint or redefine print as print if its all you use it for.

def __str__(self):
    from pprint import pprint
    return str(vars(self))

for i,line in enumerate(open(path+file_1,'r')):
    line = line.strip().split("|")
    if i == 0:
        headers = line
    if i == 1:
        record = Report(line,headers)
        pprint record

One alternative is to use a formatted output:

def __str__(self):
    return "date added:   %s\nPrice:        %s\nReport:       %s\nretail price: %s\nuser:         %s" % tuple([str(i) for i in vars(self).values()])

Hope this helped

Venlo answered 3/2, 2012 at 21:31 Comment(1)
While you've diagnosed the failure correctly, the suggested solutions are all pretty clunky; pprint.pformat is much more elegant.Aekerly
L
38

Dan's solution is just wrong, and Ismail's in incomplete.

  1. __str__() is not called, __repr__() is called.
  2. __repr__() should return a string, as pformat does.
  3. print normally indents only 1 character and tries to save lines. If you are trying to figure out structure, set the width low and indent high.

Here is an example

class S:
    def __repr__(self):
        from pprint import pformat
        return pformat(vars(self), indent=4, width=1)

a = S()
a.b = 'bee'
a.c = {'cats': ['blacky', 'tiger'], 'dogs': ['rex', 'king'] }
a.d = S()
a.d.more_c = a.c

print(a)

This prints

{   'b': 'bee',
    'c': {   'cats': [   'blacky',
                         'tiger'],
             'dogs': [   'rex',
                         'king']},
    'd': {   'more_c': {   'cats': [   'blacky',
                               'tiger'],
                  'dogs': [   'rex',
                              'king']}}}

Which is not perfect, but passable.

Loggins answered 25/5, 2014 at 3:12 Comment(0)
S
14

pprint.pprint doesn't return a string; it actually does the printing (by default to stdout, but you can specify an output stream). So when you write print record, record.__str__() gets called, which calls pprint, which returns None. str(None) is 'None', and that gets printed, which is why you see None.

You should use pprint.pformat instead. (Alternatively, you can pass a StringIO instance to pprint.)

Suspense answered 3/2, 2012 at 21:12 Comment(0)
L
8

I think beeprint is what you need.

Just pip install beeprint and change your code to:

def __str__(self):
    from beeprint import pp
    return pp(self, output=False)
Leader answered 13/9, 2016 at 10:24 Comment(0)
V
7

pprint is just another form of print. When you say pprint(vars(self)) it prints vars into stdout and returns none because it is a void function. So when you cast it to a string it turns None (returned by pprint) into a string which is then printed from the initial print statement. I would suggest changing your print to pprint or redefine print as print if its all you use it for.

def __str__(self):
    from pprint import pprint
    return str(vars(self))

for i,line in enumerate(open(path+file_1,'r')):
    line = line.strip().split("|")
    if i == 0:
        headers = line
    if i == 1:
        record = Report(line,headers)
        pprint record

One alternative is to use a formatted output:

def __str__(self):
    return "date added:   %s\nPrice:        %s\nReport:       %s\nretail price: %s\nuser:         %s" % tuple([str(i) for i in vars(self).values()])

Hope this helped

Venlo answered 3/2, 2012 at 21:31 Comment(1)
While you've diagnosed the failure correctly, the suggested solutions are all pretty clunky; pprint.pformat is much more elegant.Aekerly
C
5

For pretty-printing objects which contain other objects, etc. pprint is not enough. Try IPython's lib.pretty, which is based on a Ruby module.

from IPython.lib.pretty import pprint
pprint(complex_object)
Cyanohydrin answered 13/4, 2014 at 1:20 Comment(0)
P
0

@Anyany Pan way is the best.

Here I share a real case, when I deal with Azure resource

in AWS resources, I can use pprint to print the resource detail easily, but it doesn't work with Azure resource. Because they are different types.

from azure.identity import AzureCliCredential
from azure.mgmt.compute import ComputeManagementClient
#from pprint import pprint
from beeprint import pp
import os

# Acquire a credential object using CLI-based authentication.
credential = AzureCliCredential()

# Retrieve subscription ID from environment variable.
subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"]

compute_client = ComputeManagementClient(credential, subscription_id)
vm_list = compute_client.virtual_machines.list_all()

for vm in vm_list:
  type(vm)
  # pprint(vm) # doesn't work for Azure resource
  pp(vm)

output for reference by beeprint

<class 'azure.mgmt.compute.v2020_12_01.models._models_py3.VirtualMachine'>
instance(VirtualMachine):
  _attribute_map: {
    'additional_capabilities': {
      'key': 'properties.additionalCapabilities',
      'type': 'AdditionalCapabilities',
    },
    'availability_set': {
      'key': 'properties.availabilitySet',
      'type': 'SubResource',
    },
    'billing_profile': {
      'key': 'properties.billingProfile',
...

output by pprint

<class 'azure.mgmt.compute.v2020_12_01.models._models_py3.VirtualMachine'>
<azure.mgmt.compute.v2020_12_01.models._models_py3.VirtualMachine object at 0x1047cf4f0>
<class 'azure.mgmt.compute.v2020_12_01.models._models_py3.VirtualMachine'>
<azure.mgmt.compute.v2020_12_01.models._models_py3.VirtualMachine object at 0x1047cf5b0>
Perlaperle answered 5/9, 2022 at 8:41 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.