Debugging mit print()

Im Regelfall ist es verpönt Print-Befehle für ein Debugging zu verwenden. Ein Grund ist, dass man diese kurzfristig eingefügten Befehle vergisst, und sie somit in Produktion gelangen. Andererseits kann man manchmal schnell und einfach einen Fehler einkreisen und finden, wenn man anstatt eines Debuggers einen Print-Befehle verwendet. Vor allem in selbst geschriebenen Code kann man häufig mit temporären Ausgaben schneller agieren. Wenn man einen Debugger einsetzt, muss man sich auch zuerst Gedanken machen, an welchen Stellen man Breakpoints setzt, und welche Variablen man sich ausgeben lassen möchte.
Seit Python-Version 3.8 kann man für den Print-Befehl f-Strings verwenden, was die Ausgabe vereinfacht und teilweise auch lesbarer gestaltet.

Beispiel für die einfache Syntax von f-String:

>>> counter = 4711
>>> print(f"{counter = }")
counter = 4711

Auch Ausdrücke lassen sich als f-String ausgeben:

>>> amount = 1000
>>> counter = 10
>>> print(f"{(amount / counter) = }")
(amount / counter) = 100

Mehrstufige Struktur-Elemente können mit pprint() formatiert ausgegeben werden:

>>> import pprint
>>> dict = {'internal_dict_1': {'id': 1, 'name': 'Thomas', 'profession': 'developer'}, 'internal_dict_2': {}, 'internal_dict_3': {'no': 1, 'content': ['a', 'b', 'c']}}
>>> pprint.pprint(dict)
{'internal_dict_1': {'id': 1, 'name': 'Thomas', 'profession': 'developer'},
 'internal_dict_2': {},
 'internal_dict_3': {'content': ['a', 'b', 'c'], 'no': 1}}

Die pprint()-Ausgabe ist besonders effektiv bei einer XML-Struktur:

>>> import pprint
>>> import json
>>> from urllib.request import urlopen
>>> with urlopen('https://pypi.org/pypi/sampleproject/json') as resp:
...     project_info = json.load(resp)['info']
... 

Die print()-Funktion liefert ein kompaktes, und daher unübersichtliches Ergebnis:

>>> print(project_info)
{'author': 'A. Random Developer', 'author_email': 'author@example.com', 'bugtrack_url': None, 'classifiers': ['Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Topic :: Software Development :: Build Tools'], 'description': '# A sample Python project\n\n![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image")\n\nA sample project that exists as an aid to the [Python Packaging User\nGuide][packaging guide]\'s [Tutorial on Packaging and Distributing\nProjects][distribution tutorial].\n\nThis project does not aim to cover best practices for Python project\ndevelopment as a whole. For example, it does not provide guidance or tool\nrecommendations for version control, documentation, or testing.\n\n[The source for this project is available here][src].\n\nMost of the configuration for a Python project is done in the `setup.py` file,\nan example of which is included in this project. You should edit this file\naccordingly to adapt this sample project to your needs.\n\n----\n\nThis is the README file for the project.\n\nThe file should use UTF-8 encoding and can be written using\n[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md\nuse]. It will be used to generate the project webpage on PyPI and will be\ndisplayed as the project homepage on common code-hosting services, and should be\nwritten for that purpose.\n\nTypical contents for this file would include an overview of the project, basic\nusage examples, etc. Generally, including the project changelog in here is not a\ngood idea, although a simple “What\'s New” section for the most recent version\nmay be appropriate.\n\n[packaging guide]: https://packaging.python.org\n[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/\n[src]: https://github.com/pypa/sampleproject\n[rst]: http://docutils.sourceforge.net/rst.html\n[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant"\n[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional\n\n\n', 'description_content_type': 'text/markdown', 'docs_url': None, 'download_url': '', 'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1}, 'home_page': 'https://github.com/pypa/sampleproject', 'keywords': 'sample setuptools development', 'license': '', 'maintainer': '', 'maintainer_email': '', 'name': 'sampleproject', 'package_url': 'https://pypi.org/project/sampleproject/', 'platform': '', 'project_url': 'https://pypi.org/project/sampleproject/', 'project_urls': {'Bug Reports': 'https://github.com/pypa/sampleproject/issues', 'Funding': 'https://donate.pypi.org', 'Homepage': 'https://github.com/pypa/sampleproject', 'Say Thanks!': 'http://saythanks.io/to/example', 'Source': 'https://github.com/pypa/sampleproject/'}, 'release_url': 'https://pypi.org/project/sampleproject/2.0.0/', 'requires_dist': ['peppercorn', "check-manifest ; extra == 'dev'", "coverage ; extra == 'test'"], 'requires_python': '>=3.5, <4', 'summary': 'A sample Python project', 'version': '2.0.0', 'yanked': False, 'yanked_reason': None}

pprint() ergibt eine wesentlich übersichtlichere Ausgabe:

>>> pprint.pprint(project_info)
{'author': 'A. Random Developer',
 'author_email': 'author@example.com',
 'bugtrack_url': None,
 'classifiers': ['Development Status :: 3 - Alpha',
                 'Intended Audience :: Developers',
                 'License :: OSI Approved :: MIT License',
                 'Programming Language :: Python :: 3',
                 'Programming Language :: Python :: 3 :: Only',
                 'Programming Language :: Python :: 3.5',
                 'Programming Language :: Python :: 3.6',
                 'Programming Language :: Python :: 3.7',
                 'Programming Language :: Python :: 3.8',
                 'Topic :: Software Development :: Build Tools'],
 'description': '# A sample Python project\n'
                '\n'
                '![Python '
                'Logo](https://www.python.org/static/community_logos/python-logo.png '
                '"Sample inline image")\n'

... (aus Gründen der Übersichtlichkeit wurde hier ein Teil der Ausgabe weggelassen)

 'platform': '',
 'project_url': 'https://pypi.org/project/sampleproject/',
 'project_urls': {'Bug Reports': 'https://github.com/pypa/sampleproject/issues',
                  'Funding': 'https://donate.pypi.org',
                  'Homepage': 'https://github.com/pypa/sampleproject',
                  'Say Thanks!': 'http://saythanks.io/to/example',
                  'Source': 'https://github.com/pypa/sampleproject/'},
 'release_url': 'https://pypi.org/project/sampleproject/2.0.0/',
 'requires_dist': ['peppercorn',
                   "check-manifest ; extra == 'dev'",
                   "coverage ; extra == 'test'"],
 'requires_python': '>=3.5, <4',
 'summary': 'A sample Python project',
 'version': '2.0.0',
 'yanked': False,
 'yanked_reason': None}

Man kann locals() nutzen, um sich alle Variablen in einer Funktion anzeigen zu lassen, ohne sie alle einzelnen anzugeben:

def broken():
    numerator = 1
    denominator = 0
    pprint.pprint(locals())
    return numerator / denominator

>>> broken()
{'denominator': 0, 'numerator': 1}
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

Dies sind nur ein paar Tipps, die vielleicht weiterhelfen können, wenn man denn das print-Debugging doch manchmal verwenden möchte.