diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..3a626c3a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1fbe84..a88c88aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: pandoc/actions/setup@v1 - uses: actions/setup-python@v5 with: # Keep in sync with .readthedocs.yaml python-version-file: .python-version + - name: Install plantuml + run: | + sudo apt install plantuml - name: Setup cached uv uses: hynek/setup-cached-uv@v2 - name: Create venv and install docs dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d2f0721c..fc103c8d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,25 +19,25 @@ repos: - id: sphinx-lint types: [rst] - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 6.0.1 hooks: - id: isort additional_dependencies: ["toml"] entry: isort --profile=black name: isort (python) - repo: https://github.com/psf/black - rev: 24.10.0 + rev: 25.1.0 hooks: - id: black - repo: https://github.com/adamchainz/blacken-docs - rev: "1.19.0" + rev: "1.19.1" hooks: - id: blacken-docs args: [--line-length=79] additional_dependencies: - black - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell - repo: local diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 793af89f..b20a0ceb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,55 +1,199 @@ Changelog ========= -The versions follow `Semantic Versioning `_: -``MAJOR.MINOR.PATCH``. +Alle nennenswerten Änderungen an diesem Projekt werden in dieser Datei +dokumentiert. -``MAJOR`` - is increased when incompatible changes are published. -``MINOR`` - is increased when new compatible functionalities are released. -``PATCH`` - is increased if the changes include only compatible bug fixes. +Das Format basiert auf `Keep a Changelog +`_ und dieses Projekt hält sich an +`Calendar Versioning `_. -.. _changelog +Die erste Zahl der Version ist das Jahr. Die zweite Zahl wird mit jeder Version +erhöht, beginnend bei 1 für jedes Jahr. Die dritte Zahl ist für Notfälle, wenn +wir Zweige für ältere Versionen starten müssen. -24.2.0 +.. unreleased + +`Unreleased `_ +--------------------------------------------------------------------------------------- + +`25.1.0 `_ +------------------------------------------------------------------------------------- + +Added +~~~~~ + +* 📝 Add type hints +* 👥 Add license and acknowledgement +* 📝 Add logging section from Python4DataScience +* 🔧 📝 Add social media support + + * Add OpenGraph tag for mastodon + * Add social media links + +* 📝 Add PyPI digital attestations +* 📝 Add tip for a stride of -1 +* 📝 Add What’s new? +* 📝 Add conversion to reST + +Changed +~~~~~~~ + +* 📝 Update cookiecutter templates + + * Add badges + * Remove deprecated templates + * Add Jupyter Notebook section + +* 📝 Update glossary + + * Add constant, singleton and immutable objects + +* 📝 Update uv sections + + * Reproducing and updating uv environments + +* 🎨 Rearrange glossaries +* 📝 Update conda term +* 📝 Update GitLab package registry +* 📝 Expand the pytest plugins section +* 📝 Rearrange functions section +* 📝 Update installation of freethreaded Python +* 📝 Expand the contextmanager section +* 🎨 Restructure the documentation + + * Move packages outside libraries + * Move apps in packages + * Remove unittest2 + * Move doctests to Sphinx + * Move the sqlite database test to unittest + * Move Sphinx to a subchapter + +Removed +~~~~~~~ + +* Remove OOP design + +`24.3.0 `_ +------------------------------------------------------------------------------------- + +Added +~~~~~ + +* 📝 Add humanize +* 📝 Add testing code to documentation +* 📝 Add bump-my-version +* 📝 Add vale +* 📝 Add codespell +* 📝 Add checks +* 📝 Add LBYL and EAFP to exceptions +* 📝 Add the series of tutorials and trainings +* 📝 Adopt SOLID principles from Python4DataScience + +Changed +~~~~~~~ + +* 📝 Update description for init files +* 📝 Update pytest plugins + + * Add pytest-freethreaded to plugins for modified test sequences + * Add pytest-edit to modified output plugins + * Add playwright and pyleniumio to web dev plugins + * Add pytest-patterns to various plugins + * Remove pytest-splinter, pytest-mimesis and pytest-freezegun + +* 📝 Switch to uv for building envs, packaging etc. + + * Install different Python versions in parallel including PyPy and + free-threaded Python 3.13. + * Add tox-uv + * Publishing packages + * Update uv.lock file with a pre-commit hook + +* 📝 Update cibuildwheel +* 👷 Switch to uv in ci +* 📝 Switch to .venv directory +* 📝 Update to Python 3.13 +* 🔧 Switch to pyproject.toml +* 📝 Rearrange documentation tests +* 📝 Extend documentation of the string type +* 📝 Extend documentation of the tuple type +* 📝 Extend documentation of the list type +* 📝 Add sphinx-issues +* 📝 Add direnv tip +* 📝 Update instructions for installing packages +* 📝 Add proxy config for PyPI + +Fixed +~~~~~ + +* 📝 Fix coverage pipeline +* ✏️ Workaround for pytest lexer warnings + +`24.2.0 `_ +------------------------------------------------------------------------------------- + +Added +~~~~~ -* 📝 Update Python on mobile devices * 📝 Add design patterns -* 📝 Update Tiobe Index * 📝 Add frozenset * 📝 Add git filter for pytest * 📝 Add interrogate for docstring coverage + +Changed +~~~~~~~ + +* 📝 Update Python on mobile devices +* 📝 Update Tiobe Index * 📝 Expand section for testing the documentation -24.1.0 +`24.1.0 `_ +------------------------------------------------------------------------------------- + +Added +~~~~~ * 🌱 Add matplotlib for social cards -* 🔧 Use git tag for versioning the docs * 📝 Add links for strftime +* 📝 Add link to dataclasses +* 📝 Add exclude_also to coverage configs + +Changed +~~~~~~~ + +* 🔧 Use git tag for versioning the docs * 📝 Update None type * 📝 Update the review of values and identity * 📝 Update comparative expressions * 📝 Update dataprep example -* 🎨 pre-commit fixes -* 📝 Add link to dataclasses -* 📝 Update publishining gpackages +* 📝 Update publishining packages * Add trusted publisher -* 📝 Add exclude_also to coverage configs +Fixed +~~~~~ + +* 🎨 pre-commit fixes + +`v1.0.0 `_: 2023-11-28 +------------------------------------------------------------------------------------------------------------------------- + +Added +~~~~~ -1.0.0 +* 📝 Add dataclasses +* 📝 Add striding and link to slicing with pandas +* 📝 Add lambda functions + +Changed +~~~~~~~ * 🔖 Update to 1.0.0, add changelog * 💄 Switch to furo theme -* 📝 Add dataclasses * 📝 Switch to intersphinx links * 📝 Add note to Unicode help * 📝 Add link to pandas I/O tools and examples for serialisation files -* 📝 Add striding and link to slicing with pandas -* 📝 Add lambda functions * 📝 Update dicts type * Add setdefault diff --git a/README.rst b/README.rst index 6f5ac46c..ddece280 100644 --- a/README.rst +++ b/README.rst @@ -117,7 +117,13 @@ Installation Folgt uns --------- +.. _follow-us: + * `GitHub `_ +* `Mastodon `_ +* `Bluesky `_ + +.. _end-follow-us: Pull-Requests ------------- diff --git a/docs/_static/css/cusy.css b/docs/_static/css/cusy.css new file mode 100644 index 00000000..2cf30257 --- /dev/null +++ b/docs/_static/css/cusy.css @@ -0,0 +1,12 @@ +.accelerator { + text-decoration: underline; +} + +.field-list dt, +.option-list dt, +dl.footnote dt, +dl.glossary dt, +dl.simple dt, +dl:not([class]) dt { + font-weight: 700; +} diff --git a/docs/_templates/base.html b/docs/_templates/base.html new file mode 100644 index 00000000..c65a9139 --- /dev/null +++ b/docs/_templates/base.html @@ -0,0 +1,105 @@ + + + + {%- block site_meta -%} + + + + + + {%- if metatags %}{{ metatags }}{% endif -%} + + {%- block linktags %} + {%- if hasdoc('about') -%} + + {%- endif -%} + {%- if hasdoc('genindex') -%} + + {%- endif -%} + {%- if hasdoc('search') -%} + + {%- endif -%} + {%- if hasdoc('copyright') -%} + + {%- endif -%} + {%- if next -%} + + {%- endif -%} + {%- if prev -%} + + {%- endif -%} + {#- rel="canonical" (set by html_baseurl) -#} + {%- if pageurl %} + + {%- endif %} + {%- endblock linktags %} + + {# Favicon #} + {%- if favicon_url -%} + + {%- endif -%} + + + + {%- endblock site_meta -%} + + {#- Site title -#} + {%- block htmltitle -%} + {% if not docstitle %} + {{ title|striptags|e }} + {% elif pagename == master_doc %} + {{ docstitle|striptags|e }} + {% else %} + {{ title|striptags|e }} - {{ docstitle|striptags|e }} + {% endif %} + {%- endblock -%} + + {%- block styles -%} + + {# Custom stylesheets #} + {%- block regular_styles -%} + {%- for css in css_files -%} + {% if css|attr("filename") -%} + {{ css_tag(css) }} + {%- else -%} + + {%- endif %} + {% endfor -%} + {%- endblock regular_styles -%} + + {#- Theme-related stylesheets -#} + {%- block theme_styles %} + {% include "partials/_head_css_variables.html" with context %} + {%- endblock -%} + + {%- block extra_styles %} + {%- endblock -%} + + {%- endblock styles -%} + + {#- Custom front matter #} + {%- block extrahead -%}{%- endblock -%} + + + {% block body %} + + {% endblock %} + + {%- block scripts -%} + + {# Custom JS #} + {%- block regular_scripts -%} + {% for path in script_files -%} + {{ js_tag(path) }} + {% endfor -%} + {%- endblock regular_scripts -%} + + {# Theme-related JavaScript code #} + {%- block theme_scripts -%} + {%- endblock -%} + + {%- endblock scripts -%} + + diff --git a/docs/appendix/checks.rst b/docs/appendix/checks.rst index 7c92da06..61563d19 100644 --- a/docs/appendix/checks.rst +++ b/docs/appendix/checks.rst @@ -52,12 +52,12 @@ Checks ✅ ok, aber sehr lang und daher nur zu empfehlen, wenn zwischen vielen sehr ähnlichen Variablen unterschieden werden soll -:doc:`/types/numbers` ---------------------- +:doc:`/types/numbers/index` +--------------------------- * Erstellt einige Zahlenvariablen (Ganzzahlen, Gleitkommazahlen und komplexe Zahlen). Experimentiert ein wenig damit, was passiert, wenn ihr Operationen - mit ihnen durchführt, auch typübergreifend. + mit ihnen durchführt, auch Typ-übergreifend. .. blacken-docs:off @@ -83,6 +83,9 @@ Checks .. blacken-docs:on +:doc:`/types/numbers/complex` +----------------------------- + * Ladet das Modul :mod:`math` und probiert einige der Funktionen aus. Ladet dann auch das Modul :mod:`cmath` und macht dasselbe. @@ -103,6 +106,9 @@ Checks >>> sqrt(3) 1.7320508075688772 +:doc:`/types/numbers/bool` +-------------------------- + * Entscheidet, ob die folgenden Aussagen wahr oder falsch sind: * ``1`` → True @@ -112,8 +118,8 @@ Checks * ``1 and 0`` → False * ``1 > 0 or []`` → True -:doc:`/types/lists` -------------------- +:doc:`/types/sequences-sets/lists` +---------------------------------- * Was gibt :func:`len` für jeden der folgenden Fälle zurück: @@ -164,8 +170,8 @@ Checks .. note:: Mit diesem Code wird nur das erste Vorkommen von ``i`` entfernt. Um alle Vorkommen von ``i`` aus der Liste zu entfernen, könnte die Liste - :abbr:`z.B. (zum Beispiel)` in den :doc:`Set `-Typ umgewandelt - werden: + :abbr:`z.B. (zum Beispiel)` in den :doc:`Set + `-Typ umgewandelt werden: .. code-block:: pycon @@ -175,6 +181,8 @@ Checks ... >>> l = list(l) + Dies ändert jedoch auch die Reihenfolge der Elemente. + * Wenn ihr eine verschachtelte Liste ``ll`` habt, wie könnt ihr eine Kopie ``nll`` dieser Liste erhalten, in der ihr die Elemente ändern könnt, ohne den Inhalt von ``ll`` zu verändern? @@ -198,8 +206,8 @@ Checks * Welche anderen Optionen könntet ihr neben der expliziten Überprüfung des Typs haben? -:doc:`/types/tuples` --------------------- +:doc:`/types/sequences-sets/tuples` +----------------------------------- * Erläutert, warum die folgenden Operationen nicht auf das Tuple ``t`` angewendet werden können: @@ -217,8 +225,8 @@ Checks >>> sorted(t) -:doc:`/types/sets` ------------------- +:doc:`/types/sequences-sets/sets` +--------------------------------- * Wieviele Elemente hat ein Set, wenn es aus der folgenden Liste ``[4, 2, 3, 2, 1]`` gebildet wird? @@ -268,9 +276,9 @@ Checks >>> d[("Veit", "Tim", "Monique")] = None * Ihr könnt ein :doc:`Dictionary ` verwenden, und das wie ein - Tabelle einer Tabellenkalkulation verwenden, indem ihr :doc:`/types/tuples` - als Schlüssel Zeilen- und Spaltenwerte verwendet. Schreibt Beispielcode, um - Werte hinzuzufügen und wieder abzufragen. + Tabelle einer Tabellenkalkulation verwenden, indem ihr + :doc:`/types/sequences-sets/tuples` als Schlüssel Zeilen- und Spaltenwerte + verwendet. Schreibt Beispielcode, um Werte hinzuzufügen und wieder abzufragen. .. code-block:: pycon @@ -282,8 +290,17 @@ Checks >>> print(sheet[("A", 1)]) 2 -:doc:`/types/strings` ---------------------- +* Wie könnt ihr alle Dubletten aus einer Liste entfernen **ohne** die + Reihenfolge der Elemente in der Liste zu ändern? + + Hierfür können die Schlüssel eines :doc:`/types/dicts` verwendet werden: + + .. code-block:: pycon + + >>> list(dict.fromkeys(l)) + +:doc:`/types/strings/index` +--------------------------- * Könnt ihr :abbr:`z.B. (zum Beispiel)` eine Zeichenkette mit einer ganzen Zahl addieren oder multiplizieren, oder mit einer Gleitkommazahl oder einer @@ -309,15 +326,8 @@ Checks File "", line 1, in TypeError: can't multiply sequence by non-int of type 'complex' -* Wie könnt ihr eine Überschrift wie ``variables and expressions`` so abändern, - dass sie statt Leerzeichen Bindestriche enthält und so besser als Dateinamen - verwendet werden kann? - - .. code-block:: pycon - - >>> ve = "variables and expressions" - >>> "-".join(ve.split()) - 'variables-and-expressions' +:doc:`/types/strings/operators-functions` +----------------------------------------- * Welche der folgenden Zeichenketten können nicht in Zahlen umgewandelt werden und warum? @@ -341,6 +351,19 @@ Checks .. blacken-docs:on +:doc:`/types/strings/built-in-modules/string` +--------------------------------------------- + +* Wie könnt ihr eine Überschrift wie ``variables and expressions`` so abändern, + dass sie statt Leerzeichen Bindestriche enthält und so besser als Dateinamen + verwendet werden kann? + + .. code-block:: pycon + + >>> ve = "variables and expressions" + >>> "-".join(ve.split()) + 'variables-and-expressions' + * Wenn ihr überprüfen wollt, ob eine Zeile mit ``.. note::`` beginnt, welche Methode würdet ihr verwenden? Gibt es auch noch andere Möglichkeiten? @@ -352,7 +375,7 @@ Checks True * Angenommen, ihr habt eine Zeichenkette mit Ausrufezeichen, Anführungszeichen - und Zeilenumbrruch. Wie können diese aus der Zeichenkette entfernt werden? + und Zeilenumbruch. Wie können diese aus der Zeichenkette entfernt werden? .. code-block:: pycon @@ -372,6 +395,9 @@ Checks >>> hipy.translate(subs) 'Hello-Pythonistas--' +:doc:`/types/strings/built-in-modules/re` +----------------------------------------- + * Welchen regulären Ausdruck würdet ihr verwenden, um Zeichenfolgen zu finden, die die Zahlen zwischen -3 und +3 darstellen? @@ -388,137 +414,8 @@ Checks kleinen oder großen ``x``, gefolgt von einem oder mehreren Zeichen in den Bereichen ``0-9``, ``a-f`` oder ``A-F``. -:doc:`/types/files` -------------------- - -* Verwendet die Funktionen des :mod:`python3:os`-Moduls, um einen Pfad zu einer - Datei namens :file:`example.log` zu nehmen und einen neuen Dateipfad im selben - Verzeichnis für eine Datei namens :file:`example.log1` zu erstellen. - - .. code-block:: pycon - - >>> import os - >>> path = os.path.abspath("example.log") - >>> print(path) - /Users/veit/python-basics-tutorial-de/example.log - >>> new_path = f"{path}2" - >>> print(new_path) - /Users/veit/python-basics-tutorial-de/example.log2 - -* Welche Bedeutung hat das Hinzufügen von ``b`` als Parameter von - :func:`python3:open`? - - Dadurch wird die Datei im Binärmodus geöffnet, :abbr:`d.h. (das heißt)` es - werden Bytes und keine Zeichen gelesen und geschrieben. - -* Öffnet eine Datei :file:`my_file.txt` und fügt zusätzlichen Text am Ende der - Datei ein. Welchen Befehl würdet ihr verwenden, um :file:`my_file.txt` zu - öffnen? Welchen Befehl würdet ihr verwenden, um die Datei erneut zu öffnen und - von Anfang an zu lesen? - - .. code-block:: pycon - - >>> with open("my_file", "a") as f: - ... f.write("Hi, Pythinistas!\n") - ... - 17 - >>> with open("my_file") as f: - ... print(f.readlines()) - ... - ['Hi, Pythinistas!\n', 'Hi, Pythinistas!\n'] - -* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das - :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten - nützlich wäre? - - * beim Lesen und Schreiben einer Binärdatei - * beim Lesen von einer externen Schnittstelle, wobei die Daten genau so - gespeichert werden sollen, wie sie übermittelt wurden - -* Warum könnte :doc:`pickle ` für die folgenden - Anwendungsfälle geeignet sein oder auch nicht: - - #. Speichern einiger Zustandsvariablen von einem Durchlauf zum nächsten ✅ - #. Aufbewahren von Auswertungsergebnissen ❌, da Pickle abhängig von der - jeweiligen Python-Version sind - #. Speichern von Benutzernamen und Passwörtern ❌, da Pickle nicht sicher sind - #. Speichern eines großen Wörterbuchs mit englischen Begriffen ❌, da der - gesamte Pickle in den Speicher geladen werden müsste - -* Wenn ihr euch die `Manpage für das wc-Dienstprogramm - `_ anseht, seht ihr zwei - Befehlszeilenoptionen: - - ``-c`` - zählt die Bytes in der Datei - ``-m`` - zählt die Zeichen, die im Falle einiger Unicode-Zeichen zwei oder mehr - Bytes lang sein können - - Außerdem sollte unser Modul, wenn eine Datei angegeben wird, aus dieser Datei - lesen und sie verarbeiten, aber wenn keine Datei angegeben wird, sollte es aus - ``stdin`` lesen und verarbeiten. - - .. seealso:: - :ref:`_wcargv_stdin.py ` - -* Wenn ein Kontext-Manager in einem Skript verwendet wird, das mehrere Dateien - liest und/oder schreibt, welche der folgenden Ansätze wäre eurer Meinung nach - am besten? - - #. Legt das gesamte Skript in einen Block, der von einer ``with``-Anweisung - verwaltet wird. - #. Verwendet eine ``with``-Anweisung für alle Lesevorgänge und eine weitere - für alle Schreibvorgänge. - #. Verwendet jedes Mal eine ``with``-Anweisung, wenn ihr eine Datei lest oder - schreibt, :abbr:`d.h. (das heißt)` für jede Zeile. - #. Verwendet für jede Datei, die ihr lest oder schreibt, eine - ``with``-Anweisung. - - Wahrscheinlich ist 4. der beste Ansatz, da ein Teil der Aufgabe des - Kontextmanagers beim Dateizugriff darin besteht, sicherzustellen, dass eine - Datei geschlossen ist. - -* Archiviert :file:`*.txt`-Dateien aus dem aktuellen Verzeichnis im Verzeichnis - :file:`archive` als :file:`*.zip`-Dateien mit dem aktuellen Datum als - Dateiname. - - * Welche Module benötigt ihr hierfür? - - :mod:`python3:datetime`, :mod:`python3:pathlib` und :mod:`python3:zipfile`. - - * Schreibt eine mögliche Lösung. - - .. code-block:: pycon - :linenos: - - >>> import datetime - >>> import pathlib - >>> import zipfile - >>> file_pattern = "*.txt" - >>> archive_path = "archive" - >>> today = f"{datetime.date.today():%Y-%m-%d}" - >>> cur_path = pathlib.Path(".") - >>> paths = cur_path.glob(file_pattern) - >>> zip_path = cur_path.joinpath(archive_path, today + ".zip") - >>> zip_file = zipfile.ZipFile(str(zip_path), "w") - >>> for path in paths: - ... zip_file.write(str(path)) - ... path.unlink() - ... - - Zeile 9 - erstellt den Pfad zur ZIP-Datei im Archivverzeichnis. - Zeile 10 - öffnet das neue ZIP-Dateiobjekt zum Schreiben; :func:`str` wird - benötigt, um einen Pfad in eine Zeichenkette umzuwandeln. - Zeile 12 - schreibt die aktuelle Datei in die Zip-Datei. - Zeile 13 - entfernt die aktuelle Datei aus dem Arbeitsverzeichnis. - -:doc:`/input` -------------- +:doc:`/types/strings/input` +--------------------------- * Wie könnt ihr mit der :func:`input`-Funktion String- und Integer-Werte erhalten? @@ -597,8 +494,8 @@ Checks >>> print(personal_data[who]) 60 -:doc:`/control-flows/loops` ---------------------------- +:doc:`/control-flow/loops` +-------------------------- * Entfernt aus der Liste ``x = [ -2, -1, 0, 1, 2, 3]``, alle negativen Zahlen. @@ -664,8 +561,8 @@ Checks >>> {x: x**3 for x in range(1, 5)} {1: 1, 2: 8, 3: 27, 4: 64} -:doc:`/control-flows/exceptions` --------------------------------- +:doc:`/control-flow/exceptions` +------------------------------- * Schreibt Code, der zwei Zahlen erhält und die erste Zahl durch die zweite dividiert. Prüft, ob der :class:`python3:ZeroDivisionError` auftritt, wenn die @@ -760,8 +657,9 @@ Checks :doc:`/functions/variables` --------------------------- -* Angenommen, ``x = 1``, welchen Wert hat ``x`` nach der Ausführung von - ``func()`` und ``gfunc()``? +* Angenommen, ``x = 1``, :func:`func` setze die lokale Variable ``x`` auf ``2`` + und :func:`gfunc` die globale Variable ``x`` auf ``3``, welchen Wert nimmt + ``x`` an, nachdem :func:`func` und :func:`gfunc` durchlaufen wurden? .. code-block:: pycon @@ -935,7 +833,7 @@ Checks ``.y`` verfügbar. * Aktualisiert die Dimensionen der Klasse :class:`Triangle`, damit sie - Eigenschaften mit Gettern und Settern sind, die keine negativen Größen + Eigenschaften mit Getter- und Setter-Methoden sind, die keine negativen Größen zulassen. .. code-block:: pycon @@ -974,8 +872,8 @@ Checks >>> t1.x 3 -:doc:`/libs/distribution` -------------------------- +:doc:`/packs/distribution` +-------------------------- * Wenn ihr ein Paket für eine Aufgabenverwaltung erstellen wollt, das die Aufgaben in eine Datenbank schreibt und über ein Python-:abbr:`API (engl.: @@ -1112,3 +1010,132 @@ Checks .. seealso:: Ein vollständiges Beispiel findet ihr in `github.com/veit/items `_. + +:doc:`/save-data/files` +----------------------- + +* Verwendet die Funktionen des :mod:`python3:os`-Moduls, um einen Pfad zu einer + Datei namens :file:`example.log` zu nehmen und einen neuen Dateipfad im selben + Verzeichnis für eine Datei namens :file:`example.log1` zu erstellen. + + .. code-block:: pycon + + >>> import os + >>> path = os.path.abspath("example.log") + >>> print(path) + /Users/veit/python-basics-tutorial-de/example.log + >>> new_path = f"{path}2" + >>> print(new_path) + /Users/veit/python-basics-tutorial-de/example.log2 + +* Welche Bedeutung hat das Hinzufügen von ``b`` als Parameter von + :func:`python3:open`? + + Dadurch wird die Datei im Binärmodus geöffnet, :abbr:`d.h. (das heißt)` es + werden Bytes und keine Zeichen gelesen und geschrieben. + +* Öffnet eine Datei :file:`my_file.txt` und fügt zusätzlichen Text am Ende der + Datei ein. Welchen Befehl würdet ihr verwenden, um :file:`my_file.txt` zu + öffnen? Welchen Befehl würdet ihr verwenden, um die Datei erneut zu öffnen und + von Anfang an zu lesen? + + .. code-block:: pycon + + >>> with open("my_file", "a") as f: + ... f.write("Hi, Pythonistas!\n") + ... + 17 + >>> with open("my_file") as f: + ... print(f.readlines()) + ... + ['Hi, Pythonistas!\n', 'Hi, Pythonistas!\n'] + +* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das + :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten + nützlich wäre? + + * beim Lesen und Schreiben einer Binärdatei + * beim Lesen von einer externen Schnittstelle, wobei die Daten genau so + gespeichert werden sollen, wie sie übermittelt wurden + +* Warum könnte :doc:`pickle ` für die folgenden + Anwendungsfälle geeignet sein oder auch nicht: + + #. Speichern einiger Zustandsvariablen von einem Durchlauf zum nächsten ✅ + #. Aufbewahren von Auswertungsergebnissen ❌, da Pickle abhängig von der + jeweiligen Python-Version sind + #. Speichern von Benutzernamen und Passwörtern ❌, da Pickle nicht sicher sind + #. Speichern eines großen Wörterbuchs mit englischen Begriffen ❌, da der + gesamte Pickle in den Speicher geladen werden müsste + +* Wenn ihr euch die `Manpage für das wc-Dienstprogramm + `_ anseht, seht ihr zwei + Befehlszeilenoptionen: + + ``-c`` + zählt die Bytes in der Datei + ``-m`` + zählt die Zeichen, die im Falle einiger Unicode-Zeichen zwei oder mehr + Bytes lang sein können + + Außerdem sollte unser Modul, wenn eine Datei angegeben wird, aus dieser Datei + lesen und sie verarbeiten, aber wenn keine Datei angegeben wird, sollte es aus + ``stdin`` lesen und verarbeiten. + + .. seealso:: + :ref:`_wcargv_stdin.py ` + +* Wenn ein Kontext-Manager in einem Skript verwendet wird, das mehrere Dateien + liest und/oder schreibt, welche der folgenden Ansätze wäre eurer Meinung nach + am besten? + + #. Legt das gesamte Skript in einen Block, der von einer ``with``-Anweisung + verwaltet wird. + #. Verwendet eine ``with``-Anweisung für alle Lesevorgänge und eine weitere + für alle Schreibvorgänge. + #. Verwendet jedes Mal eine ``with``-Anweisung, wenn ihr eine Datei lest oder + schreibt, :abbr:`d.h. (das heißt)` für jede Zeile. + #. Verwendet für jede Datei, die ihr lest oder schreibt, eine + ``with``-Anweisung. + + Wahrscheinlich ist 4. der beste Ansatz, da ein Teil der Aufgabe des + Kontextmanagers beim Dateizugriff darin besteht, sicherzustellen, dass eine + Datei geschlossen ist. + +* Archiviert :file:`*.txt`-Dateien aus dem aktuellen Verzeichnis im Verzeichnis + :file:`archive` als :file:`*.zip`-Dateien mit dem aktuellen Datum als + Dateiname. + + * Welche Module benötigt ihr hierfür? + + :mod:`python3:datetime`, :mod:`python3:pathlib` und :mod:`python3:zipfile`. + + * Schreibt eine mögliche Lösung. + + .. code-block:: pycon + :linenos: + + >>> import datetime + >>> import pathlib + >>> import zipfile + >>> file_pattern = "*.txt" + >>> archive_path = "archive" + >>> today = f"{datetime.date.today():%Y-%m-%d}" + >>> cur_path = pathlib.Path(".") + >>> paths = cur_path.glob(file_pattern) + >>> zip_path = cur_path.joinpath(archive_path, today + ".zip") + >>> zip_file = zipfile.ZipFile(str(zip_path), "w") + >>> for path in paths: + ... zip_file.write(str(path)) + ... path.unlink() + ... + + Zeile 9 + erstellt den Pfad zur ZIP-Datei im Archivverzeichnis. + Zeile 10 + öffnet das neue ZIP-Dateiobjekt zum Schreiben; :func:`str` wird + benötigt, um einen Pfad in eine Zeichenkette umzuwandeln. + Zeile 12 + schreibt die aktuelle Datei in die Zip-Datei. + Zeile 13 + entfernt die aktuelle Datei aus dem Arbeitsverzeichnis. diff --git a/docs/appendix/glossary.rst b/docs/appendix/glossary.rst new file mode 100644 index 00000000..23c852b4 --- /dev/null +++ b/docs/appendix/glossary.rst @@ -0,0 +1,763 @@ +Glossar +======= + +.. glossary:: + :sorted: + + Argument + Ein Wert, der einer :term:`Funktion` übergeben wird. Es gibt zwei Arten + von Argumenten: + + Schlüsselwortargument + ein Argument, dem ein Bezeichner (:abbr:`z.B. (zum Beispiel)` + ``name=``) in einem Funktionsaufruf vorangestellt ist oder das als + Wert in einem Wörterbuch übergeben wird, dem ``**`` vorangestellt + ist. + Positionsargument + ein Argument, das kein Schlüsselwortargument ist. Positionsargumente + können am Anfang einer Argumentliste stehen und/oder als Elemente + einer Iteration mit vorangestelltem ``*`` übergeben werden. + + Ausnahme + Ausnahmebehandlung + Exception + Eine Ausnahme (englisch *exception*) reicht bestimmte Programmzustände – + meistens Fehlerzustände – an andere Programmebenen weiter. Sie ist eine + anpassbare Form von :term:`assert`. + + .. seealso:: + * :doc:`/control-flow/exceptions` + * `Ausnahmen (englisch exceptions) loggen + `_ + * :ref:`pytest_fail` + * :class:`python3:Exception` + + Dekorator + Decorator + Eine Funktion, die eine andere Funktion zurückgibt, normalerweise als + Funktionstransformation unter Verwendung der ``@wrapper``-Syntax + angewandt. Übliche Beispiele für Dekoratoren sind :ref:`classmethod` und + :ref:`staticmethod`. + + .. seealso:: + * :doc:`/functions/decorators` + + Docstring + Ein :doc:`/types/strings/built-in-modules/string`-Literal, das als erster + Ausdruck in einer Klasse, Funktion oder einem Modul erscheint. Es wird + vom Python-Compiler erkannt und in das ``__doc__``-Attribut der + umschließenden Klasse, Funktion oder des Moduls aufgenommen. + + .. seealso:: + * :doc:`/document/sphinx/docstrings` + + Duck-Typing + Programmierstil, bei dem nicht der Typ eines Objekts untersucht wird, um + festzustellen, ob es die richtige Schnittstelle hat, sondern stattdessen + die Methode oder das Attribut einfach aufgerufen wird. + + „Wenn es wie eine Ente aussieht und wie eine Ente quakt, muss es eine + Ente sein.“ + + Durch die Betonung von Schnittstellen anstelle spezifischer Typen + verbessert gut gestalteter Code seine Flexibilität, indem er polymorphe + Substitution ermöglicht. Duck-Typing vermeidet Tests mit :class:`type` + oder :func:`isinstance` und verwendet stattdessen typischerweise + :func:`hasattr`-Tests oder :term:`EAFP`-Programmierung. + + .. seealso:: + * :ref:`duck-typing` + + EAFP + Easier to ask for forgiveness than permission (englisch: Es ist + einfacher, um Vergebung zu bitten als um Erlaubnis). Dieser gängige + Python-Stil geht von der Existenz gültiger Schlüssel oder Attribute aus + und fängt :term:`Ausnahmen ` ab, wenn sich diese Annahme als + falsch erweist. Er zeichnet sich durch viele :term:`try`- und + :term:`except`-Anweisungen aus. Diese Technik steht im Gegensatz zum + :term:`LBYL`-Stil, der in vielen anderen Sprachen wie C üblich ist. + + F-String + :doc:`String `-Literal, denen ein + ``f`` oder ``F`` vorangestellt ist. + + .. seealso:: + * :ref:`f-strings` + * :pep:`498` + + Funktion + Eine Reihe von Anweisungen, die einen Wert zurückgibt. Ihr können auch + null oder mehr Argumente übergeben werden, die bei der Ausführung des + Hauptteils verwendet werden können. + + .. seealso:: + * :doc:`/functions/index` + + Garbage Collection + Prozess der Freigabe von Speicher, wenn dieser nicht mehr verwendet wird. + + .. seealso:: + * :py:mod:`gc` + + Konstante + Python hat zwar :term:`unveränderliche ` Objekte, aber + keine konstanten Variablen. Variablen verweisen auf Objekte, es gibt + jedoch keine Möglichkeit, zu verhindern, dass eine neue Zuweisung + erfolgt. + + Kontrollfluss + Control flow + Zeitliche Abfolge der einzelnen Befehle eines Computerprogramms. + + .. seealso:: + * :doc:`/control-flow/index` + + LBYL + Look before you leap (englisch: Schaue, bevor du springst). Bei diesem + Stil werden vor dem Aufruf explizit die Vorbedingungen geprüft. Dieser + Stil steht im Gegensatz zum :term:`EAFP`-Ansatz und ist durch das + Vorhandensein vieler ``if``-Anweisungen gekennzeichnet. + + Methode + Eine :term:`Funktion`, die innerhalb einer Klasse definiert ist. Wenn sie + als Attribut einer Instanz dieser Klasse aufgerufen wird, erhält die + Methode das Instanzobjekt als erstes :term:`Argument` (das normalerweise + ``self`` heißt). + + Parameter + :term:`Argument` einer :term:`Funktions `- (oder + :term:`Methoden `-) Definition. + + .. seealso:: + * :doc:`/functions/params` + + Singleton-Objekt + Eine Singleton-Klasse kann nur eine Instanz von sich selbst erzeugen. + :doc:`../types/none` ist ein Beispiel für eine Singleton-Klasse in + Python. + + Unveränderlich + Immutable + Ein Objekt, das nicht verändert (:abbr:`d.h. (das heißt)` mutiert) werden + kann. Der Wert eines unveränderlichen Objekts kann sich nicht ändern. + :doc:`Tupel <../types/sequences-sets/tuples>` sind Beispiele für + unveränderliche Objekte. + + Zen of Python + Auflistung von Python-Designprinzipien und -Philosophien, die für das + Verständnis und die Verwendung der Sprache hilfreich sind. Die Liste kann + durch Eingabe von ``import this`` ausgegeben werden. + + .. _start-packaging: + + build + ``build`` ist ein :pep:`517`-kompatibler Python-Paket-Builder. Er bietet + eine :abbr:`CLI (Command Line Interface)` zum Erstellen von Paketen + sowie eine Python-:abbr:`API (Application Programming Interface)`. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Built Distribution + bdist + Eine Struktur aus Dateien und Metadaten, die bei der Installation nur an + den richtigen Speicherort auf dem Zielsystem verschoben werden müssen. + :term:`wheel` ist ein solches Format, nicht jedoch *distutil’s* + :term:`Source Distribution`, die einen Build-Schritt erfordern. + + cibuildwheel + :doc:`/packs/cibuildwheel` ist ein Python-Paket, das :term:`wheels + ` für alle gängigen Plattformen und Python-Versionen auf den + meisten :term:`CI`-Systemen erstellt. + + .. seealso:: + * :term:`multibuild` + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + conda + Paketmanagement-Tool für die `Anaconda-Distribution + `_. Sie ist speziell auf + die wissenschaftliche Gemeinschaft ausgerichtet, insbesondere auf + Windows, wo die Installation von binären Erweiterungen oft schwierig ist. + + Conda installiert keine Pakete von :term:`PyPI` und kann nur von den + offiziellen Continuum-Repositories oder von `anaconda.org + `_ oder lokalen (:abbr:`z.B. (zum Beispiel)` + Intranet-) Paketservern installieren. + + .. note:: + :term:`pip` kann in conda installiert werden und Seite an Seite + arbeiten kann, um Distributionen von :term:`PyPI` zu verwalten. + + .. seealso:: + * `Conda: Myths and Misconceptions + `_ + * `Conda build variants + `_ + * `Docs `__ + * `GitHub `__ + + devpi + `devpi `_ ist ein leistungsstarker + :term:`PyPI`-kompatibler Server und ein PyPI-Proxy-Cache mit einem + Befehlszeilenwerkzeug um Paketierungs-, Test- und + Veröffentlichungsaktivitäten zu ermöglichen. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Distribution Package + Eine versionierte Archivdatei, die Python-:term:`Pakete + `, -:term:`Module ` und andere Ressourcendateien + enthält, die zum Verteilen eines :term:`Releases ` verwendet + werden. + + distutils + Paket der Python-Standardbibliothek, das Unterstützung für das + Bootstrapping von :term:`pip` in eine bestehende Python-Installation oder + :term:`virtuelle Umgebung` bietet. + + .. seealso:: + * :doc:`Docs ` + * `GitHub `__ + + Egg + Ein :term:`Built Distribution`-Format, das von :term:`Setuptools` + eingeführt wurde und nun durch :term:`wheel` ersetzt wird. Weitere + Informationen findet ihr unter `The Internal Structure of Python Eggs + `_ + und `Python Eggs `_. + + enscons + enscons ist ein Python-Paketierungswerkzeug, das auf `SCons + `_ basiert. Es erstellt :term:`pip`-kompatible + :term:`Source Distributions ` und :term:`wheels + ` ohne Verwendung von :term:`distutils` oder :term:`setuptools`, + einschließlich Distributionen mit C-Erweiterungen. enscons hat eine + andere Architektur und Philosophie als :term:`distutils`, da es + Python-Paketierung zu einem allgemeinen Build-System hinzufügt. enscons + kann euch helfen, :term:`sdists ` und :term:`wheels ` zu + bauen. + + .. seealso:: + * `GitHub `__ + * `PyPI `__ + + Flit + Flit bietet eine einfache Möglichkeit, reine Python-Pakete und -Module zu + erstellen und auf den :term:`Python Package Index` hochzuladen. Flit kann + eine Konfigurationsdatei generieren, um schnell ein Projekt einzurichten, + eine :term:`Source Distribution` und ein :term:`wheel` zu erstellen und + sie zu PyPI hochzuladen. + + Flit verwendet :term:`pyproject.toml`, um ein Projekt zu konfigurieren. + Flit ist nicht auf Werkzeuge wie :term:`setuptools` angewiesen, um + Distributionen zu erstellen, oder auf :term:`twine`, um sie auf + :term:`PyPI` hochzuladen. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Hatch + Hatch ist ein Kommandozeilenwerkzeug, das ihr zum Konfigurieren und + Versionieren von Paketen, zum Spezifizieren von Abhängigkeiten genutzt + werden kann. Das Plugin-System ermöglicht die einfache Erweiterung der + Funktionalitäten. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + hatchling + Build-Backend von :term:`Hatch`, das auch zum Veröffentlichen auf dem + :term:`Python Package Index` genutzt werden kann. + + Import Package + Ein Python-Modul, das andere Module oder rekursiv andere Pakete enthalten + kann. + + maturin + Vormals pyo3-pack, ist ein :pep:`621`-kompatibles Build-Tool für + :doc:`binäre Erweiterungen <../packs/binary-extensions>` in Rust. + + meson-python + Build-Backend, das das `Meson `_-Build-System + verwendet. Es unterstützt eine Vielzahl von Sprachen, einschließlich C, + und ist in der Lage, die Anforderungen der meisten komplexen + Build-Konfigurationen zu erfüllen. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Modul + Ein Objekt, das als organisatorische Einheit von Python-Code dient. + Module haben einen :doc:`Namensraum `, der beliebige + Python-Objekte enthält. Sie werden durch Importieren in Python + geladen. + + Python-Module können in zwei verschiedenen Varianten existieren: + + Pure Module + Ein Modul, das in Python geschrieben wurde und in einer einzigen + ``.py``-Datei enthalten ist (und möglicherweise zugehörigen + ``.pyc``- und/oder ``.pyo``-Dateien). + + Extension Module + In der Regel in eine einzelne dynamisch ladbare vorkompilierte + Datei, :abbr:`z.B. (zum Beispiel)` einer gemeinsamen Objektdatei + (``.so``). + + .. seealso:: + * :doc:`/libs/batteries` + + multibuild + ``multibuild`` ist ein Satz von CI-Skripten zum Erstellen und Testen von + Python-:term:`wheels ` für Linux, macOS und Windows. + + .. seealso:: + :term:`cibuildwheel` + + pdm + Python-Paketmanager mit :pep:`582`-Unterstützung. Er installiert und + verwaltet Pakete ohne dass eine :term:`virtuelle Umgebung ` erstellt werden muss. Er verwendet auch + :term:`pyproject.toml`, um Projekt-Metadaten zu speichern, wie in + :pep:`621` definiert. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + pex + Bibliothek und Werkzeug zur Erzeugung von Python Executable + (:file:`.pex`)-Dateien, die eigenständige Python-Umgebungen sind. + :file:`.pex`-Dateien sind Zip-Dateien mit ``#!/usr/bin/env python`` und + einer speziellen :file:`__main__.py`-Datei, die das Deployment von + Python-Applikationen stark vereinfachen können. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + pip + Beliebtes Werkzeug für die Installation von Python-Paketen, das in + neuen Versionen von Python enthalten ist. + + Es bietet die wesentlichen Kernfunktionen zum Suchen, Herunterladen und + Installieren von Paketen aus dem :term:`Python Package Index` und andere + Python-Paketverzeichnissen und kann über eine Befehlszeilenschnittstelle + (CLI) in eine Vielzahl von Entwicklungsabläufen eingebunden werden. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + pip-tools + Reihe von Werkzeugen, die eure Builds deterministisch halten und dennoch + mit neuen Versionen eurer Abhängigkeiten auf dem Laufenden halten können. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Pipenv + Pipenv bündelt :term:`Pipfile`, :term:`pip` und :term:`virtualenv` in + einer einzigen Toolchain. Es kann die ``requirements.txt`` automatisch + importieren und mithilfe von `safety `_ die + Umgebung auch auf CVEs prüfen. Schließlich erleichtert es auch die + Deinstallation von Paketen und deren Abhängigkeiten. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Pipfile + Pipfile.lock + ``Pipfile`` und ``Pipfile.lock`` sind eine übergeordnete, + anwendungsorientierte Alternative zu :term:`pip`’s + ``requirements.txt``-Datei. Die :pep:`PEP 508 Environment Markers + <508#environment-markers>` werden ebenfalls unterstützt. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + + pipx + pipx unterstützt euch, Abhängigkeitskonflikte mit anderen auf dem System + installierten Paketen zu vermeiden. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + piwheels + Website und zugrundeliegende Software, die + :term:`Source Distribution`-Pakete von :term:`PyPI` holt und sie in + binäre :term:`wheels ` kompiliert, die für die Installation auf + Raspberry Pis optimiert sind. + + .. seealso:: + * `Home `__ + * `Docs `__ + * `GitHub `__ + + poetry + Eine All-in-One-Lösung für reine Python-Projekte. Es ersetzt + :term:`setuptools`, :term:`venv`/:term:`pipenv`, :term:`pip`, + :term:`wheel` und :term:`twine`. Sie macht jedoch einige schlechte + Standardannahmen für Bibliotheken und die + :term:`pyproject.toml`-Konfiguration ist nicht standardkonform. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + pybind11 + Dies ist :term:`setuptools`, aber mit einer C++-Erweiterung und von + :term:`cibuildwheel` generierten :term:`wheels `. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + pypi.org + `pypi.org `_ ist der Domain-Name für den + :term:`Python Package Index` (:term:`PyPI`). Er löste 2017 den alten + Index-Domain-Namen ``pypi.python.org`` ab. Er wird von :term:`warehouse` + unterstützt. + + pyproject.toml + Werkzeugunabhängige Datei zur Spezifikation von Projekten, die in + :pep:`518` definiert ist. + + .. seealso:: + * :ref:`pyproject-toml` + * `Docs + `__ + + Python Package Index + PyPI + :term:`pypi.org` ist der Standard-Paket-Index für die Python-Community. + Alle Python-Entwickler können ihre Distributionen nutzen und verteilen. + + Python Packaging Authority + PyPA + Die `Python Packaging Authority `_ ist + eine Arbeitsgruppe, die mehrere Softwareprojekte für die Paketierung, + Verteilung und Installation von Python-Bibliotheken verwaltet. Die in + `PyPA Goals `_ genannten Ziele + sind jedoch noch während der Diskussionen um :pep:`516`, :pep:`517` und + :pep:`518` entstanden, die mit dem :term:`pyproject.toml`-basierten + Build-System konkurrierende Workflows erlaubten, die nicht interoperabel + sein müssen. + + readme_renderer + ``readme_renderer`` ist eine Bibliothek, die verwendet wird, um + Dokumentation aus Auszeichnungssprachen wie Markdown oder + reStructuredText in HTML zu rendern. Ihr könnt sie verwenden, um zu + prüfen, ob eure Paketbeschreibungen auf :term:`PyPI` korrekt angezeigt + werden. + + .. seealso:: + * `GitHub `__ + * `PyPI `__ + + Release + Der Snapshot eines Projekts zu einem bestimmten Zeitpunkt, gekennzeichnet + durch eine Versionskennung. + + Eine Veröffentlichung kann mehrere :term:`Built Distributions + ` zur Folge haben. + + scikit-build + Build-System-Generator für ``C``-, ``C++``-, ``Fortran``- und + ``Cython``-Erweiterungen, der :term:`setuptools`, :term:`wheel` und + :term:`pip` integriert. Er verwendet intern ``CMake``, um eine bessere + Unterstützung für zusätzliche Compiler, Build-Systeme, Cross-Compilation + und das Auffinden von Abhängigkeiten und deren zugehörigen + Build-Anforderungen zu bieten. Um die Erstellung großer Projekte zu + beschleunigen und zu parallelisieren, kann zusätzlich `Ninja + `_ installiert werden. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + setuptools + setuptools sind das klassische Build-System, das sehr leistungsfähig ist, + aber mit steiler Lernkurve und hohem Konfigurationsaufwand. Ab Version + 61.0.0 unterstützen die setuptools auch :term:`pyproject.toml`-Dateien. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + * `Packaging and distributing projects + `_ + + shiv + Kommandozeilenprogramm zur Erstellung von Python-Zip-Apps, wie sie in + :pep:`441` beschrieben sind, aber zusätzlich mit allen Abhängigkeiten. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + Source Distribution + sdist + Ein Verteilungsformat (das normalerweise mithilfe von ``python setup.py + sdist`` generiert wird). + + Es stellt Metadaten und die wesentlichen Quelldateien bereit, die für die + Installation mit einem Tool wie :term:`Pip` oder zum Generieren von + :term:`Built Distributions ` benötigt werden. + + Spack + Flexibler Paketmanager, der mehrere Versionen, Konfigurationen, + Plattformen und Compiler unterstützt. Beliebig viele Versionen von + Paketen können auf demselben System koexistieren. Spack wurde für die + schnelle Erstellung von wissenschaftlichen Hochleistungsanwendungen auf + Clustern und Supercomputern entwickelt. + + .. seealso:: + * :doc:`Python4DataScience:productive/envs/spack/index` + * `Docs `__ + * `GitHub `__ + + trove-classifiers + trove-classifiers sind zum einen Klassifikatoren, die im :term:`Python + Package Index` verwendet werden, um Projekte systematisch zu beschreiben + und besser auffindbar zu machen. Zum anderen sind sie ein Paket, das eine + Liste gültiger und veralteter Klassifikatoren enthält, das zur + Überprüfung verwendet werden kann. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + twine + Kommandozeilenprogramm, das Programmdateien und Metadaten an eine + Web-API übergibt. Damit lassen sich Python-Pakete auf den :term:`Python + Package Index` hochladen. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + uv + Ein extrem schneller Python-Paket- und Projektmanager, geschrieben in + `Rust `_. + + uv vereinfacht Entwicklung und Deployment von Python-Projekten erheblich: + + * :ref:`Installation ` + * :ref:`Pakete erstellen ` und auf :doc:`PyPI + <../packs/publish>` oder :doc:`GitLab <../packs/gitlab>` + veröffentlichen + * :doc:`Entwickeln von Anwendungen <../packs/apps>` + * Testen von Bibliotheken mit verschiedenen :ref:`Python-Versionen + ` und :ref:`tox_uv` + * :ref:`Reproduzieren ` und :ref:`aktualisieren + ` der Python-Umgebung, + :abbr:`ggf. (gegebenenfalls)` auch mit einem + :doc:`Python4DataScience:productive/envs/uv/dependency-bot` + * :doc:`Python4DataScience:productive/envs/uv/cicd` + * :doc:`Python4DataScience:productive/envs/uv/docker` + * Schwachstellen überprüfen mit :ref:`uv-secure ` + + .. seealso:: + * `Docs `__ + * `GitHub `__ + * `PyPI `__ + + venv + Paket, das ab Python ≥ 3.3 in der Python-Standardbibliothek ist und zur + Erstellung :term:`virtueller Umgebungen ` gedacht + ist. + + .. seealso:: + * :doc:`Docs ` + * `GitHub `__ + + virtualenv + Werkzeug, das die Befehlszeilen-Umgebungsvariable ``path`` verwendet, um + isolierte :term:`virtuelle Python-Umgebungen ` zu + erstellen, ähnlich wie :term:`venv`. Es bietet jedoch zusätzliche + Funktionalität für die Konfiguration, Wartung, Duplizierung und + Fehlerbehebung. + + Ab Version 20.22.0 unterstützt virtualenv nicht mehr die Python-Versionen + 2.7, 3.5 und 3.6. + + Virtuelle Umgebung + Eine isolierte Python-Umgebung, die die Installation von Paketen für eine + bestimmte Anwendung ermöglicht, anstatt sie systemweit zu installieren. + + .. seealso:: + * :ref:`venv` + * `Creating Virtual Environments + `_ + + Warehouse + Die aktuelle Codebasis, die den :term:`Python Package Index` + (:term:`PyPI`) antreibt. Sie wird auf :term:`pypi.org` gehostet. + + .. seealso:: + * `Docs `__ + * `GitHub `__ + + wheel + Distributionsformat, das mit :pep:`427` eingeführt wurde. Es soll das + :term:`Egg`-Format ersetzen und wird von aktuellen + :term:`pip`-Installationen unterstützt. + + C-Erweiterungen können als plattformspezifische wheels für Windows, macOS + und Linux auf dem :term:`PyPI` bereitgestellt werden. Dies hat für euch + den Vorteil, dass ihr bei der Installation des Pakets dieses nicht + kompilieren müsst. + + .. seealso:: + * `Home `__ + * `Docs `__ + * :pep:`427` + * `GitHub `__ + * `PyPI `__ + + .. seealso:: + * :ref:`wheels` + + whey + Einfacher Python-:term:`wheel`-Builder mit Automatisierungsoptionen für + :term:`trove-classifiers`. + + .. _end-packaging: + + .. _start-test-procedures: + + Statische Testverfahren + werden verwendet um den Quellcode zu überprüfen, wobei dieser jedoch + nicht ausgeführt wird. Sie unterteilen sich in + + * :ref:`Reviews ` und + * `Statische Code-Analyse + `_ + + Es gibt diverse Python-Pakete, die euch bei der statischen Code-Analyse + unterstützen können, :abbr:`u.a. (unter anderem)` + :doc:`Python4DataScience:productive/qa/flake8`, + :doc:`Python4DataScience:productive/qa/pysa` und + :doc:`Python4DataScience:productive/qa/wily`. + + Dynamische Testverfahren + dienen dem Auffinden von Fehlern beim Ausführen des Quellcodes. Dabei + wird zwischen :term:`Whitebox- ` und :term:`Blackbox-Tests + ` unterschieden. + + .. _end-test-procedures: + + .. _start-test: + + Whitebox-Test + wird unter Kenntnis des Quellcodes und der Software-Struktur entwickelt. + + In Python stehen euch verschiedene Module zur Verfügung: + + :doc:`/test/unittest` + unterstützt euch bei der Automatisierung von Tests. + :doc:`/test/mock` + erlaubt euch das Erstellen und Verwenden von Mock-Objekten. + :doc:`../document/doctest` + ermöglicht das Testen von in Python :term:`Docstrings ` + geschriebenen Tests. + :doc:`/test/tox` + ermöglicht das Testen in verschiedenen Umgebungen. + + Blackbox-Test + wird ohne Kenntnis des Quellcodes entwickelt. Neben :doc:`/test/unittest` + kann in Python auch :doc:`/test/hypothesis` für solche Tests verwendet + werden. + + ``assert`` + Ein Schlüsselwort, das die Codeausführung anhält, wenn sein Argument + falsch ist. + + Continuous Integration + CI + Kontinuierliche Integration + Automatisches Überprüfen des Erstellungs- und Testprozesses auf + verschiedenen Plattformen. + + Dummy + Objekt, das herumgereicht, aber nie wirklich benutzt wird. Normalerweise + werden Dummies nur zum Füllen von Parameter-Listen verwendet. + + ``except`` + Schlüsselwort, das verwendet wird, um eine :term:`Exception` abzufangen + und sorgfältig zu behandeln. + + Fake + Objekt, das eine tatsächlich funktionierende Implementierung hat, in der + Regel aber eine Abkürzung nimmt, die es nicht für die Produktion geeignet + macht. + + Integrationstest + Tests, die überprüfen, ob die verschiedenen Teile der Software wie + erwartet zusammenarbeiten. + + Mock + Objekte, die mit :term:`Exception` programmiert sind, die eine + Spezifikation der Aufrufe bilden, die ihr voraussichtlich erhalten + werdet. + + .. seealso:: + * `Mock-Objekt `_ + + pytest + Ein Python-Paket mit Test-Utilities. + + .. seealso:: + * :doc:`/test/pytest/index` + + Regressionstest + Tests zum Schutz vor neuen Fehlern oder Regressionen, die durch neue + Software und Updates auftreten können. + + Stubs + liefern vorgefertigte Antworten auf Aufrufe, die während des Tests + getätigt werden, und reagieren in der Regel überhaupt nicht auf + irgendetwas, das nicht für den Test programmiert wurde. + + Test-driven development + TDD + Testgetriebene Entwicklung + Eine Software-Entwicklungsstrategie, bei der die Tests vor dem Code + geschrieben werden. + + ``try`` + Ein Schlüsselwort, das einen Teil des Codes schützt, der eine + :term:`Exception` auslösen kann. + + .. _end-test: diff --git a/docs/appendix/index.rst b/docs/appendix/index.rst index a1764f27..30b254d3 100644 --- a/docs/appendix/index.rst +++ b/docs/appendix/index.rst @@ -4,6 +4,5 @@ Anhang .. toctree:: :titlesonly: + glossary checks - regex - encodings diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 00000000..958dff97 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,5 @@ +Was ist neu? +============ + +.. include:: ../CHANGELOG.rst + :start-after: unreleased diff --git a/docs/conf.py b/docs/conf.py index 904afc35..6764a672 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,9 +40,12 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named "sphinx.ext.*") or your custom ones. extensions = [ + "nbsphinx", + "IPython.sphinxext.ipython_console_highlighting", "pygments_pytest", "sphinx.ext.autodoc", "sphinx.ext.viewcode", + "sphinx.ext.graphviz", "sphinx.ext.intersphinx", "sphinxcontrib.plantuml", "sphinxcontrib.cairosvgconverter", @@ -51,7 +54,7 @@ "sphinx_inline_tabs", ] -plantuml = "/usr/bin/plantuml" +plantuml = "plantuml" plantuml_output_format = "svg" intersphinx_mapping = { @@ -69,9 +72,13 @@ r".*/_sources/.*/*.txt", # 403 Client Error r"https://anaconda.org/", + r"https://distrowatch.com/", r"https://linux.die.net/", + r"https://sourceforge.net/", ] +linkcheck_anchors_ignore = ["readme"] + # All HTTP redirections from the source URI to the canonical URI will be treated# as "working". linkcheck_allowed_redirects = { r"https://devpi\.net/docs/$": r"https://devpi\.net/docs/[-a-z]+/(?:latest|stable|master)/$", @@ -174,6 +181,10 @@ html_logo = "_static/images/logo/logo.png" html_favicon = "_static/images/logo/favicon.ico" +html_css_files = [ + "css/cusy.css", +] + # -- Options for HTMLHelp output --------------------------------------- @@ -234,3 +245,16 @@ "Miscellaneous", ), ] + + +# -- Custom documentation plugin --------------------------------------- +# https://www.sphinx-doc.org/en/master/development/tutorials/extending_syntax.html#the-setup-function + + +def setup(app: sphinx.application.Sphinx) -> None: + app.add_crossref_type( + "fixture", + "fixture", + objname="built-in fixture", + indextemplate="pair: %s; fixture", + ) diff --git a/docs/control-flow/boolean.rst b/docs/control-flow/boolean.rst new file mode 100644 index 00000000..fef3fafd --- /dev/null +++ b/docs/control-flow/boolean.rst @@ -0,0 +1,108 @@ +Boolesche Werte und Ausdrücke +============================= + +In Python gibt es mehrere Möglichkeiten, boolesche Werte auszudrücken; die +boolesche Konstante ``False``, ``0``, der Python-Typ :doc:`../types/none` und +leere Werte (:abbr:`z.B. (zum Beispiel)` die leere Liste ``[]`` oder die leere +Zeichenkette ``""``) werden alle als ``False`` betrachtet. Die boolesche +Konstante ``True`` und alles andere wird als ``True`` betrachtet. + +``<``, ``<=``, ``==``, ``>``, ``>=`` + vergleicht Werte: + + .. code-block:: pycon + + >>> x = 3 + >>> y = 3.0 + >>> z = [3, 4, 5] + >>> x == y + True + + Ihr solltet jedoch nie berechnete Fließkommazahlen miteinander vergleichen: + + .. code-block:: pycon + + >>> u = 0.6 * 7 + >>> v = 0.7 * 6 + >>> u == v + False + >>> u + 4.2 + >>> v + 4.199999999999999 + +``is``, ``is not`` + überprüft die Identität: + + .. code-block:: pycon + + >>> x is y + False + >>> x is not y + True + >>> id(x) + 4375911432 + >>> id(y) + 4367574480 + >>> id(z[0]) + 4375911432 + + Wenn ``x`` und ``z[0]`` die gleiche ID im Speicher haben, bedeutet das, dass + wir an zwei Stellen auf dasselbe Objekt verweisen. + + Der ``is``-Operator wird meist bei Werten verwendet, die nur einmal im + Speicher vorhanden sind, :abbr:`sog. (sogenannte)` :term:`Singleton-Objekte + `. So ist die Überprüfung auf :doc:`../types/none` die + häufigste Verwendung des ``is``-Operators. + + .. code-block:: pycon + + >>> x is None + False + >>> x is not None + True + + Auch der Python-Style-Guide in :pep:`8` empfiehlt, dass ihr auf Identität + mit :doc:`../types/none` und nicht auf Werte überprüfen solltet, also + niemals ``x == None``, sondern stattdessen immer ``x is None`` verwenden + solltet. + + +``in``, ``not in`` + überprüft die Zugehörigkeit: + + .. code-block:: pycon + + >>> x in z + True + + Alle eingebauten Sequenz- und Mengen-Typen unterstützen dies, ebenso wie + Dictionaries, bei denen ``in`` überprüft, ob das Dictionary diesen bestimmten + Schlüssel hat: + + .. code-block:: pycon + + >>> d = {"a": 1, "b": 2} + >>> "a" in d + True + >>> "c" in d + False + >>> 1 in d + False + + + +``and``, ``not``, ``or`` + sind logische Operatoren, mit denen wir die oben genannten Überprüfungen + verknüpfen können: + + .. code-block:: pycon + + >>> x is y and x is z[0] + False + >>> x is y or x is z[0] + True + >>> x is y and not x is z[0] + False + >>> x is z[0] and not x is y + True diff --git a/docs/control-flow/conditional.rst b/docs/control-flow/conditional.rst new file mode 100644 index 00000000..9a85eb94 --- /dev/null +++ b/docs/control-flow/conditional.rst @@ -0,0 +1,42 @@ +Bedingte Anweisungen +==================== + +Der Codeblock nach der ersten wahren Bedingung einer ``if``- oder +``elif``-Anweisung wird ausgeführt. Wenn keine der Bedingungen wahr ist, wird +der Codeblock nach dem ``else`` ausgeführt: + +.. code-block:: pycon + :linenos: + + >>> x = 1 + >>> if x < 1: + ... x = 2 + ... y = 3 + ... elif x > 1: + ... x = 4 + ... y = 5 + ... else: + ... x = 6 + ... y = 7 + ... + >>> x, y + (6, 7) + +Python verwendet Einrückungen, um Blöcke abzugrenzen. Es sind keine expliziten +Begrenzungszeichen wie Klammern oder geschweifte Klammern erforderlich. Jeder +Block besteht aus einer oder mehreren Anweisungen, die durch Zeilenumbrüche +getrennt sind. Alle diese Anweisungen müssen auf der gleichen Einrückungsebene +stehen. + +Zeile 5 + Die ``elif``-Anweisung sieht aus wie die ``if``-Anweisung und funktioniert + auch so, allerdings mit zwei wesentlichen Unterschieden: + + * ``elif`` ist nur nach einer ``if``-Anweisung oder einer anderen + ``elif``-Anweisung zulässig + * Ihr könnt so viele ``elif``-Anweisungen verwenden, wie ihr benötigt + +Zeile 8 + Die optionale ``else``-Klausel bezeichnet einen Codeblock, der nur dann + ausgeführt wird, wenn die anderen bedingten Blöcke, ``if`` und ``elif``, + alle unzutreffend sind. diff --git a/docs/control-flows/emptyFile.py b/docs/control-flow/emptyFile.py similarity index 100% rename from docs/control-flows/emptyFile.py rename to docs/control-flow/emptyFile.py diff --git a/docs/control-flows/exceptions.py b/docs/control-flow/exceptions.py similarity index 100% rename from docs/control-flows/exceptions.py rename to docs/control-flow/exceptions.py diff --git a/docs/control-flows/exceptions.rst b/docs/control-flow/exceptions.rst similarity index 57% rename from docs/control-flows/exceptions.rst rename to docs/control-flow/exceptions.rst index 167c339c..df3f5307 100644 --- a/docs/control-flows/exceptions.rst +++ b/docs/control-flow/exceptions.rst @@ -1,44 +1,40 @@ Exceptions ========== -In diesem Abschnitt geht es um Ausnahmen, :abbr:`d.h. (das heißt)` um -Sprachfunktionen, die speziell ungewöhnliche Umstände während der Ausführung -eines Programms behandeln. Die häufigste Ausnahme ist die Behandlung von -Fehlern, aber sie können auch für viele andere Zwecke effektiv eingesetzt -werden. Python bietet einen umfassenden Satz von Ausnahmen, und ihr könnt neue -Ausnahmen für eure eigenen Zwecke definieren. - -Der gesamte Exception-Mechanismus in Python ist :doc:`objektorientiert -`: Eine Exception ist ein Objekt, das automatisch von -Python-Funktionen mit einer ``raise``-Anweisung erzeugt wird. Diese -``raise``-Anweisung veranlasst die Ausführung des Python-Programms auf eine -andere Art und Weise, als üblicherweise vorgesehen: Die aktuelle Aufrufkette -wird nach einem Handler durchsucht, der die erzeugte Ausnahme behandeln kann. -Wenn ein solcher Handler gefunden wird, wird er aufgerufen und kann auf das -Ausnahmeobjekt zugreifen, um weitere Informationen zu erhalten. Wird kein -geeigneter Exception-Handler gefunden, bricht das Programm mit einer -Fehlermeldung ab. +In diesem Abschnitt geht es um :term:`Ausnahmen ` (englisch +*exceptions*), :abbr:`d.h. (das heißt)` um Sprachfunktionen, die speziell +ungewöhnliche Umstände während der Ausführung eines Programms behandeln. Die +häufigste Ausnahme ist die Behandlung von Fehlern, aber sie können auch für +viele andere Zwecke effektiv eingesetzt werden. Python bietet einen umfassenden +Satz von Ausnahmen, und ihr könnt neue Ausnahmen für eure eigenen Zwecke +definieren. + +Eine Exception ist ein Objekt, das automatisch von Python-Funktionen mit einer +:ref:`raise `-Anweisung erzeugt wird, :abbr:`z.B. (zum Beispiel)` +mit: -.. note:: - Die Art und Weise, wie Python Fehlersituationen im Allgemeinen behandelt, - unterscheidet sich von manch anderen Sprachen, :abbr:`z.B. (zum Beispiel)` - Java. Diese Sprachen prüfen mögliche Fehler so weit wie möglich, bevor sie - auftreten, da die Behandlung von Exceptions nach ihrem Auftreten kostspielig - ist. Dies wird manchmal als :abbr:`LBYL (Look before you leap, Erst schauen, - dann springen)`-Ansatz bezeichnet. +.. literalinclude:: exceptions.py + :language: python + :linenos: + :lines: 11-12 + :lineno-start: 11 - Bei Python hingegen verlässt man sich eher auf Exceptions, um Fehler zu - behandeln, nachdem sie aufgetreten sind. Obwohl dieses Vertrauen riskant - erscheinen mag, ist der Code weniger schwerfällig und leichter zu lesen, wenn - Exceptions richtig eingesetzt werden, und Fehler werden nur dann behandelt, - wenn sie auftreten. Diese pythonische Herangehensweise zur Behandlung von - Fehlern wird oft als :abbr:`EAFP (easier to ask forgiveness than permission, - engl.: leichter um Vergebung zu bitten als um Erlaubnis)` beschrieben. +Die :ref:`raise `-Anweisung veranlasst die Ausführung des +Python-Programms auf eine andere Art und Weise, als üblicherweise vorgesehen: +Die aktuelle Aufrufkette wird nach einem Handler durchsucht, der die erzeugte +Ausnahme behandeln kann. Wenn ein solcher Handler gefunden wird, wird er +aufgerufen und kann auf das Ausnahmeobjekt zugreifen, um weitere Informationen +zu erhalten, wie in unserem :class:`EmptyFileError`-Beispiel: -Es ist möglich, verschiedene Arten von Ausnahmen zu erzeugen, um die -tatsächliche Ursache des gemeldeten Fehlers oder außergewöhnlichen Umstandes zu -reflektieren. Eine Übersicht über die Klassenhierarchie eingebauter Exceptions -erhaltet ihr unter `Exception hierarchy +.. literalinclude:: exceptions.py + :language: python + :linenos: + :lines: 1-2 + +Dies definiert euren eigenen Ausnahmetyp, der vom Basistyp ``Exception`` erbt. + +Eine Übersicht über die Klassenhierarchie eingebauter Exceptions erhaltet ihr +unter `Exception hierarchy `_ in der Python-Dokumentation. Jeder Ausnahmetyp ist eine Python-Klasse, die von ihrem übergeordneten Exception-Typ erbt. So ist :abbr:`z.B. (zum Beispiel)` ein @@ -48,31 +44,38 @@ meisten Ausnahmen erben von ``Exception``, und es wird dringend empfohlen, dass alle benutzerdefinierten Ausnahmen auch die Unterklasse von ``Exception`` und nicht von ``BaseException`` bilden: +Es ist möglich, verschiedene Arten von Ausnahmen zu erzeugen, um die +tatsächliche Ursache des gemeldeten Fehlers oder außergewöhnlichen Umstandes zu +reflektieren. + .. literalinclude:: exceptions.py :language: python :linenos: - :lines: 1-2 + :lines: 8-16 + :lineno-start: 8 -Dies definiert ihr euren eigenen Ausnahmetyp, der vom Basistyp ``Exception`` -erbt. +Wenn während der Ausführung von :func:`open` im ``try``-Block ein ``OSError`` +oder ein ``EmptyFileError`` auftritt, wird der jeweils zugehörige +``except``-Block ausgeführt. + +Wird kein geeigneter Exception-Handler gefunden, bricht das Programm mit einer +Fehlermeldung ab. Daher ergänzen wir unsere ``try``-``except``-Anweisungen um +``else`` und ``finally``: .. literalinclude:: exceptions.py :language: python :linenos: - :lines: 5 - :lineno-start: 5 - -Eine Liste unterschiedlicher Datei-Arten wird definiert. + :lines: 17-21 + :lineno-start: 17 -Schließlich werden Ausnahmen oder Fehler mit Hilfe der zusammengesetzten -Anweisung ``try``-``except``-``else``-``finally`` abgefangen und behandelt. -Jede Ausnahme, die nicht abgefangen wird, führt zur Beendigung des Programms. +Nun können wir noch eine Liste unterschiedlicher Datei-Arten definieren, sodass +unser vollständiger Code folgendermaßen aussieht: .. literalinclude:: exceptions.py :language: python :linenos: - :lines: 7- - :lineno-start: 7 + :lines: 1- + :lineno-start: 1 Zeile 7 Wenn während der Ausführung der Anweisungen im ``try``-Block ein @@ -86,14 +89,24 @@ Zeile 17 Die ``else``-Klausel ist optional; sie wird ausgeführt, wenn im ``try``-Block keine Ausnahme auftritt. - .. note:: - In diesem Beispiel hätte stattdessen auch ``continue``-Anweisungen in - den ``except``-Blöcken verwendet werden können. - Zeile 19 - Die ``finally``-Klausel ist optional; sie wird am Ende des Blocks + Die ``finally``-Klausel ist ebenfalls optional und wird am Ende des Blocks ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wurde oder nicht. +.. note:: + Die Art und Weise, wie Python Fehlersituationen im Allgemeinen behandelt, + unterscheidet sich von manch anderen Sprachen, :abbr:`z.B. (zum Beispiel)` + Java. Diese Sprachen prüfen mögliche Fehler so weit wie möglich, bevor sie + auftreten, da die Behandlung von Exceptions nach ihrem Auftreten kostspielig + ist. Dies wird manchmal als :term:`LBYL`-Ansatz bezeichnet. + + Bei Python hingegen verlässt man sich eher auf Exceptions, um Fehler zu + behandeln, nachdem sie aufgetreten sind. Obwohl dieses Vertrauen riskant + erscheinen mag, ist der Code weniger schwerfällig und leichter zu lesen, wenn + Exceptions richtig eingesetzt werden, und Fehler werden nur dann behandelt, + wenn sie auftreten. Diese pythonische Herangehensweise zur Behandlung von + Fehlern wird oft als :term:`EAFP` beschrieben. + Checks ------ @@ -110,9 +123,8 @@ Checks * Schreibt eine benutzerdefinierte Ausnahme :class:`Outliers`, die eine :class:`Exception` auslöst, wenn die Variable ``x`` größer oder kleiner als - ``3`` ist? + ``3`` ist. * Handelt es sich bei der Überprüfung, ob ein Objekt eine Liste ist (:ref:`Check: list `) um eine Programmierung im Stil von - :abbr:`LBYL (look before you leap)` oder :abbr:`EAFP (easier to ask - forgiveness than permission)`? + :term:`LBYL` oder :term:`EAFP`? diff --git a/docs/control-flow/index.rst b/docs/control-flow/index.rst new file mode 100644 index 00000000..2aaa21d8 --- /dev/null +++ b/docs/control-flow/index.rst @@ -0,0 +1,34 @@ +Kontrollflüsse +============== + +Python verfügt über eine ganze Reihe von :term:`Kontrollflüssen +` um die Code-Ausführung und den Programmablauf, einschließlich +gängiger Verzweigungen und Schleifen, zu steuern: + +:doc:`boolean` + überprüfen Werte und Identität und ermöglichen Verknüpfungen zwischen + beiden. +:doc:`conditional` + führen den Codeblock nach der ersten wahren Bedingung einer ``if``- oder + ``elif``-Anweisung aus; wenn keine der Bedingungen wahr ist, wird der + Codeblock nach dem ``else`` ausgeführt. +:doc:`loops` + Während ``while``-Schleifen so lange ausgeführt werden, wie die Bedingung + wahr ist, iterieren ``for``-Schleifen über + :doc:`../types/sequences-sets/lists`, :doc:`../types/sequences-sets/tuples` + und :doc:`../types/sequences-sets/sets`. +:doc:`exceptions` + behandeln meist Fehler, die während der Ausführung von Programmen passieren. +:doc:`with` + regelt :abbr:`u.a. (unter anderem)` den Zugriff auf Dateien, das Locking von + Threads und die Unterdrückung von :doc:`exceptions`. + +.. toctree:: + :titlesonly: + :hidden: + + boolean + conditional + loops + exceptions + with diff --git a/docs/control-flows/loops.rst b/docs/control-flow/loops.rst similarity index 88% rename from docs/control-flows/loops.rst rename to docs/control-flow/loops.rst index 579dbcb5..b16437eb 100644 --- a/docs/control-flows/loops.rst +++ b/docs/control-flow/loops.rst @@ -60,10 +60,11 @@ Die ``for``-Schleife ist einfach, aber mächtig, weil sie über einen beliebigen iterierbaren Typ, wie eine Liste oder ein Tupel, iterieren kann. Anders als in vielen anderen Sprachen iteriert die ``for``-Schleife in Python über jedes Element in einer Sequenz (:abbr:`z.B. (zum Beispiel)` eine :doc:`Liste -<../types/lists>` oder ein :doc:`../types/tuples`), was sie eher zu einer -``foreach``-Schleife macht. Die folgende Schleife verwendet den `Modulo -`_-Operator ``%`` als -Bedingung für as erste Vorkommen einer ganzen Zahl, die durch ``5`` teilbar ist: +<../types/sequences-sets/lists>` oder ein :doc:`../types/sequences-sets/tuples`), +was sie eher zu einer *for each*-Schleife macht. Die folgende Schleife verwendet +den `Modulo `_-Operator +``%`` als Bedingung für das erste Vorkommen einer ganzen Zahl, die durch ``5`` +teilbar ist: .. code-block:: pycon @@ -138,9 +139,10 @@ Jede List Comprehension in Python enthält drei Elemente: ist das Objekt oder der Wert in einem :samp:`{ITERABLE}`. Im obigen Beispiel ist der Wert ``i``. :samp:`{ITERABLE}` - ist eine :doc:`Liste <../types/lists>`, ein :doc:`Set <../types/sets>`, ein - Generator oder ein anderes Objekt, das seine Elemente einzeln zurückgeben - kann. Im obigen Beispiel ist die Iterable ``range(8)``. + ist eine :doc:`Liste <../types/sequences-sets/lists>`, ein :doc:`Set + <../types/sequences-sets/sets>`, ein Generator oder ein anderes Objekt, das + seine Elemente einzeln zurückgeben kann. Im obigen Beispiel ist die Iterable + ``range(8)``. Ihr könnt mit List Comprehensions auch optional Bedingungen verwenden, die üblicherweise am Ende des Ausdruck angehängt werden: diff --git a/docs/control-flows/myFile1.py b/docs/control-flow/myFile1.py similarity index 100% rename from docs/control-flows/myFile1.py rename to docs/control-flow/myFile1.py diff --git a/docs/control-flows/myFile2.py b/docs/control-flow/myFile2.py similarity index 100% rename from docs/control-flows/myFile2.py rename to docs/control-flow/myFile2.py diff --git a/docs/control-flows/with.py b/docs/control-flow/with.py similarity index 100% rename from docs/control-flows/with.py rename to docs/control-flow/with.py diff --git a/docs/control-flow/with.rst b/docs/control-flow/with.rst new file mode 100644 index 00000000..8ef42e90 --- /dev/null +++ b/docs/control-flow/with.rst @@ -0,0 +1,83 @@ +Kontextmanagement mit ``with`` +============================== + +Eine rationellere Art, das Muster ``try``-``except``-``finally`` zu kapseln, ist +die Verwendung des Schlüsselworts ``with`` und eines Kontextmanagers. Python +definiert Kontextmanager für Dinge wie den Zugriff auf :doc:`/save-data/files` +und eigene Kontextmanager. Ein Vorteil von Kontextmanagern ist, dass sie +Bereinigungsaktionen definieren können, die immer ausgeführt werden, unabhängig +davon, ob eine Ausnahme auftritt oder nicht. + +.. seealso:: + :doc:`python3:library/contextlib` + +Öffnen und Schließen von Dateien +-------------------------------- + +Die folgende Auflistung zeigt das Öffnen und Lesen einer Datei unter Verwendung +von ``with`` und einem Kontextmanager. + +.. literalinclude:: with.py + :linenos: + +Hier wird ein Kontextmanager eingerichtet, der die Funktion ``open`` und den +darauf folgenden Block umschließt. Die vordefinierte Aufräumaktion des +Kontextmanagers schließt die Datei, auch wenn eine Ausnahme auftritt. Solange +der Ausdruck in der ersten Zeile ausgeführt wird, ohne eine Ausnahme +auszulösen, wird die Datei immer geschlossen. Dieser Code ist äquivalent zu +diesem Code: + +.. literalinclude:: with_alt.py + :linenos: + +.. seealso:: + * :doc:`../save-data/files` + +Locking +------- + +:class:`threading.Lock` kann mit ``try``-``finally`` verwendet werden: + +.. code-block:: Python + + lock = threading.Lock() + + try: + print("a Job") + print("another Job") + finally: + lock.release() + +Eleganter ist jedoch die Verwendung mit dem Kontextmanager: + +.. code-block:: Python + + with lock: + print("a Job") + print("another Job") + +.. seealso:: + `Sorgfältiges Threading mit Locks + `_ + +Exceptions unterdrücken +----------------------- + +Kontextmanager können auch verwendet werden um die Ausgabe von :doc:`exceptions` +zu unterdrücken und die Ausführung fortzusetzen. + +.. code-block:: Python + + try: + os.remove("somefile.tmp") + except FileNotFoundError: + pass + +Dies kann eleganter geschrieben werden mit: + +.. code-block:: Python + + from contextlib import suppress + + with suppress(FileNotFoundError): + os.remove("somefile.tmp") diff --git a/docs/control-flows/with_alt.py b/docs/control-flow/with_alt.py similarity index 84% rename from docs/control-flows/with_alt.py rename to docs/control-flow/with_alt.py index f289bd72..c14642bf 100644 --- a/docs/control-flows/with_alt.py +++ b/docs/control-flow/with_alt.py @@ -1,4 +1,4 @@ -filename = "myfile1.py" +filename = "myFile1.py" try: f = open(filename, "r") for line in f: diff --git a/docs/control-flows/boolean.rst b/docs/control-flows/boolean.rst deleted file mode 100644 index dfd81b69..00000000 --- a/docs/control-flows/boolean.rst +++ /dev/null @@ -1,66 +0,0 @@ -Boolesche Werte und Ausdrücke -============================= - -In Python gibt es mehrere Möglichkeiten, boolesche Werte auszudrücken; die -boolesche Konstante ``False``, ``0``, der Python-Typ :doc:`../types/none` und -leere Werte (:abbr:`z.B. (zum Beispiel)` die leere Liste ``[]`` oder die leere -Zeichenkette ``""``) werden alle als ``False`` betrachtet. Die boolesche -Konstante ``True`` und alles andere wird als ``True`` betrachtet. - -``<``, ``<=``, ``==``, ``>``, ``>=`` - vergleicht Werte. -``is``, ``is not``, ``in``, ``not in`` - überprüft die Identität. -``and``, ``not``, ``or`` - sind logischen Operatoren, mit denen die oben genannten Überprüfungen - verknüpft werden können. - -.. code-block:: pycon - - >>> x = 3 - >>> y = 3.0 - >>> z = [3, 4, 5] - >>> x == y - True - >>> x is y - False - >>> x is not y - True - >>> x in z - True - >>> id(x) - 4375911432 - >>> id(y) - 4367574480 - >>> id(z[0]) - 4375911432 - -Wenn ``x`` und ``z[0]`` die gleiche ID im Speicher haben, bedeutet das, dass wir -an zwei Stellen auf dasselbe Objekt verweisen. - -Am häufigsten werden ``is`` und ``is not`` in Verbindung mit -:doc:`../types/none` verwendet: - -.. code-block:: pycon - - >>> x is None - False - >>> x is not None - True - -Der Python-Style-Guide in :pep:`8` besagt, dass ihr Identität verwenden solltet, -um mit :doc:`../types/none` zu vergleichen. Ihr solltet also niemals ``x == -None`` verwenden, sondern stattdessen ``x is None`` eingeben. - -Ihr solltet jedoch nie berechnete Fließkommazahlen miteinander vergleichen: - -.. code-block:: pycon - - >>> u = 0.6 * 7 - >>> v = 0.7 * 6 - >>> u == v - False - >>> u - 4.2 - >>> v - 4.199999999999999 diff --git a/docs/control-flows/if-elif-else.rst b/docs/control-flows/if-elif-else.rst deleted file mode 100644 index b5e600d9..00000000 --- a/docs/control-flows/if-elif-else.rst +++ /dev/null @@ -1,33 +0,0 @@ -``if``-``elif``-``else``-Anweisung -================================== - -Der Codeblock nach der ersten wahren Bedingung einer ``if``- oder -``elif``-Anweisung wird ausgeführt. Wenn keine der Bedingungen wahr ist, wird -der Codeblock nach dem ``else`` ausgeführt: - -.. code-block:: pycon - :linenos: - - >>> x = 1 - >>> if x < 1: - ... x = 2 - ... y = 3 - ... elif x > 1: - ... x = 4 - ... y = 5 - ... else: - ... x = 6 - ... y = 7 - ... - >>> print(x, y) - 6 7 - -Zeilen 5 und 8 - Die ``elif``- und ``else``-Klauseln sind optional, und es kann eine - beliebige Anzahl von ``elif``-Klauseln geben. -Zeilen 3, 4, 6, 7, 9 und 10 - Python verwendet Einrückungen, um Blöcke abzugrenzen. Es sind keine - expliziten Begrenzungszeichen wie Klammern oder geschweifte Klammern - erforderlich. Jeder Block besteht aus einer oder mehreren Anweisungen, die - durch Zeilenumbrüche getrennt sind. Alle diese Anweisungen müssen auf der - gleichen Einrückungsebene stehen. diff --git a/docs/control-flows/index.rst b/docs/control-flows/index.rst deleted file mode 100644 index 078e246d..00000000 --- a/docs/control-flows/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -Kontrollflüsse -============== - -Python verfügt über eine ganze Reihe von Strukturen zur Kontrolle der -Code-Ausführung und des Programmablaufs, einschließlich gängiger Verzweigungen -und Schleifen. - -.. toctree:: - :titlesonly: - :hidden: - - boolean - if-elif-else - loops - exceptions - with diff --git a/docs/control-flows/with.rst b/docs/control-flows/with.rst deleted file mode 100644 index 4c77e7dc..00000000 --- a/docs/control-flows/with.rst +++ /dev/null @@ -1,25 +0,0 @@ -Kontextmanagement mit ``with`` -============================== - -Eine rationellere Art, das Muster ``try-except-finally`` zu kapseln, ist die -Verwendung des Schlüsselworts ``with`` und eines Kontextmanagers. Python -definiert Kontextmanager für Dinge wie den Zugriff auf :doc:`/types/files` und -eigene Kontextmanager. Ein Vorteil von Kontextmanagern ist, dass sie -standardmäßige Bereinigungsaktionen definieren können, die immer ausgeführt -werden, unabhängig davon, ob eine Ausnahme auftritt oder nicht. - -Die folgende Auflistung zeigt das Öffnen und Lesen einer Datei unter Verwendung -von ``with`` und einem Kontextmanager. - -.. literalinclude:: with.py - :linenos: - -Hier wird ein Kontextmanager eingerichtet, der die Funktion ``open`` und den -darauf folgenden Block umschließt. Die vordefinierte Aufräumaktion des -Kontextmanagers schließt die Datei, auch wenn eine Ausnahme auftritt. Solange -der Ausdruck in der ersten Zeile ausgeführt wird, ohne eine Ausnahme -auszulösen, wird die Datei immer geschlossen. Dieser Code ist äquivalent zu -diesem Code: - -.. literalinclude:: with_alt.py - :linenos: diff --git a/docs/test/arithmetic.py b/docs/document/arithmetic.py similarity index 82% rename from docs/test/arithmetic.py rename to docs/document/arithmetic.py index fa06fe88..91cd0587 100644 --- a/docs/test/arithmetic.py +++ b/docs/document/arithmetic.py @@ -1,6 +1,6 @@ def add(x, y): """ - >>> add(7,6) + >>> add(7, 6) 13 """ return x + y @@ -9,8 +9,8 @@ def add(x, y): def divide(x, y): """Divides the first parameter by the second >>> x, y, z = 7, -6.0, 0 - >>> divide(x, y) - -1.1666666666666667 + >>> round(divide(x, y), 8) + -1.16666667 >>> divide(x, z) Traceback (most recent call last): File "", line 1, in @@ -21,7 +21,7 @@ def divide(x, y): def multiply(x, y): """ - >>> multiply(7,6) + >>> multiply(7, 6) 42 """ return x * y @@ -29,7 +29,7 @@ def multiply(x, y): def subtract(x, y): """ - >>> subtract(7,6) + >>> subtract(7, 6) 1 """ return x - y diff --git a/docs/document/badges.rst b/docs/document/badges.rst new file mode 100644 index 00000000..0c42114b --- /dev/null +++ b/docs/document/badges.rst @@ -0,0 +1,35 @@ +Badges +====== + +Einige dieser Informationen und mehr können als Badges abgerufen werden. Sie +sind hilfreich, um einen schnellen Überblick über ein Produkt zu erhalten. Für +das `cookiecutter-namespace-template +`_ sind dies +:abbr:`z.B. (zum Beispiel)`: + +|Downloads| |Versions| |Contributors| |License| |Docs| + +.. |Downloads| image:: + https://static.pepy.tech/badge/cookiecutter-namespace-template + :target: https://pepy.tech/projects/cookiecutter-namespace-template +.. |Versions| image:: + https://img.shields.io/pypi/pyversions/cookiecutter-namespace-template/0.2.9 + :target: https://pypi.org/project/cookiecutter-namespace-template/0.2.9/ +.. |Contributors| image:: + https://img.shields.io/github/contributors/veit/cookiecutter-namespace-template.svg + :target: https://github.com/veit/cookiecutter-namespace-template/graphs/contributors +.. |License| image:: + https://img.shields.io/github/license/veit/cookiecutter-namespace-template.svg + :target: https://github.com/veit/cookiecutter-namespace-template/blob/main/LICENSE +.. |Docs| image:: + https://readthedocs.org/projects/cookiecutter-namespace-template/badge/?version=latest + :target: https://cookiecutter-namespace-template.readthedocs.io/en/latest/ + +Ihr könnt auch eigene Badges erstellen, :abbr:`z.B. (zum Beispiel)`: + +.. image:: https://img.shields.io/badge/dynamic/json?label=Mastodon&query=totalItems&url=https%3A%2F%2Fmastodon.social%2F@JupyterTutorial%2Ffollowers.json&logo=mastodon + :alt: Mastodon + :target: https://mastodon.social/@JupyterTutorial + +.. seealso:: + * `shields.io `_ diff --git a/docs/document/docstrings.rst b/docs/document/docstrings.rst deleted file mode 100644 index 4ba83bc6..00000000 --- a/docs/document/docstrings.rst +++ /dev/null @@ -1,179 +0,0 @@ -Docstrings -========== - -Mit der Sphinx-Erweiterung `sphinx.ext.autodoc -`_ können -Docstrings auch in die Dokumentation aufgenommen werden. Die folgenden drei -Direktiven können angegeben werden: - -.. rst:directive:: automodule - autoclass - autoefunction - -Diese dokumentieren ein Modul, eine Klasse oder eine Funktion unter Verwendung -des jeweiligen Docstrings. - -Installation ------------- - -``sphinx.ext.autodoc`` ist normalerweise bereits in der -Sphinx-Konfigurationsdatei ``docs/conf.py`` angegeben: - -.. code-block:: python - - extensions = ["sphinx.ext.autodoc", ...] - -Wenn euer Paket und die zugehörige Dokumentation Teil desselben Repository -sind, werden sie immer dieselbe relative Position im Dateisystem haben. In -diesem Fall könnt ihr einfach die Sphinx-Konfiguration für ``sys.path`` -bearbeiten, um den relativen Pfad zum Paket anzugeben, also: - -.. code-block:: python - - sys.path.insert(0, os.path.abspath("..")) - import MODULE - -Wenn ihr eure Sphinx-Dokumentation in einer virtuellen Umgebung installiert -habt, könnt ihr euer Paket auch dort mitinstallieren, :abbr:`z.B. (zum -Beispiel)` indem ihr es in eure :file:`requirements.txt`-Datei eintragt. - -Beispiele ---------- - -Hier findet ihr einige Beispiele aus der Dokumentation des -Python-:py:mod:`string`-Moduls: - -.. literalinclude:: autodoc-examples.rst - :language: rest - :lines: 3- - -Die Ausgabe ist :doc:`autodoc-examples`. - -.. note:: - Ihr solltet diese Richtlinien befolgen, wenn ihr Docstrings schreibt: - - * :pep:`8#comments` - * :pep:`257#specification` - -``sphinx-autodoc-typehints`` ----------------------------- - -Mit :pep:`484` wurde eine Standardmethode für den Ausdruck von Typen in -Python-Code eingeführt. Damit können Typen auch in Docstrings unterschiedlich -ausgedrückt werden. Die Variante mit Typen nach PEP 484 hat den Vorteil, dass -Typtester und IDEs zur statischen Codeanalyse eingesetzt werden können. - -Python 3 Type-Annotations: - - .. code-block:: python - - def func(arg1: int, arg2: str) -> bool: - """Summary line. - - Extended description of function. - - Args: - arg1: Description of arg1 - arg2: Description of arg2 - - Returns: - Description of return value - - """ - return True - -Typen in Docstrings: - - .. code-block:: python - - def func(arg1, arg2): - """Summary line. - - Extended description of function. - - Args: - arg1 (int): Description of arg1 - arg2 (str): Description of arg2 - - Returns: - bool: Description of return value - - """ - return True - -.. note:: - :pep:`484#suggested-syntax-for-python-2-7-and-straddling-code` are currently - not supported by Sphinx and do not appear in the generated documentation. - -.. _napoleon: - -``sphinx.ext.napoleon`` ------------------------ - -Die Sphinx-Erweiterung `sphinx.ext.napoleon -`_ ermöglicht euch, verschiedene -Abschnitte in Docstrings zu definieren, einschließlich: - -* ``Attributes`` -* ``Example`` -* ``Keyword Arguments`` -* ``Methods`` -* ``Parameters`` -* ``Warning`` -* ``Yield`` - -Es gibt zwei Arten von docstrings in ``sphinx.ext.napoleon``: - -* `Google - `_ -* `NumPy - `_ - -Der Hauptunterschied besteht darin, dass Google Einrückungen verwendet und NumPy -Unterstreichungen: - -Google: - - .. code-block:: python - - def func(arg1, arg2): - """Summary line. - - Extended description of function. - - Args: - arg1 (int): Description of arg1 - arg2 (str): Description of arg2 - - Returns: - bool: Description of return value - - """ - return True - -NumPy: - - .. code-block:: python - - def func(arg1, arg2): - """Summary line. - - Extended description of function. - - Parameters - ---------- - arg1 : int - Description of arg1 - arg2 : str - Description of arg2 - - Returns - ------- - bool - Description of return value - - """ - return True - -Detaillierte Konfigurationsoptionen findet ihr in `sphinxcontrib.napoleon.Config -`_. diff --git a/docs/test/doctest.rst b/docs/document/doctest.rst similarity index 87% rename from docs/test/doctest.rst rename to docs/document/doctest.rst index 0ae389a4..bdae8f6d 100644 --- a/docs/test/doctest.rst +++ b/docs/document/doctest.rst @@ -1,14 +1,14 @@ Doctest ======= -Das Python-Modul :doc:`doctest ` prüft, ob die in -einem Docstring angegebenen Tests erfüllt sind. +Das Python-Modul :doc:`doctest ` prüft, ob Tests in +einem Docstring oder in einer anderen Textdatei erfüllt sind. #. In :download:`arithmetic.py` könnt ihr folgenden Docstring hinzufügen: .. literalinclude:: arithmetic.py :language: python - :lines: 9-18 + :lines: 9-19 :lineno-start: 9 #. Anschließend könnt ihr ihn testen mit: @@ -28,12 +28,12 @@ einem Docstring angegebenen Tests erfüllt sind. Expecting nothing ok Trying: - divide(x, y) + round(divide(x, y), 8) Expecting: - -1.1666666666666667 + -1.16666667 ok Trying: - divide(x, z) + divide(x, y) Expecting: Traceback (most recent call last): File "", line 1, in @@ -75,12 +75,12 @@ einem Docstring angegebenen Tests erfüllt sind. Expecting nothing ok Trying: - divide(x, y) + round(divide(x, y), 8) Expecting: - -1.1666666666666667 + -1.16666667 ok Trying: - divide(x, z) + divide(x, y) Expecting: Traceback (most recent call last): File "", line 1, in @@ -114,3 +114,7 @@ einem Docstring angegebenen Tests erfüllt sind. :language: python :lines: 38- :lineno-start: 38 + +.. seealso:: + :doc:`doctest ` kann auch zum kontinuierlichen + Testen der Dokumentation verwendet werden: :ref:`ci-docs`. diff --git a/docs/document/index.rst b/docs/document/index.rst index 0797b9ee..e4e8396d 100644 --- a/docs/document/index.rst +++ b/docs/document/index.rst @@ -2,13 +2,13 @@ Dokumentieren ============= Damit euer Software-Paket sinnvoll genutzt werden kann, sind Dokumentationen -ierforderlich, die Beschreiben, wie eure Software installiert, betrieben, +erforderlich, die Beschreiben, wie eure Software installiert, betrieben, genutzt und verbessert werden kann: * Diejenigen, die euer Paket nutzen wollen, benötigen Informationen, * welche Probleme eure Software löst und was die Hauptfunktionen und - Limitationen der Software sind (``README``) + Einschränkungen der Software sind (``README``) * wie das Software beispielhaft verwendet werden kann * welche Veränderungen in aktuelleren Software-Versionen gekommen sind (``CHANGELOG``) @@ -46,111 +46,13 @@ erhalten können. * `Google Technical Writing Courses for Engineers `_ * `Cusy Design System: Schreiben - `_ + `_ .. toctree:: :titlesonly: :hidden: - start - rest - code-blocks - placeholder - ui-elements - directives - docstrings - intersphinx - uml/index - extensions - test + sphinx/index + doctest + badges shot-scraper - -Badges ------- - -Einige dieser Informationen und mehr können als Badges abgerufen werden. Sie -sind hilfreich, um einen schnellen Überblick über ein Produkt zu erhalten. Für -das `cookiecutter-namespace-template -`_ sind dies -:abbr:`z.B. (zum Beispiel)`: - -|Downloads| |Versions| |Contributors| |License| |Docs| - -.. |Downloads| image:: - https://static.pepy.tech/badge/cookiecutter-namespace-template - :target: https://www.pepy.tech/projects/cookiecutter-namespace-template -.. |Versions| image:: - https://img.shields.io/pypi/pyversions/cookiecutter-namespace-template/0.2.9 - :target: https://pypi.org/project/cookiecutter-namespace-template/0.2.9/ -.. |Contributors| image:: - https://img.shields.io/github/contributors/veit/cookiecutter-namespace-template.svg - :target: https://github.com/veit/cookiecutter-namespace-template/graphs/contributors -.. |License| image:: - https://img.shields.io/github/license/veit/cookiecutter-namespace-template.svg - :target: https://github.com/veit/cookiecutter-namespace-template/blob/main/LICENSE -.. |Docs| image:: - https://readthedocs.org/projects/cookiecutter-namespace-template/badge/?version=latest - :target: https://cookiecutter-namespace-template.readthedocs.io/en/latest/ - -Ihr könnt auch eigene Badges erstellen, :abbr:`z.B. (zum Beispiel)`: - -.. image:: https://img.shields.io/badge/dynamic/json?label=Mastodon&query=totalItems&url=https%3A%2F%2Fmastodon.social%2F@JupyterTutorial%2Ffollowers.json&logo=mastodon - :alt: Mastodon - :target: https://mastodon.social/@JupyterTutorial - -.. seealso:: - * `shields.io `_ - -Sphinx ------- - -Für umfangreiche Dokumentationen könnt ihr :abbr:`z.B.(zum Beispiel)` `Sphinx -`_ verwenden, ein Dokumentationswerkzeug, das -reStructuredText in HTML oder PDF, EPub und man pages umwandelt. Auch die Python -Basics werden mit Sphinx erstellt. Um einen ersten Eindruck von Sphinx zu -bekommen, könnt ihr euch den Quellcode dieser Seite unter dem Link `Page source -<../_sources/document/index.rst.txt>`_ ansehen. - -Ursprünglich wurde Sphinx für die Dokumentation von Python entwickelt und wird -heute in fast allen Python-Projekten verwendet, darunter `NumPy and SciPy -`_, `Matplotlib -`_, `Pandas -`_ und `SQLAlchemy -`_. - -Die Sphinx `autodoc -`_-Funktion, -die zur Erstellung von Dokumentation aus -Python-:doc:`docstrings` verwendet werden kann, könnte ebenfalls zur Verbreitung -von Sphinx unter Python-Entwicklern beitragen. Insgesamt ermöglicht es Sphinx -Entwicklungsteams, eine vollständige Dokumentation an Ort und Stelle zu -erstellen. Oft wird die Dokumentation auch im gleichen :doc:`Git -`-Repository gespeichert, so dass die -Erstellung der neuesten Software-Dokumentation einfach bleibt. - -Sphinx wird auch in Projekten außerhalb der Python-Gemeinschaft eingesetzt, -:abbr:`z.B. (zum Beispiel)` für die Dokumentation des Linux-Kernels: `Kernel -documentation update `_. - -`Read the Docs `_ wurde entwickelt, um die -Dokumentation weiter zu vereinfachen. Read the Docs erleichtert das Erstellen -und Veröffentlichen von Dokumentationen nach jedem Commit. - -Für die Projektdokumentation kann die Visualisierung von :doc:`Git Feature -Branches -` und :doc:`Tags -` mit -:doc:`Python4DataScience:productive/git/advanced/git-big-picture` hilfreich -sein. - -.. note:: - Wenn der Inhalt von ``long_description`` in ``setup()`` in reStructured Text - geschrieben ist, wird er als gut formatiertes HTML im :term:`Python Package - Index` (:term:`PyPI`) angezeigt. - -Andere Dokumentationswerkzeuge ------------------------------- - -`Pycco `_ - ist eine Python-Portierung von `Docco - `_. diff --git a/docs/document/shot-scraper.rst b/docs/document/shot-scraper.rst index cef293fb..2c498a0a 100644 --- a/docs/document/shot-scraper.rst +++ b/docs/document/shot-scraper.rst @@ -19,38 +19,38 @@ Installation Verwendung ---------- -shot-scraper kann auf zweierleis Art verwendet werden +shot-scraper kann auf zweierlei Art verwendet werden #. …für einzelne Screenshots auf der Kommandozeile: .. code-block:: console - $ shot-scraper https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html -o ~/Downloads/clean-prep.png + $ shot-scraper https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html -o ~/Downloads/clean-prep.png …oder mit zusätzlichen Optionen, z.B. für JavaScript- und CSS-Selektoren: - .. code-block:: + .. code-block:: - $ shot-scraper https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html -s '#overview' -o ~/Downloads/clean-prep.png + $ shot-scraper https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html -s '#overview' -o ~/Downloads/clean-prep.png #. …für eine Reihe von Screenshots, die in einer YAML-Datei konfiguriert sind: .. code-block:: yaml - - url: https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html - output: ~/Downloads/clean-prep.png - - url: https://www.example.org/ - width: 736 - quality: 40 - output: example.jpg + - url: https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html + output: ~/Downloads/clean-prep.png + - url: https://www.example.org/ + width: 736 + quality: 40 + output: example.jpg Anschließend kann ``shot-scraper multi`` verwendet werden, z.B.: .. code-block:: console - $ shot-scraper multi shots.yaml - Screenshot of 'https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html' written to '~(Downloads/clean-prep.png' - Screenshot of 'https://www.example.org/' written to 'example.jpg' + $ shot-scraper multi shots.yaml + Screenshot of 'https://jupyter-tutorial.readthedocs.io/de/latest/clean-prep/index.html' written to '~(Downloads/clean-prep.png' + Screenshot of 'https://www.example.org/' written to 'example.jpg' .. seealso:: * In der `README.md @@ -64,7 +64,7 @@ GitHub-Actions -------------- shot-scraper lässt sich einfach in GitHub Actions einbinden. Im -shot-scraper-demo-Repository findet sich auch eine examplarische `shots.yml +shot-scraper-demo-Repository findet sich auch eine exemplarische `shots.yml `_. Einmal am Tag werden zwei Screenshots erzeugt und zurück in das Repository übertragen. Beachtet jedoch, dass das Speichern von Bilddateien, die sich häufig diff --git a/docs/document/autodoc-examples.rst b/docs/document/sphinx/autodoc-examples.rst similarity index 100% rename from docs/document/autodoc-examples.rst rename to docs/document/sphinx/autodoc-examples.rst diff --git a/docs/document/code-blocks.rst b/docs/document/sphinx/code-blocks.rst similarity index 100% rename from docs/document/code-blocks.rst rename to docs/document/sphinx/code-blocks.rst diff --git a/docs/document/sphinx/convert.rst b/docs/document/sphinx/convert.rst new file mode 100644 index 00000000..c3c1f805 --- /dev/null +++ b/docs/document/sphinx/convert.rst @@ -0,0 +1,67 @@ +Konvertieren +============ + +Andere Dateiformate können mit pandoc in :doc:`rest` konvertiert werden. + +Installation von pandoc +----------------------- + +`Pandoc `_ ist ein leistungsfähiges +Dienstprogramm zur Dokumentumwandlung. Wir verwenden es für einfache +Konvertierungen, aber es ist zu viel mehr in der Lage. + +Installation +------------ + +Pandoc könnt ihr für die verschiedenen Plattformen installieren: + +.. tab:: Debian/Ubuntu + + .. code-block:: console + + $ sudo apt install pandoc + +.. tab:: macOS + + .. code-block:: console + + $ brew install pandoc + +.. tab:: Windows + + .. code-block:: ps1 + + $ choco install pandoc + +.. seealso:: + * `Installing pandoc `_ + +Konvertieren +------------ + +Navigiert im Terminal zu dem Verzeichnis, das die zu konvertierenden Dokumente +enthält. Gebt dann für jede Datei, die ihr konvertieren möchtet, den Befehl +:samp:`pandoc -s --toc -f {INPUT_FORMAT} -t rst {MYDOC}.{SUFFIX}` ein: + +``-s`` + erzeugt ein eigenständiges Dokument +``--toc`` + erstellt ein Inhaltsverzeichnis (optional) +``-t`` + erzeugt eine reStructuredText-Ausgabe +``-f`` + teilt pandoc das Eingabeformat mit. Einen Überblick über die verfügbaren + Eingabeformate erhaltet ihr in `General options + `_. + +Korrigieren des konvertierten Dokuments +--------------------------------------- + +Wie umfangreich die Korrektur für das konvertierte Dokument ausfällt, hängt +davon ab, aus welchem Dateiformat ihr konvertiert. Hier sind einige Dinge, auf +die ihr achten solltet: + +* Mehrzeilige Titel müssen in einzeilige konvertiert werden +* Eigenständige ``**``-Zeichen +* :samp:`***FETT***` sollte :samp:`**FETT**` sein +* Fehlerhafte Tabellen diff --git a/docs/document/directives.rst b/docs/document/sphinx/directives.rst similarity index 97% rename from docs/document/directives.rst rename to docs/document/sphinx/directives.rst index c6c85b71..e7f40d58 100644 --- a/docs/document/directives.rst +++ b/docs/document/sphinx/directives.rst @@ -8,12 +8,6 @@ werden. Sphinx macht hiervon ausgiebig Gebrauch. Hier sind einige Beispiele: Inhaltsverzeichnis ------------------ -.. toctree:: - :maxdepth: 2 - - start - docstrings - .. code-block:: rest .. toctree:: @@ -21,6 +15,7 @@ Inhaltsverzeichnis start docstrings + ... Meta-Informationen ~~~~~~~~~~~~~~~~~~ diff --git a/docs/document/sphinx/docstrings.rst b/docs/document/sphinx/docstrings.rst new file mode 100644 index 00000000..c9cfda38 --- /dev/null +++ b/docs/document/sphinx/docstrings.rst @@ -0,0 +1,169 @@ +Docstrings +========== + +Mit der Sphinx-Erweiterung `sphinx.ext.autodoc +`_ können +Docstrings auch in die Dokumentation aufgenommen werden. Die folgenden +Direktiven können angegeben werden + +… für Klassen und Ausnahmen: + +.. rst:directive:: automodule + autoclass + autoexception + +… für funktionsähnliche Objekte: + +.. rst:directive:: autofunction + automethod + autoproperty + autodecorator + +… für Daten und Attribute: + +.. rst:directive:: autodata + autoattribute + +Installation +------------ + +``sphinx.ext.autodoc`` ist normalerweise bereits in der +Sphinx-Konfigurationsdatei ``docs/conf.py`` angegeben: + +.. code-block:: python + + extensions = ["sphinx.ext.autodoc", ...] + +Wenn euer Paket und die zugehörige Dokumentation Teil desselben Repository +sind, werden sie immer dieselbe relative Position im Dateisystem haben. In +diesem Fall könnt ihr einfach die Sphinx-Konfiguration für ``sys.path`` +bearbeiten, um den relativen Pfad zum Paket anzugeben, also: + +.. code-block:: python + + sys.path.insert(0, os.path.abspath("..")) + import MODULE + +Wenn ihr eure Sphinx-Dokumentation in einer virtuellen Umgebung installiert +habt, könnt ihr euer Paket auch dort mitinstallieren, :abbr:`z.B. (zum +Beispiel)` indem ihr es in eure :file:`requirements.txt`-Datei eintragt. + +Beispiele +--------- + +Hier findet ihr einige Beispiele aus der Dokumentation des +Python-:py:mod:`string`-Moduls: + +.. literalinclude:: autodoc-examples.rst + :language: rest + :lines: 3- + +Die Ausgabe ist :doc:`autodoc-examples`. + +Richtlinien +----------- + +In :pep:`8` gibt es nur einen kurzen Hinweise auf Konventionen für einen guten +Docstring: :pep:`Documentation Strings <8#comments>`. Weitere :abbr:`PEPs (Python Enhancement Proposals)` beziehen sich auf das :pep:`Docstring Processing +System Framework <256>`: + +:pep:`257` + beschreibt Docstring-Konventionen: + + * Was sollte wo dokumentiert werden? + * Die erste Zeile soll eine einzeilige Zusammenfassung sein. + +:pep:`258` + spezifiziert die System zur Verarbeitung von Docstrings. + +:pep:`287` + spezifiziert die Docstring-Syntax. + +Im `Google Python Style Guide +`_ gibt es darüberhinaus noch +spezifischere Richtlinien für `Python Konnentare und Docstrings +`_. +Auch der `NumPy Style guide +`_ liefert noch weitere +`Docstring-Standards `_. Der wesentliche Unterschied zwischen beiden besteht darin, +dass Google Einrückungen verwendet und NumPy Unterstreichungen: + +Google Python Style Guide: + .. code-block:: python + + def func(arg1: int, arg2: str) -> bool: + """Summary line. + + Extended description of function. + + Args: + arg1 (int): Description of arg1 + arg2 (str): Description of arg2 + + Returns: + bool: Description of return value + + """ + return True + +NumPy Style Guide: + .. code-block:: python + + def func(arg1: int, arg2: str) -> bool: + """Summary line. + + Extended description of function. + + Parameters + ---------- + arg1 : int + Description of arg1 + arg2 : str + Description of arg2 + + Returns + ------- + bool + Description of return value + + """ + return True + +.. _napoleon: + +``sphinx.ext.napoleon`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Die Sphinx-Erweiterung `sphinx.ext.napoleon +`_ verarbeitet sowohl +Docstrings, die dem Google Python Style Guide als auch dem NumPy Style Guide +entsprechen: + +Detaillierte Konfigurationsoptionen findet ihr in `sphinxcontrib.napoleon.Config +`_. + +``sphinx-autodoc-typehints`` +---------------------------- + +Mit :pep:`484` wurde eine Standardmethode für den Ausdruck von Typen in +Python-Code eingeführt. Damit können Typen auch in Docstrings unterschiedlich +ausgedrückt werden. Die Variante mit Typen nach PEP 484 hat den Vorteil, dass +Typ-Tester und IDEs zur statischen Codeanalyse eingesetzt werden können. + +Python 3 Type-Annotations in Docstrings: + .. code-block:: python + + def func(arg1: int, arg2: str) -> bool: + """Summary line. + + Extended description of function. + + Args: + arg1 (int): Description of arg1 + arg2 (str): Description of arg2 + + Returns: + bool: Description of return value + + """ + return True diff --git a/docs/document/extensions.rst b/docs/document/sphinx/extensions.rst similarity index 94% rename from docs/document/extensions.rst rename to docs/document/sphinx/extensions.rst index 90b0bbc0..fc8eb94d 100644 --- a/docs/document/extensions.rst +++ b/docs/document/sphinx/extensions.rst @@ -22,7 +22,7 @@ Eingebaute Erweiterungen `sphinx.ext.intersphinx `_ ermöglicht das Einbinden anderer Projektdokumentationen. `sphinx.ext.mathjax `_ - Rendertmathematische Formeln über JavaScript. + rendert mathematische Formeln über JavaScript. `sphinx.ext.napoleon `_ unterstützt NumPy und Google Style Docstrings. `sphinx.ext.todo `_ @@ -51,7 +51,7 @@ Erweiterungen von Drittanbietern `numpydoc `_ `NumPy `_ Sphinx-Erweiterung. `Releases `_ - schreibt eine Changelog-Datei. + schreibt eine :file:`CHANGELOG`-Datei. `sphinxcontrib-napoleon `_ Präprozessor zum Parsen von NumPy- und Google-Style Docstrings. `sphinx-autodoc-annotation `_ @@ -73,15 +73,16 @@ Erweiterungen von Drittanbietern `Sphinx-Needs `_ erlaubt die Definition, Verlinkung und Filterung von need-Objekten, also :abbr:`z.B. (zum Beispiel)` Anforderungen und Testfälle -`Sphinx-pyreverse `_ +`Sphinx-pyreverse `_ erstellt ein UML-Diagramm von Python-Modulen `sphinx-jsonschema `_ zeigt ein `JSON Schema `_ in der Sphinx-Dokumentation `Sphinxcontrib-mermaid `_ - ermöglicht euch, Mermaid-Grafiken in Ihre Dokumente einzubetten. + ermöglicht euch, `Mermaid `_-Grafiken in eure + Dokumente einzubetten. `Sphinx Sitemap Generator Extension `_ - generiert multiversion- und multilanguage `sitemaps + generiert multiversion- und multilanguage-`sitemaps `_ für die HTML-Version `Sphinx Lint `_ basiert auf `rstlint.py diff --git a/docs/document/sphinx/index.rst b/docs/document/sphinx/index.rst new file mode 100644 index 00000000..07ae9be6 --- /dev/null +++ b/docs/document/sphinx/index.rst @@ -0,0 +1,63 @@ +Sphinx +====== + +Für umfangreiche Dokumentationen könnt ihr :abbr:`z.B.(zum Beispiel)` `Sphinx +`_ verwenden, ein Dokumentationswerkzeug, das +reStructuredText in HTML oder PDF, EPub und man pages umwandelt. Auch die Python +Basics werden mit Sphinx erstellt. Um einen ersten Eindruck von Sphinx zu +bekommen, könnt ihr euch den Quellcode dieser Seite unter dem Link `Page source +<../_sources/document/index.rst.txt>`_ ansehen. + +Ursprünglich wurde Sphinx für die Dokumentation von Python entwickelt und wird +heute in fast allen Python-Projekten verwendet, darunter `NumPy and SciPy +`_, `Matplotlib +`_, `pandas +`_ und `SQLAlchemy +`_. + +Die Sphinx `autodoc +`_-Funktion, +die zur Erstellung von Dokumentation aus +Python-:doc:`docstrings` verwendet werden kann, könnte ebenfalls zur Verbreitung +von Sphinx unter Python-Entwicklern beitragen. Insgesamt ermöglicht es Sphinx +Entwicklungsteams, eine vollständige Dokumentation an Ort und Stelle zu +erstellen. Oft wird die Dokumentation auch im gleichen :doc:`Git +`-Repository gespeichert, so dass die +Erstellung der neuesten Software-Dokumentation einfach bleibt. + +Sphinx wird auch in Projekten außerhalb der Python-Gemeinschaft eingesetzt, +:abbr:`z.B. (zum Beispiel)` für die Dokumentation des Linux-Kernels: `Kernel +documentation update `_. + +`Read the Docs `_ wurde entwickelt, um die +Dokumentation weiter zu vereinfachen. Read the Docs erleichtert das Erstellen +und Veröffentlichen von Dokumentationen nach jedem Commit. + +Für die Projektdokumentation kann die Visualisierung von :doc:`Git Feature +Branches +` und :doc:`Tags +` mit +:doc:`Python4DataScience:productive/git/advanced/git-big-picture` hilfreich +sein. + +.. note:: + Wenn der Inhalt von ``long_description`` in ``setup()`` in reStructured Text + geschrieben ist, wird er als gut formatiertes HTML im :term:`Python Package + Index` (:term:`PyPI`) angezeigt. + +.. toctree:: + :titlesonly: + :hidden: + + start + rest + convert + code-blocks + placeholder + ui-elements + directives + docstrings + intersphinx + uml/index + extensions + test diff --git a/docs/document/intersphinx.rst b/docs/document/sphinx/intersphinx.rst similarity index 98% rename from docs/document/intersphinx.rst rename to docs/document/sphinx/intersphinx.rst index 4e41cc84..5d3f7125 100644 --- a/docs/document/intersphinx.rst +++ b/docs/document/sphinx/intersphinx.rst @@ -72,7 +72,7 @@ Benutzerdefinierte Links ------------------------ Ihr könnt auch eure eigenen ``intersphinx``-Zuweisungen erstellen, wenn -:abbr:`z.B. (zum Beispiel)` ``objects.inv`` Fehler aufweis wie bei `Beautiful +:abbr:`z.B. (zum Beispiel)` ``objects.inv`` Fehler aufweist wie bei `Beautiful Soup `_. Der Fehler kann mit korrigiert werden: diff --git a/docs/document/main.py b/docs/document/sphinx/main.py similarity index 100% rename from docs/document/main.py rename to docs/document/sphinx/main.py diff --git a/docs/document/main.py.orig b/docs/document/sphinx/main.py.orig similarity index 100% rename from docs/document/main.py.orig rename to docs/document/sphinx/main.py.orig diff --git a/docs/document/placeholder.rst b/docs/document/sphinx/placeholder.rst similarity index 96% rename from docs/document/placeholder.rst rename to docs/document/sphinx/placeholder.rst index 71adf42a..5a5da5f4 100644 --- a/docs/document/placeholder.rst +++ b/docs/document/sphinx/placeholder.rst @@ -1,7 +1,7 @@ Platzhalter ----------- -Sphinx unterscheidet die folgenden Platzhaltervariablen: +Sphinx unterscheidet die folgenden Platzhalter-Variablen: .. rst:role:: envvar diff --git a/docs/document/rest.rst b/docs/document/sphinx/rest.rst similarity index 96% rename from docs/document/rest.rst rename to docs/document/sphinx/rest.rst index 7bf3832d..451f1a98 100644 --- a/docs/document/rest.rst +++ b/docs/document/sphinx/rest.rst @@ -36,11 +36,11 @@ werden. Inline-Auszeichnung ------------------- -*Kursiv*, **fett** und ``vorformattiert`` +*Kursiv*, **fett** und ``vorformatiert`` .. code-block:: rest - *Kursiv*, **fett** und ``vorformattiert`` + *Kursiv*, **fett** und ``vorformatiert`` Links ----- @@ -96,12 +96,12 @@ Link zur :doc:`Startseite <../index>` oder zu :doc:`docstrings`. Dokumente herunterladen ::::::::::::::::::::::: -Link zu einem Dokument, das nicht von Sohinx gerendert werden soll, :abbr:`z.B. +Link zu einem Dokument, das nicht von Sphinx gerendert werden soll, :abbr:`z.B. (zum Beispiel)` zu :download:`autodoc-examples.rst`. .. code-block:: rest - Link zu einem Dokument, das nicht von Sohinx gerendert werden soll, + Link zu einem Dokument, das nicht von Sphinx gerendert werden soll, :abbr:`z.B. (zum Beispiel)` zu :download:`docstrings-example.rst`. Bilder @@ -170,14 +170,14 @@ Definitionsliste Term Definition des Begriffs -EIn anderer Term +Ein anderer Term … und seine Definition .. code-block:: rest Term Definition des Begriffs - EIn anderer Term + Ein anderer Term … und seine Definition Verschachtelte Listen diff --git a/docs/document/start.rst b/docs/document/sphinx/start.rst similarity index 97% rename from docs/document/start.rst rename to docs/document/sphinx/start.rst index ec7dd319..870b7319 100644 --- a/docs/document/start.rst +++ b/docs/document/sphinx/start.rst @@ -10,13 +10,17 @@ Installation und Start .. code-block:: console + $ mkdir docs_proj + $ cd docs_proj $ python3 -m venv .venv .. tab:: Windows .. code-block:: ps1con - C:> python -m venv .venv + C:> mkdir docs_proj + C:> cd docs_proj + C:> py -m venv .venv #. Wechselt in die virtuelle Umgebung und installiert dort Sphinx: diff --git a/docs/document/test.rst b/docs/document/sphinx/test.rst similarity index 89% rename from docs/document/test.rst rename to docs/document/sphinx/test.rst index 77267890..7853e0ec 100644 --- a/docs/document/test.rst +++ b/docs/document/sphinx/test.rst @@ -89,13 +89,15 @@ Die Ausgabe kann dann :abbr:`z.B. (zum Beispiel)` so aussehen: … (accessibility/color: line 114) broken https://chrome.google.com/webstore/detail/nocoffee/jjeeggmbnhckmgdhmgdckeigabjfbddl - 404 Client Error: Not Found for url: https://chrome.google.com/webstore/detail/nocoffee/jjeeggmbnhckmgdhmgdckeigabjfbddl +.. _ci-docs: + Kontinuierliche Integration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ :abbr:`Ggf. (Gegebenenfalls)` könnt ihr auch automatisiert in eurer :term:`CI`-Pipeline überprüfen, ob die Dokumentation gebaut wird und die Links -gültig sind. In :doc:`../test/tox` kann die Konfiguration folgendermaßen ergänzt -werden: +gültig sind. In :doc:`../../test/tox` kann die Konfiguration folgendermaßen +ergänzt werden: .. code-block:: ini :caption: tox.ini @@ -161,8 +163,34 @@ ein: Mit :doc:`Sybil:index` könnt ihr nicht nur :doc:`rest` überprüfen, sondern :abbr:`z.B. (zum Beispiel)` auch :doc:`Markdown ` und :doc:`Myst `. Darüberhinaus kann Sybil auch Code-Blöcke in der - Dokumentation entweder mit :doc:`../test/pytest/index` oder mit - :doc:`../test/unittest` überprüfen. + Dokumentation entweder mit :doc:`../../test/pytest/index` oder mit + :doc:`../../test/unittest` überprüfen. + +.. _test_code: + +Code +---- + +Mit der eingebauten Python-Bibliothek :doc:`../doctest` könnt ihr auch Code in +eurer Dokumentation mit der :func:`doctest.testfile`-Methode testen: + +.. code-block:: Python + + import doctest + + doctest.testfile("example.rst") + +Dieses kurze Skript führt alle interaktiven Python-Beispiele aus, die in der +Datei :file:`example.rst` enthalten sind, und überprüft sie. Der Inhalt der +Datei wird so behandelt, als wäre er ein einziger riesiger Docstring. + +.. seealso:: + Ein einfaches Beispiel findet ihr in der Python-Dokumentation: `Simple Usage: + Checking Examples in a Text File + `_. + + Eine andere Möglichkeit, Code in Dokumentationen zu testen ist + `pytest-doctestplus `_. Code-Formatierung ----------------- @@ -183,7 +211,7 @@ wir die Bibliothek über das :doc:`pre-commit additional_dependencies: - black -blacken-docs unterstützt aktuell die folgenden black-Optionen: +blacken-docs unterstützt aktuell die folgenden Black-Optionen: * `line-length `_ @@ -202,7 +230,7 @@ Die englische Rechtschreibung lässt sich überprüfen mit `codespell Version der auf `Wikipedia `_ verfügbaren Wörterbücher. :abbr:`Ggf. (Gegebenenfalls)` könnt ihr jedoch auch -eigene Wörterbucher mit der ``--builtin``-Option bereitstellen. +eigene Wörterbücher mit der ``--builtin``-Option bereitstellen. Ihr könnt ``codespell`` in der :file:`pyproject.toml` konfigurieren, :abbr:`z.B. (zum Beispiel)`: @@ -242,7 +270,7 @@ Vale wird von vielen Open-Source-Projekten genutzt, :abbr:`u.a. (unter anderem)` von * GitLab (`.vale.ini - `_, `Regeln + `__, `Regeln `__) * Homebrew (`.vale.ini `__, `Regeln @@ -280,7 +308,7 @@ Vale wird in der :file:`.vale.ini`-Datei konfiguriert: BasedOnStyles = cusy-de .. seealso:: - * `Vale Configuration `_ + * `.vale.ini `__ Anschließend solltet ihr :abbr:`ggf. (gegebenenfalls)` eure :ref:`.gitignore `-Datei aktualisieren: @@ -350,7 +378,7 @@ Ihr könnt ``interrogate`` :abbr:`z.B. (zum Beispiel)` in der * `Configuration `_ -Nun könnt ihr ``interrogate`` in eure :doc:`../test/tox`-Datei einfügen, +Nun könnt ihr ``interrogate`` in eure :doc:`../../test/tox`-Datei einfügen, :abbr:`z.B. (zum Beispiel)` mit .. code-block:: ini diff --git a/docs/document/sphinx/ui-elements.rst b/docs/document/sphinx/ui-elements.rst new file mode 100644 index 00000000..9c32128f --- /dev/null +++ b/docs/document/sphinx/ui-elements.rst @@ -0,0 +1,51 @@ +UI-Elemente und Interaktionen +============================= + +Für die Dokumentation des User Interfaces und dessen Interaktionen stellt Sphinx +drei verschiedene Rollen bereit: ``guilabel``, ``kbd`` und ``menuselection``: + +.. list-table:: + :header-rows: 1 + + * - Eingabe + - Ausgabe + - Anmerkungen + * - .. code-block:: rest + + :guilabel:`Cancel` + - :guilabel:`Cancel` + - Jede im User Interface verwendete Beschriftung kann mit dieser Rolle + gekennzeichnet werden, einschließlich der Beschriftung von Schaltflächen, + Fenstertiteln, Feld-, Menü- und Menüauswahl-Namen und Werten in + Auswahllisten. + * - .. code-block:: rest + + :guilabel:`&Cancel` + - :guilabel:`&Cancel` + - Tastenkürzel für die GUI-Beschriftung können mit einem et-Zeichen (``&``) + eingefügt werden; dieses führt in der Ausgabe zur Unterstreichung des + Folgebuchstabens. + + .. note:: + Wenn ihr ein et-Zeichen einfügen wollt, könnt ihr es einfach + verdoppeln. + * - .. code-block:: rest + + :kbd:`Ctrl-s` + - :kbd:`Ctrl-s` + - Dies stellt eine Folge von Tasteneingaben dar. Welche Form die + Tastenfolge hat, kann von plattform- oder anwendungsspezifischen + Konventionen abhängen. Dabei sollten die Namen von Modifikatortasten + ausgeschrieben werden, um die Zugänglichkeit zu verbessern. + Tastaturbeschriftung referenziert werden. + * - .. code-block:: rest + + :menuselection:`File --> Save` + - :menuselection:`File --> Save` + - Eine Menüauswahl wird mit der Rolle ``menuselection`` gekennzeichnet. + Diese markiert komplette Sequenz, einschließlich der Auswahl von + Untermenüs, bestimmter Operationen oder beliebiger Untersequenzen. Die + Namen der einzelnen Auswahlen werden durch ``-->`` getrennt. + + :rst:role:`menuselection` unterstützt genau wie :rst:role:`guilabel` + Tastaturkürzel mit einem et-Zeichen (``&``). diff --git a/docs/document/uml/abstract-class.svg b/docs/document/sphinx/uml/abstract-class.svg similarity index 100% rename from docs/document/uml/abstract-class.svg rename to docs/document/sphinx/uml/abstract-class.svg diff --git a/docs/document/uml/activity-diagram.svg b/docs/document/sphinx/uml/activity-diagram.svg similarity index 100% rename from docs/document/uml/activity-diagram.svg rename to docs/document/sphinx/uml/activity-diagram.svg diff --git a/docs/document/uml/activity-sync.svg b/docs/document/sphinx/uml/activity-sync.svg similarity index 100% rename from docs/document/uml/activity-sync.svg rename to docs/document/sphinx/uml/activity-sync.svg diff --git a/docs/document/uml/annotation.svg b/docs/document/sphinx/uml/annotation.svg similarity index 100% rename from docs/document/uml/annotation.svg rename to docs/document/sphinx/uml/annotation.svg diff --git a/docs/document/uml/circle.svg b/docs/document/sphinx/uml/circle.svg similarity index 100% rename from docs/document/uml/circle.svg rename to docs/document/sphinx/uml/circle.svg diff --git a/docs/document/uml/diamond.svg b/docs/document/sphinx/uml/diamond.svg similarity index 100% rename from docs/document/uml/diamond.svg rename to docs/document/sphinx/uml/diamond.svg diff --git a/docs/document/uml/entity.svg b/docs/document/sphinx/uml/entity.svg similarity index 100% rename from docs/document/uml/entity.svg rename to docs/document/sphinx/uml/entity.svg diff --git a/docs/document/uml/enum.svg b/docs/document/sphinx/uml/enum.svg similarity index 100% rename from docs/document/uml/enum.svg rename to docs/document/sphinx/uml/enum.svg diff --git a/docs/document/sphinx/uml/index.rst b/docs/document/sphinx/uml/index.rst new file mode 100644 index 00000000..de72b35b --- /dev/null +++ b/docs/document/sphinx/uml/index.rst @@ -0,0 +1,166 @@ +Unified Modeling Language (UML) +=============================== + +Installation +------------ + +#. Installiert `plantuml `_: + + .. tab:: Linux + + .. code-block:: console + + $ sudo apt install plantuml + + .. tab:: macOS + + .. code-block:: console + + $ brew install plantuml + + .. tab:: Windows + + .. code-block:: ps1 + + $ choco install plantuml + +#. Installiert `sphinxcontrib-plantuml + `_: + + .. tab:: Linux + + .. code-block:: console + + $ python -m pip install sphinxcontrib-plantuml + + .. tab:: macOS + + .. code-block:: console + + $ python -m pip install sphinxcontrib-plantuml + + .. tab:: Windows + + .. code-block:: ps1con + + C:> python -m pip install sphinxcontrib-plantuml + +#. Konfiguriert Sphinx in der ``conf.py``-Datei: + + .. code-block:: python + + extensions = [..., "sphinxcontrib.plantuml"] + + plantuml = "/PATH/TO/PLANTUML" + + .. note:: + Auch in Windows werden in der Pfadangabe ``/`` angegeben. + +Sequenzdiagramm +--------------- + +.. image:: sequence-diagram.svg + +.. code-block:: rest + + .. uml:: + + Browser -> Server: Authentifizierungsanfrage + Server --> Browser: Authentifizierungsantwort + + Browser -> Server: Eine andere Authentifizierungsanfrage + Browser <-- Server: Eine andere Authentifizierungsantwort + +``->`` + wird verwendet, um eine Nachricht zwischen zwei Akteuren zu zeichnen. Die + Akteure müssen nicht explizit deklariert werden. +``-->`` + wird verwendet, um eine gepunktete Linie zu zeichnen. +``<- und <--`` + verändert die Zeichnung nicht, kann aber die Lesbarkeit erhöhen. + +Anwendungsfall-Diagramm +----------------------- + +.. image:: use-case-diagram.svg + +.. code-block:: rest + + .. uml:: + + :Nutzende Person: --> (Verwendung) + "Gruppe von\nAdministratoren" as Admin + "Verwenden der\nAnwendung" as (Verwendung) + Admin --> (Administrieren\nder Anwendung) + +Anwendungsfälle werden von runden Klammern ``()`` umschlossen und ähneln einem +Oval. + +Alternativ kann auch das Schlüsselwort ``usecase`` verwendet werden, um einen +Anwendungsfall zu definieren. Darüber hinaus ist es möglich, mit dem +Schlüsselwort ``as`` einen Alias zu definieren. Dieser Alias kann dann bei der +Definition von Beziehungen verwendet werden. + +Mit ``\n`` könnt ihr Zeilenumbrüche in den Namen der Anwendungsfälle einfügen. + +Aktivitätsdiagramm +------------------ + +``(*)`` + Start- und Endknoten eines Aktivitätsdiagramms. + + ``(*top)`` + In einigen Fällen kann dies verwendet werden um den Startpunkt an den + Anfang eines Diagramms zu verschieben. + +``-->`` + definiert eine Aktivität + + ``-down->`` + Pfeil nach unten (Standardwert) + ``-right-> or ->`` + Pfeil nach rechts + ``-left->`` + Pfeil nach links + ``-up->`` + Pfeil nach oben + +``if``, ``then``, ``else`` + Schlüsselworte für die Definition von Verzweigungen. + + Beispiel: + + .. code-block:: rest + + .. uml:: + + (*) --> "Initialisierung" + if "ein Test" then + -->[wahr] "Eine Aktivität" + --> "Eine andere Aktivität" + -right-> (*) + else + ->[falsch] "Etwas anderes" + -->[Ende des Prozesses] (*) + endif + + .. image:: activity-diagram.svg + +``fork``, ``fork again`` und ``end fork`` oder ``end merge`` + Schlüsselworte für die parallele Verarbeitung. + + Beispiel: + + .. code-block:: rest + + .. uml:: + + start + fork + :Aktion 1; + fork again + :Aktion 2; + end fork + stop + + .. image:: parallel.svg diff --git a/docs/document/uml/interface.svg b/docs/document/sphinx/uml/interface.svg similarity index 100% rename from docs/document/uml/interface.svg rename to docs/document/sphinx/uml/interface.svg diff --git a/docs/document/uml/parallel.svg b/docs/document/sphinx/uml/parallel.svg similarity index 100% rename from docs/document/uml/parallel.svg rename to docs/document/sphinx/uml/parallel.svg diff --git a/docs/document/uml/sequence-diagram.svg b/docs/document/sphinx/uml/sequence-diagram.svg similarity index 100% rename from docs/document/uml/sequence-diagram.svg rename to docs/document/sphinx/uml/sequence-diagram.svg diff --git a/docs/document/uml/use-case-diagram.svg b/docs/document/sphinx/uml/use-case-diagram.svg similarity index 100% rename from docs/document/uml/use-case-diagram.svg rename to docs/document/sphinx/uml/use-case-diagram.svg diff --git a/docs/document/ui-elements.rst b/docs/document/ui-elements.rst deleted file mode 100644 index f5efe933..00000000 --- a/docs/document/ui-elements.rst +++ /dev/null @@ -1,58 +0,0 @@ -UI-Elemente und Interaktionen -============================= - -.. rst:role:: guilabel - - Label, die als Teil einer interaktiven Benutzeroberfläche dargestellt werden, - sollten mit :rst:role:`guilabel` gekennzeichnet werden. Jede in der - Oberfläche verwendete Beschriftung sollte mit dieser Rolle gekennzeichnet - werden, einschließlich Beschriftung von Schaltflächen, Fenstertiteln, - Feldnamen, Menü- und Menüauswahlnamen und sogar Werte in Auswahllisten. - - Ein Tastenkürzel für die GUI-Beschriftung kann mit einem et-Zeichen (&) - eingefügt werden; dieses führt in der Ausgabe zur Unterstreichung des - Folgebuchstabens. - - :guilabel:`&Cancel` erzielt ihr :abbr:`z.B. (zum Beispiel)` mit folgender - Auszeichnung: - - .. code-block:: rest - - :guilabel:`&Cancel` - - .. note:: - Wenn ihr ein et-Zeichen einfügen wollt, könnt ihr es einfach verdoppeln. - -.. rst:role:: kbd - - Dies stellt eine Folge von Tasteneingaben dar. Welche Form die Tastenfolge - hat, kann von plattform- oder anwendungsspezifischen Konventionen abhängen. - Wenn es keine entsprechenden Konventionen gibt, sollten die Namen von - Modifikatortasten ausgeschrieben werden, um die Zugänglichkeit zu verbessern. - Auch sollte nicht auf eine bestimmte Tastaturbeschriftung referenziert - werden. - - :kbd:`Ctrl-s` erzielt ihr :abbr:`z.B. (zum Beispiel)` mit folgender - Auszeichnung: - - .. code-block:: rest - - :kbd:`Ctrl-s` - -.. rst:role:: menuselection - - Eine Menüauswahl sollte mit der Rolle ``menuselection`` markiert werden. - Diese wird verwendet, um eine komplette Sequenz zu markieren, einschließlich - der Auswahl von Untermenüs und der Auswahl bestimmter Operationein oder - beliebiger Untersequenzen. Die Namen der einzelnen Auswahlen sollten durch - ``-->`` getrennt werden. - - :menuselection:`View --> Cell Toolbar --> Slideshow` erzielt ihr :abbr:`z.B. - (zum Beispiel)` mit folgender Auszeichnung: - - .. code-block:: rest - - :menuselection:`View --> Cell Toolbar --> Slideshow` - - :rst:role:`menuselection` unterstützt genau wie :rst:role:`guilabel` auch - Tastaturkürzel mit einem et-Zeichen (&). diff --git a/docs/document/uml/activity-diagram.rst b/docs/document/uml/activity-diagram.rst deleted file mode 100644 index 611a4477..00000000 --- a/docs/document/uml/activity-diagram.rst +++ /dev/null @@ -1,61 +0,0 @@ -Aktivitätsdiagramm -================== - -``(*)`` - Start- und Endknoten eines Aktivitätsdiagramms. - - ``(*top)`` - In einigen Fällen kann dies verwendet werden um den Startpunkt an den - Anfang eines Diagramms zu verschieben. - -``-->`` - definiert eine Aktivität - - ``-down->`` - Pfeil nach unten (Standardwert) - ``-right-> or ->`` - Pfeil nach rechts - ``-left->`` - Pfeil nach links - ``-up->`` - Pfeil nach oben - -``if``, ``then``, ``else`` - Schlüsselworte für die Definition von Verzweigungen. - - Beispiel: - - .. code-block:: rest - - .. uml:: - - (*) --> "Initialisierung" - if "ein Test" then - -->[wahr] "Eine Aktivität" - --> "Eine andere Aktivität" - -right-> (*) - else - ->[falsch] "Etwas anderes" - -->[Ende des Prozesses] (*) - endif - - .. image:: activity-diagram.svg - -``fork``, ``fork again`` und ``end fork`` oder ``end merge`` - Schlüsselworte für die parallele Verarbeitung. - - Beispiel: - - .. code-block:: rest - - .. uml:: - - start - fork - :Aktion 1; - fork again - :Aktion 2; - end fork - stop - - .. image:: parallel.svg diff --git a/docs/document/uml/class-diagram.rst b/docs/document/uml/class-diagram.rst deleted file mode 100644 index edc8df96..00000000 --- a/docs/document/uml/class-diagram.rst +++ /dev/null @@ -1,101 +0,0 @@ -Klassendiagramm -=============== - - -``abstract class``, ``abstract`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - abstract class "Abstrakte Klasse" - - .. image:: abstract-class.svg - -``annotation`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - annotation Anmerkung - - .. image:: annotation.svg - -``circle``, ``()`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - circle Kreis - - .. image:: circle.svg - -``class`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - class Klasse - - .. image:: class.svg - -``diamond``, ``<>`` - An empty diamond stands for an association, a black diamond for a - composition. - - Beispiel: - - .. code-block:: rest - - .. uml:: - - diamond Assoziation - - .. image:: diamond.svg - -``entity`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - entity Entität - - .. image:: entity.svg - -``enum`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - enum Aufzählung - - .. image:: enum.svg - -``interface`` - - Beispiel: - - .. code-block:: rest - - .. uml:: - - interface Schnittstelle - - .. image:: interface.svg diff --git a/docs/document/uml/class.svg b/docs/document/uml/class.svg deleted file mode 100644 index 8a3de972..00000000 --- a/docs/document/uml/class.svg +++ /dev/null @@ -1,14 +0,0 @@ -Klasse diff --git a/docs/document/uml/index.rst b/docs/document/uml/index.rst deleted file mode 100644 index 00e23999..00000000 --- a/docs/document/uml/index.rst +++ /dev/null @@ -1,54 +0,0 @@ -Unified Modeling Language (UML) -=============================== - -Installation ------------- - -#. Installiert `plantuml `_: - - .. tab:: Linux - - .. code-block:: console - - $ sudo apt install plantuml - - .. tab:: macOS - - .. code-block:: console - - $ brew install plantuml - -#. Installiert `sphinxcontrib-plantuml - `_: - - .. tab:: Linux/macOS - - .. code-block:: console - - $ python -m pip install sphinxcontrib-plantuml - - .. tab:: Windows - - .. code-block:: ps1con - - C:> python -m pip install sphinxcontrib-plantuml - -#. Konfiguriert Sphinx in der ``conf.py``-Datei: - - .. code-block:: python - - extensions = [..., "sphinxcontrib.plantuml"] - - plantuml = "/PATH/TO/PLANTUML" - - .. note:: - Auch in Windows werden in der Pfadangabe ``/`` angegeben. - -.. toctree:: - :titlesonly: - :hidden: - - sequence-diagram - use-case-diagram - activity-diagram - class-diagram diff --git a/docs/document/uml/sequence-diagram.rst b/docs/document/uml/sequence-diagram.rst deleted file mode 100644 index dcf2fb9f..00000000 --- a/docs/document/uml/sequence-diagram.rst +++ /dev/null @@ -1,26 +0,0 @@ -Sequenzdiagramm -=============== - -.. image:: sequence-diagram.svg - -.. code-block:: rest - - .. uml:: - - Browser -> Server: Authentifizierungsanfrage - Server --> Browser: Authentifizierungsantwort - - Browser -> Server: Eine andere Authentifizierungsanfrage - Browser <-- Server: Eine andere Authentifizierungsantwort - -``->`` - wird verwendet, um eine Nachricht zwischen zwei Akteuren zu zeichnen. Die - Akteure müssen nicht explizit deklariert werden. -``-->`` - wird verwendet, um eine gepunktete Linie zu zeichnen. -``<- und <--`` - verändert die Zeichnung nicht, kann aber die Lesbarkeit erhöhen. - - .. note:: - Dies gilt nur für Sequenzdiagramme. In anderen Diagrammen können andere - Regeln gelten. diff --git a/docs/document/uml/use-case-diagram.rst b/docs/document/uml/use-case-diagram.rst deleted file mode 100644 index 34f26b92..00000000 --- a/docs/document/uml/use-case-diagram.rst +++ /dev/null @@ -1,23 +0,0 @@ -Anwendungsfall-Diagramm -======================= - -.. image:: use-case-diagram.svg - -.. code-block:: rest - - .. uml:: - - :Nutzende Person: --> (Verwendung) - "Gruppe von\nAdministratoren" as Admin - "Verwenden der\nAnwendung" as (Verwendung) - Admin --> (Administrieren\nder Anwendung) - -Anwendungsfälle werden von runden Klammern ``()`` umschlossen und ähneln einem -Oval. - -Alternativ kann auch das Schlüsselwort ``usecase`` verwendet werden, um einen -Anwendungsfall zu definieren. Darüber hinaus ist es möglich, mit dem -Schlüsselwort ``as`` einen Alias zu definieren. Dieser Alias kann dann bei der -Definition von Beziehungen verwendet werden. - -Mit ``\n`` könnt ihr Zeilenumbrüche in den Namen der Anwendungsfälle einfügen. diff --git a/docs/editors.rst b/docs/editors.rst index 5029b536..5b8f2a0a 100644 --- a/docs/editors.rst +++ b/docs/editors.rst @@ -1,12 +1,14 @@ Editoren ======== +.. _interactive_shell: + Interaktive Shell ----------------- Mit der interaktiven Shell könnt ihr einfach die meisten Beispiele in diesem Tutorial ausführen. Später lernt ihr auch, wie ihr Code, der in eine Datei -geschrieben wurde, einfach als Modul eingebunden werden kann. +geschrieben wurde, einfach als Modul einbinden könnt. .. tab:: Linux @@ -62,10 +64,11 @@ und macOS verwenden oder :kbd:`Ctrl-z` unter Windows. Alternativ könnt ihr auch IDLE ---- -IDLE ist das Akronym für eine integrierte Entwicklungsumgebung (engl.: -integrated development environment) und kombiniert einen interaktiven -Interpreter mit Werkzeugen zur Code-Bearbeitung und Fehlersuche. Das Ausführen -ist sehr einfach auf den verschiedenen Plattformen: +:doc:`python3:library/idle` ist das Akronym für eine integrierte +Entwicklungs- und Lernumgebung (engl.: Integrated Development and Learning +Environment) und kombiniert einen interaktiven Interpreter mit Werkzeugen zur +Code-Bearbeitung und Fehlersuche. Das Ausführen ist sehr einfach auf den +verschiedenen Plattformen: .. tab:: Linux/macOS diff --git a/docs/explore.rst b/docs/explore.rst index f7f0b3d9..d3628bde 100644 --- a/docs/explore.rst +++ b/docs/explore.rst @@ -1,8 +1,8 @@ Python erkunden =============== -Egal, ob ihr IDLE oder die interaktive Shell nutzt, es gibt einige nützliche -Funktionen, um Python zu erkunden. +Egal, ob ihr :ref:`idle` oder die :ref:`interactive_shell` nutzt, es gibt einige +nützliche Funktionen, um Python zu erkunden. .. _help: @@ -39,29 +39,32 @@ Typ- oder Variablennamen als Parameter übergebt, :abbr:`z.B. (zum Beispiel)`: | | __abs__(self, /) | abs(self) - ... + ... -``dir()``, ``globals()`` und ``locals()`` ------------------------------------------ +So erfahrt ihr :abbr:`z.B. (zum Beispiel)`, dass ``x`` vom Typ ``float`` ist und +eine Funktion :func:`__add__` hat, die ihr mit Punkt-Notation verwenden könnt: -:py:func:`dir` ist eine weitere nützliche Funktion, die Objekte in einem -bestimmten :doc:`Namensraum ` auflistet. Wenn ihr sie ohne -Parameter verwendet, könnt ihr herausfinden, welche Methoden und Daten lokal -verfügbar sind. Alternativ kann sie auch Objekte für ein Modul oder einen Typ -auflisten. +.. code-block:: pycon + + >>> x.__add__(1) + 5.2 + +``dir()`` +--------- + +:py:func:`dir` ist eine weitere nützliche Funktion um herauszufinden, welche +Methoden und Daten lokal oder für ein bestimmtes Objekt verfügbar sind: .. code-block:: pycon >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'x'] - >>> dir(x) - ['__abs__', '__add__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getformat__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__pow__', '__radd__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__round__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', 'as_integer_ratio', 'conjugate', 'fromhex', 'hex', 'imag', 'is_integer', 'real'] -Im Gegensatz zu :py:func:`dir` zeigen sowohl :py:func:`globals` als auch -:py:func:`locals` die mit den Objekten verbundenen Werte an. Aktuell geben beide -Funktionen dasselbe zurück: +So können wir uns :abbr:`z.B. (zum Beispiel)` mit ``dir(__builtins__)`` eine +Liste dessen anzeigen lassen, was in der Python-Standardbibliothek bereits +verfügbar ist: .. code-block:: pycon - >>> globals() - {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'x': 4.2} + >>> dir(__builtins__) + ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BaseExceptionGroup', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'ExceptionGroup', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'PythonFinalizationError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '_IncompleteInputError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] diff --git a/docs/functions/decorators.rst b/docs/functions/decorators.rst index 1b85ae80..3fab0dad 100644 --- a/docs/functions/decorators.rst +++ b/docs/functions/decorators.rst @@ -11,21 +11,21 @@ kann dann anstelle der ursprünglichen Funktion verwendet werden: .. code-block:: pycon :linenos: - >>> def inf(func): - ... print("Information about", func.__name__) - ... def details(*args): - ... print("Execute function", func.__name__, "with the argument(s)") - ... return func(*args) - ... return details - ... - >>> def my_func(*params): - ... print(params) - ... - >>> my_func = inf(my_func) - Information about my_func - >>> my_func("Hello", "Pythonistas!") - Execute function my_func with the argument(s) - ('Hello', 'Pythonistas!') + >>> def inf(func): + ... print("Information about", func.__name__) + ... def details(*args): + ... print("Execute function", func.__name__, "with the argument(s)") + ... return func(*args) + ... return details + ... + >>> def my_func(*params): + ... print(params) + ... + >>> my_func = inf(my_func) + Information about my_func + >>> my_func("Hello", "Pythonistas!") + Execute function my_func with the argument(s) + ('Hello', 'Pythonistas!') Zeile 2 Die ``inf``-Funktion gibt den Namen der Funktion, die sie umhüllt, aus. @@ -44,20 +44,20 @@ Verwendung eines Dekorators besteht ganz einfach aus zwei Teilen: #. der Verwendung eines ``@``, gefolgt von dem Dekorator, unmittelbar bevor die umhüllte Funktion definiert wird. -Die Dekorfunktion sollte eine Funktion als Parameter annehmen und eine Funktion -zurückgeben, wie folgt: +Die Dekorator-Funktion sollte eine Funktion als Parameter annehmen und eine +Funktion zurückgeben, wie folgt: .. code-block:: pycon :linenos: - >>> @inf - ... def my_func(*params): - ... print(params) - ... - Information about my_func - >>> my_func("Hello", "Pythonistas!") - Execute function my_func with the argument(s) - ('Hello', 'Pythonistas!') + >>> @inf + ... def my_func(*params): + ... print(params) + ... + Information about my_func + >>> my_func("Hello", "Pythonistas!") + Execute function my_func with the argument(s) + ('Hello', 'Pythonistas!') Zeile 1 Die Funktion ``my_func`` wird mit ``@inf`` dekoriert. @@ -65,10 +65,6 @@ Zeile 7 Die umhüllte Funktion wird aufgerufen, nachdem die Dekorator-Funktion fertig ist. -.. tip:: - `cusy Seminar: Fortgeschrittenes Python - `_ - ``functools`` ------------- @@ -77,8 +73,8 @@ also Funktionen, die auf andere Funktionen wirken oder diese zurückgeben. Meist könnt ihr sie als Dekoratoren verwenden, so :abbr:`u.a. (unter anderem)`: :func:`functools.cache` - Einfacher, leichtgewichtiger, Funktionscache ab Python ≥ 3.9, der manchmal - auch *memoize* genannt wird. Er gibt dasselbe zurück wie + Einfacher, leichtgewichtiger, Cache für Funktionen ab Python ≥ 3.9, der + manchmal auch *memoize* genannt wird. Er gibt dasselbe zurück wie :func:`functools.lru_cache` mit dem Parameter ``maxsize=None``, wobei zusätzlich ein :doc:`/types/dicts` mit den Funktionsargumenten erstellt wird. Da alte Werte nie gelöscht werden müssen, ist diese Funktion dann @@ -138,3 +134,7 @@ könnt ihr sie als Dekoratoren verwenden, so :abbr:`u.a. (unter anderem)`: 'wrapper' >>> example.__doc__ 'Wrapper docstring' + +.. tip:: + `cusy Seminar: Fortgeschrittenes Python + `_ diff --git a/docs/functions/index.rst b/docs/functions/index.rst index e2e8b8b5..f18b6230 100644 --- a/docs/functions/index.rst +++ b/docs/functions/index.rst @@ -8,24 +8,23 @@ Die grundlegende Syntax für eine Python-Funktionsdefinition lautet def function_name(param1, param2): body -Wie bei :doc:`Kontrollströmen ` verwendet Python +Wie bei :doc:`Kontrollströmen ` verwendet Python Einrückungen, um die Funktion von der Funktionsdefinition abzugrenzen. Das folgende einfache Beispiel fügt den Code in eine Funktion ein, so dass ihr diese aufrufen könnt, um die `Fakultät `_ einer Zahl zu erhalten: -.. code-block:: pycon +.. code-block:: python :linenos: - >>> def fact(n): - ... """Return the factorial of the given number.""" - ... f = 1 - ... while n > 0: - ... f = f * n - ... n = n - 1 - ... return f - ... + def fact(n): + """Return the factorial of the given number.""" + f = 1 + while n > 0: + f = f * n + n = n - 1 + return f Zeile 2 Dies ist ein optionaler Dokumentationsstring, oder ``docstring``. Ihr könnt @@ -33,7 +32,7 @@ Zeile 2 Docstrings ist es, das Verhalten einer Funktion und die Parameter, die sie annimmt, zu beschreiben, während Kommentare interne Informationen über die Funktionsweise des Codes dokumentieren sollen. Docstrings sind - :doc:`/types/strings`, die unmittelbar auf die erste Zeile einer + :doc:`/types/strings/index`, die unmittelbar auf die erste Zeile einer Funktionsdefinition folgen und normalerweise in dreifachen Anführungszeichen stehen, um mehrzeilige Beschreibungen zu ermöglichen. Bei mehrzeiligen Dokumentationsstrings ist es üblich, in der ersten Zeile eine @@ -41,7 +40,8 @@ Zeile 2 Zeile folgen zu lassen und mit dem Rest der Informationen zu enden. .. seealso:: - * :doc:`../document/docstrings` + * :doc:`../document/sphinx/docstrings` + * :pep:`257` Zeile 7 Der Wert wird nach dem Aufruf der Funktion zurückgegeben. Ihr könnt auch @@ -64,69 +64,45 @@ Rückgabewert einer Funktion verwendet wird: Zeile 1 Der Rückgabewert ist nicht mit einer Variablen verknüpft. Zeile 2 - Der Wert der ``fact``-Funktion wird nur im Interpreter ausgegeben. + Der Wert der :func:`fact`-Funktion wird nur im Interpreter ausgegeben. Zeile 3 Der Rückgabewert ist mit der Variablen ``x`` verknüpft. -Parameter ---------- +Inspiriert durch :doc:`Python4DataScience:productive/qa/mypy` wurden in Python +:abbr:`sog. (sogenannte)` *Type Hints* eingeführt, womit die Typen für +:doc:`params` und Rückgabewerte definiert werden können, in unserem +:func:`fact`-Beispiel mit: + +.. blacken-docs:off +.. code-block:: python + + def fact(n: int) -> int: + ... +.. blacken-docs:on + +oder: -Python bietet flexible Mechanismen zur Übergabe von Argumenten an Funktionen: +.. blacken-docs:off +.. code-block:: python + + def factlist(flist: list[float]) -> list[float]: + ... +.. blacken-docs:on + +Wir erhalten zur Laufzeit die Typen mit dem ``__annotations__``-Attribut: .. code-block:: pycon - :linenos: - - >>> x, y = 2, 3 - >>> def func1(u, v, w): - ... value = u + 2 * v + w**2 - ... if value > 0: - ... return u + 2 * v + w**2 - ... else: - ... return 0 - ... - >>> func1(x, y, 2) - 12 - >>> func1(x, w=y, v=2) - 15 - >>> def func2(u, v=1, w=1): - ... return u + 4 * v + w**2 - ... - >>> func2(5, w=6) - 45 - >>> def func3(u, v=1, w=1, *tup): - ... print((u, v, w) + tup) - ... - >>> func3(7) - (7, 1, 1) - >>> func3(1, 2, 3, 4, 5) - (1, 2, 3, 4, 5) - >>> def func4(u, v=1, w=1, **kwargs): - ... print(u, v, w, kwargs) - ... - >>> func4(1, 2, s=4, t=5, w=3) - 1 2 3 {'s': 4, 't': 5} -Zeile 2 - Funktionen werden mit Hilfe der ``def``-Anweisung definiert. -Zeile 5 - Die ``return``-Anweisung wird von einer Funktion verwendet, um einen Wert - urückzugeben. Dieser Wert kann von beliebigem Typ sein. Wird keine - ``return``-Anweisung gefunden, wird der Wert ``None`` von Python - zurückgegeben. -Zeile 11 - Funktionsargumente können entweder nach Position oder nach Name - (Schlüsselwort) eingegeben werden. ``z`` und ``y`` werden in unserem - Beispiel mit dem Namen angegeben. -Zeile 13 - Funktionsparameter können mit Standardwerten definiert werden, die - verwendet werden, wenn ein Funktionsaufruf sie auslässt. -Zeile 18 - Es kann ein spezieller Parameter definiert werden, der alle zusätzlichen - Positionsargumente in einem Funktionsaufruf in einem Tupel zusammenfasst. -Zeile 25 - Ebenso kann ein spezieller Parameter definiert werden, der alle - zusätzlichen Schlüsselwortargumente in einem Funktionsaufruf in einem - Dictionary zusammenfasst. + >>> fact.__annotations__ + {'n': , 'return': } + >>> factlist.__annotations__ + {'list': list[float], 'return': list[float]} + +Es findet jedoch zur Laufzeit **keine** Typ-Überprüfung statt. + +.. seealso:: + * :pep:`484` + * :doc:`python3:library/typing` .. toctree:: :titlesonly: diff --git a/docs/functions/params.rst b/docs/functions/params.rst index b9507b80..d312dbdf 100644 --- a/docs/functions/params.rst +++ b/docs/functions/params.rst @@ -1,6 +1,64 @@ Parameter ========= +Python bietet flexible Mechanismen zur Übergabe von :term:`Argumenten +` an :term:`Funktionen `: + +.. code-block:: pycon + :linenos: + + >>> x, y = 2, 3 + >>> def func1(u, v, w): + ... value = u + 2 * v + w**2 + ... if value > 0: + ... return u + 2 * v + w**2 + ... else: + ... return 0 + ... + >>> func1(x, y, 2) + 12 + >>> func1(x, w=y, v=2) + 15 + >>> def func2(u, v=1, w=1): + ... return u + 4 * v + w**2 + ... + >>> func2(5, w=6) + 45 + >>> def func3(u, v=1, w=1, *tup): + ... print((u, v, w) + tup) + ... + >>> func3(7) + (7, 1, 1) + >>> func3(1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + >>> def func4(u, v=1, w=1, **kwargs): + ... print(u, v, w, kwargs) + ... + >>> func4(1, 2, s=4, t=5, w=3) + 1 2 3 {'s': 4, 't': 5} + +Zeile 2 + Funktionen werden mit Hilfe der ``def``-Anweisung definiert. +Zeile 5 + Die ``return``-Anweisung wird von einer Funktion verwendet, um einen Wert + zurückzugeben. Dieser Wert kann von beliebigem Typ sein. Wird keine + ``return``-Anweisung gefunden, wird der Wert ``None`` von Python + zurückgegeben. +Zeile 11 + Funktionsargumente können entweder nach Position oder nach Name + (Schlüsselwort) eingegeben werden. ``z`` und ``y`` werden in unserem + Beispiel mit dem Namen angegeben. +Zeile 13 + Funktionsparameter können mit Standardwerten definiert werden, die + verwendet werden, wenn ein Funktionsaufruf sie auslässt. +Zeile 18 + Es kann ein spezieller Parameter definiert werden, der alle zusätzlichen + Positionsargumente in einem Funktionsaufruf in einem Tupel zusammenfasst. +Zeile 25 + Ebenso kann ein spezieller Parameter definiert werden, der alle + zusätzlichen Schlüsselwortargumente in einem Funktionsaufruf in einem + Dictionary zusammenfasst. + Optionen für Funktionsparameter ------------------------------- @@ -13,7 +71,7 @@ Positionsbezogene Parameter Die einfachste Art, Parameter an eine Funktion in Python zu übergeben, ist die Übergabe an der Position. In der ersten Zeile der Funktion gebt ihr den Variablennamen für jeden Parameter an; wenn die Funktion aufgerufen wird, werden -die im aufrufenden Code verwendeten Parameter den Parametervariablen der +die im aufrufenden Code verwendeten Parameter den Parameter-Variablen der Funktion auf der Grundlage ihrer Reihenfolge zugeordnet. Die folgende Funktion berechnet ``x`` als Potenz von ``y``: @@ -50,7 +108,7 @@ so: pass Es können beliebig viele Parameter mit Standardwerten versehen werden wobei -Parameter mit Standardwerten als letzte in der Parameterliste definiert werden +Parameter mit Standardwerten als letzte in der Parameter-Liste definiert werden müssen. Die folgende Funktion berechnet ``x`` ebenfalls als Potenz von ``y``. Wenn ``y`` @@ -67,7 +125,7 @@ jedoch nicht in einem Funktionsaufruf angegeben wird, wird der Standardwert ... return p ... -Wie sich das Standardargument auswirkt, können ihr im folgenden Beispiel sehen: +Wie sich das Standardargument auswirkt, könnt ihr im folgenden Beispiel sehen: .. code-block:: pycon @@ -91,7 +149,7 @@ dem vorherigen Beispiels könnt ihr Folgendes eingeben: Da die Argumente für die Potenz im letzten Aufruf mit ``x`` und ``y`` benannt sind, ist ihre Reihenfolge irrelevant; die Argumente sind mit den gleichnamigen Parametern in der Definition der Potenz verknüpft, und man erhält ``2^6`` -zurück. Diese Art der Argumentübergabe wird als Schlüsselwortübergabe +zurück. Diese Art der Argument-Übergabe wird als Schlüsselwort-Übergabe bezeichnet. Die Übergabe von Schlüsselwörtern kann in Kombination mit den Standardargumenten von Python-Funktionen sehr nützlich sein, wenn ihr Funktionen mit einer großen Anzahl von möglichen Argumenten definiert, von denen die @@ -103,13 +161,13 @@ Variable Anzahl von Argumenten Python-Funktionen können auch so definiert werden, dass sie mit einer variablen Anzahl von Argumenten umgehen können. Dies ist auf zweierlei Arten möglich. Die eine Methode sammelt eine unbekannte Anzahl von Argumenten in einer :doc:`Liste -`. Die andere Methode kann eine beliebige Anzahl von Argumenten, -die mit einem Schlüsselwort übergeben wurde und die keinen entsprechend -benannten Parameter in der Funktionsparameterliste hat, in einem :doc:`Dict -` sammeln. +`. Die andere Methode kann eine beliebige Anzahl von +Argumenten, die mit einem Schlüsselwort übergeben wurde und die keinen +entsprechend benannten Parameter in der Liste der Funktionsparameter hat, in +einem :doc:`Dict ` sammeln. Bei einer unbestimmten Anzahl von Positionsargumenten bewirkt das Voranstellen -eines ``*`` vor den endgültigen Parameternamen der Funktion, dass alle +eines ``*`` vor den endgültigen Parameter-Namen der Funktion, dass alle überschüssigen Nicht-Schlüsselwort-Argumente in einem Funktionsaufruf, :abbr:`d.h. (das heißt)` die Positionsargumente, die keinem anderen Parameter zugewiesen sind, gesammelt und als Tupel dem angegebenen Parameter zugewiesen @@ -134,7 +192,7 @@ mit: >>> mean(3, 5, 2, 4, 6) 4.0 -Eine beliebige Anzahl von Schlüsselwortargumenten kann ebenfalls verarbeitet +Eine beliebige Anzahl von Schlüsselwort-Argumenten kann ebenfalls verarbeitet werden, wenn dem letzten Parameter in der Parameterliste das Präfix ``**`` vorangestellt ist. Dann werden alle Argumente, die mit einem Schlüsselwort übergeben wurden, in einem :doc:`Dict ` gesammelt. Der Schlüssel @@ -147,17 +205,13 @@ Beispiel)`: .. code-block:: pycon - >>> def server(ip, port, **other): - ... print( - ... "ip: {0}, port: {1}, keys in 'other': {2}".format( - ... ip, port, list(other.keys()) - ... ) - ... ) - ... total = 0 - ... for k in other.keys(): - ... total = total + other[k] - ... print("The sum of the other values is {0}".format(total)) - ... + >>> def server(ip, port, **other): + ... print(f"ip: {ip}, port: {port}, other: {other}") + ... total = 0 + ... for k in other.keys(): + ... total = total + other[k] + ... print(f"The sum of the other values is {total}") + ... Das Ausprobieren dieser Funktion zeigt, dass sie die Argumente addieren kann, die unter den Schlüsselwörtern ``foo``, ``bar`` und ``baz`` übergeben werden, @@ -166,15 +220,15 @@ Parameternamen sind: .. code-block:: pycon - >>> server("127.0.0.1", port="8080", foo=3, bar=5, baz=2) - ip: 127.0.0.1, port: 8080, keys in 'other': ['foo', 'bar', 'baz'] - The sum of the other values is 10 + >>> server("127.0.0.1", port="8080", foo=3, bar=5, baz=2) + ip: 127.0.0.1, port: 8080, other: {'foo': 3, 'bar': 5, 'baz': 2} + The sum of the other values is 10 -Techniken zur Argumentübergabe mischen -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Techniken zur Argument-Übergabe mischen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Es ist möglich, alle Argumentübergabe-Möglichkeiten von Python-Funktionen -gleichzeitig zu verwenden, obwohl dies verwirrend sein kann, wenn ihr es nicht +Ihr könnt alle Möglichkeiten zur Argument-Übergabe von Python-Funktionen +gleichzeitig verwenden, obwohl dies verwirrend sein kann, wenn ihr es nicht sorgfältig macht. Dabei sollten die Positionsargumente an erster Stelle stehen, dann benannte Argumente, gefolgt von unbestimmten Positionsargumenten mit einem einfachen ``*`` und zuletzt unbestimmte Schlüsselwortargumente mit ``**``. @@ -183,13 +237,15 @@ Veränderliche Objekte als Argumente ----------------------------------- Argumente werden per Objektreferenz übergeben. Der Parameter wird zu einem neuen -Verweis auf das Objekt. Bei unveränderlichen Objekten wie :doc:`/types/tuples`, -:doc:`/types/strings` und :doc:`/types/numbers` hat das, was mit einem Parameter -gemacht wird, keine Auswirkungen außerhalb der Funktion. Wenn ihr jedoch ein -veränderliches Objekt übergeben, :abbr:`z.B. (zum Beispiel)` eine :doc:`Liste -`, ein :doc:`Dict ` oder eine Klasseninstanz, ändert -jede Änderung des Objekts, worauf das Argument außerhalb der Funktion verweist. -Die Neuzuweisung des Parameters hat keine Auswirkungen auf das Argument. +Verweis auf das Objekt. Bei :term:`unveränderlichen ` Objekten +wie :doc:`/types/sequences-sets/tuples`, :doc:`/types/strings/index` und +:doc:`/types/numbers/index` hat das, was mit einem Parameter gemacht wird, keine +Auswirkungen außerhalb der Funktion. Wenn ihr jedoch ein veränderliches Objekt +übergebt, :abbr:`z.B. (zum Beispiel)` eine :doc:`Liste +`, ein :doc:`Dict ` oder eine +Klasseninstanz, ändert jede Änderung des Objekts, worauf das Argument außerhalb +der Funktion verweist. Die Neuzuweisung des Parameters hat keine Auswirkungen +auf das Argument. .. code-block:: pycon @@ -203,13 +259,13 @@ Die Neuzuweisung des Parameters hat keine Auswirkungen auf das Argument. >>> x, y (5, [2, 4, 6, 1]) -Die Variable ``x`` wird nicht geändert, da sie unveränderlich ist. Stattdessen -wird der Funktionsparameter ``n`` so gesetzt, dass er auf den neuen Wert ``6`` -verweist. Bei ``y`` gibt es jedoch eine Änderung, weil die Liste, auf die sie -verweist, geändert wurde. +Die Variable ``x`` wird nicht geändert, da sie :term:`unveränderlich +` ist. Stattdessen wird der Funktionsparameter ``n`` so gesetzt, +dass er auf den neuen Wert ``6`` verweist. Bei ``y`` gibt es jedoch eine +Änderung, weil die Liste, auf die sie verweist, geändert wurde. Checks ------ * Schreibt eine Funktion, die eine beliebige Anzahl von unbenannten Argumenten - annehmen und deren Werte in umgekehrter Reihenfolge ausgeben kann? + annehmen und deren Werte in umgekehrter Reihenfolge ausgeben kann. diff --git a/docs/functions/variables.rst b/docs/functions/variables.rst index 3a18a767..782312b8 100644 --- a/docs/functions/variables.rst +++ b/docs/functions/variables.rst @@ -1,45 +1,66 @@ Variablen ========= -Lokale, nicht-lokale und globale Variablen ------------------------------------------- +.. _local_variables: + +Lokale Variablen +---------------- Hier kehren wir zur Definition von ``fact`` vom Anfang dieses :doc:`index`-Kapitels zurück: -.. code-block:: pycon +.. code-block:: python - >>> def fact(n): - ... """Return the factorial of the given number.""" - ... f = 1 - ... while n > 0: - ... f = f * n - ... n = n - 1 - ... return f - ... + def fact(n): + """Return the factorial of the given number.""" + f = 1 + while n > 0: + f = f * n + n = n - 1 + return f Sowohl die Variablen ``f`` als auch ``n`` sind lokal für einen bestimmten Aufruf der Funktion ``fact``; Änderungen an ihnen, die während der Ausführung der Funktion vorgenommen werden, haben keine Auswirkungen auf Variablen außerhalb -der Funktion. Alle Variablen in der Parameterliste einer Funktion und alle +der Funktion. Alle Variablen in der Parameter-Liste einer Funktion und alle Variablen, die innerhalb einer Funktion durch eine Zuweisung erzeugt werden, wie -:abbr:`z.B. (zum Beispiel)` ``f = 1``, sind für die Funktion lokal. +:abbr:`z.B. (zum Beispiel)` ``f = 1``, sind für die Funktion lokal: + +.. code-block:: pycon + + >>> fact(3) + 6 + >>> f + Traceback (most recent call last): + File "", line 1, in + f + NameError: name 'f' is not defined + >>> n + Traceback (most recent call last): + File "", line 1, in + n + NameError: name 'n' is not defined + +.. _global_variables: + +Globale Variablen +----------------- Ihr könnt eine Variable explizit zu einer globalen Variable machen, indem ihr -sie mit der ``global``-Anweisung deklariert, bevor sie verwendet wird. Globale -Variablen können von der Funktion angesprochen und geändert werden. Sie -existieren außerhalb der Funktion und können auch von anderen Funktionen, die -sie als global deklarieren, oder von Code, der sich nicht innerhalb einer -Funktion befindet, aufgerufen und geändert werden. Hier ein Beispiel, das den -Unterschied zwischen lokalen und globalen Variablen verdeutlicht: +sie mit der :ref:`global `-Anweisung deklariert, bevor sie +verwendet wird. Globale Variablen können von der Funktion angesprochen und +geändert werden. Sie existieren außerhalb der Funktion und können auch von +anderen Funktionen, die sie als global deklarieren, oder von Code, der sich +nicht innerhalb einer Funktion befindet, aufgerufen und geändert werden. Hier +ein Beispiel, das den Unterschied zwischen lokalen und globalen Variablen +verdeutlicht: -.. code-block:: pycon +.. code-block:: python - >>> def my_func(): - ... global x - ... x = 1 - ... y = 2 - ... + def my_func(): + global x + x = 1 + y = 2 .. code-block:: pycon @@ -62,21 +83,39 @@ innerhalb von ``my_func`` verweist zunächst auf denselben Wert wie die Variable ``y`` außerhalb von ``my_func``, aber die Zuweisung bewirkt, dass ``y`` auf einen neuen Wert verweist, der für die Funktion ``my_func`` lokal ist. +.. _nonlocal_variables: -.. seealso:: +Nicht-lokale Variablen +---------------------- + +Während :ref:`global ` für eine Variable der obersten Ebene +verwendet wird, bezieht sich :ref:`nonlocal ` auf jede +Variable in einem umschließenden Bereich: + +.. code-block:: python + + def enclosing(): + x = "Enclosing function variable" - * :ref:`python3:global` + def enclosed(): + nonlocal x + x = "Enclosed function variable" + + enclosed() + print(x) + +.. code-block:: pycon -Während ``global`` für eine Variable der obersten Ebene verwendet wird, bezieht -sich ``nonlocal`` auf jede Variable in einem umschließenden Bereich. + >>> enclosing() + Enclosed function variable .. seealso:: - * :ref:`python3:nonlocal` * :pep:`3104` Checks ------ -* Angenommen, ``x = 1``, welchen Wert hat ``x`` nach der Ausführung von - ``func()`` und ``gfunc()``? +* Angenommen, ``x = 1``, :func:`func` setze die lokale Variable ``x`` auf ``2`` + und :func:`gfunc` die globale Variable ``x`` auf ``3``, welchen Wert nimmt + ``x`` an, nachdem :func:`func` und :func:`gfunc` durchlaufen wurden? diff --git a/docs/index.rst b/docs/index.rst index d3b5d219..1d634703 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,6 +8,21 @@ als umfassendes Nachschlagewerk für Python gedacht, sondern das Ziel ist vielmehr, euch grundlegend mit Python vertraut zu machen und euch schnell das Schreiben eigener Programme zu ermöglichen. +Das Buch ist veröffentlicht unter der `BSD-3-Clause license +`_, +so dass ihr das Buch :abbr:`u.a. (unter anderem)` für eure Zwecke anpassen und +veröffentlichen dürft, sofern ihr die Lizenz und den Copyright-Hinweis +beibehaltet. + +Ich möchte der `cusy GmbH `_ danken, die mir +großzügig ermöglicht, meine Zeit mit dem Schreiben dieses Buches zu verbringen. +Darüberhinaus möchte ich Kristian Rother nicht nur für die Unterstützung und +den Rat danken, den er mir im Laufe der Jahre zu diesem Buch gegeben hat, +sondern auch für das Lektorat, das dieses Buch besser gemacht hat. Danken möchte +ich auch Steffen Dahlem, für die Idee und Realisierung einer nachhaltigeren +Buchproduktion. Ein herzlicher Dank geht schließlich auch an die +Rezensent*innen, deren Einblicke und Rückmeldungen eine große Hilfe waren. + .. note:: Wenn ihr Vorschläge für Verbesserungen und Ergänzungen habt, freue ich mich über eure :doc:`Verbesserungsvorschläge `. @@ -19,7 +34,7 @@ und -visualisierung: * `Python für Data Science `_ * `PyViz-Tutorial `_ * `cusy Design-System: Datenvisualisierung - `_ + `_ Alle Tutorials dienen als Seminarunterlagen für unsere aufeinander abgestimmten Trainings: @@ -95,25 +110,32 @@ Trainings: .. _`Neues aus Python für Data-Science`: https://cusy.io/de/unsere-schulungsangebote/neues-aus-python-fuer-data-science +Folgt uns auf… + +.. include:: ../README.rst + :start-after: follow-us: + :end-before: end-follow-us: + .. toctree:: :titlesonly: :hidden: intro + changelog install editors explore style variables-expressions types/index - input - control-flows/index + control-flow/index functions/index modules/index libs/index + packs/index oop/index save-data/index - dataclasses + logging/index test/index document/index appendix/index diff --git a/docs/install.rst b/docs/install.rst index aa7dbc1b..9948b2f5 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -4,7 +4,7 @@ Installation Die Installation von Python kann einfach sein. Der erste Schritt besteht darin, die aktuelle Version von `www.python.org/downloads `_ herunterzuladen. Das Tutorial basiert auf -Python 3.13.0, falls ihr jedoch Python 3.8 oder neuer installiert habt, sollte +Python 3.13.0, falls ihr jedoch Python 3.10 oder neuer installiert habt, sollte das auch kein Problem sein. .. tab:: Linux @@ -31,10 +31,6 @@ das auch kein Problem sein. um nach Sicherheitsupdates zu suchen, da es keinen integrierten Auto-Updater gibt. - Werden ältere Python-Versionen benötigt, :abbr:`z.B. (zum Beispiel)` um - Bibliotheken mit :doc:`test/tox` zu testen, verwende ich `deadsnakes - `_. - .. tab:: macOS Ihr könnt Python direkt von https://www.python.org/downloads/macos/ beziehen. @@ -48,8 +44,7 @@ das auch kein Problem sein. ``mopup`` aufrufen, um die aktuellste Version eurer Python-Installation zu erhalten. - Werden ältere Python-Versionen benötigt, :abbr:`z.B. (zum Beispiel)` um - Bibliotheken mit :doc:`test/tox` zu testen, kann `python-build-standalone + Werden ältere Python-Versionen benötigt, kann `python-build-standalone `_ verwendet werden. @@ -74,7 +69,7 @@ das auch kein Problem sein. .. code-block:: ps1con - C:\> python -V + C:\> py -V Python 3.13.0 .. warning:: @@ -82,6 +77,11 @@ das auch kein Problem sein. um nach Sicherheitsupdates zu suchen, da es keinen integrierten Auto-Updater gibt. +.. _various-python-versions: + +Um mehrere Python-Projekte mit unterschiedlichen Versionen zu verwalten +empfehle ich :term:`uv`. + .. tip:: `direnv `_ erlaubt euch, Umgebungsvariablen je nach Verzeichnis zu setzen. Damit lassen sich Umgebungsvariablen von `The diff --git a/docs/intro.rst b/docs/intro.rst index 8ba01f34..0fa490be 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -8,9 +8,9 @@ Vielleicht stellt ihr euch die Frage, warum ihr Python lernen solltet. Es gibt viele Programmiersprachen von C und C++ über Java bis hin zu Lua und Go. .. figure:: tiobe-index.svg - :alt: TIOBE Index für Juni 2024 + :alt: TIOBE Index für May 2025 - `TIOBE Index für Juni 2024 `_ + `TIOBE Index für May 2025 `_ Python hat eine sehr große Verbreitung gefunden und einer der Gründe dürfte sein, dass @@ -61,7 +61,7 @@ Open Source privater Anwendungen frei verwenden. Dabei wird Python von vielen etablierten Unternehmen genutzt und gefördert, :abbr:`u.a. (unter anderem)` von Google, Meta und Bloomberg. Und wenn ihr etwas zurückgeben wollt, könnt - ihr dies ebenfalls gerne machen : `Python Software Foundation Sponsorship + ihr dies ebenfalls gerne machen: `Python Software Foundation Sponsorship `_. Python hat zwar einige Vorteile, aber keine Sprache ist in allen Bereichen @@ -90,8 +90,8 @@ Variablentypen Anders als in vielen anderen Sprachen sind Variablen keine Container, sondern eher Etiketten, die auf verschiedene Objekte verweisen: Ganzzahlen, Zeichenketten, Klasseninstanzen und vieles mehr. Manche empfinden es als - Nachteil, dass Python hier nicht einfach eine Typvalidierung durchführt, - aber die Anzahl der Typfehler ist meist überschaubar und die Flexibilität + Nachteil, dass Python hier nicht einfach eine Typ-Validierung durchführt, + aber die Anzahl der Typ-Fehler ist meist überschaubar und die Flexibilität der dynamischen Typisierung wiegt die Probleme meist auf. Unterstützung für mobile Geräte Es gibt mittlerweile einige Optionen, Python auf mobilen Geräten laufen zu @@ -100,7 +100,7 @@ Unterstützung für mobile Geräte `_ :abbr:`u.a. (unter anderem)` für Windows, iOS und Pi OS bieten. Zudem soll es zukünftig einfacher werden, :ref:`wheels` auch für mobile Endgeräte zu erstellen, indem Tools wie - :doc:`libs/cibuildwheel` und :term:`setuptools` erweitert werden. + :doc:`packs/cibuildwheel` und :term:`setuptools` erweitert werden. .. seealso:: * `The Python Language Summit 2024: Python on Mobile diff --git a/docs/libs/batteries.rst b/docs/libs/batteries.rst index 857b03f7..988175f7 100644 --- a/docs/libs/batteries.rst +++ b/docs/libs/batteries.rst @@ -2,12 +2,13 @@ ====================== In Python kann eine Bibliothek aus mehreren Komponenten bestehen, einschließlich -eingebauter Datentypen und Konstanten, die ohne eine Importanweisung verwendet -werden können, wie :abbr:`z.B. (zum Beispiel)` :doc:`/types/numbers` und -:doc:`/types/lists`, sowie einiger eingebauter :doc:`/functions/index` und -:doc:`/control-flows/exceptions`. Der größte Teil der Bibliothek ist eine -umfangreiche Sammlung von :doc:`Modulen `. Wenn ihr Python -installiert habt, stehen euch auch verschiedene Bibliotheken zur Verfügung zum +eingebauter Datentypen und Variablen, die ohne eine Importanweisung verwendet +werden können, wie :abbr:`z.B. (zum Beispiel)` :doc:`/types/numbers/index` und +:doc:`/types/sequences-sets/lists`, sowie einiger eingebauter +:doc:`/functions/index` und :doc:`/control-flow/exceptions`. Der größte Teil der +Bibliothek ist eine umfangreiche Sammlung von :doc:`Modulen `. +Wenn ihr Python installiert habt, stehen euch auch verschiedene Bibliotheken zur +Verfügung zum * :ref:`data-types` * :ref:`files-storage` @@ -28,8 +29,9 @@ Datentypen und Zahlen. String-Module ~~~~~~~~~~~~~ -.. include:: ../types/strings.rst - :start-after: string-modules +.. include:: ../types/strings/built-in-modules/index.rst + :start-after: string-modules: + :end-before: end-string-modules: Module für Datentypen ~~~~~~~~~~~~~~~~~~~~~ @@ -62,16 +64,18 @@ Module für Datentypen Module für Zahlen ~~~~~~~~~~~~~~~~~ -.. include:: ../types/numbers.rst - :start-after: number-modules +.. include:: ../types/numbers/index.rst + :start-after: number-modules: + :end-before: end-number-modules: .. _files-storage: Ändern von Dateien ------------------ -.. include:: ../types/files.rst - :start-after: file-modules +.. include:: ../save-data/files.rst + :start-after: file-modules: + :end-before: end-file-modules: .. _os: diff --git a/docs/libs/gitlab.rst b/docs/libs/gitlab.rst deleted file mode 100644 index 7500b068..00000000 --- a/docs/libs/gitlab.rst +++ /dev/null @@ -1,113 +0,0 @@ -GitLab Package Registry -======================= - -Ihr könnt eure Verteilungspakete auch in der Paketregistrierung eures -GitLab-Projekts veröffentlichen und sowohl mit :term:`Pip` als auch mit -:term:`twine` nutzen. - -.. seealso:: - `PyPI packages in the Package Registry - `_ - -Authentifizierung ------------------ - -Zur Authentifizierung an der GitLab Package Registry könnt ihr eine der -folgenden Methoden verwenden: - -* Ein :ref:`persönliches Zugriffstoken - ` mit dem Geltungsbereich ``api``. -* Ein :ref:`Deploy-Token ` mit den Geltungsbereichen - ``read_package_registry``, ``write_package_registry`` oder beiden. -* Ein :ref:`CI-Job-Token `. - -.. _personal-access-tokens: - -… mit einem persönlichen Zugriffstoken -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Um euch mit einem persönlichen Zugriffstoken zu authentifizieren, könnt ihr in -der ``~/.pypirc``-Datei :abbr:`z.B. (zum Beispiel)` folgendes hinzufügen: - -.. code-block:: ini - - [distutils] - index-servers= - gitlab - - [gitlab] - repository = https://ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi - username = {NAME} - password = {YOUR_PERSONAL_ACCESS_TOKEN} - -.. _deploy-tokens: - -… mit einem Deploy-Token -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: ini - - [distutils] - index-servers = - gitlab - - [gitlab] - repository = https://ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi - username = {DEPLOY_TOKEN_USERNAME} - password = {DEPLOY_TOKEN} - -.. _ci-job-token: - -… mit einem Job-Token -~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: yaml - - image: python:latest - - run: - script: - - pip install build twine - - python -m build - - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/* - -… für den Zugriff auf Pakete innerhalb einer Gruppe -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Verwendet statt der :samp:`{PROJECT_ID}` die :samp:`{GROUP_URL}`. - -Veröffentlichen des Verteilungspakets -------------------------------------- - -Ihr könnt euer Paket mit Hilfe von :term:`twine` veröffentlichen: - -.. code-block:: console - - python3 -m twine upload --repository gitlab dist/* - -.. note:: - Wenn ihr versucht, ein Paket zu veröffentlichen, das bereits mit demselben - Namen und derselben Version existiert, erhaltet ihr den Fehler ``400 Bad - Request``; ihr müssen das vorhandene Paket dann zuerst löschen. - -Installieren des Pakets ------------------------ - -Ihr könnt die neueste Version eures Pakets installieren :abbr:`z.B. (zum -Beispiel)` mit - -.. code-block:: console - - pip install --index-url https://{NAME}:{PERSONAL_ACCESS_TOKEN}@ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi/simple --no-deps {PACKAGE_NAME} - -… oder von der Gruppenebene aus mit - -.. code-block:: console - - pip install --index-url https://{NAME}:{PERSONAL_ACCESS_TOKEN}@ce.cusy.io/api/v4/groups/{GROUP_ID}/-/packages/pypi/simple --no-deps {PACKAGE_NAME} - -… oder in der :file:`requirements.txt`-Datei mit - -.. code-block:: - - --extra-index-url https://ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi/simple {PACKAGE_NAME} diff --git a/docs/libs/glossary.rst b/docs/libs/glossary.rst deleted file mode 100644 index cd3256b4..00000000 --- a/docs/libs/glossary.rst +++ /dev/null @@ -1,449 +0,0 @@ -Glossar -======= - -.. glossary:: - - build - ``build`` ist ein :pep:`517`-kompatibler Python-Paket-Builder. Er bietet - eine CLI zum Erstellen von Paketen sowie eine Python-API. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Built Distribution - bdist - Eine Struktur aus Dateien und Metadaten, die bei der Installation nur an - den richtigen Speicherort auf dem Zielsystem verschoben werden müssen. - :term:`wheel` ist ein solches Format, nicht jedoch *distutil’s* - :term:`Source Distribution`, die einen Build-Schritt erfordern. - - cibuildwheel - :doc:`/libs/cibuildwheel` ist ein Python-Paket, das :term:`wheels - ` für alle gängigen Plattformen und Python-Versionen auf den - meisten CI-Systemen erstellt. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - .. seealso:: - :term:`multibuild` - - conda - Paketmanagement-Tool für die `Anaconda - `_-Distribution von - `Continuum Analytics `_. Sie ist speziell - auf die wissenschaftliche Gemeinschaft ausgerichtet, insbesondere auf - Windows, wo die Installation von binären Erweiterungen oft schwierig ist. - - Conda installiert keine Pakete von :term:`PyPI` und kann nur von den - offiziellen Continuum-Repositories oder von `anaconda.org - `_ oder lokalen (:abbr:`z.B. (zum Beispiel)` - Intranet-) Paketservern installieren. Beachtet jedoch, dass :term:`pip` - in conda installiert werden und Seite an Seite arbeiten kann, um - Distributionen von :term:`PyPI` zu verwalten. - - .. seealso:: - * `Conda: Myths and Misconceptions - `_ - * `Conda build variants - `_ - - devpi - `devpi `_ ist ein leistungsstarker - :term:`PyPI`-kompatibler Server und ein PyPI-Proxy-Cache mit einem - Befehlszeilenwerkzeug um Paketierungs-, Test- und - Veröffentlichungsaktivitäten zu ermöglichen. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Distribution Package - Eine versionierte Archivdatei, die Python-:term:`Pakete - `, -:term:`Module ` und andere Ressourcendateien - enthält, die zum Verteilen eines :term:`Releases ` verwendet - werden. - - distutils - Paket der Python-Standardbibliothek, das Unterstützung für das - Bootstrapping von :term:`pip` in eine bestehende Python-Installation oder - :term:`virtuelle Umgebung` bietet. - - `Docs `__ | - `GitHub `__ - - Egg - Ein :term:`Built Distribution`-Format, das von :term:`Setuptools` - eingeführt wurde und nun durch :term:`wheel` ersetzt wird. Weitere - Informationen findet ihr unter `The Internal Structure of Python Eggs - `_ - und `Python Eggs `_. - - enscons - enscons ist ein Python-Paketierungswerkzeug, das auf `SCons - `_ basiert. Es erstellt :term:`pip`-kompatible - :term:`Source Distributions ` und :term:`wheels - ` ohne Verwendung von :term:`distutils` oder :term:`setuptools`, - einschließlich Distributionen mit C-Erweiterungen. enscons hat eine - andere Architektur und Philosophie als :term:`distutils`, da es - Python-Paketierung zu einem allgemeinen Build-System hinzufügt. enscons - kann euch helfen, :term:`sdists ` und :term:`wheels ` zu - bauen. - - `GitHub `__ | - `PyPI `__ - - Flit - Flit bietet eine einfache Möglichkeit, reine Python-Pakete und -Module zu - erstellen und auf den :term:`Python Package Index` hochzuladen. Flit kann - eine Konfigurationsdatei generieren, um schnell ein Projekt einzurichten, - eine :term:`Source Distribution` und ein :term:`wheel` zu erstellen und - sie zu PyPI hochzuladen. - - Flit verwendet :term:`pyproject.toml`, um ein Projekt zu konfigurieren. - Flit ist nicht auf Werkzeuge wie :term:`setuptools` angewiesen, um - Distributionen zu erstellen, oder auf :term:`twine`, um sie auf - :term:`PyPI` hochzuladen. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Hatch - Hatch ist ein Kommandozeilenwerkzeug, das ihr zum Konfigurieren und - Versionieren von Paketen, zum Spezifizieren von Abhängigkeitengenutzt - werden kann. Das Plugin-System ermöglicht die einfache Erweiterung der - Funktionalitäten. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - hatchling - Build-Backend von :term:`Hatch`, das auch zum Veröffentlichen auf dem - :term:`Python Package Index` genutzt werden kann. - - Import Package - Ein Python-Modul, das andere Module oder rekursiv andere Pakete enthalten - kann. - - maturin - Vormals pyo3-pack, ist ein :pep:`621`-kompatibles Build-Tool für - :doc:`binäre Erweiterungen ` in Rust. - - meson-python - Build-Backend, das das `Meson `_-Build-System - verwendet. Es unterstützt eine Vielzahl von Sprachen, einschließlich C, - und ist in der Lage, die Anforderungen der meisten komplexen - Build-Konfigurationen zu erfüllen. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Modul - Die Grundeinheit der Wiederverwendbarkeit von Code in Python, die in - einem von zwei Typen existiert: - - Pure Module - Ein Modul, das in Python geschrieben wurde und in einer einzigen - ``.py``-Datei enthalten ist (und möglicherweise zugehörigen - ``.pyc``- und/oder ``.pyo``-Dateien). - - Extension Module - In der Regel in eine einzelne dynamisch ladbare vorkompilierte - Datei, z. B. einer gemeinsamen Objektdatei (``.so``). - - multibuild - ``multibuild`` ist ein Satz von CI-Skripten zum Erstellen und Testen von - Python-:term:`wheels ` für Linux, macOS und Windows. - - .. seealso:: - :term:`cibuildwheel` - - pdm - Python-Paketmanager mit :pep:`582`-Unterstützung. Er installiert und - verwaltet Pakete ohne dass eine :term:`virtuelle Umgebung ` erstellt werden muss. Er verwendet auch - :term:`pyproject.toml`, um Projekt-Metadaten zu speichern, wie in - :pep:`621` definiert. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - pex - Bibliothek und Werkzeug zur Erzeugung von Python EXecutable - (:file:`.pex`)-Dateien, die eigenständige Python-Umgebungen sind. - .pex-Dateien sind Zip-Dateien mit ``#!/usr/bin/env python`` und einer - speziellen :file:`__main__.py`-Datei, die das Deployment von - Python-Applikationen stark vereinfachen können. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - pip - Beliebtes Werkzeug für die Installation von Python-Paketen, das in - neuen Versionen von Python enthalten ist. - - Es bietet die wesentlichen Kernfunktionen zum Suchen, Herunterladen und - Installieren von Paketen aus dem :term:`Python Package Index` und andere - Python-Paketverzeichnissen und kann über eine Befehlszeilenschnittstelle - (CLI) in eine Vielzahl von Entwicklungsabläufen eingebunden werden. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - pip-tools - Reihe von Werkzeugen, die eure Builds deterministisch halten und dennoch - mit neuen Versionen eurer Abhängigkeiten auf dem Laufenden halten können. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Pipenv - Pipenv bündelt :term:`Pipfile`, :term:`pip` und :term:`virtualenv` in - einer einzigen Toolchain. Es kann die ``requirements.txt`` automatisch - importieren und mithilfe von `safety `_ die - Umgebung auch auf CVEs prüfen. Schließlich erleichtert es auch die - Deinstallation von Paketen und deren Abhängigkeiten. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Pipfile - Pipfile.lock - ``Pipfile`` und ``Pipfile.lock`` sind eine übergeordnete, - anwendungsorientierte Alternative zu :term:`pip`’s - ``requirements.txt``-Datei. Die :pep:`PEP 508 Environment Markers - <508#environment-markers>` werden ebenfalls unterstützt. - - `Docs `__ | - `GitHub `__ - - pipx - pipx untertüzt euch, Abhängigkeitskonflikte mit anderen auf dem System - installierten Paketen zu vermeiden. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - piwheels - Website und zugrundeliegende Software, die - :term:`Source Distribution`-Pakete von :term:`PyPI` holt und sie in - binäre :term:`wheels ` kompiliert, die für die Installation auf - Raspberry Pis optimiert sind. - - `Home `__ | - `Docs `__ | - `GitHub `__ - - poetry - Eine All-in-One-Lösung für reine Python-Projekte. Es ersetzt - :term:`setuptools`, :term:`venv`/:term:`pipenv`, :term:`pip`, - :term:`wheel` und :term:`twine`. Sie macht jedoch einige schlechte - Standardannahmen für Bibliotheken und die - :term:`pyproject.toml`-Konfiguration ist nicht standardkonform. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - pybind11 - Dies ist :term:`setuptools`, aber mit einer C++-Erweiterung und von - :term:`cibuildwheel` generierten :term:`wheels `. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - pypi.org - `pypi.org `_ ist der Domainname für den - :term:`Python Package Index` (:term:`PyPI`). Er löste 2017 den alten - Index-Domain-Namen ``pypi.python.org`` ab. Er wird von :term:`warehouse` - unterstützt. - - pyproject.toml - Werkzeugunabhängige Datei zur Spezifikation von Projekten, die in - :pep:`518` definiert ist. - - `Docs - `__ - - .. seealso:: - * :ref:`pyproject-toml` - - Python Package Index - PyPI - :term:`pypi.org` ist der Standard-Paket-Index für die Python-Community. - Alle Python-Entwickler können ihre Distributionen nutzen und verteilen. - - Python Packaging Authority - PyPA - Die `Python Packaging Authority `_ ist - eine Arbeitsgruppe, die mehrere Softwareprojekte für die Paketierung, - Verteilung und Installation von Python-Bibliotheken verwaltet. Die in - `PyPA Goals `_ genannten Ziele - sind jedoch noch während der Diskussionen um :pep:`516`, :pep:`517` und - :pep:`518` entstanden, die mit dem :term:`pyproject.toml`-basierten - Build-System konkurrierende Workflows erlaubten, die nicht interoperabel - sein müssen. - - readme_renderer - ``readme_renderer`` ist eine Bibliothek, die verwendet wird, um - Dokumentation aus Auszeichnungssprachen wie Markdown oder - reStructuredText in HTML zu rendern. Ihr könnt sie verwenden, um zu - prüfen, ob eure Paketbeschreibungen auf :term:`PyPI` korrekt angezeigt - werden. - - `GitHub `__ | - `PyPI `__ - - Release - Der Snapshot eines Projekts zu einem bestimmten Zeitpunkt, gekennzeichnet - durch eine Versionskennung. - - Eine Veröffentlichung kann mehrere :term:`Built Distributions - ` zur Folge haben. - - scikit-build - Build-System-Generator für ``C``-, ``C++``-, ``Fortran``- und - ``Cython``-Erweiterungen, der :term:`setuptools`, :term:`wheel` und - :term:`pip` integriert. Er verwendet intern ``CMake``, um eine bessere - Unterstützung für zusätzliche Compiler, Build-Systeme, Cross-Compilation - und das Auffinden von Abhängigkeiten und deren zugehörigen - Build-Anforderungen zu bieten. Um die Erstellung großer Projekte zu - beschleunigen und zu parallelisieren, kann zusätzlich Ninja installiert - werden. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - - setuptools - setuptools sind das klassische Build-System, das sehr leistungsfähig ist, - aber mit steiler Lernkurve und hohem Konfigurationsaufwand. Ab Version - 61.0.0 unterstützen die setuptools auch :term:`pyproject.toml`-Dateien. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - .. seealso:: - `Packaging and distributing projects - `_ - - shiv - Kommandozeilenprogramm zur Erstellung von Python-Zip-Apps, wie sie in - :pep:`441` beschrieben sind, aber zusätzlich mit allen Abhängigkeiten. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - Source Distribution - sdist - Ein Verteilungsformat (das normalerweise mithilfe von ``python setup.py - sdist`` generiert wird). - - Es stellt Metadaten und die wesentlichen Quelldateien bereit, die für die - Installation mit einem Tool wie :term:`Pip` oder zum Generieren von - :term:`Built Distributions ` benötigt werden. - - Spack - Flexibler Paketmanager, der mehrere Versionen, Konfigurationen, - Plattformen und Compiler unterstützt. Beliebig viele Versionen von - Paketen können auf demselben System koexistieren. Spack wurde für die - schnelle Erstellung von wissenschaftlichen Hochleistungsanwendungen auf - Clustern und Supercomputern entwickelt. - - `Docs `__ | - `GitHub `__ - - .. seealso:: - * :doc:`Python4DataScience:productive/envs/spack/index` - - trove-classifiers - trove-classifiers sind zum einen Klassifikatoren, die im :term:`Python - Package Index` verwendet werden, um Projekte systematisch zu beschreiben - und besser auffindbar zu machen. Zum anderen sind sie ein Paket, das eine - Liste gültiger und veralteter Klassifikatoren enthält, das zur - Überprüfung verwendet werden kann. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - twine - Kommandozeilenprogramm, das Programmdateien und Metadaten an eine - Web-API übergibt. Damit lassen sich Python-Pakete auf den :term:`Python - Package Index` hochladen. - - `Docs `__ | - `GitHub `__ | - `PyPI `__ - - venv - Paket, das ab Python ≥ 3.3 in der Python-Standardbibliothek ist und zur - Erstellung :term:`virtueller Umgebungen ` gedacht - ist. - - `Docs `_ | - `GitHub `__ - - virtualenv - Werkzeug, das die Befehlszeilen-Umgebungsvariable ``path`` verwendet, um - isolierte :term:`virtuelle Python-Umgebungen ` zu - erstellen, ähnlich wie :term:`venv`. Es bietet jedoch zusätzliche - Funktionalität für die Konfiguration, Wartung, Duplizierung und - Fehlerbehebung. - - Ab Version 20.22.0 unterstützt virtualenv nicht mehr die Python-Versionen - 2.7, 3.5 und 3.6. - - Virtuelle Umgebung - Eine isolierte Python-Umgebung, die die Installation von Paketen für eine - bestimmte Anwendung ermöglicht, anstatt sie systemweit zu installieren. - - .. seealso:: - * :ref:`virtuelle-umgebungen` - * `Creating Virtual Environments - `_ - - Warehouse - Die aktuelle Codebasis, die den :term:`Python Package Index` - (:term:`PyPI`) antreibt. Sie wird auf :term:`pypi.org` gehostet. - - `Docs `__ | - `GitHub `__ - - wheel - Distributionsformat, das mit :pep:`427` eingeführt wurde. Es soll das - :term:`Egg`-Format ersetzen und wird von aktuellen - :term:`pip`-Installationen unterstützt. - - C-Erweiterungen können als plattformspezifische wheels für Windows, macOS - und Linux auf dem :term:`PyPI` bereitgestellt werden. Dies hat für euch - den Vorteil, dass ihr bei der Installation des Pakets dieses nicht - kompilieren zu müssen. - - `Home `__ | - `Docs `__ | - :pep:`427` | - `GitHub `__ | - `PyPI `__ - - .. seealso:: - * :ref:`wheels` - - whey - Einfacher Python-:term:`wheel`-Builder mit Automatisierungsoptionen für - :term:`trove-classifiers`. diff --git a/docs/libs/index.rst b/docs/libs/index.rst index cbb76fa5..7b74627b 100644 --- a/docs/libs/index.rst +++ b/docs/libs/index.rst @@ -1,5 +1,5 @@ -Programmbibliotheken -==================== +Bibliotheken +============ Mehrere :doc:`/modules/index` können in einer Programmbibliothek zusammengefasst werden. Solche Bibliotheken ermöglichen euch, Module in Verzeichnissen und @@ -14,11 +14,3 @@ Initialisierungsdatei für jedes Paket oder Unterpaket. batteries install - packages-programmes - distribution - templating/index - upload-install - gitlab - cibuildwheel - binary-extensions - glossary diff --git a/docs/libs/install.rst b/docs/libs/install.rst index 0308ebd9..08649c99 100644 --- a/docs/libs/install.rst +++ b/docs/libs/install.rst @@ -7,10 +7,10 @@ unweigerlich die Situation kommen, in der ihr eine Funktionalität benötigt, die nicht in Python enthalten ist. Wenn ihr ein Modul eines Drittanbieters benötigt, das nicht für eure Plattform -vorgefertigt ist, müsst ihr dessen Quelldistribution verwenden. Dies bringt +vorgefertigt ist, müsst ihr dessen Quell-Distribution verwenden. Dies bringt jedoch zwei Probleme mit sich: -#. Um die Quelldistribution zu installieren, müsst ihr sie finden und +#. Um die Quell-Distribution zu installieren, müsst ihr sie finden und herunterladen. #. Es werden bestimmte Python-Pfade und Berechtigungen eures Systems erwartet. @@ -22,11 +22,11 @@ die Pakete nach Kategorien filtern. .. warning:: Installiert niemals irgendetwas mit ``pip`` in das globale Python, auch nicht - mit dem ``--user`` Flag. Verwendet immer :ref:`virtuelle-umgebungen`. So - vermeidet ihr, dass eure Python-Installation mit Bibliotheken verunreinigt - wird, die ihr installiert und dann vergesst. Jedes Mal, wenn ihr etwas Neues - machen müsst, solltet ihr eine neue virtuelle Umgebung erstellen. Damit - vermeidet ihr auch Bibliothekskonflikte zwischen verschiedenen Projekten. + mit dem ``--user`` Flag. Verwendet immer :ref:`venv`. So vermeidet ihr, dass + eure Python-Installation mit Bibliotheken verunreinigt wird, die ihr + installiert und dann vergesst. Jedes Mal, wenn ihr etwas Neues machen müsst, + solltet ihr eine neue virtuelle Umgebung erstellen. Damit vermeidet ihr auch + Bibliothekskonflikte zwischen verschiedenen Projekten. .. tip:: wir empfehlen euch, ``pip`` so zu konfigurieren, dass es nicht möglich ist, @@ -38,10 +38,10 @@ die Pakete nach Kategorien filtern. [global] require-virtualenv = true -.. _virtuelle-umgebungen: +.. _venv: -Virtuelle Umgebungen --------------------- +``venv`` +-------- Eine *virtuelle Umgebung* (``virtualenv``) ist eine in sich geschlossene Verzeichnisstruktur, die sowohl eine Installation von Python als auch die @@ -52,22 +52,27 @@ kollidieren, so dass verschiedene Anwendungen unterschiedliche Versionen von Python und seinen Paketen verwenden können. Das Erstellen und Verwenden einer virtuellen Umgebung erfolgt in zwei Schritten: -#. Zuerst erstellen wir die Umgebung: +#. Zuerst erstellen wir ein Projektverzeichnis und dann darin die virtuelle + Umgebung: .. tab:: Linux/macOS .. code-block:: console - $ python3 -m venv myenv + $ mkdir myproj + $ cd myproj + $ python3 -m venv .venv .. tab:: Windows .. code-block:: ps1 - > py -m venv myenv + > mkdir myproj + > cd myproj + > py -m venv .venv Hiermit wird die Umgebung mit Python und :term:`pip` in einem Verzeichnis - namens :samp:`myenv` erstellt. + namens :samp:`.venv` erstellt. #. Anschließend könnt ihr diese Umgebung aktivieren, sodass beim nächsten Aufruf von ``python`` das Python aus eurer neuen Umgebung verwendet wird: @@ -76,13 +81,13 @@ virtuellen Umgebung erfolgt in zwei Schritten: .. code-block:: console - $ . myenv/bin/activate + $ . .venv/bin/activate .. tab:: Windows .. code-block:: ps1 - > myenv\Scripts\activate.bat + > .venv\Scripts\activate #. Python-Pakete nur für diese virtuelle Umgebung installieren, :abbr:`z.B. (zum Beispiel)` die beliebte ``pandas``-Bibliothek: @@ -91,13 +96,13 @@ Beispiel)` die beliebte ``pandas``-Bibliothek: .. code-block:: console - (myenv) $ python -m pip install pandas + (.venv) $ python -m pip install pandas .. tab:: Windows .. code-block:: ps1 - (myenv) > python.exe -m pip install pandas + (.venv) > python.exe -m pip install pandas #. Wenn ihr eure Arbeit an diesem Projekt beenden wollt, könnt ihr die virtuelle Umgebung wieder deaktivieren mit @@ -106,13 +111,13 @@ Beispiel)` die beliebte ``pandas``-Bibliothek: .. code-block:: console - (myenv) $ deactivate + (.venv) $ deactivate .. tab:: Windows .. code-block:: ps1 - (myenv) > deactivate + (.venv) > deactivate .. seealso:: * :doc:`python3:tutorial/venv` @@ -191,8 +196,9 @@ virtueller Umgebungen mit ``pdm venv activate``. :::::::::::: Im Gegensatz zu Anwendungen unterstützen unsere Pakete normalerweise mehr als -eine Python-Version. Dennoch fügen wir auch bei :doc:`Paketen ` -üblicherweise die aktuelle Standard-Version in :file:`.python-version` hinzu: +eine Python-Version. Dennoch fügen wir auch bei :doc:`Paketen +<../packs/distribution>` üblicherweise die aktuelle Standard-Version in +:file:`.python-version` hinzu: .. literalinclude:: ../../.python-version :caption: .python-version @@ -206,7 +212,78 @@ für `setup-python `_ verwenden können :emphasize-lines: 9 In unseren -:doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd`-Pipelines +:doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd/index`-Pipelines verwenden wir jedoch ``requires-python`` aus der :ref:`pyproject-toml`-Datei, um :doc:`Docker-Container mit der passenden Python-Version -` zu bauen. +` zu bauen. + +.. _uv: + +``uv`` +------ + +:term:`uv` vereinfacht das Erstellen einer initialen Projektstruktur und die +Verwaltung eurer Abhängigkeiten. + +Installation +~~~~~~~~~~~~ + +``uv`` hängt nicht von Python ab. Vorkompilierte, eigenständige Binärdateien +können auf Linux, macOS und Windows installiert werden: + +.. tab:: Linux/macOS + + .. code-block:: console + + $ curl -LsSf https://astral.sh/uv/install.sh | sh + +.. tab:: Windows + + .. code-block:: ps1 + + > powershell -c "irm https://astral.sh/uv/install.ps1 | iex" + +``uv`` aktualisiert sich bei dieser Installation regelmäßig selbst. + +Automatische Shell-Vervollständigung +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Um die automatische Shell-Vervollständigung für ``uv``-Befehle zu aktivieren, +führt einen der folgenden Schritte aus: + +.. tab:: Linux/macOS + + Bestimmt eure Shell, :abbr:`z.B. (zum Beispiel)` mit ``echo $SHELL``, dann + führt einen der folgenden Befehle aus: + + .. code-block:: console + + $ echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc + $ echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc + $ echo 'uv generate-shell-completion fish | source' >> ~/.config/fish/config.fish + $ echo 'eval (uv generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elv + +.. tab:: Windows + + .. code-block:: ps1 + + Add-Content -Path $PROFILE -Value '(& uv generate-shell-completion powershell) | Out-String | Invoke-Expression' + Add-Content -Path $PROFILE -Value '(& uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression' + +Startet dann die Shell neu oder ruft ``source`` mit eurer +Shell-Konfigurationsdatei ein. + +Python-Installation +~~~~~~~~~~~~~~~~~~~ + +Mit ``uv`` lassen sich nicht nur ältere CPython-Versionen installieren, sondern +:abbr:`z.B. (zum Beispiel)` auch `PyPy `_ mit ``uv python +install pypy@3.12`` oder Free-threaded Python 3.13 mit ``uv python install +--python-preference only-managed 3.13t``. + +Projektstruktur erstellen +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Je nachdem, ob ihr eine :doc:`Bibliothek <../packs/distribution>` oder +:doc:`Anwendung <../packs/apps>` erstellen wollt, kann ``uv`` eine passende +Projektstruktur erstellen. diff --git a/docs/libs/templating/templates.rst b/docs/libs/templating/templates.rst deleted file mode 100644 index 3e07ffe6..00000000 --- a/docs/libs/templating/templates.rst +++ /dev/null @@ -1,68 +0,0 @@ -Verfügbare Templates -==================== - -Python ------- - -`cookiecutter-namespace-template `_ - Namespace-Template für Python-Pakete -`cookiecutter-pypackage `_ - Template für Python-Pakete -`cookiecutter-pytest-plugin `_ - Minimales Cookiecutter-Template zum Erstellen von `Pytest - `_-Plugins -`cookiecutter-pylibrary `_ - Umfangreiche Vorlage für Python-Pakete mit Unterstützung für Tests und - Deployments (C-Extension-Support u.a. für `cffi - `_ und `Cython `_, - Test-Unterstützung für `Tox `_, - `Pytest `_, `Travis-CI - `_, `Coveralls - `_, `Codacy - `_, und `Code - Climate `_, - Dokumentation mit `Sphinx `_, - Packaging-Checks u.a. mit `scrutinizer - `_, `Isort - `_ etc. -`cookiecutter-python-cli `_ - Template zum Erstellen einer Python-CLI-Anwendung mit `Click - `_ -`widget-cookiecutter `_ - Template zum Erstellen von Jupyter-Widgets - -Ansible -------- - -`cookiecutter-ansible-role-ci `_ - Vorlage für Ansible-Roles - -C ---- - -`bootstrap.c `_ - Template für in C mit `Autotools - `_ geschriebene Projekte -`cookiecutter-avr `_ - Template für die AVR-Entwicklung - -C++ ---- - -`BoilerplatePP `_ - cmake-Template mit Unit Tests für C++-Projekte - -Scala ------ - -`cookiecutter-scala `_ - Vorlage für ein *Hello world*-Beispiel mit ein paar wenigen Bibliotheken -`cookiecutter-scala-spark `_ - Template für eine `Apache-Spark `_-Anwendung - -LaTeX/XeTeX ------------ - -`pandoc-talk `_ - Template für Präsentation mit `pandoc `_ und `XeTeX - `_ diff --git a/docs/logging/development.ini b/docs/logging/development.ini new file mode 100644 index 00000000..0941d377 --- /dev/null +++ b/docs/logging/development.ini @@ -0,0 +1,25 @@ +; SPDX-FileCopyrightText: 2021 Veit Schiele +; +; SPDX-License-Identifier: BSD-3-Clause + +[loggers] +keys=root + +[handlers] +keys=stream_handler + +[formatters] +keys=formatter + +[logger_root] +level=DEBUG +handlers=stream_handler + +[handler_stream_handler] +class=StreamHandler +level=DEBUG +formatter=formatter +args=(sys.stderr,) + +[formatter_formatter] +format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s diff --git a/docs/logging/examples.ipynb b/docs/logging/examples.ipynb new file mode 100644 index 00000000..214d4d65 --- /dev/null +++ b/docs/logging/examples.ipynb @@ -0,0 +1,911 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Logging-Beispiele" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Erstellen einer Log-Datei" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:This is a warning message\n", + "CRITICAL:root:This is a critical message\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "\n", + "logging.warning(\"This is a warning message\")\n", + "logging.critical(\"This is a critical message\")\n", + "logging.debug(\"debug\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logging-Ebenen\n", + "\n", + "Ebene | Beschreibung\n", + ":-- | :--\n", + "``CRITICAL`` | Das Programm wurde angehalten\n", + "``ERROR`` | Ein schwerwiegender Fehler ist aufgetreten\n", + "``WARNING`` | Ein Hinweis darauf, dass etwas Unerwartetes passiert ist (Standardstufe)\n", + "``INFO`` | Bestätigung, dass die Dinge wie erwartet funktionieren.\n", + "``DEBUG`` | Detaillierte Informationen, die in der Regel nur bei der Diagnose von Problemen von Interesse sind." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setzen der Logging-Ebene" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ERROR:root:An error has happened!\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "\n", + "logging.basicConfig(filename=\"example.log\", filemode=\"w\", level=logging.INFO)\n", + "\n", + "logging.info(\"Informational message\")\n", + "logging.error(\"An error has happened!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Erstellen eines Logger-Objekts" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ERROR:example:Error!\n", + "Traceback (most recent call last):\n", + " File \"/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_65477/2646645271.py\", line 9, in \n", + " raise RuntimeError\n", + "RuntimeError\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "\n", + "logging.basicConfig(filename=\"example.log\")\n", + "logger = logging.getLogger(\"example\")\n", + "logger.setLevel(logging.INFO)\n", + "\n", + "try:\n", + " raise RuntimeError\n", + "except Exception:\n", + " logger.exception(\"Error!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ausnahmen (englisch *exceptions*) loggen" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ERROR:example:You can’t do that!\n", + "Traceback (most recent call last):\n", + " File \"/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_65477/760044062.py\", line 2, in \n", + " 1 / 0\n", + " ~~^~~\n", + "ZeroDivisionError: division by zero\n" + ] + } + ], + "source": [ + "try:\n", + " 1 / 0\n", + "except ZeroDivisionError:\n", + " logger.exception(\"You can’t do that!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logging-Handler\n", + "\n", + "### Handler-Typen\n", + "\n", + "Handler | Beschreibung\n", + ":--- | :---\n", + "`StreamHandler` | `stdout`, `stderr` oder dateiähnliche Objekte\n", + "`FileHandler` | für das Schreiben auf die Festplatte\n", + "`RotatingFileHandler` | unterstützt die Protokollrotation\n", + "`TimedRotatingFileHandler` | unterstützt die Rotation von Protokolldateien auf der Festplatte in bestimmten Zeitabständen\n", + "`SocketHandler` | sendet Logging-Ausgaben an einen Netzwerk-Socket\n", + "`SMTPHandler` | unterstützt das Senden von Logging-Nachrichten an eine E-Mail-Adresse über SMTP\n", + "\n", + "
\n", + "\n", + "**Siehe auch**\n", + "\n", + "Weitere Handler können gefunden werden unter [Logging handlers](https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers).\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### StreamHandler" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "This is an informational message\n", + "INFO:stream_logger:This is an informational message\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "\n", + "logger = logging.getLogger(\"stream_logger\")\n", + "logger.setLevel(logging.INFO)\n", + "\n", + "console = logging.StreamHandler()\n", + "\n", + "logger.addHandler(console)\n", + "logger.info(\"This is an informational message\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### SMTPHandler" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "--- Logging error ---\n", + "Traceback (most recent call last):\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/logging/handlers.py\", line 1081, in emit\n", + " smtp = smtplib.SMTP(self.mailhost, port, timeout=self.timeout)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/smtplib.py\", line 255, in __init__\n", + " (code, msg) = self.connect(host, port)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/smtplib.py\", line 341, in connect\n", + " self.sock = self._get_socket(host, port, self.timeout)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/smtplib.py\", line 312, in _get_socket\n", + " return socket.create_connection((host, port), timeout,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/socket.py\", line 851, in create_connection\n", + " raise exceptions[0]\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/socket.py\", line 836, in create_connection\n", + " sock.connect(sa)\n", + "ConnectionRefusedError: [Errno 61] Connection refused\n", + "Call stack:\n", + " File \"\", line 198, in _run_module_as_main\n", + " File \"\", line 88, in _run_code\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel_launcher.py\", line 17, in \n", + " app.launch_new_instance()\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/traitlets/config/application.py\", line 1043, in launch_instance\n", + " app.start()\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/kernelapp.py\", line 736, in start\n", + " self.io_loop.start()\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/tornado/platform/asyncio.py\", line 195, in start\n", + " self.asyncio_loop.run_forever()\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py\", line 607, in run_forever\n", + " self._run_once()\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py\", line 1922, in _run_once\n", + " handle._run()\n", + " File \"/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/events.py\", line 80, in _run\n", + " self._context.run(self._callback, *self._args)\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/kernelbase.py\", line 516, in dispatch_queue\n", + " await self.process_one()\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/kernelbase.py\", line 505, in process_one\n", + " await dispatch(*args)\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/kernelbase.py\", line 412, in dispatch_shell\n", + " await result\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/kernelbase.py\", line 740, in execute_request\n", + " reply_content = await reply_content\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/ipkernel.py\", line 422, in do_execute\n", + " res = shell.run_cell(\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/ipykernel/zmqshell.py\", line 546, in run_cell\n", + " return super().run_cell(*args, **kwargs)\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/interactiveshell.py\", line 3009, in run_cell\n", + " result = self._run_cell(\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/interactiveshell.py\", line 3064, in _run_cell\n", + " result = runner(coro)\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/async_helpers.py\", line 129, in _pseudo_sync_runner\n", + " coro.send(None)\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/interactiveshell.py\", line 3269, in run_cell_async\n", + " has_raised = await self.run_ast_nodes(code_ast.body, cell_name,\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/interactiveshell.py\", line 3448, in run_ast_nodes\n", + " if await self.run_code(code, result, async_=asy):\n", + " File \"/Users/veit/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/IPython/core/interactiveshell.py\", line 3508, in run_code\n", + " exec(code_obj, self.user_global_ns, self.user_ns)\n", + " File \"/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_65477/3660210047.py\", line 14, in \n", + " logger.info(\"This is an informational message\")\n", + "Message: 'This is an informational message'\n", + "Arguments: ()\n", + "INFO:email_logger:This is an informational message\n" + ] + } + ], + "source": [ + "import logging\n", + "import logging.handlers\n", + "\n", + "\n", + "logger = logging.getLogger(\"email_logger\")\n", + "logger.setLevel(logging.INFO)\n", + "fh = logging.handlers.SMTPHandler(\n", + " \"localhost\",\n", + " fromaddr=\"python-log@localhost\",\n", + " toaddrs=[\"logs@cusy.io\"],\n", + " subject=\"Python log\",\n", + ")\n", + "logger.addHandler(fh)\n", + "logger.info(\"This is an informational message\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Log-Formatierung\n", + "\n", + "Mit Formatierern könnt ihr den Log-Meldungen Formatierungen hinzufügen." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "formatter = logging.Formatter(\"%(asctime)s - %(name)s - %(message)s\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Neben ``%(asctime)s``, ``%(name)s`` und ``%(message)s`` findet ihr noch weitere Attribute in [LogRecord attributes](https://docs.python.org/3/library/logging.html#logrecord-attributes)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "This is an informational message\n", + "2023-08-21 18:08:14,555 - stream_logger - This is an informational message\n", + "INFO:stream_logger:This is an informational message\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "\n", + "logger = logging.getLogger(\"stream_logger\")\n", + "logger.setLevel(logging.INFO)\n", + "\n", + "console = logging.StreamHandler()\n", + "formatter = logging.Formatter(\"%(asctime)s - %(name)s - %(message)s\")\n", + "console.setFormatter(formatter)\n", + "\n", + "logger.addHandler(console)\n", + "logger.info(\"This is an informational message\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "**Bemerkung**\n", + "\n", + "Das Logging-Modul ist thread-sicher. Logging funktioniert jedoch möglicherweise nicht in asynchronen Kontexten. In solchen Fällen könnt ihr jedoch den [QueueHandler](https://docs.python.org/3/library/logging.handlers.html#queuehandler) verwenden.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "**Siehe auch**\n", + "\n", + "[Logging to a single file from multiple processes](https://docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes)\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logging an mehrere Handler" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "\n", + "def log(path, multipleLocs=False):\n", + " logger = logging.getLogger(\"Example_logger_%s\" % fname)\n", + " logger.setLevel(logging.INFO)\n", + " fh = logging.FileHandler(path)\n", + " formatter = logging.Formatter(\"%(asctime)s - %(name)s - %(message)s\")\n", + " fh.setFormatter(formatter)\n", + " logger.addHandler(fh)\n", + "\n", + " if multipleLocs:\n", + " console = logging.StreamHandler()\n", + " console.setLevel(logging.INFO)\n", + " console.setFormatter(formatter)\n", + " logger.addHandler(console)\n", + "\n", + " logger.info(\"This is an informational message\")\n", + " try:\n", + " 1 / 0\n", + " except ZeroDivisionError:\n", + " logger.exception(\"You can’t do that!\")\n", + "\n", + " logger.critical(\"This is a no-brainer!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logging konfigurieren\n", + "\n", + "
\n", + "\n", + "**Siehe auch**\n", + "\n", + "[logging configuration](https://docs.python.org/3/howto/logging.html#configuring-logging)\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### … in einer INI-Datei\n", + "\n", + "Im folgenden Beispiel wird die Datei `development.ini` in diesem Verzeichnis geladen:\n", + "\n", + "```ini\n", + "[loggers]\n", + "keys=root\n", + "\n", + "[handlers]\n", + "keys=stream_handler\n", + "\n", + "[formatters]\n", + "keys=formatter\n", + "\n", + "[logger_root]\n", + "level=DEBUG\n", + "handlers=stream_handler\n", + "\n", + "[handler_stream_handler]\n", + "class=StreamHandler\n", + "level=DEBUG\n", + "formatter=formatter\n", + "args=(sys.stderr,)\n", + "\n", + "[formatter_formatter]\n", + "format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "import logging.config\n", + "\n", + "from logging.config import fileConfig\n", + "\n", + "\n", + "logging.config.fileConfig(\"development.ini\")\n", + "logger = logging.getLogger(\"example\")\n", + "\n", + "logger.info(\"Program started\")\n", + "logger.info(\"Done!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Pro:**\n", + "\n", + "* Möglichkeit, die Konfiguration während des Betriebs zu aktualisieren, indem die Funktion `logging.config.listen()` verwendet wird um an einem Socket zu lauschen.\n", + "* In verschiedenen Umgebungen können unterschiedliche Konfigurationen verwendet werden, also z.B. kann in der `development.ini` `DEBUG` als Log-Level angegeben werden, während in der `production.ini` `WARN` verwendet wird.\n", + "\n", + "**Con:**\n", + "\n", + "* Weniger Kontrolle z.B. gegenüber benutzerdefinierten Filtern oder Logger, die im Code konfiguriert sind." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### … in einer dictConfig" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "import logging.config\n", + "\n", + "\n", + "dictLogConfig = {\n", + " \"version\": 1,\n", + " \"handlers\": {\n", + " \"fileHandler\": {\n", + " \"class\": \"logging.FileHandler\",\n", + " \"formatter\": \"exampleFormatter\",\n", + " \"filename\": \"dict_config.log\",\n", + " }\n", + " },\n", + " \"loggers\": {\n", + " \"exampleApp\": {\n", + " \"handlers\": [\"fileHandler\"],\n", + " \"level\": \"INFO\",\n", + " }\n", + " },\n", + " \"formatters\": {\n", + " \"exampleFormatter\": {\n", + " \"format\": \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n", + " }\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-21 18:08:14,573 exampleApp INFO Program started\n", + "2023-08-21 18:08:14,574 exampleApp INFO Done!\n" + ] + } + ], + "source": [ + "logging.config.dictConfig(dictLogConfig)\n", + "\n", + "logger = logging.getLogger(\"exampleApp\")\n", + "\n", + "logger.info(\"Program started\")\n", + "logger.info(\"Done!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Pro:**\n", + "\n", + "* Aktualisieren während des Betriebs\n", + "\n", + "**Con:**\n", + "\n", + "* Weniger Kontrolle als beim Konfigurieren eines Loggers im Code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### … direkt im Code" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "logger = logging.getLogger()\n", + "handler = logging.StreamHandler()\n", + "formatter = logging.Formatter(\n", + " \"%(asctime)s %(name)-12s %(levelname)-8s %(message)s\"\n", + ")\n", + "handler.setFormatter(formatter)\n", + "logger.addHandler(handler)\n", + "logger.setLevel(logging.DEBUG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## *Magic Commands*\n", + "\n", + "| Befehl | Beschreibung |\n", + "| -------------- | ----------------------------------------------------------------------------------------- |\n", + "| `%logstart` | Startet das Logging irgendwo in einer Session |\n", + "| | `%logstart [-o\\|-r\\|-t\\|-q] [log_name [log_mode]]` |\n", + "| | Wenn kein Name angegeben wird, wird `ipython_log.py` im aktuellen Verzeichnis verwendet. |\n", + "| | `log_mode` ist ein optionaler Parameter. Folgende Modi können angegeben werden: |\n", + "| | * `append` hängt die Logging-Informationen am Ende einer vorhandenen Datei an |\n", + "| | * `backup` benennt die vorhandene Datei um in `name~` und schreibt in `name` |\n", + "| | * `global` hängt die Logging-Informationen am Ende einer vorhandenen Datei im |\n", + "| | * `over` überschreibt eine existierende Log-Datei |\n", + "| | * `rotate` erstellt rotierende Log-Dateien: `name.1~`, `name.2~`, etc. |\n", + "| | Optionen: |\n", + "| | * `-o` logt auch den Output von IPython |\n", + "| | * `-r` logt *raw* Output |\n", + "| | * `-t` schreibt einen Zeitstempel vor jeden Logeintrag |\n", + "| | * `-q` unterdrückt die Logging-Ausgabe |\n", + "| `%logon` | Neustart des Logging |\n", + "| `%logoff` | Temporäres Beenden des Logging |\n", + "\n", + "**Pro:**\n", + "\n", + "* Vollständige Kontrolle über die Konfiguration\n", + "\n", + "**Con:**\n", + "\n", + "* Änderungen in der Konfiguration erfordern eine Änderung des Quellcodes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logs rotieren" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-21 18:08:14,580 Rotating Log INFO This is an example log line 0\n", + "2023-08-21 18:08:14,580 Rotating Log INFO This is an example log line 0\n", + "2023-08-21 18:08:16,086 Rotating Log INFO This is an example log line 1\n", + "2023-08-21 18:08:16,086 Rotating Log INFO This is an example log line 1\n", + "2023-08-21 18:08:17,595 Rotating Log INFO This is an example log line 2\n", + "2023-08-21 18:08:17,595 Rotating Log INFO This is an example log line 2\n", + "2023-08-21 18:08:19,103 Rotating Log INFO This is an example log line 3\n", + "2023-08-21 18:08:19,103 Rotating Log INFO This is an example log line 3\n", + "2023-08-21 18:08:20,612 Rotating Log INFO This is an example log line 4\n", + "2023-08-21 18:08:20,612 Rotating Log INFO This is an example log line 4\n", + "2023-08-21 18:08:22,122 Rotating Log INFO This is an example log line 5\n", + "2023-08-21 18:08:22,122 Rotating Log INFO This is an example log line 5\n", + "2023-08-21 18:08:23,632 Rotating Log INFO This is an example log line 6\n", + "2023-08-21 18:08:23,632 Rotating Log INFO This is an example log line 6\n", + "2023-08-21 18:08:25,137 Rotating Log INFO This is an example log line 7\n", + "2023-08-21 18:08:25,137 Rotating Log INFO This is an example log line 7\n", + "2023-08-21 18:08:26,646 Rotating Log INFO This is an example log line 8\n", + "2023-08-21 18:08:26,646 Rotating Log INFO This is an example log line 8\n", + "2023-08-21 18:08:28,155 Rotating Log INFO This is an example log line 9\n", + "2023-08-21 18:08:28,155 Rotating Log INFO This is an example log line 9\n" + ] + } + ], + "source": [ + "import logging\n", + "import time\n", + "\n", + "from logging.handlers import RotatingFileHandler\n", + "\n", + "\n", + "def create_rotating_log(path):\n", + " logger = logging.getLogger(\"Rotating Log\")\n", + " logger.setLevel(logging.INFO)\n", + "\n", + " handler = RotatingFileHandler(path, maxBytes=20, backupCount=5)\n", + " logger.addHandler(handler)\n", + "\n", + " for i in range(10):\n", + " logger.info(\"This is an example log line %s\" % i)\n", + " time.sleep(1.5)\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " log_file = \"rotated.log\"\n", + " create_rotating_log(log_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Logs zeitgesteuert rotieren" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-21 18:08:29,674 Rotating Log INFO This is an example!\n", + "2023-08-21 18:08:29,674 Rotating Log INFO This is an example!\n", + "2023-08-21 18:09:44,681 Rotating Log INFO This is an example!\n", + "2023-08-21 18:09:44,681 Rotating Log INFO This is an example!\n", + "2023-08-21 18:10:59,688 Rotating Log INFO This is an example!\n", + "2023-08-21 18:10:59,688 Rotating Log INFO This is an example!\n", + "2023-08-21 18:12:14,697 Rotating Log INFO This is an example!\n", + "2023-08-21 18:12:14,697 Rotating Log INFO This is an example!\n", + "2023-08-21 18:13:29,702 Rotating Log INFO This is an example!\n", + "2023-08-21 18:13:29,702 Rotating Log INFO This is an example!\n", + "2023-08-21 18:14:44,710 Rotating Log INFO This is an example!\n", + "2023-08-21 18:14:44,710 Rotating Log INFO This is an example!\n" + ] + } + ], + "source": [ + "import logging\n", + "import time\n", + "\n", + "from logging.handlers import TimedRotatingFileHandler\n", + "\n", + "\n", + "def create_timed_rotating_log(path):\n", + " \"\"\"\"\"\"\n", + " logger = logging.getLogger(\"Rotating Log\")\n", + " logger.setLevel(logging.INFO)\n", + "\n", + " handler = TimedRotatingFileHandler(\n", + " path, when=\"s\", interval=5, backupCount=5\n", + " )\n", + " logger.addHandler(handler)\n", + "\n", + " for i in range(6):\n", + " logger.info(\"This is an example!\")\n", + " time.sleep(75)\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " log_file = \"timed_rotation.log\"\n", + " create_timed_rotating_log(log_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Erstellen eines Logging-Dekorators\n", + "\n", + "
\n", + "\n", + "**Siehe auch**\n", + "\n", + "[How to Create an Exception Logging Decorator](https://www.blog.pythonlibrary.org/2016/06/09/python-how-to-create-an-exception-logging-decorator/)\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Einen Logging-Filter erstellen" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-21 18:15:59,730 filter_example DEBUG Message from bar\n", + "2023-08-21 18:15:59,730 filter_example DEBUG Message from bar\n" + ] + } + ], + "source": [ + "import logging\n", + "import sys\n", + "\n", + "\n", + "class ExampleFilter(logging.Filter):\n", + " def filter(self, record):\n", + " if record.funcName == \"foo\":\n", + " return False\n", + " return True\n", + "\n", + "\n", + "logger = logging.getLogger(\"filter_example\")\n", + "logger.addFilter(ExampleFilter())\n", + "\n", + "\n", + "def foo():\n", + " \"\"\"\n", + " Ignore this function’s log messages\n", + " \"\"\"\n", + " logger.debug(\"Message from function foo\")\n", + "\n", + "\n", + "def bar():\n", + " logger.debug(\"Message from bar\")\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)\n", + " foo()\n", + " bar()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.11 Kernel", + "language": "python", + "name": "python311" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + }, + "latex_envs": { + "LaTeX_envs_menu_present": true, + "autoclose": false, + "autocomplete": true, + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 1, + "hotkeys": { + "equation": "Ctrl-E", + "itemize": "Ctrl-I" + }, + "labels_anchors": false, + "latex_user_defs": false, + "report_style_numbering": false, + "user_envs_cfg": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/logging/index.rst b/docs/logging/index.rst new file mode 100644 index 00000000..93fc9a55 --- /dev/null +++ b/docs/logging/index.rst @@ -0,0 +1,51 @@ +Logging +======= + +Das `logging +`_-Modul ist Teil +der Python-Standardbibliothek. Es ist beschrieben in :pep:`0282`. Eine erste +Einführung in das Modul erhaltet ihr in `Basic Logging Tutorial +`_. + +Logging erfüllt üblicherweise zwei verschiedene Zwecke: + +* Diagnose: + + * Ihr könnt euch den Kontext von bestimmten Ereignissen anzeigen lassen. + * Tools wie `Sentry `_ gruppieren + zusammengehörende Ereignisse und erleichtern die Benutzeridentifikation + :abbr:`etc. (et cetera)`, sodass die Fehlerursache schneller gefunden werden + kann. + +* Monitoring: + + * Das Logging zeichnet Ereignisse für benutzerdefinierten Heuristiken auf, + :abbr:`z.B. (zum Beispiel)` für Geschäftsanalysen. Diese Aufzeichnungen + können für Berichte oder Optimierungen der Geschäftsziele verwendet und + :abbr:`ggf. (gegebenenfalls)` visualisiert werden. + +Welche Vorteile bietet ``logging`` nun gegenüber ``print``? + +* Die Logdatei enthält alle verfügbaren Diagnoseinformationen wie Dateiname, + Pfad, Funktion und Zeilennummer +* Alle Ereignisse sind über den Root-Logger automatisch verfügbar, sofern sie + nicht explizit herausgefiltert werden. +* Logging kann wahlweise durch eine der folgenden beiden Methoden + stummgeschaltet werden: `logging.Logger.setLevel() + `_ + oder `logging.disabled + `_. + +.. seealso:: + + * `loguru `_ macht das Protokollieren fast + so einfach wie die Verwendung von ``print``-Anweisungen. + * `structlog `_ fügt euren + Log-Einträgen Struktur hinzu. + +.. toctree:: + :hidden: + :titlesonly: + :maxdepth: 0 + + examples.ipynb diff --git a/docs/modules/index.rst b/docs/modules/index.rst index 5eaa676f..15122c88 100644 --- a/docs/modules/index.rst +++ b/docs/modules/index.rst @@ -5,28 +5,26 @@ Module werden in Python verwendet, um größere Projekte zu organisieren. Die Python-Standardbibliothek ist in Module aufgeteilt, um sie überschaubarer zu machen. Ihr müsst euren eigenen Code zwar nicht in Modulen organisieren, aber wenn ihr umfangreichere Programme schreibt, oder Code, den ihr wiederverwenden -möchten, solltet ihr dies tun. +möchtet, solltet ihr dies tun. Was ist ein Modul? ------------------ Ein Modul ist eine Datei, die Code enthält. Sie definiert eine Gruppe von Python-Funktionen oder anderen Objekten, und der Name des Moduls wird vom Namen -der Datei abgeleitet. Module enthalten meist Python-Quellcode, können aber auch -kompilierte C- oder C++-Objektdateien sein. Kompilierte Module und -Python-Source-Module werden auf die gleiche Weise verwendet. - -Module fassen nicht nur verwandte Python-Objekte zusammen, sondern helfen auch, Namenskonflikte zu vermeiden. Do könnt ihr für euer Programm ein Modul namens -``mymodule`` schreiben, das eine Funktion namens ``my_func`` definiert. Im -selben Programm möchtet ihr vielleicht auch ein anderes Modul namens -``othermodule`` verwenden, das ebenfalls eine Funktion namens ``my_func`` -definiert, aber etwas anderes tut als eure ``my_func``-Funktion. Ohne Module -wäre es unmöglich, zwei verschiedene Funktionen mit demelben Namen zu verwenden. -Mit Modulen könnt ihr in eurem Hauptprogramm auf die Funktionen -``mymodule.my_func`` und ``othermodule.my_func`` verweisen. Die Verwendung der -Modulnamen sorgt dafür, dass die beiden ``my_func``-Funktionen nicht verwechselt -werden, da Python :abbr:`sog. (sogenannte)` Namespaces verwendet. Ein Namespace -ist im Wesentlichen ein Wörterbuch mit Bezeichnungen für die dort zur Verfügung +der Datei abgeleitet. Module enthalten meist Python-Quellcode [#]_, fassen +verwandte Python-Objekte zusammen und helfen, Namenskonflikte zu vermeiden. So +könnt ihr für euer Programm ein Modul namens ``mymodule`` schreiben, das eine +Funktion namens :func:`my_func` definiert. Im selben Programm möchtet ihr +vielleicht auch ein anderes Modul namens ``othermodule`` verwenden, das +ebenfalls eine Funktion namens :func:`my_func` definiert, aber etwas anderes tut +als :func:`mymodule.my_func`. Ohne Module wäre es unmöglich, zwei verschiedene +Funktionen mit demselben Namen zu verwenden. Mit Modulen könnt ihr in eurem +Hauptprogramm auf die Funktionen :func:`mymodule.my_func` und +:func:`othermodule.my_func` verweisen. Die Verwendung der Modulnamen sorgt +dafür, dass die beiden :func:`my_func`-Funktionen nicht verwechselt werden, da +Python :abbr:`sog. (sogenannte)` Namespaces verwendet. Ein Namespace ist im +Wesentlichen ein Wörterbuch mit Bezeichnungen für die dort zur Verfügung stehenden Funktionen, Klassen, Module :abbr:`usw (und so weiter)`. Module werden auch verwendet, um Python selbst überschaubarer zu machen. Die @@ -35,7 +33,8 @@ integriert, sondern werden über spezielle Module bereitgestellt, die ihr bei Bedarf laden könnt. .. seealso:: - * :ref:`python3:py-modindex` + * :doc:`../libs/batteries` + * :ref:`python3:py-modindex` Erstellen von Modulen --------------------- @@ -56,8 +55,8 @@ dieser Datei vorkommenden Wörter ermittelt. :linenos: Zeilen 1 und 5 - :doc:`../document/docstrings` sind Standardmethoden zur Dokumentation von - Modulen, Funktionen, Methoden und Klassen. + :doc:`../document/sphinx/docstrings` sind Standardmethoden zur Dokumentation + von Modulen, Funktionen, Methoden und Klassen. Zeile 10 ``read`` gibt eine Zeichenkette zurück, die alle Zeichen in einer Datei enthält, und ``split`` gibt eine Liste der Wörter einer Zeichenkette zurück, @@ -110,9 +109,10 @@ Zeilen 25 und 26 File README.rst has 332 words (191 are unique) {'Schnelleinstieg': 1, ...} -Speichert diesen Code zunächst in einem der Verzeichnisse des Modulsuchpfads, -die in der Liste von ``sys.path`` zu finden ist. Als Dateinamensendung empfiehlt -sich ``.py``, da hierdurch die Datei als Python-Quellcode ausgewiesen wird. +Speichert diesen Code zunächst in einem der Verzeichnisse des Modul-Suchpfads, +die in der Liste von ``sys.path`` zu finden ist. Als Ende des Dateinamens +empfiehlt sich ``.py``, da hierdurch die Datei als Python-Quellcode ausgewiesen +wird. .. note:: Die Liste von Verzeichnissen, die mit ``sys.path`` angezeigt wird, hängt von @@ -217,3 +217,9 @@ Checks * Schreibt eure Version des :mod:`wc`-Dienstprogramms so um, dass es sowohl die Unterscheidung zwischen Bytes und Zeichen als auch die Möglichkeit, aus Dateien und von der Standardeingabe zu lesen, implementiert. + +---- + +.. [#] Module enthalten zwar meist Python-Quellcode, können aber auch + kompilierte C- oder C++-Objektdateien sein. Kompilierte Module und + Python-Source-Module werden auf die gleiche Weise verwendet. diff --git a/docs/modules/wc.py b/docs/modules/wc.py index d5554f6a..08a5af1f 100644 --- a/docs/modules/wc.py +++ b/docs/modules/wc.py @@ -6,9 +6,8 @@ def words_occur(): # Prompt user for the name of the file to use. file_name = input("Enter the name of the file: ") # Open the file, read it and store its words in a list. - f = open(file_name, "r") - word_list = f.read().split() - f.close() + with open(file_name, "r") as f: + word_list = f.read().split() # Count the number of occurrences of each word in the file. occurs_dict = {} for word in word_list: diff --git a/docs/modules/wcargparse.py b/docs/modules/wcargparse.py index 3c8a38a2..455f4fb3 100644 --- a/docs/modules/wcargparse.py +++ b/docs/modules/wcargparse.py @@ -11,9 +11,8 @@ def words_occur(): args = parser.parse_args() file_name = args.filename # Open the file, read it and store its words in a list. - f = open(file_name, "r") - word_list = f.read().split() - f.close() + with open(file_name, "r") as f: + word_list = f.read().split() # Count the number of occurrences of each word in the file. occurs_dict = {} for word in word_list: diff --git a/docs/modules/wcargv.py b/docs/modules/wcargv.py index 93e9cce8..cdd058fa 100644 --- a/docs/modules/wcargv.py +++ b/docs/modules/wcargv.py @@ -8,9 +8,8 @@ def words_occur(): # Prompt user for the name of the file to use. file_name = sys.argv.pop() # Open the file, read it and store its words in a list. - f = open(file_name, "r") - word_list = f.read().split() - f.close() + with open(file_name, "r") as f: + word_list = f.read().split() # Count the number of occurrences of each word in the file. occurs_dict = {} for word in word_list: diff --git a/docs/modules/wcargv_stdin.py b/docs/modules/wcargv_stdin.py index 2adcaf69..4b9f2235 100644 --- a/docs/modules/wcargv_stdin.py +++ b/docs/modules/wcargv_stdin.py @@ -1,5 +1,5 @@ """Reads a file or stdin and returns the number of lines, words and characters – - similar to the UNIX wc utility.""" +similar to the UNIX wc utility.""" import sys diff --git a/docs/oop/classes.rst b/docs/oop/classes.rst index 95dc059e..437f33db 100644 --- a/docs/oop/classes.rst +++ b/docs/oop/classes.rst @@ -91,4 +91,4 @@ anderes Ergebnis ausgibt als die vorherige ``print``-Anweisung: Checks ------ -# Schreibt eine :class:`Triangle`-Klasse, die auch die Fläche berechnen kann. +* Schreibt eine :class:`Triangle`-Klasse, die auch die Fläche berechnen kann. diff --git a/docs/oop/coherent.rst b/docs/oop/coherent.rst new file mode 100644 index 00000000..08c9cf9f --- /dev/null +++ b/docs/oop/coherent.rst @@ -0,0 +1,105 @@ +Zusammenhängendes Beispiel +========================== + +Die bisher angesprochenen Punkte, sind die Grundlagen der Verwendung von Klassen +und Objekten in Python. Diese Grundlagen werde ich nun in einem +zusammenhängenden Beispiel dargestellt: :download:`form.py`. + +#. Zunächst erstellen wir eine Basisklasse: + + .. literalinclude:: form.py + :language: python + :linenos: + :lines: 1-12 + :lineno-start: 1 + + Zeile 7 + Die ``__init__``-Methode benötigt eine Instanz (``self``) und zwei + Parameter + Zeilen 8 und 9 + Auf die beiden Instanz-Variablen ``x`` und ``y``, auf die über ``self`` + zugegriffen wird. + Zeile 10 + Die ``move``-Methode benötigt eine Instanz (``self``) und zwei + Parameter. + Zeilen 11 und 12 + Instanz-Variablen, die in der ``move``-Methode gesetzt werden. + +#. Als nächstes erstellt eine Unterklasse, die von der Basisklasse ``Form`` + erbt: + + .. literalinclude:: form.py + :language: python + :linenos: + :lines: 16-21 + :lineno-start: 16 + + Zeile 16 + Die Klasse ``Square`` erbt von der Klasse ``Form``. + Zeile 19 + ``Square``’s ``__init__`` nimmt eine Instanz (``self``) und drei + Parameter, alle mit Voreinstellungen. + Zeile 20 + ``__init__`` von Square verwendet ``super()``, um ``__init__`` von + ``Form`` aufzurufen. + +#. Schließlich erstellen wir eine weitere Unterklasse, die zudem eine statische + Methode enthält: + + .. literalinclude:: form.py + :language: python + :linenos: + :lines: 27-43 + :lineno-start: 27 + + Zeilen 29 und 30 + ``pi`` und ``circles`` sind Klassenvariablen für ``Circle``. + Zeile 34 + In der ``__init__``-Methode fügt sich die Instanz in die Liste + ``circles`` ein. + Zeilen 37 und 38 + ``circumferences`` ist eine Klassenmethode und nimmt die Klasse selbst + (``cls``) als Parameter. + Zeile 41 + verwendet den Parameter ``cls`` für den Zugriff auf die Klassenvariable + ``circles``. + +Jetzt könnt ihr einige Instanzen der Klasse ``Circle`` erstellen und sie +analysieren. Da die ``__init__``-Methode von ``Circle`` Standardparameter hat, +könnt ihr einen Kreis erstellen, ohne irgendwelche Parameter anzugeben: + +.. code-block:: pycon + + >>> import form + >>> c1 = form.Circle() + >>> c1.diameter, c1.x, c1.y + (1, 0, 0) + +Wenn ihr Parameter angebt, werden diese verwendet, um die Werte der Instanz +festzulegen: + +.. code-block:: pycon + + >>> c2 = form.Circle(2, 3, 4) + >>> c2.diameter, c2.x, c2.y + (2, 3, 4) + +Wenn ihr die ``move()``-Methode aufruft, findet Python keine ``move()``-Methode +in der Klasse ``Circle``, also wird in der Vererbungshierarchie nach oben +gegangen und die ``move()``-Methode von ``Form`` verwendet: + +.. code-block:: pycon + + >>> c2.move(5, 6) + >>> c2.diameter, c2.x, c2.y + (2, 8, 10) + +Ihr könnt auch die Klassenmethode ``circumferences()`` der Klasse ``Circle`` +aufrufen, entweder über die Klasse selbst oder durch eine Instanz: + +.. code-block:: pycon + + >>> form.Circle.circumferences() + 9.424769999999999 + >>> c2.circumferences() + 9.424769999999999 diff --git a/docs/dataclasses.rst b/docs/oop/dataclasses.rst similarity index 98% rename from docs/dataclasses.rst rename to docs/oop/dataclasses.rst index ebc5508d..aeeee0bc 100644 --- a/docs/dataclasses.rst +++ b/docs/oop/dataclasses.rst @@ -6,14 +6,10 @@ ein spezieller Shortcut, mit der wir Klassen erstellen können, die Daten speich bietet einen speziellen :doc:`Dekorator <../functions/decorators>`, wenn wir eine solche Klasse erstellen wollen. -.. tip:: - `cusy Seminar: Fortgeschrittenes Python - `_ - .. tip:: Für Tabellendaten verwende ich im Allgemeinen :doc:`pandas Series oder DataFrames ` und wenn - ich Matrizen mit Zahlen speichern muss, verwende ich :doc:`Numpy + ich Matrizen mit Zahlen speichern muss, verwende ich :doc:`NumPy `. Nehmen wir an, wir wollen eine Klasse speichern, die ein Item repräsentiert mit @@ -44,7 +40,7 @@ Im Allgemeinen werden Datenklassen als syntaktischer Zucker für die Erstellung von Klassen, die Daten speichern, verwendet. Ihr könnt euren Klassen zusätzliche Funktionalität verleihen, indem ihr Methoden definiert. Wir werden der Klasse eine Methode hinzufügen, die ein Item-Objekt aus einem :doc:`Dict -` erstellt: +<../types/dicts>` erstellt: .. code-block:: pycon @@ -63,3 +59,7 @@ der Klasse eine Methode hinzufügen, die ein Item-Objekt aus einem :doc:`Dict ... } >>> Item.from_dict(item_dict) Item(summary='My first item', owner='veit', state='todo', id=1) + +.. tip:: + `cusy Seminar: Fortgeschrittenes Python + `_ diff --git a/docs/oop/design/best_promo.py b/docs/oop/design/best_promo.py deleted file mode 100644 index 379639c8..00000000 --- a/docs/oop/design/best_promo.py +++ /dev/null @@ -1,3 +0,0 @@ -import promos - -promotions = [func for name, func in inspect.getmembers(promos, inspect.isfunction)] diff --git a/docs/oop/design/books.json b/docs/oop/design/books.json deleted file mode 100644 index b0e346c6..00000000 --- a/docs/oop/design/books.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Title": "Python basics", - "Language": "en", - "Authors": "Veit Schiele", - "License": "BSD-3-Clause", - "Publication date": "2021-10-28" -} diff --git a/docs/oop/design/caller.py b/docs/oop/design/caller.py deleted file mode 100644 index 9b157fe4..00000000 --- a/docs/oop/design/caller.py +++ /dev/null @@ -1,9 +0,0 @@ -class MacroCommand: - """A command that executes a list of command.""" - - def __init__(self, commands): - self.commands = list(commands) - - def __call__(self): - for command in self.commands: - command() diff --git a/docs/oop/design/command.rst b/docs/oop/design/command.rst deleted file mode 100644 index be17fb0b..00000000 --- a/docs/oop/design/command.rst +++ /dev/null @@ -1,78 +0,0 @@ -Kommando-Entwurfsmuster -======================= - -`Kommando `_ ist ein -weiteres Entwurfsmuster, das durch die Verwendung von Funktionen, die als -Argumente übergeben werden, vereinfacht werden kann: - -.. uml:: - - title UML-Klassendiagramm für das Kommando-Entwurfsmuster - - together { - abstract class Caller - abstract class Command { - {method} execute() - } - } - - together { - abstract class Client - abstract class Receiver { - {method} action() - } - class ConcreteCommand { - state - {method} execute() - } - } - - Caller *-> Command - Client -> Receiver - Client -> ConcreteCommand - Receiver <- ConcreteCommand - ConcreteCommand -u-|> Command - -Das Ziel des Kommando-Entwurfsmusters ist es, ein Objekt, das eine Operation -aufruft vom :obj:`Receiver`-Objekt zu entkoppeln, das die Operation -implementiert. Im Beispiel aus dem `Entwurfsmuster -`_-Buch ist jedes -:obj:`Caller`-Objekt ein Menüpunkt in einer grafischen Anwendung, und die -:obj:`Receiver`-Objekte sind das zu bearbeitende Dokument oder die Anwendung -selbst. Hierzu wird ein :obj:`Command`-Objekt zwischen die beiden gesetzt, das -eine Schnittstelle mit einer einzigen Methode implementiert, die eine Methode im -:class:`Receiver` aufruft, um die gewünschte Operation durchzuführen. Auf diese -Weise muss das :obj:`Caller`-Objekt das Interface des :class:`Receiver` nicht -kennen, und verschiedene Empfänger können durch verschiedene -:class:`Command`-Unterklassen angepasst werden. :class:`Caller` wird mit einem -:class:`ConcreteCommand`-Befehl konfiguriert und ruft dessen -:meth:`execute`-Methode auf, um ihn auszuführen. - - Command sind ein objektorientierter Ersatz für Callbacks. [#]_ - -Die Frage ist nun, ob wir in Python wirklich einen solchen objektorientierten -Ersatz für Callbacks brauchen? Können wir stattdessen nicht dem :obj:`Caller` -einfach eine Funktion geben? Anstatt also :func:`Command.execute` aufzurufen, -könnte der :class:`Caller` einfach :func:`command` aufrufen. Dabei kann -:class:`Command` eine Klasse sein, die :func:`__call__` implementiert und -Instanzen von :class:`Command` wären dann :abbr:`sog. (sogenannte)` *callables*, -die jeweils eine Liste von Funktionen für zukünftige Aufrufe enthalten, -:abbr:`z.B. (zum Beispiel)`: - -.. literalinclude:: caller.py - :language: python - :linenos: - -Zeilen 4–5 - erstellt eine Liste aus den Befehlsargumenten und stellt sicher, dass sie - iterierbar ist -Zeile 7–8 - erstellt eine lokale Kopie der Befehlsreferenzen in jeder - :class:`MacroCommand`-Instanz. Wenn eine Instanz von :class:`MacroCommand` - aufgerufen wird, wird jeder Befehl in ``self.commands`` nacheinander - aufgerufen. - ----- - -.. [#] `Entwurfsmuster (Buch) - `_ diff --git a/docs/oop/design/decorator.rst b/docs/oop/design/decorator.rst deleted file mode 100644 index 4488b757..00000000 --- a/docs/oop/design/decorator.rst +++ /dev/null @@ -1,38 +0,0 @@ -Decorator -========= - -Der Decorator ist ein Strukturmuster (:abbr:`engl. (englisch)` *structural -patterns*). Das Muster ist eine flexible Alternative zur Unterklassenbildung, um -eine Klasse um zusätzliche Funktionalitäten zu erweitern. - -.. warning:: - Das Decorator-Pattern hat nichts mit Python-:doc:`../../functions/decorators` - zu tun. - -Beispiel --------- - -Im Python-Wiki findet ihr ein Beispiel für das `DecoratorPattern -`_. Es zeigt uns, wie Dekoratoren -in die Pipeline eingebaut werden, um dynamisch viele Verhaltensweisen in ein -Objekt einzufügen. - -Vor- und Nachteile ------------------- - -Vorteile - -* Mehrere Dekorierer können hintereinandergeschaltet werden -* Die Dekorierer können zur Laufzeit und sogar nach der Instanziierung - ausgetauscht werden. -* Die zu dekorierende Klasse ist nicht unbedingt festgelegt, wohl aber deren - Schnittstelle. -* Zudem können lange und unübersichtliche Vererbungshierarchien vermieden - werden. - -Nachteile - -* Da eine dekorierte Komponente nicht identisch mit der Komponente selbst ist, - muss man beim Testen auf Objekt-Identität vorsichtig sein. -* Bei der Verwendung von dekorierten Komponenten müssen die Nachrichten vom - Dekorierer an das dekorierte Objekt weitergeleitet werden. diff --git a/docs/oop/design/factory.rst b/docs/oop/design/factory.rst deleted file mode 100644 index 37ed7da8..00000000 --- a/docs/oop/design/factory.rst +++ /dev/null @@ -1,118 +0,0 @@ -Abstrakte Fabrik -================ - -.. warning:: - Die abstrakte Fabrik ist eine ungeschickte Lösung für das Fehlen von - erstklassigen Funktionen und Klassen in weniger leistungsfähigen - Programmiersprachen. Sie passt schlecht zu Python, wo wir stattdessen - einfach eine Klasse oder eine Factory-Funktion übergeben können, wenn eine - Bibliothek Objekte in unserem Namen erstellen muss. - - – Brandon Rhodes: `The Abstract Factory Pattern - `_ - -Das :mod:`python3:json`-Modul der Python-Standardbibliothek ist ein gutes -Beispiel für eine Bibliothek, die Objekte im Namen ihres caller instanziieren -muss. Betrachten wir einen JSON-String wie diesen: - -.. literalinclude:: books.json - :language: json - -Normalerweise erzeugt die Funktion :func:`python3:json.load` des -:mod:`python3:json`-Moduls Unicode-Objekte für die Zeichenketten und ein Dict -für das JSON-Objekt der obersten Ebene. - -Für das Veröffentlichungsdatum ist dieser Standardwert jedoch nicht -zufriedenstellend, sodass wir ihn in ein Datumsformat umwandeln wollen: - -.. code-block:: pycon - :emphasize-lines: 2-4, 8, 11 - - >>> import json - >>> from datetime import datetime - >>> def convert_date(string): - ... return datetime.strptime(string, "%Y-%m-%d").date() - ... - >>> with open("books.json") as f: - ... books = json.load(f) - ... books["Publication date"] = convert_date(books["Publication date"]) - ... print(books) - ... - {'Title': 'Python basics', 'Language': 'en', 'Authors': 'Veit Schiele', 'License': 'BSD-3-Clause', 'Publication date': datetime.date(2021, 10, 28)} - -Diese einfache Fabrik wurde erfolgreich ausgeführt: das zurückgegebene Datum ist -vom Typ ``datetime.date``. - -.. note:: - Ich habe das Verb :func:`convert_date` als Namen für diese Funktion gewählt, - und nicht ein Substantiv wie :func:`date_factory`, da er ausdrückt, was die - Funktion tut, anstatt mir zu sagen, was für eine Art von Funtion sie ist. - -Einige Legacy-Sprachen unterstützen nur die Übergabe von Klasseninstanzen, nicht -auch aufrufbare Funktionen. Mit dieser Einschränkung müsste jede einfache Fabrik -von einer Funktion zu einer Methode werden: - -.. code-block:: python - - class DateFactory(object): - @staticmethod - def build_date(dict): - dict["Publication date"] = datetime.strptime( - dict["Publication date"], "%Y-%m-%d" - ).date() - -In traditioneller objektorientierter Programmierung ist das Wort *Factory* der -Name für einee Art von Klasse, die eine Methode anbietet, mit der ein Objekt -erstellt wird. Wenn wir eine Python-Klasse nicht direkt übergeben könnten, -sondern lediglich Objektinstanzen, könnte die Klasse :class:`DateFactory` nicht -als Argument an die Methode :func:`load` übergeben werden. Stattdessen müsste -:class:`DateFactory` unnötigerweise instanziiert und anschließend das -resultierende Objekt übergeben werden: - -.. code-block:: python - :emphasize-lines: 6 - - class Loader(object): - @staticmethod - def load(books_file, factory): - with open(books_file) as f: - books = json.load(f) - factory.build_date(books) - return books - -.. code-block:: pycon - - >>> df = DateFactory() - >>> b = Loader.load("books.json", df) - >>> print(b) - {'Title': 'Python basics', 'Language': 'en', 'Authors': 'Veit Schiele', 'License': 'BSD-3-Clause', 'Publication date': datetime.date(2021, 10, 28)} - -.. note:: - #. Da Python-Klassen statische und Klassenmethoden bieten, die ohne Instanz - aufgerufen werden können, müssen wir die :class:`DateFactory`-Klasse nicht - erst instanziieren – wir können sie einfach als Objekt übergeben. - #. Sprachen, die euch zwingen, den Typ jedes Methodenparameters im Voraus zu - deklarieren, schränken eure zukünftigen Möglichkeiten übermäßig ein. - -Schließlich soll m Entwurfsmuster *Abstrakte Fabrik* die Spezifikation von der -Implementierung getrennt werden, indem eine abstrakte Klasse erstellt wird. Eure -abstrakte Klasse würde lediglich versprechen, dass das -:class:`DateFactory`-Argument für :func:`load` eine Klasse sein wird, die der -erforderlichen Schnittstelle entspricht: - -.. code-block:: python - - from abc import ABCMeta, abstractmethod - - - class AbstractFactory(metaclass=ABCMeta): - - @abstractmethod - def build_date(self, dict): - pass - -Sobald die abstrakte Klasse vorhanden ist und :class:`DateFactory` von ihr erbt, -sind die Vorgänge, die zur Laufzeit ablaufen, jedoch genau dieselben wie zuvor. -Die Methoden der :class:`DateFactory` werden mit verschiedenen Argumenten -aufgerufen, die sie anweisen, verschiedene Arten von Objekten zu erstellen, ohne -dass der Aufrufer die Details kennen muss. diff --git a/docs/oop/design/forms.py b/docs/oop/design/forms.py deleted file mode 100644 index 83a0e515..00000000 --- a/docs/oop/design/forms.py +++ /dev/null @@ -1,55 +0,0 @@ -import gc - - -class Form: - - def __init__(self, x=0, y=0): - self.x = x - self.y = y - - def move(self, delta_x, delta_y): - self.x = self.x + delta_x - self.y = self.y + delta_y - - -class Square(Form): - def __init__(self, length=1, x=0, y=0): - super().__init__(x, y) - self.length = length - - def circumference(self): - return 4 * self.length - - -class Circle(Form): - pi = 3.14159 - - def __init__(self, diameter=1, x=0, y=0): - super().__init__(x, y) - self.diameter = diameter - - def circumference(self): - return self.diameter * Circle.pi - - -class SquaresAndCircles: - pi = 3.14159 - - @classmethod - def circumferences(cls): - csum = 0 - for obj in gc.get_objects(): - if isinstance(obj, Square): - csum = csum + 4 * obj.length - if isinstance(obj, Circle): - csum = csum + obj.diameter * cls.pi - return csum - - -class CircumferenceFormInstances: - def circumferences(): - csum = 0 - for obj in gc.get_objects(): - if isinstance(obj, Form) and hasattr(obj, "circumference"): - csum = csum + obj.circumference() - return csum diff --git a/docs/oop/design/index.rst b/docs/oop/design/index.rst deleted file mode 100644 index 6aa30d38..00000000 --- a/docs/oop/design/index.rst +++ /dev/null @@ -1,67 +0,0 @@ -Objektorientierte Designs -========================= - -In der Softwareentwicklung beschreibt ein Entwurfsmuster (:abbr:`engl. -(englisch)` *design patterns*) einen relativ kleinen, genau definierten Aspekt -eines Computerprogramms in Bezug auf die Art und Weise, wie Code zu schreiben -ist. Die Verwendung eines Musters dient dazu, ein bestehendes Konzept zu nutzen, -anstatt es neu zu erfinden. Dadurch kann die Zeit für die Softwareentwicklung -verkürzt und die Qualität des resultierenden Programms erhöht werden. - - Konformität mit Mustern ist kein Maßstab für Güte. [#]_ - -Obwohl Entwurfsmuster sprachunabhängig sind, bedeutet das nicht, dass jedes -Muster für jede Sprache passt. In seinem Vortrag *Design Patterns in Dynamic -Languages* aus dem Jahr 1996 stellt Peter Norvig fest, dass 16 der 23 Patterns -aus dem Buch-Klassiker `Entwurfsmuster -`_ in einer dynamischen -Sprache entweder unsichtbar oder einfacher werden [#]_. Auch die Autoren des -Buchers erkennen in ihrer Einleitung an, dass die Implementierungssprache -bestimmt, welche Muster relevant sind: - - Die Wahl der Programmiersprache ist wichtig, weil sie den Blickwinkel - beeinflusst. Unsere Muster gehen von Smalltalk/C++-Sprachmerkmalen aus, und - diese Wahl bestimmt, was leicht implementiert werden kann und was nicht. - Wären wir von prozeduralen Sprachen ausgegangen, hätten wir vielleicht - Entwurfsmuster mit den Bezeichnungen *Vererbung*, *Kapselung* und - *Polymorphismus* aufgenommen. In ähnlicher Weise werden einige unserer - Muster direkt von den weniger verbreiteten objektorientierten Sprachen - unterstützt. - -Norvig schlägt :abbr:`u.a. (unter anderem)` vor, das Strategiemuster mit -Instanzen einiger Klassen durch einfache Funktionen zu ersetzen und so eine -Menge Boilerplate-Code zu reduzieren. Im folgenden :doc:`Strategiemuster -`-Abschnitt werden wir das Strategiemuster mithilfe von -Funktionsobjekten refaktorisieren. - -:doc:`SOLID ` ist ein Akronym für fünf Designprinzipien, die -objektorientierte Designs verständlicher, flexibler und wartbarer machen sollen. - -.. tip:: - `cusy Seminar: Entwurfsmuster in Python - `_ - -.. seealso:: - * Harry Percival, Bob Gregory: `Architecture Patterns with Python - `_ - * Leonardo Giordani: `Clean Architectures in Python - `_ - * Gregor Hohpe, Bobby Woolf: `Enterprise Integration Patterns - `_ - ----- - -.. [#] Ralph Johnson, Co-Autor des `Entwurfsmuster - `_-Standardwerks. -.. [#] `Design Patterns in Dynamic Languages - `_ - -.. toctree:: - :titlesonly: - :hidden: - - solid - factory - decorator - strategy - command diff --git a/docs/oop/design/promos.py b/docs/oop/design/promos.py deleted file mode 100644 index 3a906943..00000000 --- a/docs/oop/design/promos.py +++ /dev/null @@ -1,60 +0,0 @@ -from collections import namedtuple - -Customer = namedtuple("Customer", "name loyalty") - - -class Product: - def __init__(self, product, quantity, price): - self.product = product - self.quantity = quantity - self.price = price - - def total(self): - return self.price * self.quantity - - -class Order: - """The context class.""" - - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, "__total"): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion(self) - return self.total() - discount - - def __repr__(self): - fmt = "" - return fmt.format(self.total(), self.due()) - - def loyalty_promo(order): - """5% discount for customers with 1000 or more loyalty points.""" - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 - - def quantity_item_promo(order): - """10% discount for each LineItem with 10 or more units.""" - discount = 0 - for item in order.cart: - if item.quantity >= 10: - discount += item.total() * 0.1 - return discount - - def bulk_promo(order): - """7% discount for orders with 10 or more distinct items.""" - distinct_items = {item.product for item in order.cart} - if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - -promos = [globals()[name] for name in globals() if name.endswith("_promo")] diff --git a/docs/oop/design/solid.rst b/docs/oop/design/solid.rst deleted file mode 100644 index c5ac79c1..00000000 --- a/docs/oop/design/solid.rst +++ /dev/null @@ -1,209 +0,0 @@ -SOLID-Prinzipien -================ - -`SOLID -`_ -ist ein Akronym für die ersten fünf Prinzipien des objektorientierten Designs -(OOD) von Robert C. Martin (auch bekannt als `Uncle Bob -`_). - -Diese Prinzipien legen Praktiken für die Entwicklung von Software mit -Überlegungen zur Wartung und Erweiterbarkeit fest, wenn das Projekt wächst. Die -Übernahme dieser Prinzipien kann auch dazu beitragen, Code Smells zu vermeiden, -Code zu refaktorisieren und agile oder adaptive Software zu entwickeln. - -SOLID steht für: - -S – :ref:`single-responsibility` - Die Methoden einer Klasse sollten auf einen einzigen Zweck ausgerichtet - sein. -O – :ref:`open-closed` - Objekte sollten offen für Erweiterungen, aber geschlossen für Änderungen - sein. -L – :ref:`liskov-substitution` - Unterklassen sollten durch ihre Oberklassen substituierbar sein. -I – :ref:`interface-segregation` - Objekte sollten nicht von Methoden abzuhängen, die sie nicht verwenden. -D – :ref:`dependency-inversion` - Abstraktionen sollten nicht von Details abhängen. - -.. _single-responsibility: - -Single-Responsibility-Prinzip -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Das `Single-Responsibility-Prinzip -`_ besagt, dass -jede Klasse nur eine Aufgabe erfüllen soll: - - Es sollte nie mehr als einen Grund geben, eine Klasse zu ändern. - -– `Robert C. Martin: SRP: The Single Responsibility Principle -`_ - -Nehmen wir :abbr:`z.B. (zum Beispiel)` eine Anwendung, die eine Sammlung von -Formen – Kreise und Quadrate – nimmt und die Summe der Umfänge aller Formen der -Sammlung berechnet. - -Erstellt zunächst die :class:`Form`-Klassen mit den notwendigen Parametern. Für -Quadrate ist dies die Kantenlänge und für Kreise der Durchmesser: - -.. literalinclude:: forms.py - :language: python - :lines: 4, 6-8, 13-18, 22-24, 27-29 - -Nun könnt ihr eine Klasse :class:`SquaresAndCircles` erstellen mit der Logik zur -Berechnung aller Umfänge von Quadraten und Kreisen: - -.. literalinclude:: forms.py - :language: python - :lines: 1-3, 35-46 - -Die Klasse :class:`SquaresAndCircles` übernimmt die Logik, die zur Berechnung -aller Umfänge von Quadraten und Kreisen erforderlich ist. Damit ist das Prinzip -der Einzelverantwortung erfüllt. - -.. _open-closed: - -Open-Closed-Prinzip -------------------- - -Das :abbr:`OCP (Open-Closed-Prinzip)` besagt: - - Objekte oder Entitäten sollten offen für Erweiterungen, aber geschlossen für - Änderungen sein. - -Das bedeutet, dass eine Klasse erweiterbar sein sollte, ohne die Klasse selbst -zu verändern. - -Schauen wir uns die Klasse :class:`SquaresAndCircles` an und konzentrieren uns -auf die :func:`circumferences`-Methode. Stellt euch ein Szenario vor, in dem die -Summe zusätzlicher Formen wie Dreiecke, Fünfecke, Sechsecke :abbr:`usw. (und so -weiter)` berechnet werden sollen. Ihr müsstet diese Klasse ständig bearbeiten -und weitere ``if``-Blöcke hinzufügen. Das würde gegen das Open-Closed-Prinzip -verstoßen. Eine Möglichkeit, diese Methode zu verbessern, besteht darin, die -Logik zur Berechnung des Umfangs jeder Form aus der Klasse -:class:`SquaresAndCircles` zu entfernen und sie an die Klassen der speziellen -Formen anzuhängen. Hier sind die Umfangsberechnungen in den Klassen -:class:`Square` und :class:`Circle` definiert: - -.. literalinclude:: forms.py - :language: python - :lines: 15-32 - -Die Summenmethode :func:`circumferences` in der Klasse -:class:`CircumferenceFormInstances` kann dann wie folgt umgeschrieben werden: - -.. literalinclude:: forms.py - :language: python - :lines: 49- - -Damit ist das Open-Closed-Prinzip erfüllt. - -.. tip:: - Wenn euer Code noch nicht *offen* für neue Anforderungen ist, solltet ihr - zunächst den vorhandenen Code so umordnen (refaktorieren), dass er für die - neue Funktion offen ist. Erst dann solltet ihr neuen Code hinzufügen. - - Unter Refaktorierung versteht man den Prozess, ein Softwaresystem so zu - verändern, dass das äußere Verhalten des Codes nicht verändert, aber - seine innere Struktur verbessert wird. - - – `Martin Fowler: Refactoring - `_ - -.. note:: - Sicheres Refactoring ist auf :doc:`Tests ` angewiesen. Wenn ihr - den Code wirklich umgestaltet, ohne das Verhalten zu ändern, sollten die - vorhandenen Tests bei jedem Schritt weiterhin erfolgreich sein. Die Tests - sind ein Sicherheitsnetz, das das Vertrauen in die neue Anordnung des Codes - rechtfertigt. Wenn sie versagen, - - * habt ihr den Code versehentlich beschädigt, - * oder die vorhandenen Tests sind fehlerhaft. - -.. _liskov-substitution: - -Liskovsches Substitutionsprinzip -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Das `Liskovsche Substitutionsprinzip -`_ besagt, dass -ein Programm, das Objekte der Basisklasse verwendet, auch mit Objekten der -Unterklasse korrekt funktionieren muss. - -Erweitern wir die Klasse :class:`Form`, so dass die daraus abgeleiteten Klassen -in der x- und y-Richtung verschoben werden können: - -.. literalinclude:: forms.py - :language: python - :lines: 4-12 - :emphasize-lines: 7-9 - -Anschließend könnt ihr sowohl Quadrate wie auch Kreise auf der x- und y-Achse -verschieben: - -.. code-block:: pycon - - >>> import forms - >>> s1 = forms.Square() - >>> c1 = forms.Circle() - >>> s1.x, s1.y, c1.x, c1.y - (0, 0, 0, 0) - >>> s1.move(4, 5) - >>> c1.move(2, 3) - >>> s1.x, s1.y, c1.x, c1.y - (4, 5, 2, 3) - -.. note:: - Das Liskovsche Substitutionsprinzip gilt auch für :ref:`duck-typing`: jedes - Objekt, das behauptet, eine Ente zu sein, muss die API der Ente vollständig - implementieren. Duck-Types sollten gegeneinander austauschbar sein. Die Logik - über verschiedene Datentypen von Objekten hinweg anzuwenden, nennt sich - `Polymorphie `_. - -.. _interface-segregation: - -Interface-Segregation-Prinzip -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Das `Interface-Segregation-Prinzip -`_ wendet das -:ref:`single-responsibility` auf Schnittstellen (:abbr:`engl. (englisch)`: -Interfaces) an um ein bestimmtes Verhalten zu isolieren. Wenn eine Änderung an -einem Teil eures Codes erforderlich ist, eröffnet die Extraktion eines Objekts, das eine Rolle spielt, die Möglichkeit, das neue Verhalten zu unterstützen, ohne -dass der bestehende Code geändert werden muss. Dies ist kodierten -Konkretisierungen vorzuziehen. - -So haben wir im vorigen Beispiel überprüft, ob unser :obj:`Form`-Objekt auch -tatsächlich eine :func:`circumference`-Methode bereitstellt. Dies ist notwendig, -wenn später Formen wie :class:`Point` oder :class:`Line` hinzukommen sollten, -die keinen Umfang aufweisen. - -.. note:: - In diesem Zusammenhang ist auch das `Gesetz von Demeter - `_ interessant, das besagt, - dass Objekte nur mit Objekten in ihrer unmittelbaren Umgebung kommunizieren - sollen. Damit wird die Liste der anderen Objekte wirksam eingeschränkt, an - die ein Objekt eine Nachricht senden kann und die Kopplung zwischen Objekten - verringert: ein Objekt kann nur mit seinen Nachbarn sprechen, nicht aber mit - den Nachbarn seiner Nachbarn; Objekte können nur Nachrichten an direkt - Beteiligte senden. - -.. _dependency-inversion: - -Dependency-Inversion-Prinzip -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Das `Dependency-Inversion-Prinzip -`_ kann definiert -werden als - - Abstraktionen sollten nicht von Details abhängen. Details sollten von - Abstraktionen abhängen. - -– `Robert C. Martin: The Dependency Inversion Principle -`_ - -:func:`circumferences` sollte nicht bereits in der :class:`Form`-Klasse -definiert werden, da es auch Formen ohne Umfang gibt. diff --git a/docs/oop/design/strategy.py b/docs/oop/design/strategy.py deleted file mode 100644 index 2b7ce07a..00000000 --- a/docs/oop/design/strategy.py +++ /dev/null @@ -1,179 +0,0 @@ -from abc import ABC, abstractmethod -from collections import namedtuple - -Customer = namedtuple("Customer", "loyalty") - - -class Product: - def __init__(self, product, quantity, price): - self.product = product - self.quantity = quantity - self.price = price - - def total(self): - return self.price * self.quantity - - -class Order: - """The context class.""" - - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, "__total"): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion.discount(self) - return self.total() - discount - - def __repr__(self): - fmt = "" - return fmt.format(self.total(), self.due()) - - -class Promotion(ABC): - """The abstract strategy class.""" - - @abstractmethod - def discount(self, order): - """Return discount""" - - -class LoyaltyPromo(Promotion): - """First concrete Strategy - - 5% discount for customers with 1000 or more loyalty points. - """ - - def discount(self, order): - return order.total() * 0.05 if order.customer.loyalty >= 1000 else 0 - - -class QuantityItemPromo(Promotion): - """Second concrete Strategy. - - 10% discount for each Product with 10 or more units. - """ - - def discount(self, order): - discount = 0 - for item in order.cart: - if item.quantity >= 10: - discount += item.total() * 0.1 - return discount - - -class BulkPromo(Promotion): - """Third concrete Strategy - - 7% discount for orders with 10 or more distinct items. - """ - - def discount(self, order): - distinct_items = {item.product for item in order.cart} - if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - -class Order: - """The context class.""" - - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, "__total"): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion.discount(self) - return self.total() - discount - - def __repr__(self): - fmt = "" - return fmt.format(self.total(), self.due()) - - -class Promotion(ABC): - """The abstract strategy class.""" - - @abstractmethod - def discount(self, order): - """Return discount""" - - -class LoyaltyPromo(Promotion): - """First concrete Strategy - - 5% discount for customers with 1000 or more loyalty points. - """ - - def discount(self, order): - return order.total() * 0.05 if order.customer.loyalty >= 1000 else 0 - - -class QuantityItemPromo(Promotion): - """Second concrete Strategy. - - 10% discount for each Product with 10 or more units. - """ - - def discount(self, order): - discount = 0 - for item in order.cart: - if item.quantity >= 10: - discount += item.total() * 0.1 - return discount - - -class BulkPromo(Promotion): - """Third concrete Strategy - - 7% discount for orders with 10 or more distinct items. - """ - - def discount(self, order): - distinct_items = {item.product for item in order.cart} - if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - -class Order: - """The context class.""" - - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, "__total"): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion.discount(self) - return self.total() - discount - - def __repr__(self): - fmt = "" - return fmt.format(self.total(), self.due()) diff --git a/docs/oop/design/strategy.rst b/docs/oop/design/strategy.rst deleted file mode 100644 index 946fe258..00000000 --- a/docs/oop/design/strategy.rst +++ /dev/null @@ -1,156 +0,0 @@ -Strategiemuster -=============== - -Im Entwurfsmuster-Buch wird das `Strategiemuster -`_ definiert als eine -Familie von Algorithmen, die gekapselt und austauschbar sein sollen. Dabei -variieren die Algorithmen unabhängig von den Klienten. - -.. uml:: - - title UML-Klassendiagramm für das Strategie-Entwurfsmuster - - abstract class "Client" - Client --> Context - Client --> Strategy - - together { - interface Context { - {method} context_interface() - } - abstract class Strategy { - {method} algorithm() - } - } - Context o-> Strategy - - together { - class ConcreteStrategyA { - {method} algorithm() - } - class ConcreteStrategyB { - {method} algorithm() - } - } - ConcreteStrategyA -u-|> Strategy - ConcreteStrategyB -u-|> Strategy - -Das Strategiemuster ist ein gutes Beispiel für ein Entwurfsmuster, das in Python -einfacher sein kann, wenn Funktionen als First-Class-Objekte benutzt werden. -Hierfür implementieren wir zunächst die klassische Struktur dieses Musters und -refaktorisieren anschließend diesen Code mithilfe von Funktionen. - -Ein anschauliches Beispiel für die Anwendung des Strategiemusters ist die -Berechnung von Rabatten auf Bestellungen in Abhängigkeit von den Eigenschaften -der Kund*innen und der bestellten Artikel. - -Nehmen wir ein Online-Geschäft mit den folgenden Rabattregeln: - -- Kunden mit tausend oder mehr Treuepunkten erhalten einen globalen Rabatt von - 5 % pro Bestellung. -- Ein Rabatt von 10 % wird auf jede Position mit zehn oder mehr Einheiten in - derselben Bestellung gewährt. -- Auf Bestellungen mit mindestens zehn verschiedenen Artikeln wird ein Rabatt - von 7 % gewährt. - -Dabei kann nur ein Rabatt auf eine Bestellung angewendet werden. - -Kontext - hält eine Variable von Strategie, die auf eine konkrete Strategie - referenziert. In unserem E-Commerce-Beispiel ist der - Kontext eine Bestellung (engl.: :samp:`Order`, die so konfiguriert ist, dass - sie einen Aktionsrabatt nach einem von mehreren Algorithmen anwendet. -Strategie - ist die gemeinsame Schnittstelle für die Komponenten, die die verschiedenen - Algorithmen implementieren. In unserem Beispiel wird diese Rolle von einer - abstrakten Klasse namens :samp:`Discount` übernommen. -Konkrete Strategie - ist eine der konkreten Unterklassen der abstrakten Strategie. - :samp:`LoyaltyDiscount`, :samp:`QuantityDiscount` und :samp:`BulkDiscount` - sind die drei implementierten konkreten Strategien. - -.. literalinclude:: strategy.py - :language: python - :linenos: - -Funktionsorientierte Strategie ------------------------------- - -Jede konkrete Strategie im vorigen Beispiel ist eine Klasse mit einer einzigen -Methode, :func:`discount`. Darüber hinaus haben die Strategieinstanzen keinen -Zustand (keine Instanzattribute). Im folgenden Beispiel machen wir ein -Refactoring, wobei die konkreten Strategien durch einfache Funktionen ersetzt -werden und die abstrakte Klasse :class:`Promotion` entfernt wird. - -.. literalinclude:: promos.py - :language: python - :linenos: - :lines: 1-57 - -Zeile 33: - Um einen Rabatt zu berechnen, ruft einfach die Funktion - :func:`self.promotion` auf. -Zeile 40: - Jede Strategie ist eine Funktion und keine Klasse. - -Die Autoren des Entwurfsmuster-Buch schlagen die gemeinsame Nutzung mit dem -`Fliegengewicht -`_-Entwurfsmuster -vor: - - Strategieobjekte sind oft gute Fliegengewichte. - - Ein Fliegengewicht ist ein gemeinsam genutztes Objekt, das in mehreren - Kontexten gleichzeitig verwendet werden kann. - -Die gemeinsame Nutzung wird empfohlen, um die Kosten für die Erstellung eines -neuen konkreten Strategieobjekts zu verringern, wenn dieselbe Strategie immer -wieder in jedem neuen Kontext angewendet wird – in unserem Beispiel bei jeder -neuen Bestellinstanz. Um also einen Nachteil des Strategiemusters zu überwinden -– seine Laufzeitkosten – empfehlen die Autoren die Anwendung eines weiteren -Musters. In der Zwischenzeit türmen sich die Codemenge und die Wartungskosten. - -.. tip:: - In einem schwierigeren Anwendungsfall mit komplexen konkreten Strategien, - die einen internen Zustand enthalten, können alle Teile des Strategie- und - Fliegengewichtmusters kombiniert werden. Aber oft haben konkrete Strategien - keinen internen Zustand; sie verarbeiten nur Daten aus dem Kontext. In diesem - Fall solltet Sie auf jeden Fall einfache Funktionen verwenden, anstatt - Ein-Methoden-Klassen zu kodieren, die eine Ein-Methoden-Schnittstelle - implementieren, die in einer anderen Klasse deklariert ist. Eine Funktion ist - leichtgewichtiger als eine Instanz einer benutzerdefinierten Klasse, und es - besteht keine Notwendigkeit für die Fliegengewicht-Strategie, da jede - Strategie-Funktion nur einmal von Python erstellt wird, wenn das :doc:`Modul - <../../modules/index>` kompiliert wird. Eine einfache Funktion ist auch *ein - gemeinsam genutztes Objekt, das in mehreren Kontexten gleichzeitig verwendet - werden kann*. - -Dabei kann hilfreich sein, dass sich die eingebaute Funktion :py:func:`globals` -innerhalb einer Funktion oder Methode immer auf das Modul, in dem diese Funktion -oder Methode definiert ist, bezieht – und nicht auf das Modul, aus dem sie -aufgerufen wird. - -So kann :py:func:`globals` dazu verwendet werden, um alle im Modul verfügbaren -:samp:`{special}_promo`-Funktionen automatisch zu finden: - -.. literalinclude:: promos.py - :language: python - :lines: 60 - :lineno-start: 60 - -Dies iteriert über jeden Namen im :doc:`Dictionary <../../types/dicts>`, das von -:py:func:`globals` zurückgegeben wird und wählt nur diejenigen Namen aus, die -mit dem Suffix ``_promo`` enden. - -Um die :samp:`{special}_promo`-Funktionen in einem anderen Modul zu finden, kann -die :doc:`inspect `-Bibliothek verwendet werden: - -.. literalinclude:: best_promo.py - :language: python - :linenos: - -Die Funktion :py:func:`inspect.getmembers` gibt die Attribute eines Objekts -zurück – in diesem Fall das :mod:`promos`. Anschließend verwenden wir -:py:func:`inspect.isfunction`, um nur die Funktionen des Moduls zu erhalten. -Dieses Beispiel funktioniert unabhängig von den Namen der Funktionen; wichtig -ist nur, dass das :mod:`promos`-Modul die relevanten Funktionen enthält. diff --git a/docs/oop/form_ns.py b/docs/oop/form_ns.py index 43be5d05..c63113e1 100644 --- a/docs/oop/form_ns.py +++ b/docs/oop/form_ns.py @@ -63,6 +63,7 @@ def circumferences(cls): return csum def namespaces(self): + print("Builtin namespace:", dir(__builtins__)) print("Global namespace:", list(globals().keys())) print("Superclass namespace:", dir(Form)) print("Class namespace:", dir(Circle)) diff --git a/docs/oop/index.rst b/docs/oop/index.rst index f7c9ff02..8fde5904 100644 --- a/docs/oop/index.rst +++ b/docs/oop/index.rst @@ -5,6 +5,41 @@ Python bietet volle Unterstützung für `Objektorientierte Programmierung `_ (:abbr:`OOP (Objektorientierte Programmierung)`). +.. graphviz:: + :layout: neato + + graph oop { + + graph [fontname = "Calibri", fontsize="16", overlap=false]; + node [fontname = "Calibri", fontsize="16", style="bold", penwidth="5px"]; + edge [fontname = "Calibri", fontsize="16", style="bold", penwidth="5px"]; + tooltip="Objektorientierte Programmierung"; + + oop [ + label="Objektorientierte\nProgrammierung\n(OOP)", + color="#FF66B3"] + objects [ + label="Objekte", + color="#BF80FF"] + polymorphism [ + label="Polymorphismus", + color="#9999FF"] + classes [ + label="Klassen", + color="#00FF80"] + inheritance [ + label="Vererbung", + color="#4da6ff"] + encapsulation [ + label="Kapselung", + color="#00FFFF"] + oop -- objects [color="#FF66B3;0.5:#BF80FF"] + oop -- polymorphism [color="#FF66B3;0.5:#9999FF"] + oop -- classes [color="#FF66B3;0.5:#00FF80"] + oop -- inheritance [color="#FF66B3;0.5:#4da6ff"] + oop -- encapsulation [color="#FF66B3;0.5:#00FFFF"] + } + .. toctree:: :titlesonly: :hidden: @@ -13,9 +48,9 @@ Python bietet volle Unterstützung für `Objektorientierte Programmierung variables methods inheritance - summary + coherent private property - namespaces types - design/index + namespaces + dataclasses diff --git a/docs/oop/inheritance.rst b/docs/oop/inheritance.rst index ebf4110e..826a68cb 100644 --- a/docs/oop/inheritance.rst +++ b/docs/oop/inheritance.rst @@ -17,7 +17,7 @@ dies tun, indem wir ``x``- und ``y``-Koordinaten für jede Instanz definieren: :linenos: >>> class Square: - ... def __init__(self, length=1, x=0, y=0): + ... def __init__(self, length=1, x, y): ... self.length = length ... self.x = x ... self.y = y @@ -42,7 +42,7 @@ Technik wie folgt aus: :linenos: >>> class Form: - ... def __init__(self, x=0, y=0): + ... def __init__(self, x, y): ... self.x = x ... self.y = y ... @@ -72,10 +72,10 @@ sehen könnt: ``class`` definiert wird: ``Circle`` und ``Square`` erben beide von ``Form``. #. Das zweite Element ist der explizite Aufruf der ``__init__``-Methode der geerbten Klasse. Dies erfolgt in Python nicht automatisch, sondern meist über - die ``super``-Funktion, genauer durch die Zeilen ``super().__init__(x,y)``. + die ``super``-Funktion, genauer durch die Zeilen ``super().__init__(x, y)``. Dieser Code ruft die Initialisierungsfunktion von ``Form`` mit der zu initialisierenden Instanz und den entsprechenden Argumenten auf. Andernfalls - würden für die Instanzen von ``Circle`` und ``Square`` die Instanzvariablen + würden für die Instanzen von ``Circle`` und ``Square`` die Instanz-Variablen ``x`` und ``y`` nicht gesetzt. Die Vererbung kommt auch dann zum Tragen, wenn ihr versucht, eine Methode zu diff --git a/docs/oop/methods.rst b/docs/oop/methods.rst index c881c64a..96da276c 100644 --- a/docs/oop/methods.rst +++ b/docs/oop/methods.rst @@ -8,20 +8,20 @@ definiert ihr eine weitere Methode, ``circumference``, für die Klasse ``Square``; diese Methode kann verwendet werden, um den Umfang für eine beliebige ``Square``-Instanz zu berechnen und zurückzugeben. Wie die meisten benutzerdefinierten Methoden wird ``circumference`` mit einer Syntax aufgerufen, -die dem Zugriff auf Instanzvariablen ähnelt: +die dem Zugriff auf Instanz-Variablen ähnelt: .. code-block:: pycon - >>> class Square: - ... def __init__(self): - ... self.length = 1 - ... def circumference(self): - ... return 4 * self.length - ... - >>> s = Square() - >>> s.length = 5 - >>> print(s.circumference()) - 20 + >>> class Square: + ... def __init__(self): + ... self.length = 1 + ... def circumference(self): + ... return 4 * self.length + ... + >>> s = Square() + >>> s.length = 5 + >>> print(s.circumference()) + 20 Die Syntax für Methodenaufrufe besteht aus einer Instanz, gefolgt von einem Punkt, gefolgt von der Methode, die auf der Instanz aufgerufen werden soll. Wenn @@ -34,8 +34,8 @@ Klasse sein muss, in der die Methode definiert ist, und weniger klar ist: .. code-block:: pycon - >>> print(Square.circumference(s)) - 20 + >>> print(Square.circumference(s)) + 20 Wie ``__init__`` wird auch die ``circumference``-Methode als Funktion innerhalb der Klasse definiert. Das erste Argument jeder Methode ist die Instanz, von der @@ -50,22 +50,22 @@ eines Quadrats festlegen zu müssen: .. code-block:: pycon - >>> class Square: - ... def __init__(self, length): - ... self.length = length - ... def circumference(self): - ... return 4 * self.length - ... + >>> class Square: + ... def __init__(self, length): + ... self.length = length + ... def circumference(self): + ... return 4 * self.length + ... .. warning:: - ``self.length`` und ``length`` sind nicht dasselbe! + ``self.length`` und ``length`` sind nicht dasselbe! - * ``self.length`` ist die Instanzvariable namens ``length`` - * ``length`` ist der lokale Funktionsparameter + * ``self.length`` ist die Instanz-Variable namens ``length`` + * ``length`` ist der lokale Funktionsparameter - In der Praxis würdet ihr den lokalen Funktionsparameter wahrscheinlich als - ``lng`` oder ``l`` bezeichnen, um Verwechslungen zu vermeiden. + In der Praxis würdet ihr den lokalen Funktionsparameter wahrscheinlich als + ``lng`` oder ``l`` bezeichnen, um Verwechslungen zu vermeiden. Mit dieser Definition von ``Square`` könnt ihr Quadrate mit beliebigen Kantenlängen mit einem Aufruf der Klasse ``Square`` erstellen. Im Folgenden wird @@ -73,20 +73,20 @@ ein Quadrat mit der Kantenlänge ``3`` erstellt: .. code-block:: pycon - ... s = Square(3) + ... s = Square(3) Alle Standardfunktionen von Python – Standardargumente, zusätzliche Argumente, -Schlüsselwortargumente :abbr:`usw. (und so weiter)` – können mit Methoden +Schlüsselwort-Argumente :abbr:`usw. (und so weiter)` – können mit Methoden verwendet werden. Ihr hättet die erste Zeile von ``__init__`` wie folgt definieren können: .. code-block:: pycon - ... def __init__(self, length=1): + ... def __init__(self, length=1): Dann würde der Aufruf von ``Square`` mit oder ohne zusätzliches Argument funktionieren; ``Square()`` würde ein Quadrat mit der Kantenlänge ``1`` und -``Square(3)`` ein Quadratmit der Kantenlänge ``3`` zurückgeben. +``Square(3)`` ein Quadrat mit der Kantenlänge ``3`` zurückgeben. Bei einem Methodenaufruf ``instance.method(arg1, arg2, …)`` wandelt Python diesen in einen normalen Funktionsaufruf um, indem es die folgenden Regeln @@ -106,6 +106,8 @@ anwendet: verschoben werden. So wird ``instance.method(arg1, arg2, …)`` zu ``class.method(instance, arg1, arg2, …)``. +.. _staticmethod: + Statische Methoden ------------------ @@ -124,14 +126,16 @@ Zeile 14 .. code-block:: pycon - >>> import circle - >>> c1 = circle.Circle(1) - >>> c2 = circle.Circle(2) - >>> circle.Circle.circumferences() - 9.424769999999999 - >>> c2.diameter = 3 - >>> circle.Circle.circumferences() - 12.56636 + >>> import circle + >>> c1 = circle.Circle(1) + >>> c2 = circle.Circle(2) + >>> circle.Circle.circumferences() + 9.424769999999999 + >>> c2.diameter = 3 + >>> circle.Circle.circumferences() + 12.56636 + +.. _classmethod: Klassenmethoden --------------- @@ -147,11 +151,11 @@ gehören, als erster Parameter übergeben: :lines: 23- :lineno-start: 23 -Zeile 18 +Zeile 23 Der ``@classmethod``-Dekorator wird vor der Methode ``def`` verwendet. -Zeile 19 +Zeile 24 Der Klassenparameter ist traditionell ``cls``. -Zeile 22 +Zeile 27 Ihr könnt ``cls`` anstelle von ``self.__class__`` verwenden. Durch die Verwendung einer Klassenmethode anstelle einer statischen Methode @@ -159,11 +163,11 @@ Zeile 22 .. code-block:: pycon - >>> import circle_cm - >>> c1 = circle_cm.Circle(1) - >>> c2 = circle_cm.Circle(2) - >>> circle_cm.Circle.circumferences() - 9.424769999999999 + >>> import circle_cm + >>> c1 = circle_cm.Circle(1) + >>> c2 = circle_cm.Circle(2) + >>> circle_cm.Circle.circumferences() + 9.424769999999999 Checks ------ diff --git a/docs/oop/namespaces.rst b/docs/oop/namespaces.rst index 49c64eb0..1e3cf832 100644 --- a/docs/oop/namespaces.rst +++ b/docs/oop/namespaces.rst @@ -1,6 +1,21 @@ Namensräume =========== +Ein Namensraum ist eine Sammlung von aktuell definierten symbolischen Namen und +Informationen über ein Objekt. Ihr könnt euch einen Namensraum als ein +Wörterbuch vorstellen, in dem die Schlüssel die Objektnamen und die Werte die +Objekte selbst sind. Jedes dieser Schlüssel-Wert-Paare ordnet einen Namen dem +entsprechenden Objekt zu. + + Namespaces are one honking great idea – let’s do more of those! + +– `The Zen of Python `_, von Tim Peters + +Python verwendet Namensräume mittlerweile sehr ausgiebig. Einige davon haben wir +bereits in :doc:`Funktionsvariablen <../functions/variables>` kennengelernt: +:ref:`lokale `, :ref:`globale ` und +:ref:`nicht-lokale ` Variablen. + Wenn ihr euch in der Methode einer Klasse befindet, habt ihr direkten Zugriff #. auf den **lokalen Namensraum** mit den Parametern und Variablen, die in @@ -22,7 +37,7 @@ erhaltet ihr mit .. literalinclude:: form_ns.py :language: python :linenos: - :lines: 65-70 + :lines: 65-71 :lineno-start: 65 .. code-block:: pycon @@ -30,6 +45,7 @@ erhaltet ihr mit >>> import form_ns >>> c1 = form_ns.Circle() >>> c1.namespaces() + ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BaseExceptionGroup', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'ExceptionGroup', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'PythonFinalizationError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '_IncompleteInputError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] Global namespace: ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'Form', 'Square', 'Circle'] Superclass namespace: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'move'] Class namespace: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'circles', 'circumference', 'circumferences', 'diameter', 'instance_variables', 'move', 'namespaces', 'pi'] @@ -40,9 +56,9 @@ erhaltet ihr mit #. den **Namensraum der Instanz** mit - * Instanzvariablen - * privaten Instanzvariablen und - * Instanzvariablen der Superklasse, + * Instanz-Variablen + * privaten Instanz-Variablen und + * Instanz-Variablen der Superklasse, #. den **Namensraum der Klasse** mit @@ -57,7 +73,7 @@ erhaltet ihr mit * Methoden der Superklasse und * Klassenvariablen der Superklasse. -Diese drei Namensräume werden ebenfalls in dieser Reihenfolge druchsucht. +Diese drei Namensräume werden ebenfalls in dieser Reihenfolge durchsucht. Den Namensraum der Instanz könnt ihr nun :abbr:`z.B. (zum Beispiel)` analysieren mit der Methode ``instance_variables``: @@ -65,8 +81,8 @@ mit der Methode ``instance_variables``: .. literalinclude:: form_ns.py :language: python :linenos: - :lines: 72- - :lineno-start: 72 + :lines: 73- + :lineno-start: 73 .. code-block:: pycon @@ -78,7 +94,7 @@ mit der Methode ``instance_variables``: .. note:: Während ihr auf die Methode ``move`` der Superklasse ``form`` mit ``self`` - zugreifen könnt, sind jedoch private Instanzvariablen, private Methoden und + zugreifen könnt, sind jedoch private Instanz-Variablen, private Methoden und private Klassenvariablen der Superklasse so nicht zugänglich. Wenn ihr nur Instanzen einer bestimmten Klasse ändern wollt, könnt ihr dies diff --git a/docs/oop/private.rst b/docs/oop/private.rst index 08c180c6..c34ec65d 100644 --- a/docs/oop/private.rst +++ b/docs/oop/private.rst @@ -49,19 +49,19 @@ Die ``print_y``-Methode ist nicht privat, und da sie sich in der >>> m.print_y() 2 -.. note:: +.. warning:: - Der Mechanismus, der zur Gewährleistung der Privatsphäre verwendet wird, - verfälscht den Namen privater Variablen und privater Methoden, wenn der Code - zu Bytecode kompiliert wird. Konkret bedeutet dies, dass - :samp:`_{ClassName}` dem Variablennamen vorangestellt wird: + Der Mechanismus, der zur Gewährleistung der Privatsphäre verwendet wird, + verfälscht den Namen privater Variablen und privater Methoden, wenn der Code + zu Bytecode kompiliert wird. Konkret bedeutet dies, dass :samp:`_{ClassName}` + dem Variablennamen vorangestellt wird: - .. code-block:: pycon + .. code-block:: pycon - >>> dir(m) - ['_MyClass__y', '__class__', …] + >>> dir(m) + ['_MyClass__y', '__class__', …] - Damit soll also lediglich ein versehentlicher Zugriff verhindert werden. + Damit soll also lediglich ein versehentlicher Zugriff verhindert werden. Checks ------ @@ -71,5 +71,5 @@ Checks der Klasse mit sich bringen? * Aktualisiert die Dimensionen der Klasse :class:`Triangle`, damit sie - Eigenschaften mit Gettern und Settern sind, die keine negativen Größen + Eigenschaften mit Getter- und Setter-Methoden sind, die keine negativen Größen zulassen. diff --git a/docs/oop/property.rst b/docs/oop/property.rst index 9a34b255..a460c60a 100644 --- a/docs/oop/property.rst +++ b/docs/oop/property.rst @@ -1,7 +1,7 @@ ``@property``-Dekorator ======================= -In Python könnt ihr direkt auf Instanzvariablen zugreifen, ohne zusätzliche +In Python könnt ihr direkt auf Instanz-Variablen zugreifen, ohne zusätzliche Getter- und Setter-Methoden, die häufig in Java und anderen objektorientierten Sprachen verwendet werden. Dies macht das Schreiben von Python-Klassen sauberer und einfacher, aber in manchen Situationen kann die Verwendung von Getter- und @@ -9,11 +9,11 @@ Setter-Methoden auch nützlich sein. Nehmen wir an, dass ihr einen Wert benötig bevor ihr ihn in eine Instanzvariable setzt, oder ihr einfach den Wert eines Attributs herausfinden möchtet. In beiden Fällen würden Getter- und Setter-Methoden die Aufgabe erfüllen, allerdings um den Preis, dass der einfache -Zugriff auf Instanzvariablen in Python verloren ginge. +Zugriff auf Instanz-Variablen in Python verloren ginge. Die Antwort ist die Verwendung einer *Property*. Diese kombiniert die Möglichkeit, den Zugriff auf eine Instanzvariable über Methoden wie Getter und -Setter zu übergeben, mit dem einfachen Zugriff auf Instanzvariablen über die +Setter zu übergeben, mit dem einfachen Zugriff auf Instanz-Variablen über die Punktnotation. Um eine *Property* zu erstellen, wird der :class:`python3:property`-Dekorator mit einer Methode verwendet, die den Namen der Eigenschaft hat: @@ -55,7 +55,7 @@ unserem Fall in ``length.setter``: 8 Ein großer Vorteil von Pythons Fähigkeit, Eigenschaften hinzuzufügen, besteht -darin, dass ihr zu Beginn der Entwicklung mit einfachen alten Instanzvariablen +darin, dass ihr zu Beginn der Entwicklung mit einfachen alten Instanz-Variablen arbeiten und dann nahtlos zu *Property*-Variablen wechseln könnt, wann immer und wo immer ihr dies benötigt, ohne den Client-Code zu ändern. Der Zugriff ist immer noch derselbe, unter Verwendung der Punktnotation. diff --git a/docs/oop/summary.rst b/docs/oop/summary.rst deleted file mode 100644 index b460e3c1..00000000 --- a/docs/oop/summary.rst +++ /dev/null @@ -1,105 +0,0 @@ -Zusammenfassung -=============== - -Die bisher angesprochenen Punkte, sind die Grundlagen der Verwendung von Klassen -und Objekten in Python. Diese Grundlagen werde ich nun in einem einzigen -Beispiel zusammenfassen: - -#. Zunächst erstellen wir eine Basisklasse: - - .. literalinclude:: form.py - :language: python - :linenos: - :lines: 1-9 - :lineno-start: 1 - - Zeile 4 - Die ``__init__``-Methode benötigt eine Instanz (``self``) und zwei - Parameter - Zeilen 5 und 6 - Auf die beiden Instanzvariablen ``x`` und ``y``, auf die über ``self`` - zugegriffen wird. - Zeile 7 - Die ``move``-Methode benötigt eine Instanz (``self``) und zwei - Parameter. - Zeilen 8 und 9 - Instanzvariablen, die in der ``move``-Methode gesetzt werden. - -#. Als nächstes erstellt eine Unterklasse, die von der Basisklasse ``Form`` - erbt: - - .. literalinclude:: form.py - :language: python - :linenos: - :lines: 11-17 - :lineno-start: 11 - - Zeile 11 - Die Klasse ``Square`` erbt von der Klasse ``Form``. - Zeile 13 - ``Square``’s ``__init__`` nimmt eine Instanz (``self``) und drei - Parameter, alle mit Voreinstellungen. - Zeile 14 - ``__init__`` von Circle verwendet ``super()``, um ``__init__`` von - ``Form`` aufzurufen. - -#. Schließlich erstellen wir eine weitere Unterklasse, die zudem eine statische - Methode enthält: - - .. literalinclude:: form.py - :language: python - :linenos: - :lines: 19-35 - :lineno-start: 19 - - Zeilen 21 und 22 - ``pi`` und ``circles`` sind Klassenvariablen für ``Circle``. - Zeile 26 - In der ``__init__``-Methode fügt sich die Instanz in die Liste - ``circles`` ein. - Zeilen 29 und 30 - ``circumferences`` ist eine Klassenmethode und nimmt die Klasse selbst - (``cls``) als Parameter. - Zeile 33 - verwendet den Parameter ``cls`` für den Zugriff auf die Klassenvariable - ``circles``. - -Jetzt könnt ihr einige Instanzen der Klasse ``Circle`` erstellen und sie -analysieren. Da die ``__init__``-Methode von ``Circle`` Standardparameter hat, -könnt ihr einen Kreis erstellen, ohne irgendwelche Parameter anzugeben: - - .. code-block:: pycon - - >>> import form - >>> c1 = form.Circle() - >>> c1.diameter, c1.x, c1.y - (1, 0, 0) - -Wenn ihr Parameter angebt, werden diese verwendet, um die Werte der Instanz -festzulegen: - - .. code-block:: pycon - - >>> c2 = form.Circle(2, 3, 4) - >>> c2.diameter, c2.x, c2.y - (2, 3, 4) - -Wenn ihr die ``move()``-Methode aufruft, findet Python keine ``move()``-Methode -in der Klasse ``Circle``, also wird in der Vererbungshierarchie nach oben -gegangen und die ``move()``-Methode von ``Form`` verwendet: - - .. code-block:: pycon - - >>> c2.move(5, 6) - >>> c2.diameter, c2.x, c2.y - (2, 8, 10) - -Ihr könnt auch die Klassenmethode ``circumferences()`` der Klasse ``Circle`` -aufrufen, entweder über die Klasse selbst oder durch eine Instanz: - - .. code-block:: pycon - - >>> form.Circle.circumferences() - 9.424769999999999 - >>> c2.circumferences() - 9.424769999999999 diff --git a/docs/oop/types.rst b/docs/oop/types.rst index edecc041..7adbc334 100644 --- a/docs/oop/types.rst +++ b/docs/oop/types.rst @@ -4,9 +4,9 @@ Datentypen als Objekte Inzwischen habt ihr die grundlegenden Python-:doc:`../types/index` kennengelernt und wisst, wie ihr mit Hilfe von :doc:`classes` eure eigenen Datentypen erstellen könnt. Beachtet dabei, dass Python dynamisch typisiert ist, -:abbr:`d.h.(das heißt)`, die Typen werden zur Laufzeit bestimmt, nicht zur -Kompilierzeit. Dies ist einer der Gründe, warum Python so einfach zu benutzen -ist. Ihr könnt einfach folgendes ausprobieren: +:abbr:`d.h.(das heißt)`, die Typen werden zur Laufzeit bestimmt, nicht beim +Kompilieren. Dies ist einer der Gründe, warum Python so einfach zu benutzen ist. +Ihr könnt einfach folgendes ausprobieren: .. code-block:: pycon @@ -25,7 +25,7 @@ zurück. In diesem Beispiel sagt euch die Funktion, dass ``3`` ein ``int`` Von größerem Interesse dürfte jedoch die Tatsache sein, dass Python als Antwort auf die Aufrufe von :class:`type` Objekte zurückgibt; ````, ```` und ```` sind die Bildschirmdarstellungen der -zurückgegebenen Objekte. Ihr könnt diese Python-Pbjekte also miteinander +zurückgegebenen Objekte. Ihr könnt diese Python-Objekte also miteinander vergleichen: .. code-block:: pycon @@ -35,7 +35,7 @@ vergleichen: >>> type("Hello") == type("Pythonistas!") == type(["Hello", "Pythonistas"]) False -Mit dieser Technik könnt ihr :abbr:`u.a. (unter anderem)` eine Typüberprüfung +Mit dieser Technik könnt ihr :abbr:`u.a. (unter anderem)` eine Typ-Überprüfung in euren Funktions- und Methodendefinitionen durchführen. Die häufigste Frage zu den Typen von Objekten ist jedoch, ob ein bestimmtes Objekt eine Instanz einer Klasse ist. Ein Beispiel mit einer einfachen Vererbungshierarchie macht @@ -125,16 +125,16 @@ eines Objekts oder einer Klasse korrekt zu bestimmen. Python hat jedoch auch eine Funktion, die die Verwendung von Objekten noch einfacher macht: Duck-Typing: - *„If it walks like a duck and it quacks like a duck, then it must be a - duck.“* + „Wenn es wie eine Ente aussieht und wie eine Ente quakt, muss es eine + Ente sein.“ Dies bezieht sich auf Pythons Art und Weise zu bestimmen, ob ein Objekt der erforderliche Typ für eine Operation ist, wobei der Schwerpunkt auf der Schnittstelle eines Objekts liegt. Kurz gesagt müsst ihr euch in Python nicht um -die Typüberprüfung von Funktions- oder Methodenargumenten und Ähnlichem kümmern, -sondern euch stattdessen auf lesbaren und dokumentierten Code in Verbindung mit -Tests verlassen, um sicherzustellen, dass ein Objekt bei Bedarf *„wie eine Ente -quakt.“* +die Typ-Überprüfung von Funktions- oder Methodenargumenten und Ähnlichem +kümmern, sondern euch stattdessen auf lesbaren und dokumentierten Code in +Verbindung mit Tests verlassen, um sicherzustellen, dass ein Objekt bei Bedarf +*„wie eine Ente quakt.“* Duck-Typing kann die Flexibilität von gut geschriebenem Code erhöhen und gibt euch in Kombination mit fortgeschrittenen objektorientierten Funktionen die @@ -147,8 +147,8 @@ dieser Klasse aufgerufen. Eines der einfachsten Beispiele für eine spezielle Methode ist :meth:`object.__str__`. Wenn es in einer Klasse definiert ist, wird das -``__str__``-Methodenattribut jedes Mal aufgerufen, wenn eine Instanz dieser -Klasse verwendet wird und Python eine benutzerlesbare Zeichenkettendarstellung +``__str__``-Methoden-Attribut jedes Mal aufgerufen, wenn eine Instanz dieser +Klasse verwendet wird und Python eine benutzerlesbare Zeichenketten-Darstellung dieser Instanz benötigt. Um dieses Attribut in Aktion zu sehen, verwenden wir erneut unsere ``Form``-Klasse mit der Standardmethode ``__init__`` um Instanzen der Klasse zu initialisieren, sondern auch eine ``__str__``-Methode um @@ -167,26 +167,26 @@ Zeichenketten zurückzugeben, die Instanzen in einem lesbaren Format darstellen: >>> print(f) Position: x=2, y=3 -Auch wenn unser spezielles ``__str__``-Methodenattribut nicht von unserem Code +Auch wenn unser spezielles ``__str__``-Methoden-Attribut nicht von unserem Code explizit aufgerufen wurde, konnte es dennoch von Python verwendet werden, da Python weiß, dass das ``__str__``-Attribut, falls vorhanden, eine Methode zur Umwandlung von Objekten in benutzerlesbare Zeichenketten definiert. Und genau -dies zeichnet die speziellen Methodenattribute aus. So ist es :abbr:`z.B. (zum +dies zeichnet die speziellen Methoden-Attribute aus. So ist es :abbr:`z.B. (zum Beispiel)` oft eine gute Idee, das ``__str__``-Attribut für eine Klasse zu definieren, damit ihr im Debugging-Code ``print(instance)`` aufrufen könnt und eine informative Aussage über euer Objekt zu erhalten. Umgekehrt kann es jedoch auch verwundern, dass ein Objekttyp anders auf -spezielle Methodenattribute reagiert. Daher verwende ich spezielle -Methodenattribute meist nur in einer der folgenden beiden Fälle: +spezielle Methoden-Attribute reagiert. Daher verwende ich spezielle +Methoden-Attribute meist nur in einer der folgenden beiden Fälle: * in einer häufig verwendeten Klasse, meist für Sequenzen, die sich ähnlich wie ein in Python eingebauter Typ verhält, und die durch spezielle - Methodenattribute nützlicher wird. + Methoden-Attribute nützlicher wird. * in einer Klasse, die sich fast identisch zu einer eingebauten Klasse verhält, :abbr:`z.B. (zum Beispiel)` Listen, die als balancierte Bäume implementiert sind, um das Einfügen zu beschleunigen, kann ich die speziellen - Methodenattribute definieren. + Methoden-Attribute definieren. Checks ------ diff --git a/docs/oop/variables.rst b/docs/oop/variables.rst index 845f9be7..a160a682 100644 --- a/docs/oop/variables.rst +++ b/docs/oop/variables.rst @@ -1,27 +1,27 @@ Variablen ========= -Instanzvariablen ----------------- +Instanz-Variablen +----------------- -Im vorigen Beispiel ist ``length`` eine Instanzvariable von +Im vorigen Beispiel ist ``length`` eine Instanz-Variable von ``Square``-Instanzen, :abbr:`d.h. (das heißt)`, jede Instanz der Klasse ``Square`` hat ihre eigene Kopie von ``length``, und der in dieser Kopie gespeicherte Wert kann sich von den Werten unterscheiden, die in der ``length``-Variable in anderen Instanzen gespeichert sind. In Python könnt ihr -Instanzvariablen nach Bedarf erstellen, indem ihr sie dem Feld einer +Instanz-Variablen nach Bedarf erstellen, indem ihr sie dem Feld einer Klasseninstanz zuweist. Wenn die Variable noch nicht existiert, wird sie automatisch erstellt. -Alle Verwendungen von Instanzvariablen, sowohl die Zuweisung als auch der +Alle Verwendungen von Instanz-Variablen, sowohl die Zuweisung als auch der Zugriff, erfordern die explizite Erwähnung der enthaltenen Instanz, :abbr:`d.h. (das heißt)` ``instance.variable``. Ein Verweis auf eine Variable an sich ist -kein Verweis auf eine Instanzvariable, sondern auf eine lokale Variable in der +kein Verweis auf eine Instanz-Variable, sondern auf eine lokale Variable in der ausführenden Methode. Dies ist ein Unterschied zu C++ und Java, wo -Instanzvariablen auf die gleiche Weise referenziert werden wie lokale +Instanz-Variablen auf die gleiche Weise referenziert werden wie lokale Funktionsvariablen der Methode. Python schreibt hier die explizite Erwähnung der enthaltenen Instanz vor, und dies ermöglicht eine klare Unterscheidung zwischen -Instanzvariablen und lokalen Funktionsvariablen. +Instanz-Variablen und lokalen Funktionsvariablen. Klassenvariablen ---------------- @@ -33,11 +33,11 @@ Informationen auf Klassenebene zu speichern, :abbr:`z.B. (zum Beispiel)` wie viele Instanzen der Klasse zu einem bestimmten Zeitpunkt erstellt wurden. Python stellt Klassenvariablen zur Verfügung, obwohl deren Verwendung etwas mehr Aufwand erfordert als in den meisten anderen Sprachen. Außerdem müsst ihr auf -eine Wechselwirkung zwischen Klassen- und Instanzvariablen achten. +eine Wechselwirkung zwischen Klassen- und Instanz-Variablen achten. Eine Klassenvariable wird durch eine Zuweisung in der Klasse, jedoch außerhalb der ``__init__``-Funktion, erzeugt. Nachdem sie erstellt wurde, kann sie von -allen Instanzen der Klasse gesehen werden. ihr könnt eine Klassenvariable +allen Instanzen der Klasse gesehen werden. Ihr könnt eine Klassenvariable verwenden, um einen Wert für ``pi`` für alle Instanzen der Klasse ``Circle`` zugänglich zu machen: @@ -68,7 +68,7 @@ Wenn ihr diese Definition eingegeben habt, könnt ihr ``pi`` abfragen mit: Ihr könnt auch von einer Methode einer Klasse aus über den Klassennamen auf eine Klassenvariable zugreifen. Ihr tut dies in der Definition von -``Circle.circumference``, wo die Funktion ``circumference`` einen speziellen +``Circle.circumference``, wo die Methode ``circumference`` einen speziellen Verweis auf ``Circle.pi`` enthält: .. code-block:: pycon @@ -110,18 +110,19 @@ könnte, wenn ihr euch dessen nicht bewusst seid. .. warning:: - Wenn Python eine Instanzvariable sucht und keine Instanzvariable mit diesem - Namen findet, wird der Wert in einer Klassenvariablen mit demselben Namen - gesucht und zurückzugeben. Nur wenn keine passende Klassenvariable gefunden - werden kann, gibt Python einen Fehler aus. Damit können zwar effizient - Standardwerte für Instanzvariablen implementiert werden; dies führt jedoch - auch leicht dazu, versehentlich auf eine Instanzvariable statt auf eine - Klassenvariable zu verweisen, ohne dass ein Fehler gemeldet wird. + Wenn Python eine Instanz-Variable sucht und keine Instanz-Variable mit + diesem Namen findet, wird der Wert in einer Klassenvariablen mit demselben + Namen gesucht und zurückzugeben. Nur wenn keine passende Klassenvariable + gefunden werden kann, gibt Python einen Fehler aus. Damit können zwar + effizient Standardwerte für Instanz-Variablen implementiert werden; dies + führt jedoch auch leicht dazu, versehentlich auf eine Instanz-Variable + statt auf eine Klassenvariable zu verweisen, ohne dass ein Fehler gemeldet + wird. Zunächst könnt ihr euch auf die Variable ``c.pi`` beziehen, obwohl ``c`` - keine zugehörige Instanzvariable namens ``pi`` hat. Python versucht - zunächst, eine solche Instanzvariable zu finden und erst, wenn es keine - Instanzvariable finden kann, wird eine Klassenvariable ``pi`` in ``Circle`` + keine zugehörige Instanz-Variable namens ``pi`` hat. Python versucht + zunächst, eine solche Instanz-Variable zu finden und erst, wenn es keine + Instanz-Variable finden kann, wird eine Klassenvariable ``pi`` in ``Circle`` gesucht: .. code-block:: pycon @@ -140,7 +141,7 @@ könnte, wenn ihr euch dessen nicht bewusst seid. >>> c1.pi 3.141592653589793 - Ihr habt jetzt jedoch lediglich ``c1`` eine neue Instanzvariable ``pi`` + Ihr habt jetzt jedoch lediglich ``c1`` eine neue Instanz-Variable ``pi`` hinzugefügt. Die Klassenvariable ``Circle.pi`` und alle anderen daraus abgeleiteten Instanzen haben weiterhin nur fünf Nachkommastellen: diff --git a/docs/libs/.github/workflows/build_wheels.yml b/docs/packs/.github/workflows/build_wheels.yml similarity index 100% rename from docs/libs/.github/workflows/build_wheels.yml rename to docs/packs/.github/workflows/build_wheels.yml diff --git a/docs/libs/.gitlab-ci.yml b/docs/packs/.gitlab-ci.yml similarity index 100% rename from docs/libs/.gitlab-ci.yml rename to docs/packs/.gitlab-ci.yml diff --git a/docs/packs/apps.rst b/docs/packs/apps.rst new file mode 100644 index 00000000..cf3db7b5 --- /dev/null +++ b/docs/packs/apps.rst @@ -0,0 +1,164 @@ +Apps +==== + +App-Projekte sind für Webserver, Skripte und Befehlszeilenschnittstellen +(:abbr:`CLI (engl.: Command Line Interface)`) geeignet. Auch sie können wir mit +``uv init --package`` erstellen: + +.. code-block:: console + + $ uv init --package myapp + $ tree myapp -a + myapp + ├── .git + │ └── ... + ├── .gitignore + ├── .python-version + ├── README.md + ├── pyproject.toml + └── src + └── myapp + └── __init__.py + +.. note:: + Ich bin der festen Überzeugung, dass eine Python-Anwendung richtig gepackt + sein sollte, um die vielen Vorteile zu genießen, wie :abbr:`z.B. (zum + Beispiel)`: + + * die Ressourcenverwaltung mit :doc:`importlib ` + * mit ``project.scripts`` ausführbare Skripte anstelle angehängter + :file:`scripts`-Ordner + * die Vorteile des :file:`src`-Layouts mit einer allgemeinen, dokumentierten + und gut verstandenen Struktur. + +:file:`myapp/pyproject.toml` + Die Datei :file:`pyproject.toml` enthält einen ``scripts``-Einstiegspunkt + ``myapp:main``: + + .. literalinclude:: myapp/pyproject.toml + :caption: myapp/pyproject.toml + :lines: 12-13 + +:file:`myapp/src/myapp/__init__.py` + Das Modul definiert eine CLI-Funktion :func:`main`: + + .. literalinclude:: myapp/src/myapp/__init__.py + :caption: myapp/src/myapp/__init__.py + + Sie kann mit ``uv run`` aufgerufen werden: + + .. code-block:: console + + $ uv run mypapp + Hello from myapp! + + Alternativ könnt ihr auch eine :ref:`virtuelle Umgebung ` bauen und + dann :func:`main` aus Python heraus aufrufen: + + .. code-block:: console + + $ uv add --dev . + Resolved 1 package in 1ms + Audited in 0.01ms + >>> import myapp + >>> myapp.main() + Hello from myapp! + +.. _uv_lock: + +:file:`uv.lock`-Datei + Mit ``uv add --dev .`` wurde auch die :file:`uv.lock`-Datei neben der + :file:`pyproject.toml`-Datei erstellt. :file:`uv.lock` ist ein + plattformübergreifendes Lockfile, das die Pakete erfasst, die über alle + möglichen Python-Merkmale wie Betriebssystem, Architektur und Python-Version + installiert werden sollen. + + Im Gegensatz zur :file:`pyproject.toml`, die die allgemeinen Anforderungen + eures Projekts spezifiziert, enthält :file:`uv.lock` die genauen aufgelösten + Versionen, die in der Projektumgebung installiert sind. Diese Datei sollte + in die Versionskontrolle :doc:`Git + ` eingecheckt werden, um + konsistente und reproduzierbare Installationen auf verschiedenen Rechnern zu + ermöglichen. + + .. literalinclude:: myapp/uv.lock + :caption: myapp/uv.lock + + :file:`uv.lock` ist eine für Menschen lesbare + :doc:`Python4DataScience:data-processing/serialisation-formats/toml/index`-Datei, + wird aber von ``uv`` verwaltet und sollte nicht manuell bearbeitet werden. + + .. note:: + Wenn ``uv`` in andere Tools oder Workflows integriert werden soll, könnt + ihr die Inhalte mit :samp:`uv export --format requirements-txt > + {CONSTRAINTS.TXT}` in das `Requirements File Format + `_ + exportieren. Umgekehrt kann die erzeugte :samp:`{CONSTRAINTS.TXT}`-Datei + dann mit ``uv pip install`` oder anderen Tools verwendet werden. + + .. seealso:: + * `Project lockfile + `_ + +.. _reproduce-virtual-env: + +Reproduzieren der Python-Umgebung +--------------------------------- + +In produktiven Umgebungen sollten immer exakt die Versionen verwendet werden, +die auch getestet wurden. Mit ``uv sync --locked`` könnt ihr in eurer Umgebung +sicherstellen, dass die :file:`uv.lock`-Datei mit den Projekt-Metadaten +übereinstimmt. Ansonsten wird eine Fehlermeldung ausgegeben. + +Mit ``uv sync --frozen`` kann dann in der produktiven Umgebung erreicht werden, +dass die Versionen von :file:`uv.lock` als Quelle der Wahrheit verwendet werden.Sollte die :file:`uv.lock`-Datei jedoch in der produktiven Umgebung fehlen, wird +``uv sync --frozen`` mit einem Fehler beendet. Schließlich werden Änderungen an +Abhängigkeiten in der :file:`pyproject.toml`-Datei ignoriert, wenn diese noch +nicht in der :file:`uv.lock`-Datei festgeschrieben sind. + +Wollt ihr ``uv run`` in einer produktiven Umgebung verwenden, so wird mit der +``--no-sync``-Option die Aktualisierung der Umgebung vermieden. + +.. _update-uv-lock: + +Aktualisieren der Python-Umgebung +--------------------------------- + +Standardmässig bevorzugt ``uv`` bei der Ausführung von ``uv sync`` und +``uv lock`` die gesperrten Versionen der Pakete. Paketversionen werden nur dann +geändert, wenn die Abhängigkeitsbedingungen des Projekts die vorherige, +gesperrte Version ausschließen. + +Mit ``uv lock --upgrade`` könnt ihr alle Pakete aktualisieren und mit :samp:`uv +lock --upgrade-package {PACKAGE}=={VERSION}` lassen sich einzelnes Pakete auf +eine bestimmte Version aktualisieren. + +.. tip:: + Ihr könnt auch mit dem + :doc:`Python4DataScience:productive/git/advanced/hooks/pre-commit` regelmäßig + eure eure :file:`uv.lock`-Datei aktualisieren: + + .. code-block:: yaml + :caption: .pre-commit-config.yaml + + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.5.21 + hooks: + - id: uv-lock + +Plattform- und Python-Versionen einschränken +-------------------------------------------- + +Wenn euer Projekt nur eine begrenzte Anzahl von Plattformen oder +Python-Versionen unterstützt, könnt ihr dies in der +:file:`pyprojects.toml`-Datei :pep:`508`-konform tun, :abbr:`z.B. (zum +Beispiel)` um euer Projekt nur auf macOS und Linux einzuschränken könnt ihr in +eurer :file:`pyproject.toml`-Datei folgenden Abschnitt hinzufügen: + +.. code-block:: toml + + [tool.uv] + environments = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + ] diff --git a/docs/libs/binary-extensions.rst b/docs/packs/binary-extensions.rst similarity index 97% rename from docs/libs/binary-extensions.rst rename to docs/packs/binary-extensions.rst index 2827d388..8b96cfbf 100644 --- a/docs/libs/binary-extensions.rst +++ b/docs/packs/binary-extensions.rst @@ -64,7 +64,7 @@ Früher war der Hauptnachteil bei der Verwendung von Beschleunigungsmodulen, das dadurch die Distribution der Software erschwert wurde. Heute ist dieser Nachteil durch :term:`wheel` kaum noch vorhanden. Einige Nachteile bleiben dennoch: -* Die Installation aus den Sourcen bleibt weiterhin kompliziert. +* Die Installation aus dem Quellcode bleibt weiterhin kompliziert. * Ggf. gibt es kein passendes :term:`wheel` für den verwendeten Build des CPython-Interpreters oder alternativen Interpretern wie `PyPy `__, `IronPython `_ oder `Jython @@ -88,7 +88,7 @@ sollten auch eine Reihe anderer Alternativen in Betracht gezogen werden: * Sucht nach vorhandenen optimierten Alternativen. Die CPython-Standardbibliothek enthält eine Reihe optimierter Datenstrukturen und Algorithmen, insbesondere in - den builtins und den Modulen ``collections`` und ``itertools``. + den builtins und den Modulen :mod:`collections` und :mod:`itertools`. Gelegentlich bietet auch der :term:`Python Package Index` (:term:`PyPI`) zusätzliche Alternativen. Manchmal kann ein Modul eines Drittanbieters die @@ -296,11 +296,12 @@ Zufallszahlen mit Werten zwischen 1 und 1000 erstellt. Schreiben eigener Extension-Module in C: `Extending Python with C or C++ `_. Beachtet jedoch, dass diese Einführung nur die grundlegenden Tools zum Erstellen von Erweiterungen - beshreibt, die im Rahmen von CPython bereitgestellt werden. Third-Party-Tools - wie `Cython `__, `cffi `_, - `SWIG `__ und `Numba `__ - bieten sowohl einfachere als auch ausgeklügeltere Ansätze zum Erstellen von - C- und C ++- Erweiterungen für Python. + beschreibt, die im Rahmen von CPython bereitgestellt werden. + Third-Party-Tools wie `Cython `__, `cffi + `_, `SWIG `__ und `Numba + `__ bieten sowohl einfachere als auch + ausgeklügeltere Ansätze zum Erstellen von C- und C ++- Erweiterungen für + Python. `Python Packaging User Guide: Binary Extensions `_ diff --git a/docs/libs/cibuildwheel.rst b/docs/packs/cibuildwheel.rst similarity index 95% rename from docs/libs/cibuildwheel.rst rename to docs/packs/cibuildwheel.rst index ce77298c..7a933e33 100644 --- a/docs/libs/cibuildwheel.rst +++ b/docs/packs/cibuildwheel.rst @@ -6,7 +6,7 @@ Integration (CI) Workflows. Genauer gesagt baut es Manylinux-, macOS 10.9+- und Windows-Wheels für CPython und PyPy mit GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, oder -:doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd`. +:doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd/index`. Darüber hinaus bündelt es gemeinsam genutzte Bibliotheksabhängigkeiten unter Linux und macOS durch `auditwheel `_ und @@ -31,7 +31,7 @@ Schließlich können die Tests auch gegen die Wheels laufen. ``workflow_dispatch`` ermöglicht euch, in der grafischen Benutzeroberfläche auf eine Schaltfläche zu klicken, um einen Build auszulösen. Das ist perfekt zum - manuellen Testen von Wheels vor einer Veröffentlichung eignet, da ihr + manuellen Testen von Wheels vor einer Veröffentlichung geeignet, da ihr sie einfach von *artifacts* herunterladen könnt. .. seealso:: @@ -71,8 +71,8 @@ Schließlich können die Tests auch gegen die Wheels laufen. .. tab:: GitLab CI/CD Um Linux-, macOS- und Windows-Wheels mit - :doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd` zu bauen, - erstellt eine :file:`.gitlab-ci.yml`-Datei in eurem :doc:`Git + :doc:`Python4DataScience:productive/git/advanced/gitlab/ci-cd/index` zu + bauen, erstellt eine :file:`.gitlab-ci.yml`-Datei in eurem :doc:`Git `-Repository: .. literalinclude:: .gitlab-ci.yml diff --git a/docs/libs/dataprep/LICENSE b/docs/packs/dataprep/LICENSE similarity index 100% rename from docs/libs/dataprep/LICENSE rename to docs/packs/dataprep/LICENSE diff --git a/docs/libs/dataprep/MANIFEST.in b/docs/packs/dataprep/MANIFEST.in similarity index 100% rename from docs/libs/dataprep/MANIFEST.in rename to docs/packs/dataprep/MANIFEST.in diff --git a/docs/libs/dataprep/README.rst b/docs/packs/dataprep/README.rst similarity index 100% rename from docs/libs/dataprep/README.rst rename to docs/packs/dataprep/README.rst diff --git a/docs/libs/dataprep/docs/Makefile b/docs/packs/dataprep/docs/Makefile similarity index 100% rename from docs/libs/dataprep/docs/Makefile rename to docs/packs/dataprep/docs/Makefile diff --git a/docs/libs/dataprep/docs/conf.py b/docs/packs/dataprep/docs/conf.py similarity index 100% rename from docs/libs/dataprep/docs/conf.py rename to docs/packs/dataprep/docs/conf.py diff --git a/docs/libs/dataprep/docs/index.rst b/docs/packs/dataprep/docs/index.rst similarity index 100% rename from docs/libs/dataprep/docs/index.rst rename to docs/packs/dataprep/docs/index.rst diff --git a/docs/libs/dataprep/docs/make.bat b/docs/packs/dataprep/docs/make.bat similarity index 100% rename from docs/libs/dataprep/docs/make.bat rename to docs/packs/dataprep/docs/make.bat diff --git a/docs/libs/dataprep/pyproject.toml b/docs/packs/dataprep/pyproject.toml similarity index 100% rename from docs/libs/dataprep/pyproject.toml rename to docs/packs/dataprep/pyproject.toml diff --git a/docs/libs/dataprep/setup.py b/docs/packs/dataprep/setup.py similarity index 100% rename from docs/libs/dataprep/setup.py rename to docs/packs/dataprep/setup.py diff --git a/docs/libs/dataprep/src/dataprep/__init__.py b/docs/packs/dataprep/src/dataprep/__init__.py similarity index 100% rename from docs/libs/dataprep/src/dataprep/__init__.py rename to docs/packs/dataprep/src/dataprep/__init__.py diff --git a/docs/libs/dataprep/src/dataprep/cymean.pyx b/docs/packs/dataprep/src/dataprep/cymean.pyx similarity index 100% rename from docs/libs/dataprep/src/dataprep/cymean.pyx rename to docs/packs/dataprep/src/dataprep/cymean.pyx diff --git a/docs/libs/dataprep/src/dataprep/loaders.py b/docs/packs/dataprep/src/dataprep/loaders.py similarity index 100% rename from docs/libs/dataprep/src/dataprep/loaders.py rename to docs/packs/dataprep/src/dataprep/loaders.py diff --git a/docs/libs/dataprep/src/dataprep/mean.py b/docs/packs/dataprep/src/dataprep/mean.py similarity index 100% rename from docs/libs/dataprep/src/dataprep/mean.py rename to docs/packs/dataprep/src/dataprep/mean.py diff --git a/docs/libs/distribution.rst b/docs/packs/distribution.rst similarity index 78% rename from docs/libs/distribution.rst rename to docs/packs/distribution.rst index 1e0a5945..1599ca1d 100644 --- a/docs/libs/distribution.rst +++ b/docs/packs/distribution.rst @@ -9,25 +9,11 @@ sind Archive, die in einen Paket-Index wie :abbr:`z.B. (zum Beispiel)` `cusy Seminar: Fortgeschrittenes Python `_ -Einige der folgenden Befehle erfordern eine neue Version von pip, sodass ihr -sicherstellen solltet, dass ihr die neueste Version installiert habt: - -.. tab:: Linux/macOS - - .. code-block:: console - - $ python3 -m pip install --upgrade pip - -.. tab:: Windows - - .. code-block:: ps1 - - > python -m pip install --upgrade pip - Struktur -------- -Ein minimales Distribution Package kann :abbr:`z.B. (zum Beispiel)` so aussehen: +Ein minimales *Distribution Package* kann :abbr:`z.B. (zum Beispiel)` so +aussehen: .. code-block:: console @@ -183,7 +169,7 @@ In :file:`pyproject.toml` könnt ihr auch Metadaten zu eurem Paket angeben, wie version = {attr = "dataprep.VERSION"} .. tip:: - Wenn die Version in mehreren Textdateien steht, kann sich die Vwerwendung + Wenn die Version in mehreren Textdateien steht, kann sich die Verwendung von `Bump My Version `_ empfehlen. @@ -221,7 +207,7 @@ In :file:`pyproject.toml` könnt ihr auch Metadaten zu eurem Paket angeben, wie `_ ``authors`` - wird verwendet, um die Autoren des Pakets anahnd ihrer Namen und + wird verwendet, um die Autoren des Pakets anhand ihrer Namen und E-Mail-Adressen zu identifizieren. Ihr könnt auch ``maintainers`` im selben Format auflisten. @@ -230,7 +216,7 @@ In :file:`pyproject.toml` könnt ihr auch Metadaten zu eurem Paket angeben, wie ist eine kurze Zusammenfassung des Pakets, die aus einem Satz besteht. ``readme`` ist ein Pfad zu einer Datei, die eine detaillierte Beschreibung des Pakets - enthält. Diese wird auf der Paketdetailseite auf :term:`Python Package + enthält. Diese wird auf der Paket-Detailseite auf :term:`Python Package Index` (:term:`PyPI`) angezeigt. In diesem Fall wird die Beschreibung aus ``README.rst`` geladen. ``requires-python`` @@ -249,8 +235,8 @@ In :file:`pyproject.toml` könnt ihr auch Metadaten zu eurem Paket angeben, wie Außerdem haben sie eine nützliche Zusatzfunktion: Um zu verhindern, dass ein Paket zu :term:`PyPI` hochgeladen wird, verwendet den speziellen - Klassifikator ``"Private :: Do Not Upload"``. :term:`PyPI` wird immer Pakete - ablehnen, deren Klassifizierer mit ``"Private ::"`` beginnt. + Klassifizierer ``"Private :: Do Not Upload"``. :term:`PyPI` wird immer + Pakete ablehnen, deren Klassifizierer mit ``"Private ::"`` beginnt. ``dependencies`` gibt die Abhängigkeiten für euer Paket in einem Array an. @@ -336,11 +322,28 @@ eurem Arbeitsverzeichnis ausgeführt werden. Projektnamen übereinstimmen um die Konfiguration zu vereinfachen und für diejenigen, die das Paket installieren, besser erkennbar zu sein. :file:`__init__.py` - ist erforderlich, um das Verzeichnis als Paket zu importieren. Die Datei - sollte leer sein. + ist erforderlich, um das Verzeichnis als Paket zu importieren. Dies erlaubt + euch folgende Importe: + + .. code-block:: python + + import dataprep.loaders + + oder + + .. code-block:: python + + from dataprep import loaders + + Obwohl :file:`__init__.py`-Dateien oft leer sind, können sie auch Code + enthalten. + + .. seealso:: + * :ref:`python3:tut-packages` + :file:`loaders.py` ist ein Beispiel für ein Modul innerhalb des Pakets, das die Logik - (Funktionen, Klassen, Konstanten, :abbr:`etc. (et cetera)`) eures Pakets + (Funktionen, Klassen, Variablen, :abbr:`etc. (et cetera)`) eures Pakets enthalten könnte. Andere Dateien @@ -368,8 +371,8 @@ Form mit, wie sie es nutzen können. * `Make a README `_ * `readme.so `_ -Wenn ihr das Dokument in :doc:`/document/rest` schreibt, könnt ihr die Inhalte -auch als ausführliche Beschreibung in euer Paket übernehmen: +Wenn ihr das Dokument in :doc:`/document/sphinx/rest` schreibt, könnt ihr die +Inhalte auch als ausführliche Beschreibung in euer Paket übernehmen: .. literalinclude:: dataprep/pyproject.toml :language: toml @@ -377,7 +380,7 @@ auch als ausführliche Beschreibung in euer Paket übernehmen: :lines: 5, 12 Zudem könnt ihr sie dann auch in eure :doc:`Sphinx-Dokumentation -` mit ``.. include:: ../../README.rst`` übernehmen. +` mit ``.. include:: ../../README.rst`` übernehmen. :file:`CHANGELOG.rst` ~~~~~~~~~~~~~~~~~~~~~ @@ -460,7 +463,58 @@ Weitere Anweisungen in ``Manifest.in`` findet ihr in `MANIFEST.in commands Wenn Dateien und Verzeichnisse aus :file:`MANIFEST.in` auch installiert werden sollen, :abbr:`z.B. (zum Beispiel)` wenn es sich um laufzeitrelevante Daten handelt, könnt ihr dies mit ``include_package_data=True`` in eurem - ``setup()``-Aufruf angeben. + :func:`setup`-Aufruf angeben. + +.. _uv-package-structure: + +Paketstruktur erstellen +----------------------- + +Mit :samp:`uv init --package {MYPACK}` lässt sich einfach eine initiale +Dateistruktur für Pakete erstellen: + +.. code-block:: console + + $ uv init --package mypack + $ tree mypack -a + mypack + ├── .git + │   └── ... + ├── .gitignore + ├── .python-version + ├── README.md + ├── pyproject.toml + └── src + └── mypack + └── __init__.py + +:file:`mypack/pyproject.toml` + Die Datei :file:`pyproject.toml` enthält einen ``scripts``-Einstiegspunkt + ``mypack:main``: + + .. literalinclude:: mypack/pyproject.toml + :caption: mypack/pyproject.toml + :emphasize-lines: 12-13 + +:file:`mypack/src/mypack/__init__.py` + Das Modul definiert eine CLI-Funktion :func:`main`: + + .. literalinclude:: mypack/src/mypack/__init__.py + :caption: mypack/src/mypack/__init__.py + + Sie kann mit ``uv run`` aufgerufen werden: + + .. code-block:: console + + $ uv run mypack + Hello from mypack! + + .. note:: + :abbr:`Ggf. (Gegebenenfalls)` erstellt ``uv run`` eine :ref:`virtuelle + Python-Umgebung ` im Ordner :file:`.venv` bevor :func:`main` + ausgeführt wird. + +.. _uv-build: Build ----- @@ -469,8 +523,6 @@ Der nächste Schritt besteht darin, Distributionspakete für das Paket zu erstellen. Dies sind Archive, die in den :term:`Python Package Index` (:term:`PyPI`) hochgeladen und von :term:`pip` installiert werden können. -Stellt sicher, dass ihr die neueste Version von ``build`` installiert habt: - Führt nun den Befehl in demselben Verzeichnis aus, in dem sich :file:`pyproject.toml` befindet: @@ -478,40 +530,30 @@ Führt nun den Befehl in demselben Verzeichnis aus, in dem sich .. code-block:: console - $ python -m pip install build - $ cd /PATH/TO/YOUR/DISTRIBUTION_PACKAGE - $ rm -rf build dist - $ python -m build + $ uv build + Building source distribution... + Building wheel from source distribution... + Successfully built dist/mypack-0.1.0.tar.gz and dist/mypack-0.1.0-py3-none-any.whl .. tab:: Windows .. code-block:: ps1 - > python -m pip install build - > cd C:\PATH\TO\YOUR\DISTRIBUTION_PACKAGE - > rm -rf build dist - > python -m build - -Die zweite Zeile stellt sicher, dass ein sauberes Build ohne Artefakte früherer -Builds erstellt wird. Die dritte Zeile sollte eine Menge Text ausgeben und nach -Abschluss zwei Dateien im :file:`dist`-Verzeichnis erzeugen: - -.. code-block:: console - - dist - ├── dataprep-0.1.0-py3-none-any.whl - └── dataprep-0.1.0.tar.gz - -:file:`dataprep-0.1.0-py3-none-any.whl` - ist eine Build-Distribution. Neuere pip-Versionen installieren bevorzugt - Build-Distributionen, greifen aber bei Bedarf auf Source-Distributionen - zurück. Ihr solltet immer eine Source-Distribution hochladen und - Build-Distributionen für die Plattformen bereitstellen, mit denen euer - Projekt kompatibel ist. In diesem Fall ist unser Beispielpaket mit Python - auf jeder Plattform kompatibel, so dass nur eine Build-Distribution benötigt - wird: - - ``dataprep`` + > uv build + Building source distribution... + Building wheel from source distribution... + Successfully built dist/mypack-0.1.0.tar.gz and dist/mypack-0.1.0-py3-none-any.whl + +:file:`dist/mypack-0.1.0-py3-none-any.whl` + ist eine Build-Distribution. :term:`pip` installiert bevorzugt + Build-Distributionen und greift lediglich auf die Source-Distributionen + zurück, wenn keine passende Build-Distribution vorhanden ist. Ihr solltet + immer eine Source-Distribution hochladen und Build-Distributionen für die + Plattformen bereitstellen, mit denen euer Projekt kompatibel ist. In diesem + Fall ist unser Beispiel-Paket mit Python auf jeder Plattform kompatibel, so + dass nur eine Build-Distribution benötigt wird: + + ``mypack`` ist der normalisierte Paketname ``0.1.0`` ist die Version des Distrubitionspakets @@ -525,74 +567,62 @@ Abschluss zwei Dateien im :file:`dist`-Verzeichnis erzeugen: ``any`` eignet sich für jede Prozessorarchitektur, ``x86_64`` hingegen nur für Chips mit dem x86-Befehlssatz und einer 64-Bit-Architektur -:file:`dataprep-0.1.0.tar.gz` +:file:`mypack-0.1.0.tar.gz` ist eine :term:`Source Distribution`. .. seealso:: Die Referenz für die Dateinamen findet ihr in :pep:`427`. - Weitere Infos zu Source-Distributionen erhaltet ihr in `Creating a Source - Distribution - `__. - und :pep:`376`. + Weitere Infos zu Source-Distributionen erhaltet ihr in `Core metadata + specifications + `_ + und `PyPA specifications + `_. Testen ------ -.. tab:: Linux/macOS - - .. code-block:: console - - $ mkdir test_env - $ cd test_env - $ python3 -m venv .venv - $ . .venv/bin/activate - $ python -m pip install dist/dataprep-0.1.0-cp313-cp313-macosx_13_0_arm64.whl - Processing ./dist/dataprep-0.1.0-cp313-cp313-macosx_13_0_arm64.whl - Collecting Cython (from dataprep==0.1.0) - Using cached Cython-3.0.11-py2.py3-none-any.whl.metadata (3.2 kB) - … - Successfully installed Cython-3.0.11 dataprep-0.1.0 numpy-2.1.2 pandas-2.2.3 python-dateutil-2.9.0.post0 pytz-2024.2 six-1.16.0 tzdata-2024.2 - -.. tab:: Windows - - .. code-block:: console - - > mkdir test_env - > cd test_env - > python -m venv .venv - > .venv\Scripts\activate.bat - > python -m pip install dist/dataprep-0.1.0-cp313-cp313-win_amd64.whl - Processing ./dist/dataprep-0.1.0-cp313-cp313-win_amd64.whl - Collecting Cython (from dataprep==0.1.0) - Using cached Cython-3.0.11-cp313-cp313-win_amd64.whl.metadata (3.2 kB) - … - Successfully installed Cython-3.0.11 dataprep-0.1.0 numpy-2.1.2 pandas-2.2.3 python-dateutil-2.9.0.post0 pytz-2024.2 six-1.16.0 tzdata-2024.2 - Anschließend könnt ihr die :term:`Wheel`-Datei überprüfen mit: .. code-block:: console - $ python -m pip install check-wheel-contents - $ check-wheel-contents dist/*.whl + $ uv add check-wheel-contents + Resolved 17 packages in 8ms + Built mypack @ file:///Users/veit/sandbox/mypack + Prepared 1 package in 442ms + Uninstalled 1 package in 0.89ms + Installed 10 packages in 5ms + + annotated-types==0.7.0 + + attrs==24.2.0 + + check-wheel-contents==0.6.0 + + click==8.1.7 + ~ mypack==0.1.0 (from file:///Users/veit/sandbox/mypack) + + packaging==24.1 + + pydantic==2.9.2 + + pydantic-core==2.23.4 + + typing-extensions==4.12.2 + + wheel-filename==1.4.1 + $ uv run check-wheel-contents dist/*.whl dist/dataprep-0.1.0-py3-none-any.whl: OK -Alternativ könnt ihr das Paket auch installieren: +Alternativ könnt ihr das Paket auch in einem neuen Projekt installieren, +:abbr:`z.B. (zum Beispiel)` in :samp:`myapp`: .. code-block:: console - $ python -m pip install dist/dataprep-0.1.0-py3-none-any.whl - Processing ./dist/dataprep-0.1-py3-none-any.whl - Collecting pandas - … - Installing collected packages: numpy, pytz, six, python-dateutil, pandas, dataprep - Successfully installed dataprep-0.1 numpy-1.21.4 pandas-1.3.4 python-dateutil-2.8.2 pytz-2021.3 six-1.16.0 + $ uv init --app myapp + $ cd myapp + $ uv add ../mypack/dist/mypack-0.1.0-py3-none-any.whl + Resolved 8 packages in 130ms + Installed 1 package in 3ms + + mypack==0.1.0 (from file:///Users/veit/sandbox/mypack/dist/mypack-0.1.0-py3-none-any.whl) -Anschließend könnt ihr Python aufrufen und euer ``loaders``-Modul importieren: +Anschließend könnt ihr ``mypack`` mit ``uv run`` aufrufen können: -.. code-block:: python +.. code-block:: console - from dataprep import loaders + $ uv run mypack + Hello from mypack! .. note:: Es gibt immer noch viele Anleitungen, die einen Schritt zum Aufruf der @@ -607,7 +637,7 @@ Checks * Wenn ihr ein Paket für eine Aufgabenverwaltung erstellen wollt, das die Aufgaben in eine Datenbank schreibt und über ein Python-:abbr:`API (engl.: Application Programming Interface)` und eine Befehlszeilenschnittstelle - (:abbr:`CLI (engl.: Command-Line Interface)` bereitstellt, wie würdet ihr die + (:abbr:`CLI (engl.: Command-Line Interface))` bereitstellt, wie würdet ihr die Dateien strukturieren? * Überlegt euch, wie ihr die oben genannten Aufgaben erledigen wollt. Welche diff --git a/docs/packs/gitlab.rst b/docs/packs/gitlab.rst new file mode 100644 index 00000000..18d7a5ba --- /dev/null +++ b/docs/packs/gitlab.rst @@ -0,0 +1,136 @@ +GitLab Package Registry +======================= + +Ihr könnt eure Verteilungspakete auch in der Paketregistrierung eures +GitLab-Projekts veröffentlichen. Folgende Bedingungen müssen hierfür jedoch +erfüllt sein: + +* Ihr müsst euch bei der `Paketregistrierung authentifizieren + `_. +* Eure `Versionsangabe + `_ + muss gültig sein. +* Das Paket muss kleiner als 5 GB sein und ``description`` darf höchstens 4000 + Zeichen lang sein. +* Das Paket wurde noch nicht in der Paketregistrierung veröffentlicht. Der + Versuch, die gleiche Version eines Pakets zu veröffentlichen, liefert ``400 + Bad Request`` zurück. + +Anschließend könnt ihr sowohl mit :term:`pip` als auch mit :term:`uv` das Paket +nutzen. + +.. seealso:: + `Publish a PyPI package + `_ + +Authentifizierung +----------------- + +Zur Authentifizierung an der GitLab Package Registry könnt ihr eine der +folgenden Methoden verwenden: + +* Ein `persönlicher Zugriffstoken + `_ mit + dem Geltungsbereich ``api``. +* Ein `Deploy-Token + `_ mit den + Geltungsbereichen ``read_package_registry``, ``write_package_registry`` oder + beiden. +* Ein `CI-Job-Token `_. + +.. tab:: Persönlicher Zugriffstoken + + .. code-block:: yaml + :caption: .gitlab-ci.yml + :emphasize-lines: 5-6 + + variables: + UV_VERSION: 0.5 + PYTHON_VERSION: 3.12 + BASE_LAYER: bookworm-slim + UV_PUBLISH_USERNAME: + UV_PUBLISH_PASSWORD: + +.. tab:: Deploy-Token + + .. code-block:: yaml + :caption: .gitlab-ci.yml + :emphasize-lines: 5-6 + + variables: + UV_VERSION: 0.5 + PYTHON_VERSION: 3.12 + BASE_LAYER: bookworm-slim + UV_PUBLISH_USERNAME: + UV_PUBLISH_PASSWORD: + +.. tab:: Job-Token + + .. code-block:: yaml + :caption: .gitlab-ci.yml + :emphasize-lines: 5-6 + + variables: + UV_VERSION: 0.5 + PYTHON_VERSION: 3.12 + BASE_LAYER: bookworm-slim + UV_PUBLISH_USERNAME: + UV_PUBLISH_PASSWORD: $CI_JOB_TOKEN + +Authentifizierung für eine Gruppe +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Verwendet statt der :samp:`{PROJECT_ID}` die :samp:`{GROUP_URL}`. + +Veröffentlichen des Verteilungspakets +------------------------------------- + +Nun könnt ihr euer Paket auf GitLab veröffentlichen mit: + +.. code-block:: yaml + :caption: .gitlab-ci.yml + + … + stages: + - publish + + uv: + stage: publish + image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER + script: + - uv build + - uv publish --publish-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/* + +.. tip:: + :abbr:`Ggf. (Gegebenenfalls)` könnt ihr mit ``RUST_LOG=uv=trace`` weitere + Informationen zu den Authentifizierungsversuchen erhalten, also :abbr:`z.B. + (zum Beispiel)` mit ``RUST_LOG=uv=trace uv --verbose publish --publish-url + ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*``. + +.. seealso:: + In :ref:`uv-gitlab` erhaltet ihr weitere Hinweise zur Konfiguration der + :file:`.gitlab-ci.yml`-Datei. + +Installieren des Pakets +----------------------- + +Ihr könnt die neueste Version eures Pakets installieren :abbr:`z.B. (zum +Beispiel)` mit + +.. code-block:: console + + $ uv add -i https://{NAME}:{PERSONAL_ACCESS_TOKEN}@ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi/simple --no-deps {PACKAGE_NAME} + +… oder von der Gruppenebene aus mit + +.. code-block:: console + + $ uv add -i https://{NAME}:{PERSONAL_ACCESS_TOKEN}@ce.cusy.io/api/v4/groups/{GROUP_ID}/-/packages/pypi/simple --no-deps {PACKAGE_NAME} + +… oder in der :file:`pyproject.toml`-Datei mit: + +.. code-block:: toml + :caption: pyproject.toml + + [tool.uv] + extra-index-url = ["https://ce.cusy.io/api/v4/projects/{PROJECT_ID}/packages/pypi/simple {PACKAGE_NAME}"] diff --git a/docs/libs/packages-programmes.rst b/docs/packs/index.rst similarity index 86% rename from docs/libs/packages-programmes.rst rename to docs/packs/index.rst index 0aa26919..076194ef 100644 --- a/docs/libs/packages-programmes.rst +++ b/docs/packs/index.rst @@ -3,7 +3,7 @@ Pakete und Programme .. _wheels: -wheels +Pakete ------ Das derzeitige Standardformat zur Verteilung von Python-Bibliotheken und @@ -20,19 +20,34 @@ den Anforderungen und dem Prozess zur Erstellung von wheels findet ihr in * `Python Package Build Tools `_ +.. toctree:: + :titlesonly: + :hidden: + + distribution + templating/index + publish + gitlab + cibuildwheel + binary-extensions + apps + +Programme +--------- + ``py2exe`` und ``py2app`` -------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~ `py2exe `_ erstellt eigenständige Windows-Programme und `py2app `_ dasselbe für macOS. In beiden Fällen handelt es sich um einzelne ausführbare Dateien, die auch auf Rechnern laufen können, auf denen Python nicht installiert ist. In vielerlei -Hinsicht sind jedoch eigenständige ausführbare Dateien nicht ideal, da sie +Hinsicht sind jedoch eigenständig ausführbare Dateien nicht ideal, da sie tendenziell größer und weniger flexibel sind als native Python-Anwendungen, aber in manchen Situationen können sie auch die beste oder einzige Lösung sein. ``freeze`` ----------- +~~~~~~~~~~ Auch das ``freeze``-Tool erstellt ein ausführbares Python-Programm, das auf Rechnern läuft, auf denen Python nicht installiert ist. Wenn ihr das @@ -50,7 +65,7 @@ bereitstellt. * `Tools/freeze `_ PyInstaller and PyOxidizer --------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~ `PyInstaller `_ und `PyOxidizer `_ bündeln eine @@ -59,7 +74,7 @@ Python-Anwendung und alle ihre Abhängigkeiten in einem einzigen Paket. .. _briefcase: Briefcase ---------- +~~~~~~~~~ `Briefcase `__ ist ein Werkzeug zur Konvertierung eines Python-Projekts in eine eigenständige native @@ -68,7 +83,7 @@ Anwendung für Mac, Windows, Linux, iPhone/iPad und Android. .. _beeware: BeeWare -------- +~~~~~~~ `BeeWare `__ konvertiert euer Python-Projekt in eine eigenständige iOS, Android, Windows, MacOS, Linux, Web und tvOS-App. diff --git a/docs/packs/myapp/.gitignore b/docs/packs/myapp/.gitignore new file mode 100644 index 00000000..505a3b1c --- /dev/null +++ b/docs/packs/myapp/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/docs/packs/myapp/README.md b/docs/packs/myapp/README.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/packs/myapp/pyproject.toml b/docs/packs/myapp/pyproject.toml new file mode 100644 index 00000000..b17df705 --- /dev/null +++ b/docs/packs/myapp/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "myapp" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [ + { name = "Veit Schiele", email = "veit@cusy.io" } +] +requires-python = ">=3.13" +dependencies = [] + +[project.scripts] +myapp = "myapp:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/docs/packs/myapp/src/myapp/__init__.py b/docs/packs/myapp/src/myapp/__init__.py new file mode 100644 index 00000000..6deccc2c --- /dev/null +++ b/docs/packs/myapp/src/myapp/__init__.py @@ -0,0 +1,2 @@ +def main() -> None: + print("Hello from myapp!") diff --git a/docs/packs/myapp/uv.lock b/docs/packs/myapp/uv.lock new file mode 100644 index 00000000..7fe727db --- /dev/null +++ b/docs/packs/myapp/uv.lock @@ -0,0 +1,12 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "myapp" +version = "0.1.0" +source = { editable = "." } + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "myapp", editable = "." }] diff --git a/docs/packs/mypack/.gitignore b/docs/packs/mypack/.gitignore new file mode 100644 index 00000000..505a3b1c --- /dev/null +++ b/docs/packs/mypack/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/docs/packs/mypack/README.md b/docs/packs/mypack/README.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/packs/mypack/pyproject.toml b/docs/packs/mypack/pyproject.toml new file mode 100644 index 00000000..bcac75a1 --- /dev/null +++ b/docs/packs/mypack/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "mypack" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [ + { name = "Veit Schiele", email = "veit@cusy.io" } +] +requires-python = ">=3.13" +dependencies = [] + +[project.scripts] +mypack = "mypack:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/docs/packs/mypack/src/mypack/__init__.py b/docs/packs/mypack/src/mypack/__init__.py new file mode 100644 index 00000000..2c1c0bc8 --- /dev/null +++ b/docs/packs/mypack/src/mypack/__init__.py @@ -0,0 +1,2 @@ +def main() -> None: + print("Hello from mypack!") diff --git a/docs/libs/upload-install.rst b/docs/packs/publish.rst similarity index 50% rename from docs/libs/upload-install.rst rename to docs/packs/publish.rst index 8b5315d7..81158f17 100644 --- a/docs/libs/upload-install.rst +++ b/docs/packs/publish.rst @@ -1,17 +1,18 @@ -Paket hochladen -=============== +Paket veröffentlichen +===================== Schließlich könnt ihr das Paket auf dem :term:`Python Package Index` (:term:`PyPI`) oder einem anderen Index, :abbr:`z.B. (zum Beispiel)` :doc:`gitlab` oder :term:`devpi`, bereitstellen. -Hierfür müsst ihr euch bei *Test PyPI* registrieren. *Test-PyPI* ist eine -separate Instanz, die zum Testen und Experimentieren vorgesehen ist. Um dort -ein Konto einzurichten, geht ihr auf https://test.pypi.org/account/register/. -Weitere Informationen findet ihr unter `Using TestPyPI +Für den :term:`Python Package Index` müsst ihr euch bei *Test PyPI* +registrieren. *Test-PyPI* ist eine separate Instanz, die zum Testen und +Experimentieren vorgesehen ist. Um dort ein Konto einzurichten, geht ihr auf +https://test.pypi.org/account/register/. Weitere Informationen findet ihr unter +`Using TestPyPI `_. -Nun könnt ihr eine :file:`~/.pypirc`-Datei erstellen: +Nun könnt ihr eine :file:`~/.config/pip/pip.conf`-Datei erstellen: .. code-block:: ini @@ -29,67 +30,33 @@ Nun könnt ihr eine :file:`~/.pypirc`-Datei erstellen: `_. Nachdem ihr registriert seid, könnt ihr euer :term:`Distribution Package` mit -:term:`twine` hochladen. Hierzu müsst ihr jedoch zunächst twine installieren -mit: +``uv publish`` hochladen. -.. code-block:: console - - $ python -m pip install --upgrade pip build twine - … - All dependencies are now up-to-date! - -.. note:: - Führt diesen Befehl vor jedem Release aus um sicherzustellen, dass alle - Release-Tools auf dem neuesten Stand sind. - -Nun könnt ihr eure :term:`Distribution Packages ` -erstellen mit: - -.. code-block:: console - - $ cd /path/to/your/distribution_package - $ rm -rf build dist - $ pyproject-build . - -Nach der Installation von Twine könnt ihr alle Archive unter ``/dist`` auf den -Python Package Index hochladen mit: +Dabei könnt ihr ``uv publish`` entweder mit der Option ``--username __token__`` +verwenden oder die Umgebungsvariable ``UV_PUBLISH_USERNAME=__token__`` setzen, +um alle Archive unter :file:`/dist` auf den :term:`Python Package Index` +hochzuladen: .. code-block:: console - $ twine upload -r test -s dist/* - -``-r``, ``--repository`` - Das Repository zum Hochladen des Pakets. - - In unserem Fall wird ``test``-Abschnitt aus der :file:`~/.pypirc`-Datei - verwendet. - -``-s``, ``--sign`` - signiert die hochzuladenden Dateien mit GPG. + $ uv publish --publish-url https://test.pypi.org/legacy/ --username __token__ dist/* -Ihr werdet nach eurem Passwort gefragt, mit dem ihr euch bei *Test PyPI* -registriert habt. Anschließend solltet ihr eine ähnliche Ausgabe sehen: +``--publish-url`` + Die URL des Upload-Endpunkts (nicht die Index-URL). -.. code-block:: console - - Uploading distributions to https://test.pypi.org/legacy/ - Enter your username: veit - Enter your password: - Uploading example-0.0.1-py3-none-any.whl - 100%|█████████████████████| 4.65k/4.65k [00:01<00:00, 2.88kB/s] - Uploading example-0.0.1.tar.gz - 100%|█████████████████████| 4.25k/4.25k [00:01<00:00, 3.05kB/s] +``--username`` + Den Benutzernamen für den Upload. .. note:: Wenn ihr eine ähnliche Fehlermeldung erhaltet wie .. code-block:: console - The user 'veit' isn't allowed to upload to project 'example' + The user 'veit' isn't allowed to upload to project 'example' müsst ihr einen eindeutigen Namen für euer Paket auswählen: - #. ändert das ``name``-Argument in der :file:`setup.py`-Datei + #. ändert das ``name``-Argument in der :file:`pyproject.toml.`-Datei #. entfernt das ``dist``-Verzeichnis #. generiert die Archive neu @@ -99,51 +66,39 @@ registriert habt. Anschließend solltet ihr eine ähnliche Ausgabe sehen: Installation ~~~~~~~~~~~~ -Ihr könnt :term:`pip` verwenden um euer Paket zu installieren und zu überprüfen, -ob es funktioniert. Erstellt eine neue :term:`virtuelle Umgebung` und -installiert euer Paket von *Test PyPI*: +Ihr könnt ``uv`` verwenden um euer Paket von *Test PyPI* zu installieren und zu +überprüfen, ob es funktioniert: .. code-block:: console - $ python3 -m venv test_env - $ . test_env/bin/activate - $ pip install -i https://test.pypi.org/simple/ minimal_example + uv add -i https://test.pypi.org/simple/ mypack .. note:: - Wenn ihr einen anderen Paketnamen verwendet habt, ersetzt ihn im obigen - Befehl durch euren Paketnamen. + Wenn ihr einen anderen Paketnamen verwendet habt als ``mypack``, ersetzt ihn + im obigen Befehl durch euren Paketnamen. -:term:`pip` sollte das Paket von *Test PyPI* installieren und die Ausgabe sollte +``uv add`` sollte das Paket von *Test PyPI* installieren und die Ausgabe sollte in etwa so aussehen: .. code-block:: console - Looking in indexes: https://test.pypi.org/simple/ - Collecting minimal_example - … - Installing collected packages: minimal_example - Successfully installed minimal_example-0.0.1 + Resolved 8 packages in 5ms + Installed 7 packages in 36ms + + mypack==0.1.0 -Ihr könnt testen, ob euer Paket korrekt installiert wurde indem ihr das Modul -importiert und auf die ``name``-Eigenschaft referenziert, die zuvor in -``__init__.py`` eingegeben wurde: +Ihr könnt testen, ob euer Paket korrekt installiert wurde indem ihr :func:`main` +aufruft: .. code-block:: console - $ python - Python 3.13.0 (main, Oct 7 2024, 05:02:14) [Clang 15.0.0 (clang-1500.1.0.2.5)] on darwin - … - >>> import minimal_example - >>> minimal_example.name - 'minimal_example' + $ uv run mypack + Hello from mypack! .. note:: Die Pakete auf *Test-PyPI* werden nur temporär gespeichert. Wenn ihr ein Paket in den echten :term:`Python Package Index` (:term:`PyPI`) hochladen - wollt, könnt ihr dies tun, indem ihr ein Konto auf :term:`pypi.org` anlegt - und die gleichen Anweisungen befolgt, jedoch ``twine upload dist/*`` - verwendet. + wollt, könnt ihr dies tun, indem ihr ein Konto auf :term:`pypi.org` anlegt. README ~~~~~~ @@ -157,24 +112,7 @@ PyPI Registriert euch nun beim :term:`Python Package Index` (:term:`PyPI`) und stellt sicher, dass die `Zwei-Faktor-Authentifizierung `_ -aktiviert ist indem ihr die :file:`~/.pypirc`-Datei ergänzt: - -.. code-block:: ini - - [distutils] - index-servers= - pypi - test - - [test] - repository = https://test.pypi.org/legacy/ - username = veit - - [pypi] - username = __token__ - -Mit dieser Konfiguration wird nicht mehr die Name/Passwort-Kombination beim -Hochladen verwendet sondern ein Upload-Token. +aktiviert ist. .. seealso:: * `PyPI now supports uploading via API token @@ -182,11 +120,11 @@ Hochladen verwendet sondern ein Upload-Token. * `What is two factor authentication and how does it work on PyPI? `_ -Schließlich könnt ihr nun euer Paket auf PyPI veröffentlichen: +Schließlich könnt ihr nun euer Paket auf :term:`PyPI` veröffentlichen: .. code-block:: console - $ twine upload -r pypi -s dist/* + $ uv publish dist/* .. note:: Ihr könnt Releases nicht einfach ersetzen da ihr Pakete mit derselben @@ -201,8 +139,10 @@ Schließlich könnt ihr nun euer Paket auf PyPI veröffentlichen: ``==`` oder ``===`` angegeben wurde. .. seealso:: - * `PyPI Release Checklist - `_ + * `PyPI Release Checklist + `_ + +.. _pypi_github_action: GitHub Action ------------- @@ -212,7 +152,9 @@ hochlädt. Eine solche :file:`.github/workflows/pypi.yml`-Datei könnte folgendermaßen aussehen: .. code-block:: yaml + :caption: .github/workflows/pypi.yml :linenos: + :emphasize-lines: 3-5, 12, 31, 36, 38- name: Publish Python Package @@ -228,47 +170,61 @@ folgendermaßen aussehen: needs: [test] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' - cache: pip + python-version-file: .python-version cache-dependency-path: '**/pyproject.toml' - - name: Install dependencies + - name: Setup cached uv + uses: hynek/setup-cached-uv@v2 + - name: Create venv run: | - python -m pip install -U pip - python -m pip install -U setuptools build twine wheel + uv venv + echo "$PWD/.venv/bin" >> $GITHUB_PATH - name: Build run: | - python -m build - - name: Publish - env: - TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} - TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} - run: | - twine upload dist/* + uv build + - name: Retrieve and publish + steps: + - name: Retrieve release distributions + uses: actions/download-artifact@v4 + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + username: __token__ + password: ${{ secrets.PYPI_TOKEN }} Zeilen 3–5 Dies stellt sicher, dass der Arbeitsablauf jedes Mal ausgeführt wird, wenn ein neues GitHub-Release für das Repository erstellt wird. Zeile 12 Der Job wartet auf das Bestehen des ``test``-Jobs bevor er ausgeführt wird. +Zeile 31 + Hier sollte :samp:`{mypack}` durch euren Paketnamen ersetzt werden. +Zeile 36 + Die GitHub-Aktion ``actions/download-artifact`` stellt die gebauten + Verteilungspakete bereit. +Zeile 38–41 + Die GitHub-Aktion ``pypa/gh-action-pypi-publish`` veröffentlicht die Pakete + mit dem Upload-Token auf :term:`PyPI`. .. seealso:: * `GitHub Actions `_ * :doc:`cibuildwheel` +.. _trusted_publishers: + Trusted Publishers ------------------ `Trusted Publishers `_ ist ein -alternatives Verfahren zum Veröffentlichen von Paketen auf dem :term:`PyPI`. Sie -basiert auf OpenID Connect und erfordert weder Passwort noch Token. Dazu sind -lediglich die folgenden Schritte erforderlich: +Verfahren zum Veröffentlichen von Paketen auf dem :term:`PyPI`. Es basiert auf +OpenID Connect und erfordert weder Passwort noch Token. Dazu sind lediglich die +folgenden Schritte erforderlich: #. Fügt einen *Trusted Publishers* auf PyPI hinzu @@ -308,36 +264,77 @@ lediglich die folgenden Schritte erforderlich: unserem Repository: .. code-block:: yaml - :linenos: + :caption: .github/workflows/pypi.yml + :lineno-start: 10 + :emphasize-lines: 3, 4-5 - … - jobs: - … - deploy: + package-and-deploy: runs-on: ubuntu-latest - environment: release - permissions: - id-token: write + + environment: release + + permissions: + + id-token: write needs: [test] steps: - - name: Checkout - … - - name: Set up Python - … - - name: Install dependencies - … - - name: Build - … - - name: Publish - uses: pypa/gh-action-pypi-publish@release/v1 - - Zeile 6 - Dies wird benötigt, weil wir eine Umgebung in :term:`PyPI` konfiguriert - haben. - Zeilen 7–8 - Sie sind erforderlich, damit die OpenID Connect-Token-Authentifizierung - funktioniert. - Zeilen 19–20 - Das Paket verwendet die Aktion `github.com/pypa/gh-action-pypi-publish - `_, um das Paket zu - veröffentlichen. + + Zeile 12 + Die Angabe einer GitHub-Umgebung ist optional, wird aber dringend + empfohlen. + Zeilen 13–14 + Die ``write``-Berechtigung ist für *Trusted Publishing* erforderlich. + + Zeilen 42–44 + ``username`` und ``password`` werden für die GitHub-Aktion + ``pypa/gh-action-pypi-publish`` nicht mehr benötigt. + + .. code-block:: yaml + :caption: .github/workflows/pypi.yml + :lineno-start: 40 + :emphasize-lines: 3- + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + - with: + - username: __token__ + - password: ${{ secrets.PYPI_TOKEN }} + +.. _digital-attestations: + +Digital Attestations +-------------------- + +Seit 14. November 2024 unterstützt :term:`PyPI` auch :pep:`740` mit `Digital +Attestations `_. PyPI verwendet das +`in-toto Attestation Framework `_ zum +Ausstellen der Digital Attestations `SLSA Provenance +`_ und `PyPI Publish Attestation (v1) +`_. + +Die Erstellung und Veröffentlichung erfolgt standardmäßig, sofern über +:ref:`Trusted Publishing ` und die GitHub-Action +`pypa/gh-action-pypi-publish `_ +zum Veröffentlichen verwendet werden: + +.. code-block:: yaml + :caption: .github/workflows/pypi.yml + + jobs: + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/{YOUR-PYPI-PROJECT-NAME} + permissions: + id-token: write + steps: + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + +.. note:: + Die Unterstützung für die automatische Erstellung von Digital Attestations + und die Veröffentlichung aus anderen Trusted Publisher-Umgebungen ist + geplant. + +.. seealso:: + `PyPI now supports digital attestations + `_ diff --git a/docs/libs/pyproject.toml b/docs/packs/pyproject.toml similarity index 100% rename from docs/libs/pyproject.toml rename to docs/packs/pyproject.toml diff --git a/docs/libs/templating/advanced.rst b/docs/packs/templating/advanced.rst similarity index 99% rename from docs/libs/templating/advanced.rst rename to docs/packs/templating/advanced.rst index 963a28a0..45f0edfc 100644 --- a/docs/libs/templating/advanced.rst +++ b/docs/packs/templating/advanced.rst @@ -4,7 +4,7 @@ Fortgeschrittene Nutzung Hooks ----- -Ihr könnt :abbr:`sog. (sogenannte)` Pre- oder Post-Generate-Hooks schreiben, di +Ihr könnt :abbr:`sog. (sogenannte)` Pre- oder Post-Generate-Hooks schreiben, die entweder vor oder nach dem Generieren der Vorlage in den Ablauf eingehängt werden. Dabei werden die Jinja-Template-Variablen in die Skripte integriert, :abbr:`z.B. (zum Beispiel)`: diff --git a/docs/libs/templating/cruft.rst b/docs/packs/templating/cruft.rst similarity index 88% rename from docs/libs/templating/cruft.rst rename to docs/packs/templating/cruft.rst index b8527cfd..6050a72a 100644 --- a/docs/libs/templating/cruft.rst +++ b/docs/packs/templating/cruft.rst @@ -3,7 +3,8 @@ cruft Ein Problem mit cookiecutter-Vorlagen besteht darin, dass Projekte, die auf älteren Versionen der Vorlage basieren, veralten, wenn sich nur die Vorlage im -Laufe der Zeit den sich ändeernden Anforderungen angepasst wird. `cruft `_ versucht, die Übernahme von Änderungen im +Laufe der Zeit den sich ändernden Anforderungen angepasst wird. `cruft +`_ versucht, die Übernahme von Änderungen im Git-Repository des :doc:`Cookiecutter-Templates ` in daraus abgeleitete Projekte zu vereinfachen. @@ -80,10 +81,10 @@ die Datei :file:`.cruft.json` aktualisieren. Ein Projekt überprüfen ---------------------- -Um festzustellen, ob ein Projekt eine Vorlagenaktualisierung verpasst hat, könnt -ihr ganz einfach, ``cruft check`` aufrufen. Wenn das Projekt veraltet ist, wird -ein Fehler und der :samp:`Exit-Code 1` zurückgegeben. ``cruft check`` kann auch -zu :doc:`Python4DataScience:productive/git/advanced/hooks/pre-commit` und +Um festzustellen, ob ein Projekt eine Vorlagen-Aktualisierung verpasst hat, +könnt ihr ganz einfach, ``cruft check`` aufrufen. Wenn das Projekt veraltet ist, +wird ein Fehler und der :samp:`Exit-Code 1` zurückgegeben. ``cruft check`` kann +auch zu :doc:`Python4DataScience:productive/git/advanced/hooks/pre-commit` und CI-Pipelines hinzugefügt werden, um sicherzustellen, dass Projekte nicht ungewollt veralten. diff --git a/docs/libs/templating/features.rst b/docs/packs/templating/features.rst similarity index 94% rename from docs/libs/templating/features.rst rename to docs/packs/templating/features.rst index 3a3520ff..0557665b 100644 --- a/docs/libs/templating/features.rst +++ b/docs/packs/templating/features.rst @@ -1,4 +1,4 @@ -CookieCutter-Features +Cookiecutter-Features ===================== * Cross-platform: Windows, Mac und Linux werden unterstützt @@ -20,7 +20,7 @@ CookieCutter-Features $ cookiecutter cookiecutter-namespace-template -* Alternativ könnt ihr CookieCutter auch mit Python verwenden: +* Alternativ könnt ihr Cookiecutter auch mit Python verwenden: .. code-block:: console @@ -71,7 +71,7 @@ CookieCutter-Features github_username: "veit" cookiecutters_dir: "~/.cookiecutters/" -* CookieCutter-Templates, die aus einem Repository geladen wurden, werden +* Cookiecutter-Templates, die aus einem Repository geladen wurden, werden üblicherweise in ``~/.cookiecutters/`` gespeichert. Anschließend können sie direkt über ihren Verzeichnisnamen referenziert werden, also z.B. mit: diff --git a/docs/libs/templating/index.rst b/docs/packs/templating/index.rst similarity index 68% rename from docs/libs/templating/index.rst rename to docs/packs/templating/index.rst index 150205ed..3ade2305 100644 --- a/docs/libs/templating/index.rst +++ b/docs/packs/templating/index.rst @@ -2,8 +2,8 @@ Vorlagen ======== Mit `Cookiecutter `_ lassen sich -Dateistrukturen erstellen, die u.a. das Erstellen von Python-Paketen deutlich -vereinfachen. +Dateistrukturen erstellen, die :abbr:`u.a. (unter anderem)` das Erstellen von +Python-Paketen deutlich vereinfachen. .. seealso:: * `Copier `_ diff --git a/docs/libs/templating/install.rst b/docs/packs/templating/install.rst similarity index 95% rename from docs/libs/templating/install.rst rename to docs/packs/templating/install.rst index 75add2d9..3e4b5a8e 100644 --- a/docs/libs/templating/install.rst +++ b/docs/packs/templating/install.rst @@ -29,7 +29,7 @@ Voraussetzungen .. tab:: Windows - Stellt sicher, dass das Verzeichnis, in dem CookieCutter installiert wird, + Stellt sicher, dass das Verzeichnis, in dem Cookiecutter installiert wird, sich in eurem ``Path`` befindet, damit ihr es direkt aufrufen könnt. Sucht dazu auf eurem Computer nach *Environment Variables* und fügt dieses Verzeichnis zu ``Path`` hinzu, also :abbr:`z.B.(zum Beispiel)` diff --git a/docs/libs/templating/overview.rst b/docs/packs/templating/overview.rst similarity index 96% rename from docs/libs/templating/overview.rst rename to docs/packs/templating/overview.rst index dfb492af..bcea5d77 100644 --- a/docs/libs/templating/overview.rst +++ b/docs/packs/templating/overview.rst @@ -1,7 +1,7 @@ Übersicht ========= -Ein minimales CookieCutter-Template sieht so aus: +Ein minimales Cookiecutter-Template sieht so aus: .. code-block:: console :linenos: diff --git a/docs/packs/templating/templates.rst b/docs/packs/templating/templates.rst new file mode 100644 index 00000000..d77a29e6 --- /dev/null +++ b/docs/packs/templating/templates.rst @@ -0,0 +1,178 @@ +Verfügbare Templates +==================== + +Python +------ + +`cookiecutter-pypackage `_ + Template für Python-Pakete + + .. image:: https://raster.shields.io/github/stars/audreyfeldroy/cookiecutter-pypackage + :alt: Stars + :target: https://github.com/audreyfeldroy/cookiecutter-pypackage + + .. image:: https://raster.shields.io/github/contributors/audreyfeldroy/cookiecutter-pypackage + :alt: Contributors + :target: https://github.com/audreyfeldroy/cookiecutter-pypackage/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/audreyfeldroy/cookiecutter-pypackage + :alt: Commit activity + :target: https://github.com/audreyfeldroy/cookiecutter-pypackage/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/audreyfeldroy/cookiecutter-pypackage + :alt: Lizenz + :target: https://github.com/audreyfeldroy/cookiecutter-pypackage?tab=BSD-3-Clause-1-ov-file#readme + +`cookiecutter-pylibrary `_ + Umfangreiche Vorlage für Python-Pakete mit Unterstützung für Tests und + Deployments (C-Extension-Support u.a. für `cffi + `_ und `Cython `_, + Test-Unterstützung für `Tox `_, + `Pytest `_, `Travis-CI + `_, `Coveralls + `_, `Codacy + `_, und `Code + Climate `_, + Dokumentation mit `Sphinx `_, + Packaging-Checks u.a. mit `scrutinizer + `_, `Isort + `_ etc. + + .. image:: https://raster.shields.io/github/stars/ionelmc/cookiecutter-pylibrary + :alt: Stars + :target: https://github.com/ionelmc/cookiecutter-pylibrary + + .. image:: https://raster.shields.io/github/contributors/ionelmc/cookiecutter-pylibrary + :alt: Contributors + :target: https://github.com/ionelmc/cookiecutter-pylibrary/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/ionelmc/cookiecutter-pylibrary + :alt: Commit activity + :target: https://github.com/ionelmc/cookiecutter-pylibrary/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/ionelmc/cookiecutter-pylibrary + :alt: Lizenz + :target: https://github.com/ionelmc/cookiecutter-pylibrary?tab=BSD-2-Clause-1-ov-file#readme + +`cookiecutter-pytest-plugin `_ + Minimales Cookiecutter-Template zum Erstellen von `Pytest + `_-Plugins + + .. image:: https://raster.shields.io/github/stars/pytest-dev/cookiecutter-pytest-plugin + :alt: Stars + :target: https://github.com/pytest-dev/cookiecutter-pytest-plugin + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/cookiecutter-pytest-plugin + :alt: Contributors + :target: https://github.com/pytest-dev/cookiecutter-pytest-plugin/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/cookiecutter-pytest-plugin + :alt: Commit activity + :target: https://github.com/pytest-dev/cookiecutter-pytest-plugin/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/cookiecutter-pytest-plugin + :alt: Lizenz + :target: https://github.com/pytest-dev/cookiecutter-pytest-plugin?tab=MIT-1-ov-file#readme + +`cookiecutter-python-cli `_ + Template zum Erstellen einer Python-CLI-Anwendung mit `Click + `_ + + .. image:: https://raster.shields.io/github/stars/seanluong/cookiecutter-python-cli + :alt: Stars + :target: https://github.com/seanluong/cookiecutter-python-cli + + .. image:: https://raster.shields.io/github/contributors/seanluong/cookiecutter-python-cli + :alt: Contributors + :target: https://github.com/seanluong/cookiecutter-python-cli/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/seanluong/cookiecutter-python-cli + :alt: Commit activity + :target: https://github.com/seanluong/cookiecutter-python-cli/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/seanluong/cookiecutter-python-cli + :alt: Lizenz + :target: https://github.com/seanluong/cookiecutter-python-cli?tab=BSD-3-Clause-1-ov-file#readme + +`cookiecutter-namespace-template `_ + Namespace-Template für Python-Pakete + + .. image:: https://raster.shields.io/github/stars/veit/cookiecutter-namespace-template + :alt: Stars + :target: https://github.com/veit/cookiecutter-namespace-template + + .. image:: https://raster.shields.io/github/contributors/veit/cookiecutter-namespace-template + :alt: Contributors + :target: https://github.com/veit/cookiecutter-namespace-template/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/veit/cookiecutter-namespace-template + :alt: Commit activity + :target: https://github.com/veit/cookiecutter-namespace-template/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/veit/cookiecutter-namespace-template + :alt: Lizenz + :target: https://github.com/veit/cookiecutter-namespace-template?tab=BSD-3-Clause-1-ov-file#readme + +Jupyter Notebooks +----------------- + +`anywidget `_ + Spezifikation und Toolkit für die Erstellung von wiederverwendbaren + webbasierten Widgets. + + .. image:: https://raster.shields.io/github/stars/manzt/anywidget + :alt: Stars + :target: https://github.com/manzt/anywidget + + .. image:: https://raster.shields.io/github/contributors/manzt/anywidget + :alt: Contributors + :target: https://github.com/manzt/anywidget/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/manzt/anywidget + :alt: Commit activity + :target: https://github.com/manzt/anywidget/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/manzt/anywidget + :alt: Lizenz + :target: https://github.com/manzt/anywidget?tab=MIT-1-ov-file#readme + +`widget-ts-cookiecutter `_ + Cookiecutter-Template für ipywidget-Erweiterungen + + .. image:: https://raster.shields.io/github/stars/jupyter-widgets/widget-ts-cookiecutter + :alt: Stars + :target: https://github.com/jupyter-widgets/widget-ts-cookiecutter + + .. image:: https://raster.shields.io/github/contributors/jupyter-widgets/widget-ts-cookiecutter + :alt: Contributors + :target: https://github.com/jupyter-widgets/widget-ts-cookiecutter/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/jupyter-widgets/widget-ts-cookiecutter + :alt: Commit activity + :target: https://github.com/jupyter-widgets/widget-ts-cookiecutter/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/jupyter-widgets/widget-ts-cookiecutter + :alt: Lizenz + :target: https://github.com/jupyter-widgets/widget-ts-cookiecutter?tab=BSD-3-Clause-1-ov-file#readme + +Ansible +------- + +`cookiecutter-ansible-role `_ + Vorlage zum Erstellen von Ansible-Rollen + + .. image:: https://raster.shields.io/github/stars/idealista/cookiecutter-ansible-role + :alt: Stars + :target: https://github.com/idealista/cookiecutter-ansible-role + + .. image:: https://raster.shields.io/github/contributors/idealista/cookiecutter-ansible-role + :alt: Contributors + :target: https://github.com/idealista/cookiecutter-ansible-role/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/idealista/cookiecutter-ansible-role + :alt: Commit activity + :target: https://github.com/idealista/cookiecutter-ansible-role/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/idealista/cookiecutter-ansible-role + :alt: Lizenz + :target: https://github.com/idealista/cookiecutter-ansible-role?tab=Apache-2.0-1-ov-file#readme diff --git a/docs/save-data/files.rst b/docs/save-data/files.rst new file mode 100644 index 00000000..22083d27 --- /dev/null +++ b/docs/save-data/files.rst @@ -0,0 +1,338 @@ +Dateien +======= + +Öffnen von Dateien +------------------ + +In Python öffnet und lest ihr eine Datei, indem ihr die eingebaute Funktion +:func:`python3:open` und verschiedene eingebaute Leseoperationen verwendet. Das +folgende kurze Python-Programm liest eine Zeile aus einer Textdatei namens +:samp:`{myfile.txt}` ein: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r") + >>> line = f.readline() + +:func:`python3:open` liest nichts aus der Datei, sondern gibt ein :abbr:`sog. +(sogenanntes)` Datei-Objekt zurück, mit dem ihr auf die geöffnete Datei +zugreifen könnt. Es behält den Überblick über eine Datei und darüber, wie viel +von der Datei gelesen oder geschrieben wurde. Alle Dateieingaben in Python +werden mit Dateiobjekten und nicht mit Dateinamen durchgeführt. + +Der erste Aufruf von :meth:`readline() ` gibt die +erste Zeile des Datei-Objekts zurück, also alles bis einschließlich des ersten +Zeilenumbruchs oder die gesamte Datei, wenn es keinen Zeilenumbruch in der Datei +gibt; der nächste Aufruf von ``readline`` gibt die zweite Zeile zurück, wenn sie +existiert, :abbr:`usw (und so weiter)`. + +Das erste Argument der Funktion ``open`` ist ein Pfadname. Im vorigen Beispiel +öffnet ihr eine Datei, von der ihr annehmt, dass sie sich im aktuellen +Arbeitsverzeichnis befindet. Das folgende Beispiel öffnet eine Datei an einem +absoluten Speicherort – :samp:`{C:\Meine Dokumente\\myfile.txt}`: + +.. code-block:: pycon + + >>> import os + >>> pathname = os.path.join("C:/", "Users", "Veit", "Documents", "myfile.txt") + >>> with open(pathname, "r") as f: + ... line = f.readline() + ... + +.. note:: + + In diesem Beispiel wird das Schlüsselwort ``with`` verwendet, :abbr:`d.h. + (das heißt)`, dass die Datei mit einem Kontextmanager geöffnet wird, der + in :doc:`/control-flow/with` näher erläutert wird. Diese Art des Öffnens + von Dateien verwaltet mögliche I/O-Fehler besser und sollte im Allgemeinen + bevorzugt werden. + +Schließen von Dateien +--------------------- + +Nachdem alle Daten aus einem Datei-Objekt gelesen oder in dieses geschrieben +wurden, sollte das Datei-Objekt wieder geschlossen werden damit Systemressourcen +freigegeben werden, das Lesen oder Schreiben der zugrunde liegenden Datei durch +anderen Code ermöglicht wird und das Programm insgesamt zuverlässiger wird. Bei +kleinen Skripten hat dies in der Regel keine großen Auswirkungen, da +Dateiobjekte automatisch geschlossen werden, wenn das Skript oder Programm +beendet wird. Bei größeren Programmen können zu viele offene Datei-Objekte +jedoch die Systemressourcen erschöpfen, was zum Abbruch des Programms führt. +Ihr schließt ein Dateiobjekt mit der ``close``-Methode, wenn das Datei-Objekt +nicht mehr benötigt wird: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r") + >>> line = f.readline() + >>> f.close() + +Die Verwendung eines :doc:`/control-flow/with` bleibt meist jedoch die bessere +Möglichkeit, um Dateien automatisch zu schließen, wenn ihr fertig seid: + +.. code-block:: pycon + + >>> with open("docs/types/myfile.txt", "r") as f: + ... line = f.readline() + ... + +Öffnen von Dateien im Schreib- oder anderen Modi +------------------------------------------------ + +Das zweite Argument des Befehls :func:`python3:open` ist eine Zeichenkette, die +angibt, wie die Datei geöffnet werden soll. ``"r"`` öffnet die Datei zum Lesen +(engl. *read*), ``"w"`` öffnet die Datei zum Schreiben (engl. *write*) und +``"a"`` öffnet die Datei zum Anhängen (engl. *attach*). Wenn ihr die Datei zum +Lesen öffnen wollen, könnt ihr das zweite Argument weglassen, da ``"r"`` der +Standardwert ist. Das folgende kurze Programm schreibt :samp:`Hi, Pythonistas!` +in eine Datei: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "w") + >>> f.write("Hi, Pythonistas!\n") + 17 + >>> f.close() + +Je nach Betriebssystem kann :func:`python3:open` auch Zugang zu weiteren +Dateimodi haben. Diese Modi sind jedoch für die meisten Zwecke nicht notwendig. + +``open`` kann ein optionales drittes Argument annehmen, das definiert, wie +Lese- oder Schreibvorgänge für diese Datei gepuffert werden. Beim Puffern werden +Daten so lange im Speicher gehalten, bis genügend Daten angefordert oder +geschrieben wurden, um die Zeitaufwände für einen Plattenzugriff zu +rechtfertigen. Andere Parameter für ``open`` steuern die Kodierung für +Textdateien und die Behandlung von Zeilenumbrüchen in Textdateien. Auch hier +gilt, dass ihr euch in der Regel keine Gedanken über diese Funktionen machen +müsst, aber wenn ihr mit Python fortgeschrittener werdet, solltet ihr euch +vielleicht darüber informieren. + +Lese- und Schreib-Funktionen +---------------------------- + +``readline`` +~~~~~~~~~~~~ + +Die häufigste Funktion zum Lesen von Textdateien, :meth:`readline() +`, habe ich bereits vorgestellt. Diese Funktion +liest eine einzelne Zeile aus einem Datei-Objekt und gibt sie zurück, +einschließlich aller Zeilenumbrüche am Ende der Zeile. Wenn es nichts mehr zu +lesen gibt, gibt readline einen leeren String zurück, was es einfach macht, +:abbr:`z.B. (zum Beispiel)` die Anzahl der Zeilen in einer Datei zu ermitteln: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r") + >>> lc = 0 + >>> while f.readline() != "": + ... lc = lc + 1 + ... + >>> print(lc) + 2 + >>> f.close() + +``readlines`` +~~~~~~~~~~~~~ + +Ein kürzerer Weg, alle Zeilen zu zählen, gibt es mit der ebenfalls eingebauten +:meth:`readlines() `-Methode, die alle Zeilen +einer Datei liest und sie als Liste von Strings mit einen String pro Zeile +zurückgibt: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r") + >>> print(len(f.readlines())) + 1 + >>> f.close() + +Wenn ihr alle Zeilen einer großen Datei zählt, kann diese Methode dazu führen, +dass der Speicher vollläuft, weil die gesamte Datei auf einmal gelesen wird. Es +ist auch möglich, dass der Speicher mit :meth:`readline() +` überläuft, wenn ihr versucht, eine Zeile aus +einer großen Datei zu lesen, die keine Zeilenumbruchzeichen enthält. Um mit +solchen Situationen besser umgehen zu können, haben beide Methoden ein +optionales Argument, das die Menge der zu einem Zeitpunkt gelesenen Daten +beeinflusst. Eine andere Möglichkeit, über alle Zeilen einer Datei zu iterieren, +besteht darin, das Dateiobjekt als Iterator in einer :ref:`for-loop` zu +behandeln: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r") + >>> lc = 0 + >>> for l in f: + ... lc = lc + 1 + ... + >>> print(lc) + 1 + >>> f.close() + +Diese Methode hat den Vorteil, dass die Zeilen je nach Bedarf in den Speicher +eingelesen werden, so dass selbst bei großen Dateien kein Speicherplatzmangel zu +befürchten ist. Der andere Vorteil dieser Methode ist, dass sie einfacher und +lesbarer ist. + +Ein mögliches Problem mit der Lesemethode kann jedoch entstehen, wenn auf +Windows und macOS Übersetzungen im Textmodus erfolgen, wenn ihr den Befehl +:func:`open` im Textmodus verwendet, :abbr:`d.h. (das heißt)` ohne ein ``b`` +anzuhängen. Im Textmodus wird auf macOS jedes ``\r`` in ``\n`` umgewandelt, +während unter Windows ``\r\n``-Paare in ``\n`` umgewandelt werden. Ihr könnt die +Behandlung von Zeilenumbrüchen festlegen, indem ihr beim Öffnen der Datei den +Parameter ``newline`` verwendet und ``newline="\n"``, ``\r`` oder ``\r\n`` +angebt, wodurch nur diese Zeichenfolge als Zeilenumbruch verwendet wird: + +.. code-block:: pycon + + >>> f = open("docs/types/myfile.txt", "r", newline="\r\n") + +In diesem Beispiel wird nur ``\n`` als Zeilenumbruch gewertet. Wenn die Datei +jedoch im Binärmodus geöffnet wurde, ist der Parameter ``newline`` nicht +erforderlich, da alle Bytes genau so zurückgegeben werden, wie sie in der Datei +stehen. + +``write`` und ``writelines`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Die Schreibmethoden, die den Methoden :meth:`readline() +` und :meth:`readlines() +` entsprechen, sind :meth:`write() +` und :meth:`writelines() +`. Beachtet, dass es keine +``writeline``-Funktion gibt. :meth:`write() ` +schreibt eine einzelne Zeichenkette, die sich über mehrere Zeilen erstrecken +kann, wenn Zeilenumbruchzeichen in die Zeichenkette eingebettet sind, wie im +folgenden Beispiel: + +.. code-block:: python + + f.write("Hi, Pythinistas!\n\n") + +Die Methode :meth:`writelines() ` ist jedoch +verwirrend, weil sie nicht unbedingt mehrere Zeilen schreibt; sie nimmt eine +Liste von Zeichenketten als Argument und schreibt sie nacheinander in das +angegebene Datei-Objekt, ohne Zeilenumbrüche zwischen den Listenelementen +einzufügen; nur wenn die Zeichenketten in der Liste Zeilenumbrüchen enthalten, +kommen Zeilenumbrüche im Datei-Objekt hinzu; andernfalls werden sie +aneinandergereiht. :meth:`writelines() ` ist +damit die genaue Umkehrung von :meth:`readlines() +`, da sie auf die von :meth:`readlines() +` zurückgegebene Liste angewendet werden kann, um +eine Datei zu schreiben, die identisch mit der Ausgangsdatei ist. Unter der +Annahme, dass :file:`myfile.txt` existiert und eine Textdatei ist, erzeugt das +folgende Beispiel eine exakte Kopie von :file:`myfile.txt` mit dem Namen +:file:`myfile2.txt`: + +.. code-block:: pycon + + >>> input_file = open("myfile.txt", "r") + >>> lines = input_file.readlines() + >>> input_file.close() + >>> output_file = open("myfile2.txt", "w") + >>> output_file.writelines(lines) + >>> output_file.close() + +Verwendung des Binärmodus +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Wenn ihr alle Daten in einer Datei in ein einziges Byte-Objekt (partiell) +einlesen und in den Speicher übertragen möchtet um sie als Byte-Sequenz +behandeln zu können, könnt ihr die :meth:`read() +`-Methode verwenden. Ohne ein Argument liest sie die +gesamte Datei ab der aktuellen Position ein und gibt die Daten als Bytes-Objekt +zurück. Mit einem ganzzahligen Argument liest sie maximal diese Anzahl von Bytes +und gibt ein Bytes-Objekt der angegebenen Größe zurück: + +.. code-block:: pycon + :linenos: + + >>> f = open("myfile.txt", "rb") + >>> head = f.read(16) + >>> print(head) + b'Hi, Pythonistas!' + >>> body = f.read() + >>> print(body) + b'\n\n' + >>> f.close() + +Zeile 1 + öffnet eine Datei zum Lesen im Binärmodus +Zeile 2 + liest die ersten 16 Bytes als ``head``-String +Zeile 3 + gibt den ``head``-String aus +Zeile 5 + liest den Rest der Datei + +.. note:: + + Dateien, die im Binärmodus geöffnet werden, arbeiten nur mit Bytes und nicht + mit Zeichenketten. Um die Daten als Zeichenketten zu verwenden, müsst ihr + alle Byte-Objekte in String-Objekte dekodieren. Dieser Punkt ist oft wichtig + im Umgang mit Netzwerkprotokollen, wo sich Datenströme oft wie Dateien + verhalten, aber als Bytes und nicht als Strings interpretiert werden müssen. + +Checks +------ + +* Verwendet die Funktionen des :mod:`python3:os`-Moduls, um einen Pfad zu einer + Datei namens :file:`example.log` zu nehmen und einen neuen Dateipfad im selben + Verzeichnis für eine Datei namens :file:`example.log1` zu erstellen. + +* Welche Bedeutung hat das Hinzufügen von ``b`` als Parameter von + :func:`python3:open`? + +* Öffnet eine Datei :file:`my_file.txt` und fügt zusätzlichen Text am Ende der + Datei ein. Welchen Befehl würdet ihr verwenden, um :file:`my_file.txt` zu + öffnen? Welchen Befehl würdet ihr verwenden, um die Datei erneut zu öffnen und + von Anfang an zu lesen? + +* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das + :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten + nützlich wäre? + +* Warum könnte :doc:`pickle ` für die folgenden + Anwendungsfälle geeignet sein oder auch nicht: + + #. Speichern einiger Zustandsvariablen von einem Durchlauf zum nächsten + #. Aufbewahren von Auswertungsergebnissen + #. Speichern von Benutzernamen und Passwörtern + #. Speichern eines großen Wörterbuchs mit englischen Begriffen + +* Wenn ihr euch die `Manpage für das wc-Dienstprogramm + `_ anseht, seht ihr zwei + Befehlszeilenoptionen: + + ``-c`` + zählt die Bytes in der Datei + ``-m`` + zählt die Zeichen, die im Falle einiger Unicode-Zeichen zwei oder mehr + Bytes lang sein können + + Außerdem sollte unser Modul, wenn eine Datei angegeben wird, aus dieser Datei + lesen und sie verarbeiten, aber wenn keine Datei angegeben wird, sollte es aus + ``stdin`` lesen und verarbeiten. + +* Schreibt eure Version des :mod:`wc`-Dienstprogramms so um, dass es sowohl die + Unterscheidung zwischen Bytes und Zeichen als auch die Möglichkeit, aus + Dateien und von der Standardeingabe zu lesen, implementiert. + +* Wenn ein Kontext-Manager in einem Skript verwendet wird, das mehrere Dateien + liest und/oder schreibt, welche der folgenden Ansätze wäre eurer Meinung nach + am besten? + + #. Legt das gesamte Skript in einen Block, der von einer ``with``-Anweisung + verwaltet wird. + #. Verwendet eine ``with``-Anweisung für alle Lesevorgänge und eine weitere + für alle Schreibvorgänge. + #. Verwendet jedes Mal eine ``with``-Anweisung, wenn ihr eine Datei lest oder + schreibt, :abbr:`d.h. (das heißt)` für jede Zeile. + #. Verwendet für jede Datei, die ihr lest oder schreibt, eine + ``with``-Anweisung. + +* Archiviert :file:`*.txt`-Dateien aus dem aktuellen Verzeichnis im Verzeichnis + :file:`archive` als :file:`*.zip`-Dateien mit dem aktuellen Datum als + Dateiname. + + * Welche Module benötigt ihr hierfür? + * Schreibt eine mögliche Lösung. diff --git a/docs/save-data/filesystem.rst b/docs/save-data/filesystem.rst index 5d5517d0..0725c5e3 100644 --- a/docs/save-data/filesystem.rst +++ b/docs/save-data/filesystem.rst @@ -27,9 +27,9 @@ als erstes Zeichen im Pfadnamen verwiesen wird, während das Windows-Dateisystem für jedes Laufwerk ein eigenes Stammverzeichnis hat, das mit ``C:\`` :abbr:`usw. (und so weiter)` bezeichnet wird. Aufgrund dieser Unterschiede haben die Dateien auf den verschiedenen Betriebssystemen unterschiedliche Pfadnamen. Eine -Datei namens :samp:`C:\data\myfile` unter Windows könnte unter Linux und macOS -:samp:`/data/myfile` sein. Python bietet Funktionen und Konstanten, mit denen -ihr gängige Pfadnamenmanipulationen durchführen könnt, ohne sich um solche +Datei namens :samp:`C:\\data\\myfile` unter Windows könnte unter Linux und macOS +:samp:`/data/myfile` sein. Python bietet Funktionen und Variablen, mit denen +ihr gängige Pfadnamen-Manipulationen durchführen könnt, ohne sich um solche syntaktischen Details kümmern zu müssen. Mit ein wenig Sorgfalt können ihr eure Python-Programme so schreiben, dass sie unabhängig vom zugrunde liegenden Dateisystem korrekt ausgeführt werden. @@ -81,7 +81,7 @@ Relative Pfadnamen Dieser Kontext wird in der Regel auf eine der beiden folgenden Arten bereitgestellt: - * Der relative Pfad wird an einen vorhandenen absoluten Pfad anzuhängt, + * Der relative Pfad wird an einen vorhandenen absoluten Pfad angehängt, wodurch ein neuer absoluter Pfad entsteht. Wenn ihr einen relativen Windows-Pfad :samp:`{Start Menu\\Programs\\Python 3.13}` und einen absoluten Pfad :samp:`{C:\\Users\\Veit}` habt, dann kann durch Anhängen @@ -119,14 +119,14 @@ Relative Pfadnamen .. note:: ``os.getcwd()`` wird als Funktionsaufruf ohne Argumente verwendet um zu - verdeutlichen, dass der zurückgegebene Wert keine Konstante ist, - sondern sich ändert, wenn ihr den Wert des aktuellen + verdeutlichen, dass der zurückgegebene Wert keine :term:`Konstante` + ist, sondern sich ändert, wenn ihr den Wert des aktuellen Arbeitsverzeichnisses ändert. Im obigen Beispiel ist das Ergebnis das Home-Verzeichnis auf einem meiner Linux-Rechner. Auf Windows-Rechnern würden zusätzliche Backslashes in den Pfad eingefügt: - ``C:\\Users\\Veit``, da Windows den Backslash ``\`` als Pfadseparator - verwendet, der in :doc:`/types/strings` jedoch eine andere Bedeutung - hat. + ``C:\\Users\\Veit``, da Windows den Backslash ``\`` als Pfad-Separator + verwendet, der in :doc:`/types/strings/index` jedoch eine andere + Bedeutung hat. Um euch die Inhalte des aktuellen Verzeichnisses anzeigen zu lassen, könnt ihr folgendes eingeben: @@ -158,9 +158,9 @@ betriebssystemspezifische Syntax verwenden zu müssen. .. code-block:: pycon - >>> import os - >>> print(os.path.join("save-data", "filesystem.rst")) - save-data\filesystem.rst + >>> import os + >>> print(os.path.join("save-data", "filesystem.rst")) + save-data\filesystem.rst Dabei werden die Argumente interpretiert als eine Reihe von Verzeichnis- oder Dateinamen, die zu einer einzigen Zeichenkette verbunden @@ -173,9 +173,9 @@ betriebssystemspezifische Syntax verwenden zu müssen. .. code-block:: pycon - >>> import os - >>> print(os.path.join("save-data", "filesystem.rst")) - save-data/filesystem.rst + >>> import os + >>> print(os.path.join("save-data", "filesystem.rst")) + save-data/filesystem.rst Ihr könnt mit dieser Methode also Dateipfade unabhängig vom Betriebssystem, auf dem euer Programm läuft, erstellen. @@ -188,13 +188,13 @@ betriebssystemspezifische Syntax verwenden zu müssen. .. code-block:: pycon - >>> import os - >>> print( - ... os.path.join( - ... "python-basics-tutorial-de\\docs", "save-data\\filesystem.rst" - ... ) - ... ) - python-basics-tutorial-de\docs\save-data\filesystem.rst + >>> import os + >>> print( + ... os.path.join( + ... "python-basics-tutorial-de\\docs", "save-data\\filesystem.rst" + ... ) + ... ) + python-basics-tutorial-de\docs\save-data\filesystem.rst :func:`os.path.split` gibt ein Tupel mit zwei Elementen zurück, das den Basisnamen eines Pfades @@ -202,27 +202,27 @@ betriebssystemspezifische Syntax verwenden zu müssen. .. code-block:: pycon - >>> import os - >>> print(os.path.split(os.getcwd())) - ('/home/veit/python-basics-tutorial-de', 'docs') + >>> import os + >>> print(os.path.split(os.getcwd())) + ('/home/veit/python-basics-tutorial-de', 'docs') :func:`python3:os.path.basename` gibt nur den Basisnamen des Pfades zurück: .. code-block:: pycon - >>> import os - >>> print(os.path.basename(os.getcwd())) - docs + >>> import os + >>> print(os.path.basename(os.getcwd())) + docs :func:`python3:os.path.dirname` gibt den Pfad bis zum Basisnamen zurück: .. code-block:: pycon - >>> import os - >>> print(os.path.dirname(os.getcwd())) - /home/veit/python-basics-tutorial-de + >>> import os + >>> print(os.path.dirname(os.getcwd())) + /home/veit/python-basics-tutorial-de :func:`python3:os.path.splitext` gibt die gepunktete Erweiterungsnotation aus, die von den meisten @@ -230,35 +230,35 @@ betriebssystemspezifische Syntax verwenden zu müssen. .. code-block:: pycon - >>> import os - >>> print(os.path.splitext("filesystem.rst")) - ('filesystem', '.rst') + >>> import os + >>> print(os.path.splitext("filesystem.rst")) + ('filesystem', '.rst') Das letzte Element des zurückgegebenen Tupels enthält die gepunktete Erweiterung der angegebenen Datei. :func:`python3:os.path.commonpath` - ist eine spezialisiertere Funktionen, um Pfadnamen zu manipulieren. Sie + ist eine spezialisiertere Funktion, um Pfadnamen zu manipulieren. Sie findet den gemeinsamen Pfad für eine Gruppe von Pfaden und ist so gut geeignet um das Verzeichnis der untersten Ebene zu finden, das jede Datei in einer Gruppe von Dateien enthält: .. code-block:: pycon - >>> import os - >>> print(os.path.commonpath(["save-data/filesystem.rst", "save-data/index.rst"])) - save-data + >>> import os + >>> print(os.path.commonpath(["save-data/filesystem.rst", "save-data/index.rst"])) + save-data :func:`python3:os.path.expandvars` erweitert Umgebungsvariablen in Pfaden: .. code-block:: pycon - >>> os.path.expandvars("$HOME/python-basics-tutorial-de") - '/home/veit/python-basics-tutorial-de' + >>> os.path.expandvars("$HOME/python-basics-tutorial-de") + '/home/veit/python-basics-tutorial-de' -Nützliche Konstanten und Funktionen -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Nützliche Variablen und Funktionen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :data:`python3:os.name` gibt den Namen des Python-Moduls zurück, das importiert wurde, um die @@ -267,27 +267,27 @@ Nützliche Konstanten und Funktionen .. code-block:: pycon - >>> import os - >>> os.name - 'nt' + >>> import os + >>> os.name + 'nt' .. note:: - Die meisten Versionen von Windows, mit Ausnahme von Windows CE, werden - als ``nt`` identifiziert. + Die meisten Versionen von Windows, mit Ausnahme von Windows CE, werden + als ``nt`` identifiziert. Auf macOS und Linux lautet die Antwort ``posix``. Je nach Plattform könnt ihr mit dieser Antwort spezielle Operationen durchführen: .. code-block:: pycon - >>> import os - >>> if os.name == "posix": - ... root_dir = "/" - ... elif os.name == "nt": - ... root_dir = "C:\\" - ... else: - ... print("The operating system was not recognised!") - ... + >>> import os + >>> if os.name == "posix": + ... root_dir = "/" + ... elif os.name == "nt": + ... root_dir = "C:\\" + ... else: + ... print("The operating system was not recognised!") + ... Informationen über Dateien erhalten ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -315,7 +315,7 @@ Weitere ähnliche Funktionen stellen speziellere Abfragen bereit: Sinne keine echten Links und geben ``False`` zurück. Nur mit ``mklink()`` erstellte Links geben ebenfalls ``True`` zurück. :func:`python3:os.path.ismount` - gibt unter ``possix``-Dateisystemen ``True`` zurück, wenn der Pfad ein + gibt unter ``posix``-Dateisystemen ``True`` zurück, wenn der Pfad ein :abbr:`sog. (sogenannter)` *Mount Point* oder Einhängepunkt ist. :func:`python3:os.path.samefile` gibt ``True`` zurück, wenn die beiden Pfadargumente auf dieselbe Datei @@ -328,14 +328,14 @@ Weitere ähnliche Funktionen stellen speziellere Abfragen bereit: :func:`python3:os.path.getmtime` gibt das Änderungsdatum der Datei oder des Verzeichnisses an. :func:`python3:os.path.getatime` - gibt de letzte Zugriffszeit für eine Datei oder ein Verzeichnis an. + gibt die letzte Zugriffszeit für eine Datei oder ein Verzeichnis an. -Weitere Dateisystemoperationen ------------------------------- +Weitere Dateisystem-Operationen +------------------------------- -Python verfügt über weitere, sehr nützlicher Befehle im :mod:`python3:os`-Modul: +Python verfügt über weitere, sehr nützliche Befehle im :mod:`python3:os`-Modul: Im Folgenden beschreibe ich nur einige betriebssystemübergreifende Operationen, -es werden jedoch auch spezifischere Dateisystemfunktionen bereitgestellt. +es werden jedoch auch spezifischere Dateisystem-Funktionen bereitgestellt. :func:`os.rename` benennt oder verschiebt eine Datei oder ein Verzeichnis, :abbr:`z.B. (zum @@ -343,14 +343,14 @@ es werden jedoch auch spezifischere Dateisystemfunktionen bereitgestellt. .. code-block:: pycon - >>> os.rename("filesystem.rst", "save-data/filesystem.rst") + >>> os.rename("filesystem.rst", "save-data/filesystem.rst") :func:`os.remove` löscht Dateien, :abbr:`z.B. (zum Beispiel)` .. code-block:: pycon - >>> os.remove("filesystem.rst") + >>> os.remove("filesystem.rst") :func:`os.rmdir` löscht ein leeres Verzeichnis. Um nicht leere Verzeichnisse zu entfernen, @@ -362,7 +362,7 @@ es werden jedoch auch spezifischere Dateisystemfunktionen bereitgestellt. .. code-block:: pycon - >>> os.makedirs("save-data/filesystem") + >>> os.makedirs("save-data/filesystem") Verarbeitung aller Dateien in einem Verzeichnis ----------------------------------------------- @@ -387,7 +387,7 @@ onerror=None, followlinks= False)``. ``onerror`` kann auf eine Funktion gesetzt werden, um Fehler zu behandeln, die aus Aufrufen von :func:`os.listdir` resultieren, die standardmäßig ignoriert - werden. Üblicherweise wird symbolische Links nicht gefolgt, es sei denn, ihr + werden. Üblicherweise wird symbolischen Links nicht gefolgt, es sei denn, ihr gebt den Parameter ``follow-links=True`` an. .. code-block:: pycon diff --git a/docs/save-data/index.rst b/docs/save-data/index.rst index dafad904..cbc5fce2 100644 --- a/docs/save-data/index.rst +++ b/docs/save-data/index.rst @@ -1,39 +1,43 @@ Daten speichern und abrufen =========================== -Um Daten persistent zu speichern, kann ein Prozess verwendet werden, der sich -*Serialisierung* oder *Marshalling* nennt. In ihm werden Datenstrukturen in eine -lineare Form umgewandelt und gespeichert. Der umgekehrte Vorgang wird dann -*Deserialisierung* oder *Unmarshalling* genannt. Python bietet in der -Standardbibliothek mehrere Module, mit denen ihr Objekte serialisieren und -deserialisieren könnt: - -das :doc:`marshal `-Modul - wird im Wesentlichen intern von Python genutzt und sollte nicht verwendet - werden um Daten abwärtskompatibel zu speichern. -das :doc:`pickle `-Modul - könnt ihr verwenden, wenn ihr weder ein lesbares Format noch - Interoperabilität benötigt. -das :doc:`json `-Modul - könnt ihr verwenden um Daten für verschiedene Sprachen in einer lesbaren - Form auszutauschen. -das :doc:`xml `-Modul - könnt ihr ebenfalls verwenden um Daten in verschiedene Sprachen in einer - lesbaren Form auszutauschen. +Ihr könnt eure Daten persistent in :doc:`files` im :doc:`filesystem` speichern. +In der Python-Standardbibliothek gibt es darüberhinaus mehrere Module, um Daten +in eine lineare Form umzuwandeln. Dieser Prozess wird *Serialisierung* oder +*Marshalling* genannt. Der umgekehrte Vorgang heißt dann *Deserialisierung* oder +*Unmarshalling*. Und wenn die :ref:`eingebauten Module ` +nicht ausreichen sollten, könnt ihr auch die :ref:`pandas-io-tools` verwenden. .. tip:: `cusy Seminar: Daten lesen, schreiben und bereitstellen mit Python `_ +.. toctree:: + :titlesonly: + :hidden: + + files + filesystem + modules + pickle + xml + Die Python-Datenbank-API ------------------------ Die Python Database :abbr:`API (Application Programming Interface)` definiert -eine Standardschnittstelle für Python-Datenbankzugriffsmodule. Sie ist in +eine Standardschnittstelle für Python-Datenbank-Zugriffsmodule. Sie ist in :pep:`249` definiert und wird häufig verwendet, :abbr:`z.B. (zum Beispiel)` von -:doc:`sqlite `, :doc:`psycopg `, and `mysql-python +:doc:`sqlite `, :doc:`psycopg `, and `mysql-python `_. +.. toctree:: + :titlesonly: + :hidden: + + sqlite/index + psycopg + SQLAlchemy ---------- @@ -42,7 +46,7 @@ verbreitetes Datenbank-Toolkit. Es bietet nicht nur als ein :abbr:`ORM (Object Relational Mapper)`, sondern bietet auch eine allgemeine API zum Schreiben von datenbankagnostischem Code ohne SQL. :doc:`Python4DataScience:data-processing/postgresql/alembic` basiert auf -SQLAlchemy und dient als Datenbankmigrationswerkzeug. +SQLAlchemy und dient als Datenbank-Migrationswerkzeug. NoSQL-Datenbanken @@ -51,21 +55,3 @@ NoSQL-Datenbanken Es gibt Daten, die sich nur schwer in ein relationales Datenmodell übertragen lassen. Dann solltet ihr zumindest einen Blick auf :doc:`Python4DataScience:data-processing/nosql/index` werfen. - -.. toctree:: - :titlesonly: - :hidden: - - filesystem - pickle - xml - sqlite - create-db - create-data - create-data-from-csv - query-data - update-data - delete-data - normalise - query-normalised - psycopg diff --git a/docs/save-data/modules.rst b/docs/save-data/modules.rst new file mode 100644 index 00000000..be2b325a --- /dev/null +++ b/docs/save-data/modules.rst @@ -0,0 +1,97 @@ +Module für Dateien +================== + +.. _builtin-file-modules: + +Eingebaute Module +----------------- + +Die Python-Standardbibliothek enthält eine Reihe eingebauter Module, mit denen +ihr Dateien managen könnt: + +.. _file-modules: + ++-----------------------------------+-------------------------------------------------------------------------------+ +| Modul | Beschreibung | ++===================================+===============================================================================+ +| :py:mod:`os.path` | führt allgemeine Pfadnamenmanipulationen durch | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`pathlib` | manipuliert Pfadnamen | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`fileinput` | iteriert über mehrere Eingabedateien | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`filecmp` | vergleicht Dateien und Verzeichnisse | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`tempfile` | erzeugt temporäre Dateien und Verzeichnisse | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`glob`, | verwenden UNIX-ähnlicher Pfad- und Dateinamensmuster | +| :py:mod:`fnmatch` | | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`linecache` | greift zufällig auf Textzeilen zu | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`shutil` | führt Dateioperationen auf höherer Ebene aus | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`mimetypes` | Zuordnung von Dateinamen zu MIME-Typen | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`pickle`, | aktivieren von Python-Objektserialisierung und -persistenz, :abbr:`s.a. (siehe| +| :py:mod:`shelve` | auch)` :doc:`../save-data/pickle` | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`csv` | liest und schreibt CSV-Dateien | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`json` | JSON-Kodierer und -Dekodierer | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`sqlite3` | bietet eine DB-API 2.0-Schnittstelle für SQLite-Datenbanken, :abbr:`s.a. | +| | (siehe auch)` :doc:`../save-data/sqlite/index` | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`xml`, | liest und schreibt XML-Dateien, :abbr:`s.a. (siehe auch)` | +| :py:mod:`xml.parsers.expat`, | :doc:`../save-data/xml` | +| :py:mod:`xml.dom`, | | +| :py:mod:`xml.sax`, | | +| :py:mod:`xml.etree.ElementTree` | | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`html.parser`, | Parsen von HTML und XHTML | +| :py:mod:`html.entities` | | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`configparser` | liest und schreibt Windows-ähnliche Konfigurationsdateien (``.ini``) | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`base64`, | Kodierung/Dekodierung von Dateien oder Streams | +| :py:mod:`binhex`, | | +| :py:mod:`binascii`, | | +| :py:mod:`quopri`, | | +| :py:mod:`uu` | | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`struct` | konvertiert zwischen Python-Werten und C-Strukturen, die als | +| | als Python-Bytes-Objekte dargestellt werden. | ++-----------------------------------+-------------------------------------------------------------------------------+ +| :py:mod:`zlib`, | für das Arbeiten mit Archivdateien und Komprimierungen | +| :py:mod:`gzip`, | | +| :py:mod:`bz2`, | | +| :py:mod:`zipfile`, | | +| :py:mod:`tarfile` | | ++-----------------------------------+-------------------------------------------------------------------------------+ + +.. _end-file-modules: + +.. _pandas-io-tools: + +pandas IO tools +--------------- + +* :doc:`Python4DataScience:data-processing/pandas-io` + + Beispiele für die Serialisierungsformate: + + * :doc:`CSV + ` + * :doc:`JSON + ` + * :doc:`Excel + ` + * :doc:`XML/HTML + ` + * :doc:`YAML + ` + * :doc:`TOML + ` + * :doc:`Pickle + ` diff --git a/docs/save-data/pickle.rst b/docs/save-data/pickle.rst index f2e7c32d..d2159e13 100644 --- a/docs/save-data/pickle.rst +++ b/docs/save-data/pickle.rst @@ -43,13 +43,14 @@ einer Datei namens ``data.pickle`` speichern: :py:func:`pickle.dump` speichert alles. Das Pickle-Modul kann fast alles auf diese Weise speichern. Es kann mit - :doc:`/types/numbers`, :doc:`/types/lists`, :doc:`/types/tuples`, - :doc:`/types/dicts`, :doc:`/types/strings` und so ziemlich allem umgehen, was - aus diesen Objekttypen besteht, also auch mit allen Klasseninstanzen. Es geht - auch mit gemeinsam genutzten Objekten, zyklischen Referenzen und anderen - komplexen Speicherstrukturen korrekt um, indem es gemeinsam genutzte Objekte - nur einmal speichert und sie als gemeinsam genutzte Objekte wiederherstellt, - nicht als identische Kopien. + :doc:`/types/numbers/index`, :doc:`/types/sequences-sets/lists`, + :doc:`/types/sequences-sets/tuples`, :doc:`/types/dicts`, + :doc:`/types/strings/index` und so ziemlich allem umgehen, was aus diesen + Objekttypen besteht, also auch mit allen Klasseninstanzen. Es geht auch mit + gemeinsam genutzten Objekten, zyklischen Referenzen und anderen komplexen + Speicherstrukturen korrekt um, indem es gemeinsam genutzte Objekte nur einmal + speichert und sie als gemeinsam genutzte Objekte wiederherstellt, nicht als + identische Kopien. #. Laden der gepickelten Daten: diff --git a/docs/save-data/psycopg.rst b/docs/save-data/psycopg.rst index 1161809a..cb67c245 100644 --- a/docs/save-data/psycopg.rst +++ b/docs/save-data/psycopg.rst @@ -39,7 +39,7 @@ Das ``psycopg``-Modul :lines: 3-4 :lineno-start: 3 -#. Abragen der Datenbank: +#. Abfragen der Datenbank: .. literalinclude:: psycopg.py :language: python diff --git a/docs/save-data/query-data.rst b/docs/save-data/query-data.rst deleted file mode 100644 index 4b10ef01..00000000 --- a/docs/save-data/query-data.rst +++ /dev/null @@ -1,45 +0,0 @@ -Daten abfragen -============== - -#. Alle Datensätze eines Autors auswählen: - - .. literalinclude:: query_data.py - :language: python - :lines: 7-12 - :lineno-start: 7 - - Für die ``print``-Ausgabe verwenden wir durch ein vorangestelltes ``f`` - ein formatiertes Stringliteral oder :term:`python3:f-string`. - -#. Alle Daten auswählen und nach Autor sortieren: - - .. literalinclude:: query_data.py - :language: python - :lines: 15-18 - :lineno-start: 15 - -#. Alle Titel auswählen, die Python enthalten: - - .. literalinclude:: query_data.py - :language: python - :lines: 21-27 - :lineno-start: 21 - -#. Schließlich können die Daten abgefragt werden mit: - - .. literalinclude:: query_data.py - :language: python - :lines: 30- - :lineno-start: 30 - - .. code-block:: rest - - All books from Veit Schiele: - [(1, 'Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28'), (2, 'Jupyter Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2019-06-27'), (3, 'Jupyter Tutorial', 'de', 'Veit Schiele', 'BSD-3-Clause', '2020-10-26'), (4, 'PyViz Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2020-04-13')] - Listing of all books sorted by author: - (1, 'Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28') - (2, 'Jupyter Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2019-06-27') - (3, 'Jupyter Tutorial', 'de', 'Veit Schiele', 'BSD-3-Clause', '2020-10-26') - (4, 'PyViz Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2020-04-13') - All books with Python in the title: - [(1, 'Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28')] diff --git a/docs/save-data/query-normalised.rst b/docs/save-data/query-normalised.rst deleted file mode 100644 index e65a3c4a..00000000 --- a/docs/save-data/query-normalised.rst +++ /dev/null @@ -1,35 +0,0 @@ -Abfragen normalisierter Daten -============================= - -#. Abfragen aller Bücher sortiert nach ``language_id`` und ``title``: - - .. literalinclude:: query_normalised.py - :language: python - :lines: 7-13 - :lineno-start: 7 - - .. code-block:: rest - - All books ordered by language id and title: - (1, 'Veit Schiele', 'Jupyter Tutorial') - (2, 'Veit Schiele', 'Jupyter Tutorial') - (2, 'Veit Schiele', 'PyViz Tutorial') - (2, 'Veit Schiele', 'Python basics') - -#. Um nun nicht nur die ID der Sprachen zu erhalten sondern die zugehörigen - Sprachcodes wird mit ``JOIN`` über die ``id``-Spalte in der - ``languages``-Tabelle eine Verbindung zu den dort hinterlegten Sprachcodes - hergestellt: - - .. literalinclude:: query_normalised.py - :language: python - :lines: 16-24 - :lineno-start: 16 - - .. code-block:: rest - - All books ordered by language code and title: - ('de', 'Veit Schiele', 'Jupyter Tutorial') - ('en', 'Veit Schiele', 'Jupyter Tutorial') - ('en', 'Veit Schiele', 'PyViz Tutorial') - ('en', 'Veit Schiele', 'Python basics') diff --git a/docs/save-data/create-data-from-csv.rst b/docs/save-data/sqlite/create-data-from-csv.rst similarity index 100% rename from docs/save-data/create-data-from-csv.rst rename to docs/save-data/sqlite/create-data-from-csv.rst diff --git a/docs/save-data/create-data.rst b/docs/save-data/sqlite/create-data.rst similarity index 100% rename from docs/save-data/create-data.rst rename to docs/save-data/sqlite/create-data.rst diff --git a/docs/save-data/create-db.rst b/docs/save-data/sqlite/create-db.rst similarity index 100% rename from docs/save-data/create-db.rst rename to docs/save-data/sqlite/create-db.rst diff --git a/docs/save-data/create_data.py b/docs/save-data/sqlite/create_data.py similarity index 100% rename from docs/save-data/create_data.py rename to docs/save-data/sqlite/create_data.py diff --git a/docs/save-data/create_data_from_csv.py b/docs/save-data/sqlite/create_data_from_csv.py similarity index 85% rename from docs/save-data/create_data_from_csv.py rename to docs/save-data/sqlite/create_data_from_csv.py index 144c2943..62603b16 100644 --- a/docs/save-data/create_data_from_csv.py +++ b/docs/save-data/sqlite/create_data_from_csv.py @@ -5,7 +5,7 @@ cursor = conn.cursor() # Read the csv file -with open("books.csv", encoding="utf-8") as f: +with open("../books.csv", encoding="utf-8") as f: reader = csv.reader(f, delimiter=",") # Insert records from into the database cursor.executemany("INSERT INTO books VALUES (?,?,?,?,?)", reader) diff --git a/docs/save-data/create_db.py b/docs/save-data/sqlite/create_db.py similarity index 100% rename from docs/save-data/create_db.py rename to docs/save-data/sqlite/create_db.py diff --git a/docs/save-data/delete-data.rst b/docs/save-data/sqlite/delete-data.rst similarity index 100% rename from docs/save-data/delete-data.rst rename to docs/save-data/sqlite/delete-data.rst diff --git a/docs/save-data/delete_data.py b/docs/save-data/sqlite/delete_data.py similarity index 100% rename from docs/save-data/delete_data.py rename to docs/save-data/sqlite/delete_data.py diff --git a/docs/save-data/sqlite.rst b/docs/save-data/sqlite/index.rst similarity index 76% rename from docs/save-data/sqlite.rst rename to docs/save-data/sqlite/index.rst index 14c3fab7..8b7b53b7 100644 --- a/docs/save-data/sqlite.rst +++ b/docs/save-data/sqlite/index.rst @@ -17,3 +17,16 @@ in vielen anderen Anwendungen. * `sqlite home `_ * :doc:`python3:library/sqlite3` * `W3Schools SQL tutorial `_ + +.. toctree:: + :titlesonly: + :hidden: + + create-db + create-data + create-data-from-csv + query-data + update-data + delete-data + normalise + query-normalised diff --git a/docs/save-data/normalise.py b/docs/save-data/sqlite/normalise.py similarity index 100% rename from docs/save-data/normalise.py rename to docs/save-data/sqlite/normalise.py diff --git a/docs/save-data/normalise.rst b/docs/save-data/sqlite/normalise.rst similarity index 99% rename from docs/save-data/normalise.rst rename to docs/save-data/sqlite/normalise.rst index c3627ca2..dc5a921f 100644 --- a/docs/save-data/normalise.rst +++ b/docs/save-data/sqlite/normalise.rst @@ -10,7 +10,7 @@ Beispiel -------- Im folgenden Beispiel normalisieren wir die Sprache, in der die Bücher -veräffentlicht wurden. +veröffentlicht wurden. #. Hierfür erstellen wir zunächst eine neue Tabelle ``languages`` mit den Spalten ``id`` und ``language_code`` anlegen: diff --git a/docs/save-data/sqlite/query-data.rst b/docs/save-data/sqlite/query-data.rst new file mode 100644 index 00000000..585dbf09 --- /dev/null +++ b/docs/save-data/sqlite/query-data.rst @@ -0,0 +1,51 @@ +Daten abfragen +============== + +#. Alle Datensätze eines Autors auswählen: + + .. literalinclude:: query_data.py + :language: python + :lines: 7-12 + :lineno-start: 7 + + Für die ``print``-Ausgabe verwenden wir durch ein vorangestelltes ``f`` + ein formatiertes String-Literal oder :term:`python3:f-string`. + +#. Alle Daten auswählen und nach Autor sortieren: + + .. literalinclude:: query_data.py + :language: python + :lines: 15-18 + :lineno-start: 15 + +#. Alle Titel auswählen, die Python enthalten: + + .. literalinclude:: query_data.py + :language: python + :lines: 21-27 + :lineno-start: 21 + +#. Schließlich können die Daten abgefragt werden mit: + + .. literalinclude:: query_data.py + :language: python + :lines: 30- + :lineno-start: 30 + + .. code-block:: pycon + + >>> import create_db + >>> import create_data_from_csv + >>> import query_data + All books from Veit Schiele: + ('Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28') + ('Jupyter Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2019-06-27') + ('Jupyter Tutorial', 'de', 'Veit Schiele', 'BSD-3-Clause', '2020-10-26') + ('PyViz Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2020-04-13') + Listing of all books sorted by author: + ('Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28') + ('Jupyter Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2019-06-27') + ('Jupyter Tutorial', 'de', 'Veit Schiele', 'BSD-3-Clause', '2020-10-26') + ('PyViz Tutorial', 'en', 'Veit Schiele', 'BSD-3-Clause', '2020-04-13') + All books with Python in the title: + [('Python basics', 'en', 'Veit Schiele', 'BSD-3-Clause', '2021-10-28')] diff --git a/docs/save-data/sqlite/query-normalised.rst b/docs/save-data/sqlite/query-normalised.rst new file mode 100644 index 00000000..81780197 --- /dev/null +++ b/docs/save-data/sqlite/query-normalised.rst @@ -0,0 +1,41 @@ +Abfragen normalisierter Daten +============================= + +#. Abfragen aller Bücher sortiert nach ``language_id`` und ``title``: + + .. literalinclude:: query_normalised.py + :language: python + :lines: 7-13 + :lineno-start: 7 + + .. code-block:: pycon + + >>> import create_db + >>> import create_data_from_csv + >>> import normalise + >>> import query_normalised + All books ordered by language id and title: + (1, 'Veit Schiele', 'Jupyter Tutorial') + (2, 'Veit Schiele', 'Jupyter Tutorial') + (2, 'Veit Schiele', 'PyViz Tutorial') + (2, 'Veit Schiele', 'Python basics') + +#. Um nun nicht nur die ID der Sprachen zu erhalten sondern die zugehörigen + Sprachcodes wird mit ``JOIN`` über die ``id``-Spalte in der + ``languages``-Tabelle eine Verbindung zu den dort hinterlegten Sprachcodes + hergestellt: + + .. literalinclude:: query_normalised.py + :language: python + :lines: 16-24 + :lineno-start: 16 + + .. code-block:: pycon + + >>> import query_normalised + … + All books ordered by language code and title: + ('de', 'Veit Schiele', 'Jupyter Tutorial') + ('en', 'Veit Schiele', 'Jupyter Tutorial') + ('en', 'Veit Schiele', 'PyViz Tutorial') + ('en', 'Veit Schiele', 'Python basics') diff --git a/docs/save-data/query_data.py b/docs/save-data/sqlite/query_data.py similarity index 100% rename from docs/save-data/query_data.py rename to docs/save-data/sqlite/query_data.py diff --git a/docs/save-data/query_normalised.py b/docs/save-data/sqlite/query_normalised.py similarity index 100% rename from docs/save-data/query_normalised.py rename to docs/save-data/sqlite/query_normalised.py diff --git a/docs/save-data/test_sqlite.py b/docs/save-data/sqlite/test_sqlite.py similarity index 100% rename from docs/save-data/test_sqlite.py rename to docs/save-data/sqlite/test_sqlite.py diff --git a/docs/save-data/update-data.rst b/docs/save-data/sqlite/update-data.rst similarity index 100% rename from docs/save-data/update-data.rst rename to docs/save-data/sqlite/update-data.rst diff --git a/docs/save-data/update_data.py b/docs/save-data/sqlite/update_data.py similarity index 100% rename from docs/save-data/update_data.py rename to docs/save-data/sqlite/update_data.py diff --git a/docs/save-data/xml.rst b/docs/save-data/xml.rst index 8234613a..bcfd8718 100644 --- a/docs/save-data/xml.rst +++ b/docs/save-data/xml.rst @@ -16,8 +16,8 @@ Im folgenden Beispiel analysieren wir :download:`books.xml`: :lines: 1- :lineno-start: 1 -#. Hierzu impportieren wir zunächst das ``minidom``-Modul und geben ihm - denselben Namen, damit es leichter referenziert werden kann: +#. Hierzu importieren wir zunächst das ``minidom``-Modul und geben ihm denselben + Namen, damit es leichter referenziert werden kann: .. literalinclude:: minidom_example.py :language: py @@ -81,7 +81,9 @@ Parsen mit ElementTree .. code-block:: pycon - + >>> from elementtree_example import parseXML + >>> parseXML("books.xml") + tag=catalog, attrib={} #. Ausgeben der XML-Kindelemente von ``book``: @@ -93,6 +95,10 @@ Parsen mit ElementTree .. code-block:: pycon + >>> from elementtree_example import parseXML + >>> parseXML("books.xml") + + tag=catalog, attrib={} book {'id': '1'} title language @@ -111,6 +117,9 @@ Parsen mit ElementTree .. code-block:: pycon + >>> from elementtree_example import parseXML + >>> parseXML("books.xml") + … -------------------- Iterating using iter -------------------- @@ -123,4 +132,4 @@ Parsen mit ElementTree date=2021-10-28 book= title=Jupyter Tutorial - ... + … diff --git a/docs/style.rst b/docs/style.rst index 8b5f3417..57660c3c 100644 --- a/docs/style.rst +++ b/docs/style.rst @@ -6,7 +6,7 @@ Einrückung und Blöcke Python unterscheidet sich von den meisten anderen Programmiersprachen, weil es Einrückungen verwendet, um die Struktur zu bestimmen (:abbr:`d.h.(das heißt)` um -zu bestimmen, was die :doc:`while `-Klausel einer Bedingung +zu bestimmen, was die :doc:`while `-Klausel einer Bedingung :abbr:`usw. (und so weiter)` darstellt). Die meisten anderen Sprachen verwenden dazu geschweifte Klammern. Im folgenden Beispiel wird durch die Einrückung der Zeilen 3–6 festgelegt, dass sie zur ``while``-Anweisung gehören: @@ -38,7 +38,7 @@ Kommentare Meist ist alles, was hinter ``#`` folgt ein Kommentar und wird bei der Ausführung des Codes nicht beachtet. Die offensichtliche Ausnahme ist ``#`` in -einer :doc:`Zeichenkette `: +einer :doc:`Zeichenkette `: .. code-block:: pycon @@ -61,24 +61,26 @@ ihr in der folgenden Tabelle: | Modul- und Paketnamen | kurz, Kleinbuchstaben, | ``math``, ``sys`` | | | Unterstriche nur bei Bedarf | | +-----------------------+-------------------------------+-------------------------------+ -| Funktionsnamen | Kleinbuchstaben, :abbr:`ggf.` | ``my_func()`` | +| Funktionsnamen | Kleinbuchstaben, :abbr:`ggf. | :func:`my_func` | | | (gegebenenfalls)` mit | | | | Unterstrichen | | +-----------------------+-------------------------------+-------------------------------+ -| Variablennamen | Kleinbuchstaben, :abbr:`ggf.` | ``my_var`` | +| Variablennamen | Kleinbuchstaben, :abbr:`ggf. | ``my_var`` | | | (gegebenenfalls)` mit | | | | Unterstrichen | | +-----------------------+-------------------------------+-------------------------------+ | Klassennamen | CamelCase-Schreibweise | ``MyClass`` | +-----------------------+-------------------------------+-------------------------------+ -| Konstantennamen | Versalien mit Unterstrichen | ``PI`` | +| Namen für | Versalien mit Unterstrichen | ``PI`` | +| :term:`Konstanten | | | +| ` | | | +-----------------------+-------------------------------+-------------------------------+ | Einrückung | Vier Leerzeichen pro Ebene, | | | | keine Tabs | | +-----------------------+-------------------------------+-------------------------------+ | Vergleiche | nicht explizit mit ``True`` | ``if my_var:``, | -| | oder ``False``,siehe auch | ``if not my_var:`` | -| | :doc:`control-flows/boolean` | | +| | oder ``False``, siehe auch | ``if not my_var:`` | +| | :doc:`control-flow/boolean` | | +-----------------------+-------------------------------+-------------------------------+ .. seealso:: diff --git a/docs/test/glossary.rst b/docs/test/glossary.rst deleted file mode 100644 index 4d6d24b4..00000000 --- a/docs/test/glossary.rst +++ /dev/null @@ -1,64 +0,0 @@ -Glossar -======= - -.. glossary:: - - ``assert`` - Ein Schlüsselwort, das die Codeausführung anhält, wenn sein Argument - falsch ist. - - Continuous Integration - CI - Kontinuierliche Integration - Automatisches Überprüfen des Erstellungs- und Testprozesses auf - verschiedenen Plattformen. - - Dummy - Objekt, das herumgereicht, aber nie wirklich benutzt. Normalerweise - werden sie nur zum Füllen von Parameter-Listen verwendet. - - ``exception`` - Anpassbare Form von :term:`assert`. - - ``except`` - Schlüsselwort, das verwendet wird, um eine :term:`exception` abzufangen - und sorgfältig zu behandeln. - - Fake - Objekt, das eine tatsächlich funktionierende Implementierung hat, in der - Regel aber eine Abkürzung nehmen, die sie nicht für die Produktion - geeignet macht. - - Integrationstest - Tests, die überprüfen, ob die verschiedenen Teile der Software wie - erwartet zusammenarbeiten. - - Mock - Objekte, die mit :term:`exception` programmiert sind, die eine - Spezifikation der Aufrufe bilden, die ihr voraussichtlich erhalten - werdet. - - .. seealso:: - * `Mock-Objekt `_ - - pytest - Ein Python-Paket mit Test-Utilities. - - Regressionstest - Tests zum Schutz vor neuen Fehlern oder Regressionen, die durch neue - Software und Updates auftreten können. - - Stubs - liefern vorgefertigte Antworten auf Aufrufe, die während des Tests - getätigt werden, und reagieren in der Regel überhaupt nicht auf - irgendetwas, das nicht für den Test programmiert wurde. - - Test-driven development - TDD - Testgetriebene Entwicklung - Eine Software-Entwicklungsstrategie, bei der die Tests vor dem Code - geschrieben werden. - - ``try`` - Ein Schlüsselwort, das einen Teil des Codes schützt, der eine - :term:`exception` auslösen kann. diff --git a/docs/test/hypothesis.rst b/docs/test/hypothesis.rst index edfe7899..d5de50f5 100644 --- a/docs/test/hypothesis.rst +++ b/docs/test/hypothesis.rst @@ -2,10 +2,10 @@ Hypothesis ========== `Hypothesis `_ ist eine Bibliothek, mit der -ihr Tests schreiben könnt, die aus einer Quelle von Beispielen parametrisiert -werden. Anschließend werden einfache und verständliche Beispiele generiert, die -dazu verwendet werden können, eure Tests fehlschlagen zu lassen und Fehler mit -wenig Aufwand zu finden. +ihr Tests schreiben könnt, die aus einer Quelle von Beispielen +:term:`parametrisiert ` werden. Anschließend werden einfache und +verständliche Beispiele generiert, die dazu verwendet werden können, eure Tests +fehlschlagen zu lassen und Fehler mit wenig Aufwand zu finden. #. Installiert Hypothesis: @@ -55,83 +55,83 @@ wenig Aufwand zu finden. .. tab:: Linux/macOS - .. code-block:: console + .. code-block:: pytest - $ python -m pytest test_hypothesis.py - ============================= test session starts ============================== - platform darwin -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 - rootdir: /Users/veit/cusy/trn/python-basics/docs/test - plugins: hypothesis-6.114.1 - collected 1 item + $ python -m pytest test_hypothesis.py + ============================= test session starts ============================== + platform darwin -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 + rootdir: /Users/veit/cusy/trn/python-basics/docs/test + plugins: hypothesis-6.114.1 + collected 1 item - test_hypothesis.py F [100%] + test_hypothesis.py F [100%] - =================================== FAILURES =================================== - __________________________________ test_mean ___________________________________ + =================================== FAILURES =================================== + __________________________________ test_mean ___________________________________ - @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) - > def test_mean(ls): + @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) + > def test_mean(ls): - test_hypothesis.py:6: - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + test_hypothesis.py:6: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - ls = [9.9792015476736e+291, 1.7976931348623157e+308] + ls = [9.9792015476736e+291, 1.7976931348623157e+308] - @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) - def test_mean(ls): - mean = sum(ls) / len(ls) - > assert min(ls) <= mean <= max(ls) - E assert inf <= 1.7976931348623157e+308 - E + where 1.7976931348623157e+308 = max([9.9792015476736e+291, 1.7976931348623157e+308]) + @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) + def test_mean(ls): + mean = sum(ls) / len(ls) + > assert min(ls) <= mean <= max(ls) + E assert inf <= 1.7976931348623157e+308 + E + where 1.7976931348623157e+308 = max([9.9792015476736e+291, 1.7976931348623157e+308]) - test_hypothesis.py:8: AssertionError - ---------------------------------- Hypothesis ---------------------------------- - Falsifying example: test_mean( - ls=[9.9792015476736e+291, 1.7976931348623157e+308], - ) - =========================== short test summary info ============================ - FAILED test_hypothesis.py::test_mean - assert inf <= 1.7976931348623157e+308 - ============================== 1 failed in 0.44s =============================== + test_hypothesis.py:8: AssertionError + ---------------------------------- Hypothesis ---------------------------------- + Falsifying example: test_mean( + ls=[9.9792015476736e+291, 1.7976931348623157e+308], + ) + =========================== short test summary info ============================ + FAILED test_hypothesis.py::test_mean - assert inf <= 1.7976931348623157e+308 + ============================== 1 failed in 0.44s =============================== .. tab:: Windows - .. code-block:: console + .. code-block:: pytest + + C:> python -m pytest test_hypothesis.py + ============================= test session starts ============================== + platform win32 -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 + rootdir: C:\Users\veit\python-basics\docs\test + plugins: plugins: hypothesis-6.114.1 + collected 1 item + + test_hypothesis.py F [100%] + + =================================== FAILURES =================================== + __________________________________ test_mean ___________________________________ + + @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) + > def test_mean(ls): + + test_hypothesis.py:6: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + ls = [9.9792015476736e+291, 1.7976931348623157e+308] + + @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) + def test_mean(ls): + mean = sum(ls) / len(ls) + > assert min(ls) <= mean <= max(ls) + E assert inf <= 1.7976931348623157e+308 + E + where 1.7976931348623157e+308 = max([9.9792015476736e+291, 1.7976931348623157e+308]) - C:> python -m pytest test_hypothesis.py - ============================= test session starts ============================== - platform win32 -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 - rootdir: C:\Users\veit\python-basics\docs\test - plugins: plugins: hypothesis-6.114.1 - collected 1 item - - test_hypothesis.py F [100%] - - =================================== FAILURES =================================== - __________________________________ test_mean ___________________________________ - - @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) - > def test_mean(ls): - - test_hypothesis.py:6: - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - - ls = [9.9792015476736e+291, 1.7976931348623157e+308] - - @given(lists(floats(allow_nan=False, allow_infinity=False), min_size=1)) - def test_mean(ls): - mean = sum(ls) / len(ls) - > assert min(ls) <= mean <= max(ls) - E assert inf <= 1.7976931348623157e+308 - E + where 1.7976931348623157e+308 = max([9.9792015476736e+291, 1.7976931348623157e+308]) - - test_hypothesis.py:8: AssertionError - ---------------------------------- Hypothesis ---------------------------------- - Falsifying example: test_mean( - ls=[9.9792015476736e+291, 1.7976931348623157e+308], - ) - =========================== short test summary info ============================ - FAILED test_hypothesis.py::test_mean - assert inf <= 1.7976931348623157e+308 - ============================== 1 failed in 0.44s =============================== + test_hypothesis.py:8: AssertionError + ---------------------------------- Hypothesis ---------------------------------- + Falsifying example: test_mean( + ls=[9.9792015476736e+291, 1.7976931348623157e+308], + ) + =========================== short test summary info ============================ + FAILED test_hypothesis.py::test_mean - assert inf <= 1.7976931348623157e+308 + ============================== 1 failed in 0.44s =============================== .. seealso:: `Hypothesis for the Scientific Stack diff --git a/docs/test/index.rst b/docs/test/index.rst index 9c30e01a..2437c934 100644 --- a/docs/test/index.rst +++ b/docs/test/index.rst @@ -1,43 +1,12 @@ Testen ====== -Grundsätzlich wird zwischen statischen und dynamischen Testverfahren unterschieden. +Grundsätzlich wird zwischen statischen und dynamischen Testverfahren +unterschieden. -.. glossary:: - - Statische Testverfahren - werden verwendet um den Quellcode zu überprüfen, wobei dieser jedoch nicht - ausgeführt wird. Sie unterteilen sich in - - * :ref:`Reviews ` und - * `Statische Code-Analyse - `_ - - Es gibt diverse Python-Pakete, die euch bei der statischen Code-Analyse - unterstützen können, u.a. :doc:`Python4DataScience:productive/qa/flake8`, - :doc:`Python4DataScience:productive/qa/pysa` und - :doc:`Python4DataScience:productive/qa/wily`. - - Dynamische Testverfahren - dienen dem Auffinden von Fehlern beim Ausführen des Quellcodes. Dabei wird - zwischen Whitebox- und Backbox-Tests unterschieden. - - Whitebox-Tests - werden unter Kenntnis des Quellcodes und der Software-Struktur entwickelt. - In Python stehen euch verschiedene Module zur Verfügung: - - :doc:`unittest` - unterstützt euch bei der Automatisierung von Tests. - :doc:`mock` - erlaubt euch das Erstellen und Verwenden von Mock-Objekten. - :doc:`doctest` - ermöglicht das Testen von in Python Docstrings geschriebenen Tests. - :doc:`tox` - ermöglicht das Testen in verschiedenen Umgebungen. - - Blackbox-Tests - werden ohne Kenntnis des Quellcodes entwickelt. Neben :doc:`unittest` - kann in Python auch :doc:`hypothesis` für solche Tests verwendet werden. +.. include:: ../appendix/glossary.rst + :start-after: start-test-procedures: + :end-before: end-test-procedures: .. tip:: `cusy Seminar: Effizient Testen mit Python @@ -51,13 +20,8 @@ Grundsätzlich wird zwischen statischen und dynamischen Testverfahren unterschie :titlesonly: :hidden: - unittest - sqlite - doctest - hypothesis pytest/index - coverage + unittest mock + hypothesis tox - unittest2 - glossary diff --git a/docs/test/mock.rst b/docs/test/mock.rst index 293c929d..0d85e5b8 100644 --- a/docs/test/mock.rst +++ b/docs/test/mock.rst @@ -32,7 +32,7 @@ Beispiel Zunächst wollten wir mit einem einfachen Beispiel starten und überprüfen, ob die Arbeitstage von Montag bis Freitag korrekt ermittelt werden. -Zunächst importieren wir ``datetime.datetime`` und ``Mock``: +#. Zunächst importieren wir ``datetime.datetime`` und ``Mock``: .. literalinclude:: test_mock.py :language: python @@ -148,9 +148,9 @@ wir :func:`mock.patch.object` als Kontextmanager verwenden: In unserem Testcode importieren wir ``items``. Das resultierende items-Objekt ist das, was wir patchen werden. Der Aufruf von :func:`mock.patch.object`, der -als :doc:`Kontextmanager <../control-flows/with>` innerhalb eines -``with``-Blocks verwendet wird, gibt ein Mock-Objekt zurück, das nach dem -``with``-Block aufgeräumt wird: +als :doc:`Kontextmanager <../control-flow/with>` innerhalb eines ``with``-Blocks +verwendet wird, gibt ein Mock-Objekt zurück, das nach dem ``with``-Block +aufgeräumt wird: #. In diesem Fall wird das Attribut ``__version__`` von ``items`` für die Dauer des ``with``-Blocks durch ``"100.0.0"`` ersetzt. @@ -171,7 +171,7 @@ In :file:`src/items/cli.py` haben wir :func:`config` folgendermaßen definiert: with items_db() as db: print(db.path()) -:func:`items_db` ist ein :doc:`Kontextmanager <../control-flows/with>`, der ein +:func:`items_db` ist ein :doc:`Kontextmanager <../control-flow/with>`, der ein ``items.ItemsDB``-Objekt zurückgibt. Das zurückgegebene Objekt wird dann als ``db`` verwendet, um :func:`db.path` aufzurufen. Wir sollten hier also zwei Dinge zu mocken: ``items.ItemsDB`` und eine seiner Methoden, :func:`path`. @@ -298,10 +298,10 @@ aufgerufen hat. Die Implementierung des Befehls :func:`add` ruft schließlich .. code-block:: python :emphasize-lines: 4 - def test_add_with_owner(mock_itemsdb, items_cli): - items_cli("add some task -o veit") - expected = items.Item("some task", owner="veit", state="todo") - mock_itemsdb.add_item.assert_called_with(expected) + def test_add_with_owner(mock_itemsdb, items_cli): + items_cli("add some task -o veit") + expected = items.Item("some task", owner="veit", state="todo") + mock_itemsdb.add_item.assert_called_with(expected) Wenn :func:`add_item` nicht aufgerufen wird oder mit dem falschen Typ oder dem falschen Objektinhalt aufgerufen wird, schlägt der Test fehl. Wenn wir @@ -311,23 +311,23 @@ schreiben, aber nicht im CLI-Aufruf, erhalten wir folgende Ausgabe: .. code-block:: pytest :emphasize-lines: 10-13, 16 - $ pytest -s tests/cli/test_add.py::test_add_with_owner - ============================= test session starts ============================== - ... - configfile: pyproject.toml - plugins: cov-4.1.0, Faker-19.11.0 - collected 1 item - - tests/cli/test_add.py F - ... - > raise AssertionError(_error_message()) from cause - E AssertionError: expected call not found. - E Expected: add_item(Item(summary='some task', owner='Veit', state='todo', id=None)) - E Actual: add_item(Item(summary='some task', owner='veit', state='todo', id=None)) - ... - =========================== short test summary info ============================ - FAILED tests/cli/test_add.py::test_add_with_owner - AssertionError: expected call not found. - ============================== 1 failed in 0.08s =============================== + $ pytest -s tests/cli/test_add.py::test_add_with_owner + ============================= test session starts ============================== + ... + configfile: pyproject.toml + plugins: cov-4.1.0, Faker-19.11.0 + collected 1 item + + tests/cli/test_add.py F + ... + > raise AssertionError(_error_message()) from cause + E AssertionError: expected call not found. + E Expected: add_item(Item(summary='some task', owner='Veit', state='todo', id=None)) + E Actual: add_item(Item(summary='some task', owner='veit', state='todo', id=None)) + ... + =========================== short test summary info ============================ + FAILED tests/cli/test_add.py::test_add_with_owner - AssertionError: expected call not found. + ============================== 1 failed in 0.08s =============================== .. seealso:: Es gibt eine ganze Reihe von Varianten von :func:`assert_called`. Eine @@ -408,12 +408,12 @@ folgendermaßen testen: .. code-block:: python - def test_add_with_owner(items_db, items_cli): - items_cli("add some task -o veit") - expected = items.Item("some task", owner="veit", state="todo") - all = items_db.list_items() - assert len(all) == 1 - assert all[0] == expected + def test_add_with_owner(items_db, items_cli): + items_cli("add some task -o veit") + expected = items.Item("some task", owner="veit", state="todo") + all = items_db.list_items() + assert len(all) == 1 + assert all[0] == expected Mocking testet die Implementierung der Befehlszeilenschnittstelle und stellt sicher, dass ein API-Aufruf mit bestimmten Parametern erfolgt. Beim @@ -425,16 +425,16 @@ schnell: .. code-block:: pytest - $ pytest -s tests/cli/test_add.py::test_add_with_owner - ============================= test session starts ============================== - ... - configfile: pyproject.toml - plugins: cov-4.1.0, Faker-19.11.0 - collected 1 item + $ pytest -s tests/cli/test_add.py::test_add_with_owner + ============================= test session starts ============================== + … + configfile: pyproject.toml + plugins: cov-4.1.0, Faker-19.11.0 + collected 1 item - tests/cli/test_add.py . + tests/cli/test_add.py . - ============================== 1 passed in 0.03s =============================== + ============================== 1 passed in 0.03s =============================== Wir könnten Mocking auch auf eine andere Weise vermeiden. Wir könnten das Verhalten vollständig über die CLI testen. Dazu müsste möglicherweise die diff --git a/docs/test/api.png b/docs/test/pytest/api.png similarity index 100% rename from docs/test/api.png rename to docs/test/pytest/api.png diff --git a/docs/test/pytest/builtin-fixtures.rst b/docs/test/pytest/builtin-fixtures.rst index 8614d9c7..2b3ade6b 100644 --- a/docs/test/pytest/builtin-fixtures.rst +++ b/docs/test/pytest/builtin-fixtures.rst @@ -5,7 +5,7 @@ Die Wiederverwendung gemeinsamer Fixtures ist eine so gute Idee, dass pytest einige häufig verwendete Fixtures integriert hat. Die eingebauten Fixtures, helfen euch, einige sehr nützliche Dinge in euren Tests einfach und konsistent zu tun. Unter anderem enthält pytest eingebaute Fixtures, die mit temporären -Verzeichnissen und Dateien umgehen, auf Kommandozeilenoptionen zugreifen, +Verzeichnissen und Dateien umgehen, auf Kommandozeilen-Optionen zugreifen, zwischen Testsitzungen kommunizieren, Ausgabeströme validieren, Umgebungsvariablen verändern und Warnungen abfragen können. @@ -81,7 +81,7 @@ Ihr könnt auch euer eigenes Basisverzeichnis angeben mit :samp:`pytest Manchmal soll der Anwendungscode etwas auf ``stdout``, ``stderr`` :abbr:`usw. (und so weiter)` ausgeben. Das Items-Beispielprojekt hat deswegen auch eine -Kommandozeilenschnittstelle, die wir nun testen wollen. +Kommandozeilen-Schnittstelle, die wir nun testen wollen. Der Befehl ``items version`` soll die Version ausgeben: @@ -167,10 +167,10 @@ Wenn wir den Test jedoch ausführen, sehen wir keine Ausgabe: ============================== 1 passed in 0.00s =============================== -pytest fängt die gesamte Ausgabe auf. Dies hilft zwar, die Kommandozeilensitzung -sauber zu halten, es kann jedoch vorkommen, dass wir die gesamte Ausgabe sehen -wollen, auch bei bestandenen Tests. Hierfür können die Option ``-s`` oder -``--capture=no`` verwenden: +pytest fängt die gesamte Ausgabe auf. Dies hilft zwar, die +Kommandozeilen-Sitzung sauber zu halten, es kann jedoch vorkommen, dass wir die +gesamte Ausgabe sehen wollen, auch bei bestandenen Tests. Hierfür können die +Option ``-s`` oder ``--capture=no`` verwenden: .. code-block:: pytest :emphasize-lines: 7 @@ -217,15 +217,16 @@ Nun wird sie Ausgabe im ``with``-Block immer angezeigt, auch ohne die .. seealso:: - ``capfd`` + :fixture:`pytest:capfd` Wie ``capsys``, erfasst aber die Dateideskriptoren 1 und 2, die normalerweise dasselbe wie ``stdout`` und ``stderr`` - ``capsysbinary`` + :fixture:`pytest:capsysbinary` Während capsys Text erfasst, erfasst capsysbinary Bytes - ``capfdbinary`` + :fixture:`pytest:capfdbinary` erfasst Bytes in den Dateideskriptoren 1 und 2 - ``caplog`` - erfasst Ausgaben, die mit dem Logging-Paket geschrieben wurden + :fixture:`pytest:caplog` + erfasst Logging-Daten, :abbr:`s.a. (siehe auch)` + :doc:`pytest:how-to/logging` .. _monkeypatch-fixture: @@ -280,38 +281,39 @@ wurde. Das ``monkeypatch``-Fixture bietet die folgenden Funktionen: -+-------------------------------------------------------+-----------------------+ -| Funktion | Beschreibung | -+=======================================================+=======================+ -| :samp:`setattr(TARGET, NAME, VALUE, raising=True)` | setzt ein Attribut | -| [1]_ | | -+-------------------------------------------------------+-----------------------+ -| :samp:`delattr(TARGET, NAME, raising=True)` [1]_ | löscht ein Attribut | -+-------------------------------------------------------+-----------------------+ -| :samp:`setitem(DICT, NAME, VALUE)` | setzt einen | -| | Dict-Eintrag | -+-------------------------------------------------------+-----------------------+ -| :samp:`delitem(DICT, NAME, raising=True)` [1]_ | löscht einen | -| | Dict-Eintrag | -+-------------------------------------------------------+-----------------------+ -| :samp:`setenv(NAME, VALUE, prepend=None)` [2]_ | setzt eine | -| | Umgebungsvariable | -+-------------------------------------------------------+-----------------------+ -| :samp:`delenv(NAME, raising=True)` [1]_ | löscht eine | -| | Umgebungsvariable | -+-------------------------------------------------------+-----------------------+ -| :samp:`syspath_prepend(PATH)` | erweitert den Pfad | -| | ``sys.path`` | -+-------------------------------------------------------+-----------------------+ -| :samp:`chdir(PATH)` | wechselt das aktuelle | -| | Arbeitsverzeichnis | -+-------------------------------------------------------+-----------------------+ - -.. [1] Der ``raising``-Parameter teilt pytest mit, ob eine Exception ausgelöst - werden soll, wenn das Element (noch) nicht vorhanden ist. -.. [2] Der ``prepend``-Parameter von ``setenv()`` kann ein Zeichen sein. Wenn er - gesetzt ist, wird der Wert der Umgebungsvariablen in :samp:`{VALUE} + - prepend + {OLD_VALUE}` geändert. ++-----------------------------------------------+-----------------------+ +| Funktion | Beschreibung | ++===============================================+=======================+ +| :meth:`pytest.MonkeyPatch.setattr` | setzt ein Attribut | +| [1]_ | | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.delattr` [1]_ | löscht ein Attribut | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.setitem` | setzt einen | +| | Dict-Eintrag | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.delitem` [1]_ | löscht einen | +| | Dict-Eintrag | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.setenv` [2]_ | setzt eine | +| | Umgebungsvariable | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.delenv` [1]_ | löscht eine | +| | Umgebungsvariable | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.syspath_prepend` | erweitert den Pfad | +| | :py:data:`sys.path` | ++-----------------------------------------------+-----------------------+ +| :meth:`pytest.MonkeyPatch.chdir` | wechselt das aktuelle | +| | Arbeitsverzeichnis | ++-----------------------------------------------+-----------------------+ + +.. [1] Der ``raising``-:term:`Parameter` teilt pytest mit, ob eine + :doc:`Exception <../../control-flow/exceptions>` ausgelöst werden soll, + wenn das Element (noch) nicht vorhanden ist. +.. [2] Der ``prepend``-:term:`Parameter` von ``setenv()`` kann ein Zeichen sein. + Wenn er gesetzt ist, wird der Wert der Umgebungsvariablen in + :samp:`{VALUE} + prepend + {OLD_VALUE}` geändert. Wir können ``monkeypatch`` verwenden, um die :abbr:`CLI (Command Line Interface)` auf ein temporäres Verzeichnis für die Datenbank umzuleiten, und @@ -368,8 +370,8 @@ Kommandozeile: result = runner.invoke(items.app, params) return result.output.rstrip() -Anschließend können wir dann unseren Test schreiben, der die gesamte -``get_path()``-Funktion patcht: +Anschließend können wir dann unseren Test schreiben, der einen Patch für die +:func:`get_path`-Funktion enthält: .. code-block:: python @@ -411,61 +413,62 @@ In unserem Fall könnte sinnvoll sein, eine Umgebungsvariable Verbleibende Built-in-Fixtures ------------------------------ -+-------------------------------+-----------------------------------------------+ -| Built-in-Fixture | Beschreibung | -+===============================+===============================================+ -| ``capfd``, | Varianten von ``capsys``, die mit | -| ``capfdbinary``, | Dateideskriptoren und/oder binärer Ausgabe | -| ``capsysbinary`` | arbeiten. | -+-------------------------------+-----------------------------------------------+ -| ``caplog`` | ähnlich wie ``capsys``; wird für Meldungen | -| | verwendet, die mit Pythons Logging-System | -| | erstellt werden. | -+-------------------------------+-----------------------------------------------+ -| ``cache`` | wird zum Speichern und Abrufen von Werten | -| | über mehrere Pytest-Läufe hinweg verwendet. | -| | | -| | Es erlaubt ``last-failed``, ``failed-first`` | -| | und ähnliche Optionen. | -+-------------------------------+-----------------------------------------------+ -| ``doctest_namespace`` | nützlich, wenn ihr pytest verwenden möchtet, | -| | um :doc:`Doctests <../doctest>` | -| | durchzuführen. | -+-------------------------------+-----------------------------------------------+ -| ``pytestconfig`` | wird verwendet, um Zugriff auf | -| | Konfigurationswerte, Plugin-Manager und | -| | -Hooks zu erhalten. | -+-------------------------------+-----------------------------------------------+ -| ``record_property``, | wird verwendet, um dem Test oder der | -| ``record_testsuite_property`` | Testsuite zusätzliche Eigenschaften | -| | hinzuzufügen. | -| | | -| | Besonders nützlich für das Hinzufügen von | -| | Daten zu einem Bericht, der von :abbr:`CI | -| | (Continuous Integration)`-Tools verwendet | -| | wird. | -+-------------------------------+-----------------------------------------------+ -| ``recwarn`` | wird verwendet, um Warnmeldungen zu testen. | -| | | -+-------------------------------+-----------------------------------------------+ -| ``request`` | wird verwendet, um Informationen über die | -| | ausgeführte Testfunktion bereitzustellen. | -| | | -| | wird meist bei der Parametrisierung von | -| | Fixtures verwendet | -+-------------------------------+-----------------------------------------------+ -| ``pytester``, ``testdir`` | Wird verwendet, um ein temporäres | -| | Testverzeichnis bereitzustellen, um die | -| | Ausführung und das Testen von pytest-Plugins | -| | zu unterstützen. ``pytester`` ist der | -| | ``pathlib``-basierte Ersatz für das | -| | ``py.path``-basierte ``testdir``. | -+-------------------------------+-----------------------------------------------+ -| ``tmpdir``, | ähnlich wie ``tmp_path`` und | -| ``tmpdir_factory`` | ``tmp_path_factory``; dient der Rückgabe | -| | eines ``py.path.local``-Objekts anstelle | -| | eines ``pathlib.Path``-Objekts. | -+-------------------------------+-----------------------------------------------+ ++-----------------------------------------------+-----------------------------------------------+ +| Built-in-Fixture | Beschreibung | ++==============+================================+===============================================+ +| :fixture:`pytest:capfd`, | Varianten von ``capsys``, die mit | +| :fixture:`pytest:capfdbinary`, | Dateideskriptoren und/oder binärer Ausgabe | +| :fixture:`pytest:capsysbinary` | arbeiten. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:caplog` | ähnlich wie ``capsys``; wird für Meldungen | +| | verwendet, die mit Pythons Logging-System | +| | erstellt werden. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:cache` | wird zum Speichern und Abrufen von Werten | +| | über mehrere Pytest-Läufe hinweg verwendet. | +| | | +| | Es erlaubt ``last-failed``, ``failed-first`` | +| | und ähnliche Optionen. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:doctest_namespace` | nützlich, wenn ihr pytest verwenden möchtet, | +| | um :doc:`Doctests | +| | <../../document/doctest>` | +| | durchzuführen. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:pytestconfig` | wird verwendet, um Zugriff auf | +| | Konfigurationswerte, Plugin-Manager und | +| | -Hooks zu erhalten. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:record_property`, | wird verwendet, um dem Test oder der | +| :fixture:`pytest:record_testsuite_property` | Testsuite zusätzliche Eigenschaften | +| | hinzuzufügen. | +| | | +| | Besonders nützlich für das Hinzufügen von | +| | Daten zu einem Bericht, der von :abbr:`CI | +| | (Continuous Integration)`-Tools verwendet | +| | wird. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:recwarn` | wird verwendet, um Warnmeldungen zu testen. | +| | | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:request` | wird verwendet, um Informationen über die | +| | ausgeführte Testfunktion bereitzustellen. | +| | | +| | wird meist bei der Parametrisierung von | +| | Fixtures verwendet | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:pytester`, | Wird verwendet, um ein temporäres | +| :fixture:`pytest:testdir` | Testverzeichnis bereitzustellen, um die | +| | Ausführung und das Testen von pytest-Plugins | +| | zu unterstützen. ``pytester`` ist der | +| | ``pathlib``-basierte Ersatz für das | +| | ``py.path``-basierte ``testdir``. | ++-----------------------------------------------+-----------------------------------------------+ +| :fixture:`pytest:tmpdir`, | ähnlich wie ``tmp_path`` und | +| :fixture:`pytest:tmpdir_factory` | ``tmp_path_factory``; dient der Rückgabe | +| | eines ``py.path.local``-Objekts anstelle | +| | eines ``pathlib.Path``-Objekts. | ++-----------------------------------------------+-----------------------------------------------+ Ihr könnt die vollständige Liste der Built-in-Fixtures erhalten, indem ihr ``pytest --fixtures`` ausführt. diff --git a/docs/test/ci.yaml b/docs/test/pytest/ci.yaml similarity index 100% rename from docs/test/ci.yaml rename to docs/test/pytest/ci.yaml diff --git a/docs/test/pytest/config.rst b/docs/test/pytest/config.rst index 89f2bb6a..670f4f5d 100644 --- a/docs/test/pytest/config.rst +++ b/docs/test/pytest/config.rst @@ -36,7 +36,7 @@ Sie Konfigurationsdatei legt das oberste Verzeichnis fest, von dem aus ``pytest`` gestartet wird. Schauen wir uns einige dieser Dateien im Zusammenhang mit einer -Projektverzeichnisstruktur an: +Projekt-Verzeichnisstruktur an: .. code-block:: console :emphasize-lines: 3, 7, 8 @@ -61,15 +61,15 @@ Speichern von Einstellungen und Optionen in :file:`pytest.ini` .. code-block:: ini - [pytest] - addopts = - --strict-markers - --strict-config - -ra - testpaths = tests - markers = - smoke: Small subset of all tests - exception: Only run expected exceptions + [pytest] + addopts = + --strict-markers + --strict-config + -ra + testpaths = tests + markers = + smoke: Small subset of all tests + exception: Only run expected exceptions ``[pytest]`` kennzeichnet den Beginn des pytest-Abschnitts. Danach folgen die einzelnen Einstellungen. Bei Konfigurationseinstellungen, die @@ -122,7 +122,7 @@ Zeilen durch: .. seealso:: In den Konfigurationsdateien könnt ihr viele weitere - Konfigurationseinstellungen und Befehlszeilenoptionen angeben, die ihr euch + Konfigurationseinstellungen und Befehlszeilen-Optionen angeben, die ihr euch mit dem Befehl ``pytest --help`` anzeigen lassen könnt. Andere Konfigurationsdateien verwenden @@ -149,19 +149,19 @@ das Format auch ein wenig anders: .. code-block:: toml - [tool.pytest.ini_options] - addopts = [ - "--strict-markers", - "--strict-config", - "-ra" - ] - testpaths = "tests" - markers = [ - "exception: Only run expected exceptions", - "finish: Only run finish tests", - "smoke: Small subset of all tests", - "num_items: Number of items to be pre-filled for the items_db fixture" - ] + [tool.pytest.ini_options] + addopts = [ + "--strict-markers", + "--strict-config", + "-ra" + ] + testpaths = "tests" + markers = [ + "exception: Only run expected exceptions", + "finish: Only run finish tests", + "smoke: Small subset of all tests", + "num_items: Number of items to be pre-filled for the items_db fixture" + ] Anstelle von ``[pytest]`` beginnt der Abschnitt mit ``[tool.pytest.ini_options]``, die Werte müssen in Anführungszeichen gesetzt @@ -175,15 +175,15 @@ Das Dateiformat der :file:`setup.cfg` entspricht einer :file:`.ini`-Datei: .. code-block:: ini - [tool:pytest] - addopts = - --strict-markers - --strict-config - -ra - testpaths = tests - markers = - smoke: Small subset of all tests - exception: Only run expected exceptions + [tool:pytest] + addopts = + --strict-markers + --strict-config + -ra + testpaths = tests + markers = + smoke: Small subset of all tests + exception: Only run expected exceptions Der einzige Unterschied zwischen dieser und der :file:`pytest.ini` ist die Angabe des Abschnitts ``[tool:pytest]``. @@ -220,16 +220,16 @@ findet: .. code-block:: pytest :emphasize-lines: 5, 6 - $ cd items - $ pytest - ============================= test session starts ============================== - ... - rootdir: /Users/veit/cusy/prj/items - configfile: pyproject.toml - testpaths: tests - plugins: Faker-19.11.0 - collected 39 items - ... + $ cd items + $ pytest + ============================= test session starts ============================== + … + rootdir: /Users/veit/cusy/prj/items + configfile: pyproject.toml + testpaths: tests + plugins: Faker-19.11.0 + collected 39 items + … :file:`conftest.py` für die gemeinsame Nutzung von lokalen Fixtures und Hook-Funktionen --------------------------------------------------------------------------------------- @@ -288,7 +288,7 @@ in beiden Verzeichnissen liegt: $ pytest ============================= test session starts ============================== - ... + … rootdir: /Users/veit/cusy/prj/items configfile: pyproject.toml testpaths: tests diff --git a/docs/test/coverage.png b/docs/test/pytest/coverage.png similarity index 100% rename from docs/test/coverage.png rename to docs/test/pytest/coverage.png diff --git a/docs/test/coverage.rst b/docs/test/pytest/coverage.rst similarity index 99% rename from docs/test/coverage.rst rename to docs/test/pytest/coverage.rst index 07a3f581..84e8408e 100644 --- a/docs/test/coverage.rst +++ b/docs/test/pytest/coverage.rst @@ -21,8 +21,8 @@ Testsuite durchlaufen wird. `Coverage.py `_ ist das bevorzugte Python-Tool, das die Codeabdeckung misst. Und `pytest-cov `_ ist ein beliebtes -:doc:`Pytest-Plugin `, das oft in Verbindung mit Coverage.py -verwendet wird. +:doc:`Pytest-Plugin `, das oft in Verbindung mit Coverage.py verwendet +wird. Coverage.py mit pytest-cov verwenden ------------------------------------ @@ -53,7 +53,7 @@ und entweder einen Pfad zu dem Code angeben, den ihr messen wollt, oder das installierte Paket, das ihr testet. In unserem Fall ist das Projekt Items ein installiertes Paket, so dass wir es mit ``--cov=items`` testen werden. -Auf die normale pytest-Ausgabe folgt der Abdeckungsbericht, wie hier gezeigt: +Auf die normale pytest-Ausgabe folgt der Coverage-Bericht, wie hier gezeigt: .. code-block:: pytest diff --git a/docs/test/pytest/examples.rst b/docs/test/pytest/examples.rst index 83611c67..2ecad404 100644 --- a/docs/test/pytest/examples.rst +++ b/docs/test/pytest/examples.rst @@ -18,7 +18,7 @@ Tests ausgelöst wird, führt dazu, dass der Test fehlschlägt. pytest ausführen ---------------- -.. code-block:: console +.. code-block:: pytest $ cd docs/test/pytest $ pytest test_one.py @@ -36,7 +36,7 @@ der Testsitzung bisher durchgeführt wurden. Da es nur einen Test gibt, entspricht ein Test 100% der Tests. Wenn ihr mehr Informationen benötigt, könnt ihr ``-v`` oder ``--verbose`` verwenden: -.. code-block:: console +.. code-block:: pytest $ pytest -v test_one.py ============================= test session starts ============================== @@ -49,7 +49,7 @@ ihr ``-v`` oder ``--verbose`` verwenden: :file:`test_two.py` schlägt hingegen fehl: -.. code-block:: console +.. code-block:: pytest $ pytest test_two.py collected 1 item @@ -76,7 +76,7 @@ erste Fehler ist. Dieser zusätzliche Abschnitt wird Traceback genannt. Das sind schon eine Menge Informationen, aber es gibt eine Zeile, die besagt, dass wir mit ``-v`` den kompletten Diff erhalten. Lasst uns das tun: -.. code-block:: console +.. code-block:: pytest $ pytest -v test_two.py ============================= test session starts ============================== @@ -114,7 +114,7 @@ gesucht, die mit :file:`test_` beginnen oder mit :file:`_test` enden. Wenn ihr pytest im Verzeichnis :file:`docs/test/pytest` ohne Optionen startet, werden zwei Dateien mit Tests ausgeführt: -.. code-block:: console +.. code-block:: pytest $ pytest --tb=no ============================= test session starts ============================== @@ -135,7 +135,7 @@ Wir können auch eine Testfunktion innerhalb einer Testdatei angeben, die ausgeführt werden soll, indem wir :samp:`::test_{name}` zum Dateinamen hinzufügen: -.. code-block:: console +.. code-block:: pytest $ pytest -v test_one.py::test_sorted ============================= test session starts ============================== diff --git a/docs/test/pytest/fixtures.rst b/docs/test/pytest/fixtures.rst index e332507d..5af2f058 100644 --- a/docs/test/pytest/fixtures.rst +++ b/docs/test/pytest/fixtures.rst @@ -53,7 +53,7 @@ zurückgeben. In diesem Fall dekoriert ``@pytest.fixture()`` die Funktion :func:`some_data` als Parameter. pytest erkennt dies und sucht nach einer Fixture mit diesem Namen. -Testfixtures in pytest beziehen sich auf den Mechanismus, der die Trennung von +Test-Fixtures in pytest beziehen sich auf den Mechanismus, der die Trennung von *Vorbereitungen für*- und *Aufräumen nach*-Code von euren Testfunktionen ermöglicht. pytest behandelt Exceptions während Fixtures anders als während einer Testfunktion. Eine ``Exception`` oder ein ``assert``-Fehler oder ein @@ -71,7 +71,7 @@ Fixtures für Setup und Teardown verwenden Fixtures werden uns beim Testen der Items-Anwendung eine große Hilfe sein. Die Items-Anwendung besteht aus einer API, die den Großteil der Arbeit und der Logik -übernimmt, einem schlanken :abbr:`CLI (Command Line Interface)` und eine +übernimmt, einem schlanken :abbr:`CLI (Command Line Interface)` und einer Datenbank. Der Umgang mit der Datenbank ist ein Bereich, in dem Fixtures eine große Hilfe sein werden: @@ -102,7 +102,7 @@ Diese Testfunktion enthält jedoch einige Probleme: Der Code, um die Datenbank einzurichten, bevor wir :func:`count` aufrufen, ist nicht wirklich das, was wir testen wollen. Auch kann die ``assert``-Anweisung nicht vor dem Aufruf von :func:`db.close` erfolgen, denn wenn die ``assert``-Anweisung fehlschlägt, wird -de Datenbankverbindung nicht mehr geschlossen. Diese Probleme lassen sich mit +die Datenbankverbindung nicht mehr geschlossen. Diese Probleme lassen sich mit pytest-Fixture lösen: .. code-block:: python @@ -485,8 +485,8 @@ Ich habe die alte ``items_db`` in ``db`` umbenannt und sie in den Session-Bereich verschoben. Die ``items_db``-Fixture hat ``db`` in ihrer Parameter-Liste, was bedeutet, dass -sie von der ``db``-Fixture abhängt. Außerdem ist ``items_db`` -``function``-orientiert, was einen engeren Bereich als ``db`` darstellt. Wenn +sie von der ``db``-Fixture abhängt. Außerdem ist ``items_db`` im +``function``-Bereich, was einen engeren Bereich als ``db`` darstellt. Wenn Fixtures von anderen Fixtures abhängen, können sie nur Fixtures verwenden, die den gleichen oder einen größeren Geltungsbereich haben. @@ -560,7 +560,7 @@ Und auch Fixtures können mehrere andere Fixtures verwenden: @pytest.fixture(scope="function") def populated_db(items_db, items_list): """ItemsDB object populated with 'items_list'""" - for i in some_items: + for i in items_list: items_db.add_item(i) return items_db @@ -586,8 +586,8 @@ Fixture-Scope dynamisch festlegen --------------------------------- Nehmen wir an, wir haben die Fixtures so eingerichtet wie jetzt, mit ``db`` im -``session``-Scope und ``items_db`` im ``function``-Scope. Nun besteht jedoch die -Gefahr, dass das ``items_db``-Fixture leer ist, weil es :func:`delete_all` +``session``-Scope und ``items_db`` im ``function``-Bereich. Nun besteht jedoch +die Gefahr, dass das ``items_db``-Fixture leer ist, weil es :func:`delete_all` aufruft. Deshalb wollen wir eine Möglichkeit schaffen, die Datenbank für jede Testfunktion vollständig einzurichten, indem wir den Scope der ``db``-Fixture zur Laufzeit dynamisch festlegen. Hierfür ändern wir zuerst den Scope von @@ -616,10 +616,10 @@ Anstelle eines bestimmten Bereichs haben wir einen Funktionsnamen eingegeben: Es gibt viele Möglichkeiten, wie wir herausfinden können, welchen Bereich wir verwenden sollen. In diesem Fall habe ich mich für eine neue -Kommandozeilenoption ``--fdb`` entschieden. Damit wir diese neue Option mit -pytestverwenden können, müssen wir eine Hook-Funktion in der -:file:`conftest.py`-Datei schreiben, die ich in :doc:`plugins` näher erläutern -werde: +Kommandozeilen-Option ``--fdb`` für den ``function``-Bereich der Datenbank +entschieden. Damit wir diese neue Option mit pytest verwenden können, müssen wir +eine Hook-Funktion in der :file:`conftest.py`-Datei schreiben, die ich in +:doc:`plugins` näher erläutern werde: .. code-block:: python @@ -657,7 +657,7 @@ Nach all dem ist das Standardverhalten dasselbe wie vorher, mit ``db`` im ============================== 3 passed in 0.00s =============================== Wenn wir jedoch die neue Option verwenden, erhalten wir eine ``db``-Fixture im -``function``-Scope: +``function``-Bereich: .. code-block:: pytest @@ -692,7 +692,7 @@ abgebaut. ---------------------------------------------------- Bisher wurden alle von Tests verwendeten Fixtures durch die Tests oder eine -andere Fixture in einer Parameterliste benannt. Ihr könnt jedoch +andere Fixture in einer Parameter-Liste benannt. Ihr könnt jedoch ``autouse=True`` verwenden, um ein Fixture immer laufen zu lassen. Dies eignet sich gut für Code, der zu bestimmten Zeiten ausgeführt werden soll, aber Tests sind nicht wirklich von einem Systemzustand oder Daten aus der Fixture abhängig, @@ -742,10 +742,10 @@ sind nicht wirklich von einem Systemzustand oder Daten aus der Fixture abhängig Fixtures umbenennen ------------------- -Der Name einer Fixture, der in der Parameterliste von Tests und anderen Fixtures -aufgeführt ist, die diese Fixture verwenden, ist normalerweise derselbe wie der -Funktionsname der Fixture. Pytest erlaubt jedoch das Umbenennen von Fixtures mit -einem Namensparameter an ``@pytest.fixture``: +Der Name einer Fixture, der in der Parameter-Liste von Tests und anderen +Fixtures aufgeführt ist, die diese Fixture verwenden, ist normalerweise +derselbe wie der Funktionsname der Fixture. Pytest erlaubt jedoch das Umbenennen +von Fixtures mit einem Namensparameter an ``@pytest.fixture``: .. code-block:: python diff --git a/docs/test/pytest/functions.rst b/docs/test/pytest/functions.rst index 947242e1..c272a32c 100644 --- a/docs/test/pytest/functions.rst +++ b/docs/test/pytest/functions.rst @@ -143,24 +143,26 @@ Wenn wir den Test nun mit Python durchführen, erhalten wir folgendes Ergebnis: .. code-block:: console - python tests/test_item_fails.py - Traceback (most recent call last): - File "tests/test_item_fails.py", line 11, in - test_equality_fails() - File "tests/test_item_fails.py", line 7, in test_equality_fails - assert i1 == i2 - ^^^^^^^^ - AssertionError + python tests/test_item_fails.py + Traceback (most recent call last): + File "tests/test_item_fails.py", line 11, in + test_equality_fails() + File "tests/test_item_fails.py", line 7, in test_equality_fails + assert i1 == i2 + ^^^^^^^^ + AssertionError Das sagt uns nicht viel. Die pytest-Ausgabe gibt uns viel mehr Informationen darüber, warum unsere Annahmen fehlgeschlagen sind. +.. _pytest_fail: + Fehlschlagen mit ``pytest.fail()`` und Exceptions ------------------------------------------------- Das Fehlschlagen von Behauptungen ist die Hauptursache dafür, dass Tests -fehlgeschlagen. Aber das ist nicht der einzige Weg. Ein Test schlägt auch fehl, -wenn es eine nicht abgefangene :doc:`/control-flows/exceptions` gibt. Das kann +fehlschlagen. Aber das ist nicht der einzige Weg. Ein Test schlägt auch fehl, +wenn es eine nicht abgefangene :doc:`/control-flow/exceptions` gibt. Das kann passieren, wenn * eine ``assert``-Anweisung fehlschlägt, was zu einer @@ -253,7 +255,7 @@ dass fehlgeschlagene Tests nicht in den Traceback aufgenommen werden. Das normale ``assert i1 == i2`` wird dann verwendet, um alles außer ``id`` auf Gleichheit zu prüfen. -Schließlich werden die IDs überprüft ``pytest.fail()`` verwendet, um den Test +Schließlich werden die IDs überprüft und ``pytest.fail()`` verwendet, um den Test mit einer hilfreichen Meldung fehlschlagen zu lassen. Schauen wir uns an, wie das nach der Ausführung aussieht: diff --git a/docs/test/pytest/index.rst b/docs/test/pytest/index.rst index 1f8124ed..4a612f9c 100644 --- a/docs/test/pytest/index.rst +++ b/docs/test/pytest/index.rst @@ -17,8 +17,7 @@ Merkmale Installation ------------ -Ihr könnt pytest in :ref:`virtuellen Umgebungen ` -installieren mit: +Ihr könnt pytest in :ref:`virtuellen Umgebungen ` installieren mit: .. tab:: Linux/macOS @@ -52,3 +51,4 @@ installieren mit: plugins config debug + coverage diff --git a/docs/test/pytest/markers.rst b/docs/test/pytest/markers.rst index cc7c5315..19d705d0 100644 --- a/docs/test/pytest/markers.rst +++ b/docs/test/pytest/markers.rst @@ -76,21 +76,21 @@ Und er scheitert: .. code-block:: pytest - pytest --tb=short tests/test_compare.py - ============================= test session starts ============================== - ... - collected 2 items - - tests/test_compare.py F. [100%] - - =================================== FAILURES =================================== - ________________________________ test_less_than ________________________________ - tests/test_compare.py:7: in test_less_than - assert i1 < i2 - E TypeError: '<' not supported between instances of 'Item' and 'Item' - =========================== short test summary info ============================ - FAILED tests/test_compare.py::test_less_than - TypeError: '<' not supported between instances of 'Item' and 'Item' - ========================= 1 failed, 1 passed in 0.03s ========================== + pytest --tb=short tests/test_compare.py + ============================= test session starts ============================== + … + collected 2 items + + tests/test_compare.py F. [100%] + + =================================== FAILURES =================================== + ________________________________ test_less_than ________________________________ + tests/test_compare.py:7: in test_less_than + assert i1 < i2 + E TypeError: '<' not supported between instances of 'Item' and 'Item' + =========================== short test summary info ============================ + FAILED tests/test_compare.py::test_less_than - TypeError: '<' not supported between instances of 'Item' and 'Item' + ========================= 1 failed, 1 passed in 0.03s ========================== Der Fehler liegt einfach daran, dass wir diese Funktion noch nicht implementiert haben. Dennoch müssen wir diesen Test nicht wieder wegwerfen; wir können ihn @@ -99,50 +99,50 @@ einfach auslassen: .. code-block:: python :emphasize-lines: 1, 6 - import pytest + import pytest - from items import Item + from items import Item - @pytest.mark.skip(reason="Items do not yet allow a < comparison") - def test_less_than(): - i1 = Item("Update pytest section") - i2 = Item("Update cibuildwheel section") - assert i1 < i2 + @pytest.mark.skip(reason="Items do not yet allow a < comparison") + def test_less_than(): + i1 = Item("Update pytest section") + i2 = Item("Update cibuildwheel section") + assert i1 < i2 Der Marker ``@pytest.mark.skip()`` weist pytest an, den Test zu überspringen. Die Angabe eines Grundes ist zwar optional, aber sie hilft bei der weiteren Entwicklung. Wenn wir übersprungene Tests ausführen, werden sie als ``s`` angezeigt: -.. code-block:: +.. code-block:: pytest :emphasize-lines: 6 - $ pytest --tb=short tests/test_compare.py - ============================= test session starts ============================== - ... - collected 2 items + $ pytest --tb=short tests/test_compare.py + ============================= test session starts ============================== + … + collected 2 items - tests/test_compare.py s. [100%] + tests/test_compare.py s. [100%] - ========================= 1 passed, 1 skipped in 0.00s ========================= + ========================= 1 passed, 1 skipped in 0.00s ========================= -… oder verbos als ``SKIPPED``: +oder verbos als ``SKIPPED``: -.. code-block:: +.. code-block:: pytest :emphasize-lines: 1, 10 - $ pytest -v -ra tests/test_compare.py - ============================= test session starts ============================== - ... - collected 2 items + $ pytest -v -ra tests/test_compare.py + ============================= test session starts ============================== + … + collected 2 items - tests/test_compare.py::test_less_than SKIPPED (Items do not yet allo...) [ 50%] - tests/test_compare.py::test_equality PASSED [100%] + tests/test_compare.py::test_less_than SKIPPED (Items do not yet allo...) [ 50%] + tests/test_compare.py::test_equality PASSED [100%] - =========================== short test summary info ============================ - SKIPPED [1] tests/test_compare.py:6: Items do not yet allow a < comparison - ========================= 1 passed, 1 skipped in 0.00s ========================= + =========================== short test summary info ============================ + SKIPPED [1] tests/test_compare.py:6: Items do not yet allow a < comparison + ========================= 1 passed, 1 skipped in 0.00s ========================= Da wir pytest mit ``-r`` angewiesen haben, eine kurze Zusammenfassung unserer Tests auszugeben, erhalten wir eine zusätzliche Zeile am unteren Ende, die den @@ -264,7 +264,7 @@ funktioniert. Und so sieht das Ergebnis aus: pytest -v -ra tests/test_xfail.py ============================= test session starts ============================== - ... + … collected 3 items tests/test_xfail.py::test_less_than XFAIL (The comparison with < is ...) [ 33%] @@ -371,7 +371,7 @@ Option ``-m smoke`` verwenden: $ pytest -v -m smoke tests/test_start.py ============================= test session starts ============================== - ... + … collected 2 items / 1 deselected / 1 selected tests/test_start.py::test_start PASSED [100%] @@ -391,24 +391,24 @@ benutzerdefinierte Marker registrieren, indem wir einen Marker-Abschnitt zu .. code-block:: ini - [pytest] - markers = - smoke: Small subset of all tests + [pytest] + markers = + smoke: Small subset of all tests Jetzt warnt uns pytest nicht mehr vor einem unbekannten Marker: -.. code-block:: +.. code-block:: pytest :emphasize-lines: 4 - $ pytest -v -m smoke tests/test_start.py - ============================= test session starts ============================== - ... - configfile: pytest.ini - collected 2 items / 1 deselected / 1 selected + $ pytest -v -m smoke tests/test_start.py + ============================= test session starts ============================== + … + configfile: pytest.ini + collected 2 items / 1 deselected / 1 selected - tests/test_start.py::test_start PASSED [100%] + tests/test_start.py::test_start PASSED [100%] - ======================= 1 passed, 1 deselected in 0.00s ======================== + ======================= 1 passed, 1 deselected in 0.00s ======================== Machen wir dasselbe mit der ``exception``-Markierung für ``test_start_non_existent``. @@ -445,7 +445,7 @@ Machen wir dasselbe mit der ``exception``-Markierung für $ pytest -v -m exception tests/test_start.py ============================= test session starts ============================== - ... + … configfile: pytest.ini collected 2 items / 1 deselected / 1 selected @@ -587,7 +587,7 @@ anstatt eine Testdatei auszuwählen: $ cd tests $ tests % pytest -v -m exception ============================= test session starts ============================== - ... + … configfile: pytest.ini collected 36 items / 34 deselected / 2 selected @@ -608,7 +608,7 @@ zusammen mit Schlüsselwörtern zur Auswahl von Testfällen in :ref:`Testsuite pytest -v -m "finish and exception" ============================= test session starts ============================== - ... + … configfile: pytest.ini collected 36 items / 35 deselected / 1 selected @@ -620,34 +620,34 @@ Wir können auch alle logischen Verknüpfungen zusammen verwenden: .. code-block:: pytest - $ pytest -v -m "(exception or smoke) and (not finish)" - ============================= test session starts ============================== - ... - configfile: pytest.ini - collected 36 items / 34 deselected / 2 selected + $ pytest -v -m "(exception or smoke) and (not finish)" + ============================= test session starts ============================== + … + configfile: pytest.ini + collected 36 items / 34 deselected / 2 selected - test_start.py::test_start PASSED [ 50%] - test_start.py::test_start_non_existent PASSED [100%] + test_start.py::test_start PASSED [ 50%] + test_start.py::test_start_non_existent PASSED [100%] - ======================= 2 passed, 34 deselected in 0.08s ======================= + ======================= 2 passed, 34 deselected in 0.08s ======================= Schließlich können wir auch Marker und Keywords für die Auswahl kombinieren, :abbr:`z.B. (zum Beispiel)` um Smoke-Tests auszuführen, die nicht Teil der Klasse :class:`TestFinish` sind: -.. code-block:: +.. code-block:: console - $ pytest -v -m smoke -k "not TestFinish" - ============================= test session starts ============================== - ... - configfile: pytest.ini - collected 36 items / 33 deselected / 3 selected + $ pytest -v -m smoke -k "not TestFinish" + ============================= test session starts ============================== + … + configfile: pytest.ini + collected 36 items / 33 deselected / 3 selected - test_finish.py::test_finish[in progress] PASSED [ 33%] - test_finish.py::test_finish_non_existent PASSED [ 66%] - test_start.py::test_start PASSED [100%] + test_finish.py::test_finish[in progress] PASSED [ 33%] + test_finish.py::test_finish_non_existent PASSED [ 66%] + test_start.py::test_start PASSED [100%] - ======================= 3 passed, 33 deselected in 0.07s ======================= + ======================= 3 passed, 33 deselected in 0.07s ======================= Bei der Verwendung von Markern und Keywords ist zu beachten, dass die Namen der Marker bei der Option :samp:`-m {MARKERNAME}` vollständig sein müssen, während @@ -677,7 +677,7 @@ Wenn diese Warnung stattdessen ein Fehler sein soll, können wir die Option :emphasize-lines: 3-4 [pytest] - ... + … addopts = --strict-markers @@ -769,7 +769,7 @@ notwendig: [pytest] markers = - ... + … num_items: Number of items to be pre-filled for the items_db fixture #. Nun modifizieren wir die ``items_db``-Fixture in der @@ -780,7 +780,7 @@ notwendig: .. code-block:: python :linenos: - :emphasize-lines: 5, 12- + :emphasize-lines: 5, 13- import os from pathlib import Path @@ -814,23 +814,23 @@ notwendig: Zeile 13 Wir haben ``request`` und ``faker`` in die Liste der ``items_db``-Parameter aufgenommen. - Zeile 17 + Zeile 18 Dies setzt die Zufälligkeit von Faker, so dass wir jedes Mal die gleichen Daten erhalten. Dabei verwenden wir Faker hier nicht für sehr zufällige Daten, sondern um zu vermeiden, dass wir selbst Daten erfinden müssen. - Zeile 18 + Zeile 19 Hier verwenden wir ``request``, genauer ``request.node`` für die pytest-Repräsentation eines Tests. ``get_closest_marker('num_items')`` gibt ein Marker-Objekt zurück, wenn der Test mit ``num_items`` markiert ist, andernfalls gibt es ``None`` zurück. Die :func:`get_closest_marker`-Funktion gibt den Marker zurück, der dem Test am nächsten liegt, und das ist normalerweise das, was wir wollen. - Zeile 19 + Zeile 20 Der Ausdruck ist wahr, wenn der Test mit ``num_items`` markiert ist und ein Argument angegeben wird. Die zusätzliche ``len``-Prüfung dient dazu, dass, falls jemand versehentlich nur ``pytest.mark.num_items`` verwendet, ohne die Anzahl der Items anzugeben, dieser Teil übersprungen wird. - Zeile 20–22 + Zeile 21–24 Sobald wir wissen, wie viele Items wir erstellen müssen, lassen wir Faker einige Daten für uns erstellen. Faker stellt die Faker-Fixture zur Verfügung. @@ -845,7 +845,7 @@ notwendig: könnt. Schaut hierfür in die `Faker-Dokumentation `_. - * Neben Faker gibt es nach weitere Bibliothkeen, die Fake-Daten + * Neben Faker gibt es nach weitere Bibliotheken, die Fake-Daten bereitstellen, siehe :ref:`Fake Plugins `. Führen wir die Tests nun aus, um sicherzustellen, dass alles richtig @@ -855,7 +855,7 @@ funktioniert: $ pytest -v -s test_items.py ============================= test session starts ============================== - ... + … configfile: pytest.ini plugins: Faker-19.10.0 collected 3 items @@ -887,7 +887,7 @@ funktioniert: $ pytest -v -s test_items.py ============================= test session starts ============================== - ... + … configfile: pytest.ini plugins: Faker-19.10.0 collected 3 items @@ -913,7 +913,7 @@ Built-in-Marker. Und wenn wir anfangen, :doc:`plugins` zu verwenden, können noc weitere Marker hinzukommen. Um alle verfügbaren Marker mit Beschreibungen und Parameter aufzulisten, könnt ihr ``pytest --markers`` ausführen: -.. code-block:: console +.. code-block:: pytest $ pytest --markers @pytest.mark.exception: Only run expected exceptions @@ -925,7 +925,7 @@ Parameter aufzulisten, könnt ihr ``pytest --markers`` ausführen: @pytest.mark.num_items: Number of items to be pre-filled for the items_db fixture @pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings - ... + … Dies ist eine sehr praktische Funktion, mit der wir schnell nach Markern suchen können, und ein guter Grund, nützliche Beschreibungen zu unseren eigenen Markern diff --git a/docs/test/missing.png b/docs/test/pytest/missing.png similarity index 100% rename from docs/test/missing.png rename to docs/test/pytest/missing.png diff --git a/docs/test/pytest/params.rst b/docs/test/pytest/params.rst index 2f72e376..8fcd7f82 100644 --- a/docs/test/pytest/params.rst +++ b/docs/test/pytest/params.rst @@ -1,12 +1,12 @@ Testparametrisierung ==================== -Durch Parametrisierung können wir eine Testfunktion in viele Testfälle -umwandeln, um mit weniger Arbeit gründlicher zu testen. Hierfür übergeben wir -dem Test mehrere Sätze von Argumenten, um neue Testfälle zu erstellen. Wir -werfen einen Blick auf redundanten Code, den wir mit Parametrisierung vermeiden. -Dann werden wir uns drei Möglichkeiten ansehen, und zwar in der Reihenfolge, in -der sie ausgewählt werden sollten: +Durch :term:`Parametrisierung ` können wir eine Testfunktion in viele +Testfälle umwandeln, um mit weniger Arbeit gründlicher zu testen. Hierfür +übergeben wir dem Test mehrere Sätze von Argumenten, um neue Testfälle zu +erstellen. Wir werfen einen Blick auf redundanten Code, den wir mit +Parametrisierung vermeiden. Dann werden wir uns drei Möglichkeiten ansehen, und +zwar in der Reihenfolge, in der sie ausgewählt werden sollten: - Parametrisierung von Funktionen - Parametrisierung von Fixtures @@ -29,9 +29,9 @@ Tests für die API-Methode ``finish()`` aus :file:`src/items/api.py`: .. code-block:: python - def finish(self, item_id: int): - """Set an item state to done.""" - self.update_item(item_id, Item(state="done")) + def finish(self, item_id: int): + """Set an item state to done.""" + self.update_item(item_id, Item(state="done")) Die in der Anwendung verwendeten Zustände sind *todo*, *in progress* und *done*, und ``finish()`` setzt den Zustand einer Karte auf *done*. Um dies zu testen, @@ -47,47 +47,47 @@ oder sogar schon "done" sein. Lasst uns alle drei testen: .. code-block:: python - from items import Item + from items import Item - def test_finish_from_in_prog(items_db): - index = items_db.add_item( - Item("Update pytest section", state="in progress") - ) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish_from_in_prog(items_db): + index = items_db.add_item( + Item("Update pytest section", state="in progress") + ) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" - def test_finish_from_done(items_db): - index = items_db.add_item( - Item("Update cibuildwheel section", state="done") - ) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish_from_done(items_db): + index = items_db.add_item( + Item("Update cibuildwheel section", state="done") + ) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" - def test_finish_from_todo(items_db): - index = items_db.add_item(Item("Update mock tests", state="todo")) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish_from_todo(items_db): + index = items_db.add_item(Item("Update mock tests", state="todo")) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Lassen wir es laufen: .. code-block:: pytest - pytest -v tests/test_finish.py - ============================= test session starts ============================== - … - collected 3 items + pytest -v tests/test_finish.py + ============================= test session starts ============================== + … + collected 3 items - tests/test_finish.py::test_finish_from_in_prog PASSED [ 33%] - tests/test_finish.py::test_finish_from_done PASSED [ 66%] - tests/test_finish.py::test_finish_from_todo PASSED [100%] + tests/test_finish.py::test_finish_from_in_prog PASSED [ 33%] + tests/test_finish.py::test_finish_from_done PASSED [ 66%] + tests/test_finish.py::test_finish_from_todo PASSED [100%] - ============================== 3 passed in 0.00s =============================== + ============================== 3 passed in 0.00s =============================== Die Testfunktionen sind sehr ähnlich. Die einzigen Unterschiede sind der Ausgangszustand und die Zusammenfassung. Eine Möglichkeit, den redundanten Code @@ -96,32 +96,32 @@ zusammenzufassen, etwa so: .. code-block:: python - from items import Item + from items import Item - def test_finish(items_db): - for i in [ - Item("Update pytest section", state="done"), - Item("Update cibuildwheel section", state="in progress"), - Item("Update mock tests", state="todo"), - ]: - index = items_db.add_item(i) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish(items_db): + for i in [ + Item("Update pytest section", state="done"), + Item("Update cibuildwheel section", state="in progress"), + Item("Update mock tests", state="todo"), + ]: + index = items_db.add_item(i) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Nun lassen wir :file:`tests/test_finish.py` erneut laufen: .. code-block:: pytest - $ pytest -v tests/test_finish.py - ============================= test session starts ============================== - … - collected 1 item + $ pytest -v tests/test_finish.py + ============================= test session starts ============================== + … + collected 1 item - tests/test_finish.py::test_finish PASSED [100%] + tests/test_finish.py::test_finish PASSED [100%] - ============================== 1 passed in 0.00s =============================== + ============================== 1 passed in 0.00s =============================== Auch dieser Test ist bestanden, und wir haben den überflüssigen Code eliminiert. Aber es ist doch nicht dasselbe: @@ -144,25 +144,25 @@ zu übergebenden Argumente zu definieren, etwa so: .. code-block:: python - import pytest + import pytest - from items import Item + from items import Item - @pytest.mark.parametrize( - "start_summary, start_state", - [ - ("Update pytest section", "done"), - ("Update cibuildwheel section", "in progress"), - ("Update mock tests", "todo"), - ], - ) - def test_finish(items_db, start_summary, start_state): - initial_item = Item(summary=start_summary, state=start_state) - index = items_db.add_item(initial_item) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + @pytest.mark.parametrize( + "start_summary, start_state", + [ + ("Update pytest section", "done"), + ("Update cibuildwheel section", "in progress"), + ("Update mock tests", "todo"), + ], + ) + def test_finish(items_db, start_summary, start_state): + initial_item = Item(summary=start_summary, state=start_state) + index = items_db.add_item(initial_item) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Die ``test_finish()``-Funktion hat jetzt ihre ursprüngliche ``items_db``-Fixture als Parameter, aber auch zwei neue Parameter: @@ -181,18 +181,18 @@ Argument von ``@pytest.mark.parametrize()`` überein. pytest führt diesen Test einmal für jedes ``(start_summary, start_state)``-Paar durch und meldet jeden als separaten Test: -.. code-block:: +.. code-block:: console - $ pytest -v tests/test_finish.py - ============================= test session starts ============================== - … - collected 3 items + $ pytest -v tests/test_finish.py + ============================= test session starts ============================== + … + collected 3 items - tests/test_finish.py::test_finish[Update pytest section-done] PASSED [ 33%] - tests/test_finish.py::test_finish[Update cibuildwheel section-in progress] PASSED [ 66%] - tests/test_finish.py::test_finish[Update mock tests-todo] PASSED [100%] + tests/test_finish.py::test_finish[Update pytest section-done] PASSED [ 33%] + tests/test_finish.py::test_finish[Update cibuildwheel section-in progress] PASSED [ 66%] + tests/test_finish.py::test_finish[Update mock tests-todo] PASSED [100%] - ============================== 3 passed in 0.00s =============================== + ============================== 3 passed in 0.00s =============================== Diese Verwendung von ``parametrize()`` funktioniert für unsere Zwecke. Allerdings ist es für diesen Test ``start_summary`` nicht wirklich wichtig und @@ -201,41 +201,41 @@ macht jeden Testfall komplexer. Ändern wir die Parametrisierung in .. code-block:: python - import pytest + import pytest - from items import Item + from items import Item - @pytest.mark.parametrize( - "start_state", - [ - "done", - "in progress", - "todo", - ], - ) - def test_finish(items_db, start_state): - i = Item("Update pytest section", state=start_state) - index = items_db.add_item(i) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + @pytest.mark.parametrize( + "start_state", + [ + "done", + "in progress", + "todo", + ], + ) + def test_finish(items_db, start_state): + i = Item("Update pytest section", state=start_state) + index = items_db.add_item(i) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Wenn wir die Tests jetzt ausführen, konzentrieren sie sich auf die Veränderung, die uns wichtig ist: -.. code-block:: +.. code-block:: console - $ pytest -v tests/test_finish.py - ============================= test session starts ============================== - … - collected 3 items + $ pytest -v tests/test_finish.py + ============================= test session starts ============================== + … + collected 3 items - tests/test_finish.py::test_finish[done] PASSED [ 33%] - tests/test_finish.py::test_finish[in progress] PASSED [ 66%] - tests/test_finish.py::test_finish[todo] PASSED [100%] + tests/test_finish.py::test_finish[done] PASSED [ 33%] + tests/test_finish.py::test_finish[in progress] PASSED [ 66%] + tests/test_finish.py::test_finish[todo] PASSED [100%] - ============================== 3 passed in 0.01s =============================== + ============================== 3 passed in 0.01s =============================== Die Ausgabe der beiden Beispiele, unterscheidet sich insofern, dass jetzt nur noch der Ausgangszustand aufgelistet wird, also *todo*, *in progress* und @@ -255,22 +255,22 @@ jeden Fixture-Wert einmal aufgerufen. Auch die Syntax ist anders: .. code-block:: python - import pytest + import pytest - from items import Item + from items import Item - @pytest.fixture(params=["done", "in progress", "todo"]) - def start_state(request): - return request.param + @pytest.fixture(params=["done", "in progress", "todo"]) + def start_state(request): + return request.param - def test_finish(items_db, start_state): - i = Item("Update pytest section", state=start_state) - index = items_db.add_item(i) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish(items_db, start_state): + i = Item("Update pytest section", state=start_state) + index = items_db.add_item(i) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Das bedeutet, dass pytest ``start_state()`` dreimal aufruft, jeweils einmal für alle Werte in ``params``. Jeder Wert von ``params`` wird in ``request.param`` @@ -284,18 +284,18 @@ Funktionsparametrisierung verwendet haben, jedoch ohne den Dekorator einmal für jeden Wert auf, der an die ``start_state()``-Fixture übergeben wird. Und nach all dem sieht die Ausgabe genauso aus wie vorher: -.. code-block:: +.. code-block:: console - $ pytest -v tests/test_finish.py - ============================= test session starts ============================== - … - collected 3 items + $ pytest -v tests/test_finish.py + ============================= test session starts ============================== + … + collected 3 items - tests/test_finish.py::test_finish[done] PASSED [ 33%] - tests/test_finish.py::test_finish[in progress] PASSED [ 66%] - tests/test_finish.py::test_finish[todo] PASSED [100%] + tests/test_finish.py::test_finish[done] PASSED [ 33%] + tests/test_finish.py::test_finish[in progress] PASSED [ 66%] + tests/test_finish.py::test_finish[todo] PASSED [100%] - ============================== 3 passed in 0.01s =============================== + ============================== 3 passed in 0.01s =============================== Auf den ersten Blick erfüllt die Fixture-Parametrisierung in etwa den gleichen Zweck wie die Funktionsparametrisierung, allerdings mit etwas mehr Code. Die @@ -323,20 +323,20 @@ sieht wie folgt aus: .. code-block:: python - from items import Item + from items import Item - def pytest_generate_tests(metafunc): - if "start_state" in metafunc.fixturenames: - metafunc.parametrize("start_state", ["done", "in progress", "todo"]) + def pytest_generate_tests(metafunc): + if "start_state" in metafunc.fixturenames: + metafunc.parametrize("start_state", ["done", "in progress", "todo"]) - def test_finish(items_db, start_state): - i = Item("Update pytest section", state=start_state) - index = items_db.add_item(i) - items_db.finish(index) - item = items_db.get_item(index) - assert item.state == "done" + def test_finish(items_db, start_state): + i = Item("Update pytest section", state=start_state) + index = items_db.add_item(i) + items_db.finish(index) + item = items_db.get_item(index) + assert item.state == "done" Die ``test_finish()``-Funktion hat sich nicht geändert; wir haben nur die Art und Weise geändert, wie pytest den Wert für ``initial_state`` bei jedem diff --git a/docs/test/pytest/plugins.rst b/docs/test/pytest/plugins.rst index 40225189..e76c9fdd 100644 --- a/docs/test/pytest/plugins.rst +++ b/docs/test/pytest/plugins.rst @@ -54,17 +54,122 @@ Ablauf eines Test: `pytest-xdist `_ führt Tests parallel aus, entweder mit mehreren CPUs auf einer Maschine oder mehreren entfernten Maschinen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-xdist + :alt: Stars + :target: https://github.com/pytest-dev/pytest-xdist/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-xdist + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-xdist/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-xdist + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-xdist/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-xdist + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-xdist?tab=MIT-1-ov-file#readme + +`pytest-freethreaded `_ + für die Überprüfung, ob Tests und Bibliotheken mit dem experimentellen + Freethreaded-Modus von Python 3.13 thread-sicher sind. + + .. image:: https://raster.shields.io/github/stars/tonybaloney/pytest-freethreaded + :alt: Stars + :target: https://github.com/tonybaloney/pytest-freethreaded/stargazers + + .. image:: https://raster.shields.io/github/contributors/tonybaloney/pytest-freethreaded + :alt: Contributors + :target: https://github.com/tonybaloney/pytest-freethreaded/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/tonybaloney/pytest-freethreaded + :alt: Commit activity + :target: https://github.com/tonybaloney/pytest-freethreaded/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/tonybaloney/pytest-freethreaded + :alt: Lizenz + :target: https://github.com/tonybaloney/pytest-freethreaded?tab=MIT-1-ov-file#readme + `pytest-rerunfailures `_ führt fehlgeschlagene Tests erneut aus und ist :abbr:`v.a. (vor allem)` hilfreich bei fehlerhaften Tests. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-rerunfailures + :alt: Stars + :target: https://github.com/pytest-dev/pytest-rerunfailures/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-rerunfailures + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-rerunfailures/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-rerunfailures + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-rerunfailures/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-rerunfailures + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-rerunfailures?tab=License-1-ov-file#readme + `pytest-repeat `_ macht es einfach, einen oder mehrere Tests zu wiederholen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-repeat + :alt: Stars + :target: https://github.com/pytest-dev/pytest-repeat/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-repeat + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-repeat/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-repeat + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-repeat/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-repeat + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-repeat?tab=License-1-ov-file#readme + `pytest-order `_ ermöglicht die Festlegung der Reihenfolge durch :doc:`markers`. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-order + :alt: Stars + :target: https://github.com/pytest-dev/pytest-order/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-order + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-order/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-order + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-order/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-xdist + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-xdist?tab=MIT-1-ov-file#readme + `pytest-randomly `_ lässt die Tests in zufälliger Reihenfolge ablaufen, zuerst nach Datei, dann nach Klasse, dann schließlich nach Testdatei. + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-randomly + :alt: Stars + :target: https://github.com/pytest-dev/pytest-randomly/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-randomly + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-randomly/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-randomly + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-randomly/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-randomly + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-randomly?tab=MIT-1-ov-file#readme + + … veränderten Output ~~~~~~~~~~~~~~~~~~~~ @@ -78,36 +183,193 @@ verändern: fehlgeschlagenen Tests direkt nach dem Fehlschlag meldet. Normalerweise meldet pytest Tracebacks und Ausgaben von fehlgeschlagenen Tests erst, nachdem alle Tests abgeschlossen wurden. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-instafail + :alt: Stars + :target: https://github.com/pytest-dev/pytest-instafail/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-instafail + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-instafail/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-instafail + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-instafail/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-instafail + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-rerunfailures?tab=License-1-ov-file#readme + +`pytest-edit `_ + öffnet einen Editor nach einem fehlgeschlagenen Test. + + .. image:: https://raster.shields.io/github/stars/mrmino/pytest-edit + :alt: Stars + :target: https://github.com/mrmino/pytest-edit/stargazers + + .. image:: https://raster.shields.io/github/contributors/mrmino/pytest-edit + :alt: Contributors + :target: https://github.com/MrMino/pytest-edit/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/mrmino/pytest-edit + :alt: Commit activity + :target: https://github.com/mrmino/pytest-edit/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/mrmino/pytest-edit + :alt: Lizenz + :target: https://github.com/mrmino/pytest-edit?tab=MIT-1-ov-file#readme + `pytest-sugar `_ zeigt grüne Häkchen anstelle von Punkten für bestandene Tests und hat einen schönen Fortschrittsbalken. Es zeigt, wie pytest-instafail auch, Fehlschläge sofort an. + + .. image:: https://raster.shields.io/github/stars/Teemu/pytest-sugar + :alt: Stars + :target: https://github.com/Teemu/pytest-sugar/stargazers + + .. image:: https://raster.shields.io/github/contributors/Teemu/pytest-sugar + :alt: Contributors + :target: https://github.com/Teemu/pytest-sugar/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/Teemu/pytest-sugar + :alt: Commit activity + :target: https://github.com/Teemu/pytest-sugar/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/Teemu/pytest-sugar + :alt: Lizenz + :target: https://github.com/Teemu/pytest-sugar?tab=License-1-ov-file#readme + `pytest-html `_ ermöglicht die Erstellung von HTML-Berichten. Berichte können mit zusätzlichen Daten und Bildern, wie :abbr:`z.B. (zum Beispiel)` Screenshots von Fehlerfällen, erweitert werden. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-html + :alt: Stars + :target: https://github.com/pytest-dev/pytest-html/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-html + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-html/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-html + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-html/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-html + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-html?tab=License-1-ov-file#readme + `pytest-icdiff `_ verbessert Diffs in den Fehlermeldungen der Pytest-Assertion mit `ICDiff `_. + .. image:: https://raster.shields.io/github/stars/hjwp/pytest-icdiff + :alt: Stars + :target: https://github.com/hjwp/pytest-icdiff/stargazers + + .. image:: https://raster.shields.io/github/contributors/hjwp/pytest-icdiff + :alt: Contributors + :target: https://github.com/hjwp/pytest-icdiff/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/hjwp/pytest-icdiff + :alt: Commit activity + :target: https://github.com/hjwp/pytest-icdiff/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/hjwp/pytest-icdiff + :alt: Lizenz + :target: https://github.com/hjwp/pytest-icdiff?tab=MIT-1-ov-file#readme + + … für die Webentwicklung ~~~~~~~~~~~~~~~~~~~~~~~~ pytest wird ausgiebig für das Testen von Webprojekten verwendet und es gibt eine lange Liste von Plugins, die das Testen weiter vereinfachen: -`pytest-selenium `_ - stellt Fixtures zur Verfügung, die eine einfache Konfiguration von - browserbasierten Tests mit `Selenium `_ - ermöglichen. -`pytest-splinter `_ - bieten die High-Level-API des auf Selenium aufbauenden `Splinter - `_ um einfacher von pytest aus verwendet - zu werden. `pytest-httpx `_ erleichtert das Testen von `HTTPX `_ und `FastAPI `_-Anwendungen. + .. image:: https://raster.shields.io/github/stars/Colin-b/pytest_httpx + :alt: Stars + :target: https://github.com/Colin-b/pytest_httpx/stargazers + + .. image:: https://raster.shields.io/github/contributors/Colin-b/pytest_httpx + :alt: Contributors + :target: https://github.com/Colin-b/pytest_httpx/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/Colin-b/pytest_httpx + :alt: Commit activity + :target: https://github.com/Colin-b/pytest_httpx/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/Colin-b/pytest_httpx + :alt: Lizenz + :target: https://github.com/Colin-b/pytest_httpx?tab=MIT-1-ov-file#readme + +`Playwright for Python `_ + wurde speziell für End-to-End-Tests entwickelt. Playwright unterstützt alle + modernen Rendering-Engines wie Chromium, WebKit und Firefox mit einer + einzigen :abbr:`API (Application Programming Interface)`. + + .. image:: https://raster.shields.io/github/stars/Microsoft/playwright-python + :alt: Stars + :target: https://github.com/Microsoft/playwright-python/stargazers + + .. image:: https://raster.shields.io/github/contributors/Microsoft/playwright-python + :alt: Contributors + :target: https://github.com/Microsoft/playwright-python/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/Microsoft/playwright-python + :alt: Commit activity + :target: https://github.com/Microsoft/playwright-python/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/Microsoft/playwright-python + :alt: Lizenz + :target: https://github.com/Microsoft/playwright-python?tab=MIT-1-ov-file#readme + +`pyleniumio `_ + ist ein dünner Python-Wrapper um Selenium mit einfacher und klarer Syntax. + + .. image:: https://raster.shields.io/github/stars/ElSnoMan/pyleniumio + :alt: Stars + :target: https://github.com/ElSnoMan/pyleniumio/stargazers + + .. image:: https://raster.shields.io/github/contributors/ElSnoMan/pyleniumio + :alt: Contributors + :target: https://github.com/ElSnoMan/pyleniumio/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/ElSnoMan/pyleniumio + :alt: Commit activity + :target: https://github.com/ElSnoMan/pyleniumio/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/ElSnoMan/pyleniumio + :alt: Lizenz + :target: https://github.com/ElSnoMan/pyleniumio?tab=MIT-1-ov-file#readme + +`pytest-selenium `_ + stellt Fixtures zur Verfügung, die eine einfache Konfiguration von + Browser-basierten Tests mit `Selenium `_ + ermöglichen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-selenium + :alt: Stars + :target: https://github.com/pytest-dev/pytest-selenium/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-selenium + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-selenium + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-selenium + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-selenium/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-selenium + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-selenium?tab=License-1-ov-file#readme + + .. _fake_plugins: … für Fake-Daten @@ -122,36 +384,225 @@ Bedarf decken: `Faker `_ generiert Fake-Daten für euch und bietet ein Faker Fixture für die Verwendung mit pytest. + + .. image:: https://raster.shields.io/github/stars/joke2k/faker + :alt: Stars + :target: https://github.com/joke2k/faker/stargazers + + .. image:: https://raster.shields.io/github/contributors/joke2k/faker + :alt: Contributors + :target: https://github.com/joke2k/faker/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/joke2k/faker + :alt: Commit activity + :target: https://github.com/joke2k/faker/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/joke2k/faker + :alt: Lizenz + :target: https://github.com/joke2k/faker?tab=MIT-1-ov-file#readme + `pytest-factoryboy `_ enthält Fixtures für `factory-boy `_, - einen Datenbankmodelldatengenerator. -`pytest-mimesis `_ - erzeugt Fake-Daten ähnlich wie Faker, aber `Mimesis - `_ ist um einiges schneller. + einen Datenbankmodell-Datengenerator. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-factoryboy + :alt: Stars + :target: https://github.com/pytest-dev/pytest-factoryboy/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-factoryboy + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-factoryboy/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-factoryboy + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-factoryboy/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-factoryboy + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-factoryboy?tab=MIT-1-ov-file#readme + … für Verschiedenes ~~~~~~~~~~~~~~~~~~~ +`pytest-testinfra `_ + ist ein `Serverspec `_-Äquivalent für pytest, um + den aktuellen Zustand eurer Server mit Management-Tools wie `Salt + `_, `Ansible + `_, `Puppet + `_, `Chef `_ :abbr:`usw. (und + so weiter)` zu testen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-testinfra + :alt: Stars + :target: https://github.com/pytest-dev/pytest-testinfra/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-testinfra + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-testinfra/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-testinfra + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-testinfra/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-testinfra + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-testinfra?tab=Apache-2.0-1-ov-file + `pytest-cov `_ - führt die :doc:`../coverage` beim Testen aus. + führt die :doc:`../pytest/coverage` beim Testen aus. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-cov + :alt: Stars + :target: https://github.com/pytest-dev/pytest-cov/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-cov + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-cov/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-cov + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-cov/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-cov + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-cov?tab=MIT-1-ov-file#readme + `pytest-benchmark `_ führt Benchmark-Timing für Code innerhalb von Tests durch. + + .. image:: https://raster.shields.io/github/stars/ionelmc/pytest-benchmark + :alt: Stars + :target: https://github.com/ionelmc/pytest-benchmark/stargazers + + .. image:: https://raster.shields.io/github/contributors/ionelmc/pytest-benchmark + :alt: Contributors + :target: https://github.com/ionelmc/pytest-benchmark/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/ionelmc/pytest-benchmark + :alt: Commit activity + :target: https://github.com/ionelmc/pytest-benchmark/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/ionelmc/pytest-benchmark + :alt: Lizenz + :target: https://github.com/ionelmc/pytest-benchmark?tab=BSD-2-Clause-1-ov-file#readme + `pytest-timeout `_ lässt Tests nicht zu lange laufen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-timeout + :alt: Stars + :target: https://github.com/pytest-dev/pytest-timeout/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-timeout + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-timeout/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-timeout + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-timeout/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-timeout + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-timeout?tab=MIT-1-ov-file#readme + `pytest-asyncio `_ testet asynchrone Funktionen. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-asyncio + :alt: Stars + :target: https://github.com/pytest-dev/pytest-asyncio/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-asyncio + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-asyncio/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-asyncio + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-asyncio/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-asyncio + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-asyncio?tab=MIT-1-ov-file#readme + `pytest-mock `_ ist ein dünner Wrapper um die :doc:`unittest.mock <../mock>`-Patching-API. -`pytest-freezegun `_ - friert die Zeit ein, so dass jeder Code, der die Zeit, Datum oder Uhrzeit, - liest, während eines Tests denselben Wert erhält. + + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-mock + :alt: Stars + :target: https://github.com/pytest-dev/pytest-mock/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-mock + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-mock/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-mock + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-mock/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-mock + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-mock?tab=MIT-1-ov-file#readme + +`pytest-patterns `_ + stellt eine für Tests optimierte Pattern-Matching-Engine bereit. + + .. image:: https://raster.shields.io/github/stars/flyingcircusio/pytest-patterns + :alt: Stars + :target: https://github.com/flyingcircusio/pytest-patterns/stargazers + + .. image:: https://raster.shields.io/github/contributors/flyingcircusio/pytest-patterns + :alt: Contributors + :target: https://github.com/flyingcircusio/pytest-patterns/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/flyingcircusio/pytest-patterns + :alt: Commit activity + :target: https://github.com/flyingcircusio/pytest-patterns/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/flyingcircusio/pytest-patterns + :alt: Lizenz + :target: https://github.com/flyingcircusio/pytest-patterns?tab=MIT-1-ov-file#readme + :doc:`pytest-grpc ` ist ein Pytest-Plugin für :doc:`Python4DataScience:data-processing/apis/grpc/index`. + + .. image:: https://raster.shields.io/github/stars/kataev/pytest-grpc + :alt: Stars + :target: https://github.com/kataev/pytest-grpc/stargazers + + .. image:: https://raster.shields.io/github/contributors/kataev/pytest-grpc + :alt: Contributors + :target: https://github.com/kataev/pytest-grpc/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/kataev/pytest-grpc + :alt: Commit activity + :target: https://github.com/kataev/pytest-grpc/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/kataev/pytest-grpc + :alt: Lizenz + :target: https://github.com/kataev/pytest-grpc?tab=MIT-1-ov-file#readme + `pytest-bdd `_ schreibt :abbr:`BDD (Behavior Driven Development, deutsch: verhaltensgetriebene Softwareentwicklung)`-Tests mit pytest. + .. image:: https://raster.shields.io/github/stars/pytest-dev/pytest-bdd + :alt: Stars + :target: https://github.com/pytest-dev/pytest-bdd/stargazers + + .. image:: https://raster.shields.io/github/contributors/pytest-dev/pytest-bdd + :alt: Contributors + :target: https://github.com/pytest-dev/pytest-bdd/graphs/contributors + + .. image:: https://raster.shields.io/github/commit-activity/y/pytest-dev/pytest-bdd + :alt: Commit activity + :target: https://github.com/pytest-dev/pytest-bdd/graphs/commit-activity + + .. image:: https://raster.shields.io/github/license/pytest-dev/pytest-bdd + :alt: Lizenz + :target: https://github.com/pytest-dev/pytest-bdd?tab=MIT-1-ov-file#readme + Eigene Plugins -------------- diff --git a/docs/test/pytest/testsuite.rst b/docs/test/pytest/testsuite.rst index 5ab9ba73..ca01c23e 100644 --- a/docs/test/pytest/testsuite.rst +++ b/docs/test/pytest/testsuite.rst @@ -60,7 +60,7 @@ Tests mit Klassen gruppieren ---------------------------- Bislang haben wir Testfunktionen innerhalb von Testmodulen in einem -Dateisystemverzeichnis geschrieben. Diese Strukturierung des Testcodes +Dateisystem-Verzeichnis geschrieben. Diese Strukturierung des Testcodes funktioniert eigentlich ganz gut und ist für viele Projekte ausreichend. pytest erlaubt uns jedoch auch, Tests mit Klassen zu gruppieren. Nehmen wir einige der Testfunktionen, die sich auf die Gleichheit der Items beziehen, und diff --git a/docs/test/sqlite.rst b/docs/test/sqlite.rst deleted file mode 100644 index 0a8c0f8b..00000000 --- a/docs/test/sqlite.rst +++ /dev/null @@ -1,52 +0,0 @@ -Beispiel: SQLite-Datenbank testen -================================= - -#. Zum Testen, ob die Datenbank ``library.db`` mit :download:`create_db.py - <../save-data/create_db.py>` angelegt wurde, importieren wir neben - :doc:`sqlite3 ` und :doc:`unittest - ` auch noch :download:`create_db.py - <../save-data/create_db.py>` und :doc:`os `: - - .. literalinclude:: ../save-data/test_sqlite.py - :language: python - :lines: 1-5 - :lineno-start: 1 - -#. Anschließend definieren wir zunächst eine Testklasse ``TestCreateDB``: - - .. literalinclude:: ../save-data/test_sqlite.py - :language: python - :lines: 8 - :lineno-start: 8 - -#. In ihr definieren wir dann die Testmethode ``test_db_exists``, in der wir mit - ``assert`` die Annahme treffen, dass die Datei in :doc:`os.path - ` existiert: - - .. literalinclude:: ../save-data/test_sqlite.py - :language: python - :lines: 9-10 - :lineno-start: 9 - -#. Nun überprüfen wir auch noch, ob die Tabelle ``books`` angelegt wurde. - Hierfür versuchen wir, die Tabelle erneut anzulegen und erwarten mit - ``assertRaises``, dass ``sqlite`` mit einem ``OperationalError`` beendet - wird: - - .. literalinclude:: ../save-data/test_sqlite.py - :language: python - :lines: 12-14 - :lineno-start: 12 - -#. Weitere Tests wollen wir nicht an einer Datenbank im Dateisystem - durchführen sondern in einer SQLite-Datenbank im Arbeitsspeicher: - - .. literalinclude:: ../save-data/test_sqlite.py - :language: python - :lines: 17-20 - :lineno-start: 17 - -.. seealso:: - Weitere Beispiele zum Testen eurer SQLite-Datenbankfunktionen findet ihr in - der SQLite Testsuite `test_sqlite3 - `_. diff --git a/docs/test/tox.rst b/docs/test/tox.rst index fdb95a81..07eeefda 100644 --- a/docs/test/tox.rst +++ b/docs/test/tox.rst @@ -19,9 +19,9 @@ auf Python-Versionen beschränkt. Ihr könnt es zum Testen mit verschiedenen Abhängigkeits-Konfigurationen und verschiedenen Konfigurationen für verschiedene Betriebssysteme verwenden. tox verwendet dabei Projektinformationen aus der :file:`setup.py`- oder :file:`pyproject.toml`-Datei für das zu testende Paket, -um eine installierbare :doc:`Distribution eures Pakets <../libs/distribution>` -zu erstellen. Es sucht in der :file:`tox.ini`-Datei nach einer Liste von -Umgebungen, und führt dann jeweils folgende Schritte aus: +um eine installierbare :doc:`Distribution eures Pakets +<../packs/distribution>` zu erstellen. Es sucht in der :file:`tox.ini`-Datei +nach einer Liste von Umgebungen, und führt dann jeweils folgende Schritte aus: #. erstellt eine :term:`virtuelle Umgebung `, #. installiert einige Abhängigkeiten mit :term:`pip`, @@ -169,7 +169,7 @@ Python-Versionen hinzuzufügen: :emphasize-lines: 2, 4 [tox] - envlist = py39, py310, py311, py312, py313 + envlist = py3{9,10,11,12,13,13t,14,14t} isolated_build = True skip_missing_interpreters = True @@ -184,41 +184,41 @@ der folgenden Darstellung lediglich die Unterschiede hervorhebe: .. code-block:: pytest :emphasize-lines: 3-4, 8-12, 16-20, 24-28, 32- - $ python -m tox - ... - py39: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/17/items-0.1.0.tar.gz - py39: commands[0]> coverage run -m pytest - ============================= test session starts ============================== - ... - ============================== 49 passed in 0.16s ============================== - py39: OK ✔ in 2.17 seconds - py310: skipped because could not find python interpreter with spec(s): py310 - py310: SKIP ⚠ in 0.01 seconds - py311: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/18/items-0.1.0.tar.gz - py311: commands[0]> coverage run -m pytest - ============================= test session starts ============================== - ... - ============================== 49 passed in 0.15s ============================== - py311: OK ✔ in 1.41 seconds - py312: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/19/items-0.1.0.tar.gz - py312: commands[0]> coverage run -m pytest - ============================= test session starts ============================== - ... - ============================== 49 passed in 0.15s ============================== - py312: OK ✔ in 1.43 seconds - py313: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/20/items-0.1.0.tar.gz - py313: commands[0]> coverage run -m pytest - ============================= test session starts ============================== - ... - ============================== 49 passed in 0.16s ============================== - .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build - py313: OK ✔ in 1.48 seconds - py39: OK (2.17=setup[1.54]+cmd[0.63] seconds) - py310: SKIP (0.01 seconds) - py311: OK (1.41=setup[0.81]+cmd[0.60] seconds) - py312: OK (1.43=setup[0.82]+cmd[0.61] seconds) - py313: OK (1.48=setup[0.82]+cmd[0.66] seconds) - congratulations :) (10.46 seconds) + $ python -m tox + ... + py39: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/17/items-0.1.0.tar.gz + py39: commands[0]> coverage run -m pytest + ============================= test session starts ============================== + ... + ============================== 49 passed in 0.16s ============================== + py39: OK ✔ in 2.17 seconds + py310: skipped because could not find python interpreter with spec(s): py310 + py310: SKIP ⚠ in 0.01 seconds + py311: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/18/items-0.1.0.tar.gz + py311: commands[0]> coverage run -m pytest + ============================= test session starts ============================== + ... + ============================== 49 passed in 0.15s ============================== + py311: OK ✔ in 1.41 seconds + py312: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/19/items-0.1.0.tar.gz + py312: commands[0]> coverage run -m pytest + ============================= test session starts ============================== + ... + ============================== 49 passed in 0.15s ============================== + py312: OK ✔ in 1.43 seconds + py313: install_package> python -I -m pip install --force-reinstall --no-deps /Users/veit/cusy/prj/items/.tox/.tmp/package/20/items-0.1.0.tar.gz + py313: commands[0]> coverage run -m pytest + ============================= test session starts ============================== + ... + ============================== 49 passed in 0.16s ============================== + .pkg: _exit> python /Users/veit/cusy/prj/items/.venv/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build + py313: OK ✔ in 1.48 seconds + py39: OK (2.17=setup[1.54]+cmd[0.63] seconds) + py310: SKIP (0.01 seconds) + py311: OK (1.41=setup[0.81]+cmd[0.60] seconds) + py312: OK (1.43=setup[0.82]+cmd[0.61] seconds) + py313: OK (1.48=setup[0.82]+cmd[0.66] seconds) + congratulations :) (10.46 seconds) Tox-Umgebungen parallel ausführen --------------------------------- @@ -229,18 +229,18 @@ lassen: .. code-block:: pytest - $ python -m tox -p - py310: SKIP ⚠ in 0.09 seconds - py312: OK ✔ in 2.08 seconds - py313: OK ✔ in 2.18 seconds - py311: OK ✔ in 2.23 seconds - py39: OK ✔ in 2.91 seconds - py39: OK (2.91=setup[2.17]+cmd[0.74] seconds) - py310: SKIP (0.09 seconds) - py311: OK (2.23=setup[1.27]+cmd[0.96] seconds) - py312: OK (2.08=setup[1.22]+cmd[0.86] seconds) - py313: OK (2.18=setup[1.23]+cmd[0.95] seconds) - congratulations :) (3.05 seconds) + $ python -m tox -p + py310: SKIP ⚠ in 0.09 seconds + py312: OK ✔ in 2.08 seconds + py313: OK ✔ in 2.18 seconds + py311: OK ✔ in 2.23 seconds + py39: OK ✔ in 2.91 seconds + py39: OK (2.91=setup[2.17]+cmd[0.74] seconds) + py310: SKIP (0.09 seconds) + py311: OK (2.23=setup[1.27]+cmd[0.96] seconds) + py312: OK (2.08=setup[1.22]+cmd[0.86] seconds) + py313: OK (2.18=setup[1.23]+cmd[0.95] seconds) + congratulations :) (3.05 seconds) .. note:: Die Ausgabe ist nicht abgekürzt; dies ist die gesamte Ausgabe, die ihr seht, @@ -256,37 +256,37 @@ installiert wird. Das Einbinden von ``pytest-cov`` schließt auch alle seine Abhängigkeiten ein, wie :abbr:`z.B. (zum Beispiel)` Coverage. Wir erweitern dann ``commands`` zu ``pytest --cov=items``: -.. code-block:: +.. code-block:: ini :emphasize-lines: 12- - [tox] - envlist = py3{9,10,11,12,13} - isolated_build = True - skip_missing_interpreters = True + [tox] + envlist = py3{9,10,11,12,13,13t,14,14t} + isolated_build = True + skip_missing_interpreters = True - [testenv] - deps = - pytest>=6.0 - faker - commands = pytest + [testenv] + deps = + pytest>=6.0 + faker + commands = pytest - [testenv:coverage-report] - description = Report coverage over all test runs. - deps = coverage[toml] - skip_install = true - allowlist_externals = coverage - commands = - coverage combine - coverage report + [testenv:coverage-report] + description = Report coverage over all test runs. + deps = coverage[toml] + skip_install = true + allowlist_externals = coverage + commands = + coverage combine + coverage report Bei der Verwendung von Coverage mit ``tox`` kann es manchmal sinnvoll sein, in der :file:`pyproject.toml`-Datei einen Abschnitt einzurichten, der Coverage -mitteilt, welche Quelltextpfade als identisch betrachtet werden sollen: +mitteilt, welche Quelltext-Pfade als identisch betrachtet werden sollen: .. code-block:: ini - [tool.coverage.paths] - source = ["src", ".tox/py*/**/site-packages"] + [tool.coverage.paths] + source = ["src", ".tox/py*/**/site-packages"] Der Items-Quellcode befindet sich zunächst in :file:`src/items/`, bevor von tox die virtuellen Umgebungen erstellt und Items in der Umgebung installiert wird. @@ -296,25 +296,25 @@ Dann befindet es sich :abbr:`z.B. (zum Beispiel)` in .. code-block:: console :emphasize-lines: 1 - $ python -m tox - ... - coverage-report: commands[0]> coverage combine - Combined data file .coverage.fay.local.19539.XpQXpsGx - coverage-report: commands[1]> coverage report - Name Stmts Miss Branch BrPart Cover Missing - -------------------------------------------------------------- - src/items/api.py 68 1 12 1 98% 88 - -------------------------------------------------------------- - TOTAL 428 1 32 1 99% - - 26 files skipped due to complete coverage. - py39: OK (2.12=setup[1.49]+cmd[0.63] seconds) - py310: SKIP (0.01 seconds) - py311: OK (1.41=setup[0.80]+cmd[0.62] seconds) - py312: OK (1.43=setup[0.81]+cmd[0.62] seconds) - py313: OK (1.46=setup[0.83]+cmd[0.62] seconds) - coverage-report: OK (0.16=setup[0.00]+cmd[0.07,0.09] seconds) - congratulations :) (10.26 seconds) + $ python -m tox + ... + coverage-report: commands[0]> coverage combine + Combined data file .coverage.fay.local.19539.XpQXpsGx + coverage-report: commands[1]> coverage report + Name Stmts Miss Branch BrPart Cover Missing + -------------------------------------------------------------- + src/items/api.py 68 1 12 1 98% 88 + -------------------------------------------------------------- + TOTAL 428 1 32 1 99% + + 26 files skipped due to complete coverage. + py39: OK (2.12=setup[1.49]+cmd[0.63] seconds) + py310: SKIP (0.01 seconds) + py311: OK (1.41=setup[0.80]+cmd[0.62] seconds) + py312: OK (1.43=setup[0.81]+cmd[0.62] seconds) + py313: OK (1.46=setup[0.83]+cmd[0.62] seconds) + coverage-report: OK (0.16=setup[0.00]+cmd[0.07,0.09] seconds) + congratulations :) (10.26 seconds) Mindestabdeckungsgrad festlegen ------------------------------- @@ -324,16 +324,16 @@ Mindestabdeckungsgrad festzulegen, um eventuelle Ausrutscher bei der Coverage zu erkennen. Dies wird mit der Option ``--cov-fail-under`` erreicht: .. code-block:: console - :emphasize-lines: 8 + :emphasize-lines: 8 - Name Stmts Miss Branch BrPart Cover Missing - -------------------------------------------------------------- - src/items/api.py 68 1 12 1 98% 88 - -------------------------------------------------------------- - TOTAL 428 1 32 1 99% + Name Stmts Miss Branch BrPart Cover Missing + -------------------------------------------------------------- + src/items/api.py 68 1 12 1 98% 88 + -------------------------------------------------------------- + TOTAL 428 1 32 1 99% - 26 files skipped due to complete coverage. - Coverage failure: total of 99 is less than fail-under=100 + 26 files skipped due to complete coverage. + Coverage failure: total of 99 is less than fail-under=100 Dadurch wird der Ausgabe die hervorgehobene Zeile hinzugefügt. @@ -348,49 +348,49 @@ vornehmen, damit Parameter an pytest übergeben werden können: .. code-block:: ini :emphasize-lines: 17 - [tox] - envlist = - pre-commit - docs - py3{9,10,11,12,13} - coverage-report - isolated_build = True - skip_missing_interpreters = True - - [testenv] - extras = - tests: tests - deps = - tests: coverage[toml] - allowlist_externals = coverage - commands = - coverage run -m pytest {posargs} + [tox] + envlist = + pre-commit + docs + py3{9,10,11,12,13,13t,14,14t} + coverage-report + isolated_build = True + skip_missing_interpreters = True + + [testenv] + extras = + tests: tests + deps = + tests: coverage[toml] + allowlist_externals = coverage + commands = + coverage run -m pytest {posargs} Um Argumente an pytest zu übergeben, fügt sie zwischen den tox-Argumenten und den pytest-Argumenten ein. In diesem Fall wählen wir ``test_version``-Tests mit der Schlüsselwort-Option ``-k`` aus. Wir verwenden auch ``--no-cov``, um die Abdeckung zu deaktivieren: -.. code-block:: +.. code-block:: pytest :emphasize-lines: 1, 3 - $ tox -e py312 -- -k test_version --no-cov - ... - py312: commands[0]> coverage run -m pytest -k test_version --no-cov - ============================= test session starts ============================== - ... - configfile: pyproject.toml - testpaths: tests - plugins: cov-5.0.0, Faker-25.0.0 - collected 49 items / 47 deselected / 2 selected + $ tox -e py312 -- -k test_version --no-cov + ... + py312: commands[0]> coverage run -m pytest -k test_version --no-cov + ============================= test session starts ============================== + … + configfile: pyproject.toml + testpaths: tests + plugins: cov-5.0.0, Faker-25.0.0 + collected 49 items / 47 deselected / 2 selected - tests/api/test_version.py . [ 50%] - tests/cli/test_version.py . [100%] + tests/api/test_version.py . [ 50%] + tests/cli/test_version.py . [100%] - ======================= 2 passed, 47 deselected in 0.09s ======================= - .pkg: _exit> python /Users/veit/cusy/prj/items_env/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build - py312: OK (2.22=setup[1.12]+cmd[1.10] seconds) - congratulations :) (2.25 seconds) + ======================= 2 passed, 47 deselected in 0.09s ======================= + .pkg: _exit> python /Users/veit/cusy/prj/items_env/lib/python3.13/site-packages/pyproject_api/_backend.py True hatchling.build + py312: OK (2.22=setup[1.12]+cmd[1.10] seconds) + congratulations :) (2.25 seconds) ``tox`` eignet sich nicht nur hervorragend für die lokale Automatisierung von Testprozessen, sondern hilft auch bei Server-basierter :term:`CI`. Fahren wir @@ -499,7 +499,7 @@ Badge anzeigen Nun könnt ihr in eurer :file:`README.rst`-Datei noch ein Badge eures :term:`CI`-Status hinzufügen, :abbr:`z.B. (zum Beispiel)` mit: -.. code-block:: +.. code-block:: rest .. image:: https://github.com/YOU/YOUR_PROJECT/workflows/CI/badge.svg?branch=main :target: https://github.com/YOU/YOUR_PROJECT/actions?workflow=CI @@ -521,8 +521,8 @@ einer :file:`pyproject.toml`-Datei: .. code-block:: toml - [project.entry-points.tox] - my_plugin = "my_plugin.hooks" + [project.entry-points.tox] + my_plugin = "my_plugin.hooks" Um das Plugin zu verwenden, muss es daher lediglich in der gleichen Umgebung installiert werden, in der auch tox läuft, und es wird über den definierten @@ -534,14 +534,50 @@ Hooks erstellt. Der folgende Codeschnipsel würde zum Beispiel ein neues --my .. code-block:: python - from tox.config.cli.parser import ToxParser - from tox.plugin import impl + from tox.config.cli.parser import ToxParser + from tox.plugin import impl - @impl - def tox_add_option(parser: ToxParser) -> None: - parser.add_argument("--my", action="store_true", help="my option") + @impl + def tox_add_option(parser: ToxParser) -> None: + parser.add_argument("--my", action="store_true", help="my option") .. seealso:: * `Extending tox `_ * `tox development team `_ + +.. _tox_uv: + +``tox-uv`` +---------- + +`tox-uv `_ ist ein Tox-Plugin, das +:term:`virtualenv` und :term:`pip` durch :term:`uv` in euren Tox-Umgebungen +ersetzt. + +Ihr könnt ``tox`` und ``tox-uv`` installieren mit: + +.. code-block:: console + + $ uv tool install tox --with tox-uv + +``uv.lock``-Unterstützung +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Wenn ihr für eine Tox-Umgebung ``uv sync`` mit einer ``uv.lock``-Datei verwenden +wollt, müsst ihr für diese Tox-Umgebung den Runner auf ``uv-venv-lock-runner`` +ändern. Außerdem solltet ihr in solchen Umgebungen die ``extras``-Konfiguration +verwenden, um ``uv`` anzuweisen, die angegebenen Extras zu installieren, zum +Beispiel: + +.. code-block:: ini + :caption: tox.ini + + [testenv] + runner = uv-venv-lock-runner + extras = + dev + commands = pytest + +``dev`` verwendet den ``uv-venv-lock-runner`` und nutzt ``uv sync``, um +Abhängigkeiten in der Umgebung mit den ``dev``-Extras zu installieren. diff --git a/docs/test/unittest.rst b/docs/test/unittest.rst index 9100a51a..0b102f3e 100644 --- a/docs/test/unittest.rst +++ b/docs/test/unittest.rst @@ -36,7 +36,7 @@ Beispiel Angenommen, ihr habt im Modul :download:`test_arithmetic.py` die folgende Methode zum Hinzufügen implementiert: -.. literalinclude:: arithmetic.py +.. literalinclude:: /document/arithmetic.py :language: python :lines: 1-6 :lineno-start: 1 @@ -125,3 +125,56 @@ Methode zum Hinzufügen implementiert: .. seealso:: * :doc:`python3:library/unittest` + +Beispiel: SQLite-Datenbank testen +--------------------------------- + +#. Zum Testen, ob die Datenbank ``library.db`` mit :download:`create_db.py + <../save-data/sqlite/create_db.py>` angelegt wurde, importieren wir neben + :doc:`sqlite3 ` und :doc:`unittest + ` auch noch :download:`create_db.py + <../save-data/sqlite/create_db.py>` und :doc:`os `: + + .. literalinclude:: ../save-data/sqlite/test_sqlite.py + :language: python + :lines: 1-5 + :lineno-start: 1 + +#. Anschließend definieren wir zunächst eine Testklasse ``TestCreateDB``: + + .. literalinclude:: ../save-data/sqlite/test_sqlite.py + :language: python + :lines: 8 + :lineno-start: 8 + +#. In ihr definieren wir dann die Testmethode ``test_db_exists``, in der wir mit + ``assert`` die Annahme treffen, dass die Datei in :doc:`os.path + ` existiert: + + .. literalinclude:: ../save-data/sqlite/test_sqlite.py + :language: python + :lines: 9-10 + :lineno-start: 9 + +#. Nun überprüfen wir auch noch, ob die Tabelle ``books`` angelegt wurde. + Hierfür versuchen wir, die Tabelle erneut anzulegen und erwarten mit + ``assertRaises``, dass ``sqlite`` mit einem ``OperationalError`` beendet + wird: + + .. literalinclude:: ../save-data/sqlite/test_sqlite.py + :language: python + :lines: 12-14 + :lineno-start: 12 + +#. Weitere Tests wollen wir nicht an einer Datenbank im Dateisystem + durchführen sondern in einer SQLite-Datenbank im Arbeitsspeicher: + + .. literalinclude:: ../save-data/sqlite/test_sqlite.py + :language: python + :lines: 17-20 + :lineno-start: 17 + +.. seealso:: + Weitere Beispiele zum Testen eurer SQLite-Datenbankfunktionen findet ihr in + der SQLite Testsuite `test_sqlite3 + `_. diff --git a/docs/test/unittest2.rst b/docs/test/unittest2.rst deleted file mode 100644 index 4776e531..00000000 --- a/docs/test/unittest2.rst +++ /dev/null @@ -1,40 +0,0 @@ -``unittest2`` -============= - -`unittest2 `_ ist ein Backport von -:mod:`unittest`, mit verbesserter API und besseren *Assertions* als in früheren -Python-Versionen. - -Beispiel --------- - -Möglicherweise wollt ihr das Modul unter dem Namen ``unittest`` importieren um -die Portierung von Code auf neuere Versionen des Moduls in Zukunft zu -vereinfachen: - -.. code-block:: python - - import unittest2 as unittest - - - class MyTest(unittest.TestCase): - pass - -Auf diese Weise könnt ihr, wenn ihr zu einer neueren Python-Version wechselt und -das Modul ``unittest2`` nicht mehr benötigt, einfach den Import in eurem -Testmodul ändern, ohne dass ihr weiteren Code ändern müsst. - -Installation ------------- - - .. tab:: Linux/macOS - - .. code-block:: console - - $ python -m pip install unittest2 - - .. tab:: Windows - - .. code-block:: ps1con - - C:> python -m pip install unittest2 diff --git a/docs/tiobe-index.svg b/docs/tiobe-index.svg old mode 100755 new mode 100644 index df89b391..92b43145 --- a/docs/tiobe-index.svg +++ b/docs/tiobe-index.svg @@ -1 +1 @@ -Created with Highcharts 11.4.3Ratings (%)PythonC++CJavaC#JavaScriptGoSQLVisual BasicFortran200220042006200820102012201420162018202020222024051015202530Saturday, 30 Jun 2001 C: 20.24% +Created with Highcharts 12.2.0Ratings (%)PythonC++CJavaC#JavaScriptGoVisual BasicDelphi/Object PascalSQL2010202020022004200620082012201420162018202220240102030Friday, May 30, 2003 Delphi/Object Pascal: 1.47% diff --git a/docs/types/dicts.rst b/docs/types/dicts.rst index 49dee5cd..4e745890 100644 --- a/docs/types/dicts.rst +++ b/docs/types/dicts.rst @@ -1,47 +1,95 @@ Dictionaries ============ -Pythons eingebauter Dictionary-Datentyp bietet assoziative Array-Funktionalität, -die mit Hilfe von Hash-Tabellen implementiert wird. Die eingebaute Funktion -``len`` gibt die Anzahl der Schlüssel-Wert-Paare in einem Wörterbuch zurück. Die -``del``-Anweisung kann zum Löschen eines Schlüssel-Wert-Paares verwendet werden. -Wie bei :doc:`lists` sind mehrere Dictionary-Methoden (:py:meth:`clear -`, :py:meth:`copy `, :py:meth:`get `, -:py:meth:`items `, :py:meth:`keys `, :py:meth:`update -` und :py:meth:`values `) verfügbar. - -.. code-block:: pycon - - >>> x = {1: "eins", 2: "zwei"} - >>> x[3] = "drei" - >>> x["viertes"] = "vier" - >>> list(x.keys()) - [1, 2, 3, 'viertes'] - >>> x[1] - 'eins' - >>> x.get(1, "nicht vorhanden") - 'eins' - >>> x.get(5, "nicht vorhanden") - 'nicht vorhanden' - -Schlüssel müssen vom unveränderlichen Typ sein, einschließlich :doc:`numbers`, -:doc:`strings` und Tupel. +Dictionaries bestehen aus Schlüssel-Wert-Paaren. Schlüssel müssen vom +unveränderlichen Typ sein, einschließlich :doc:`numbers/index`, +:doc:`strings/index` und :doc:`sequences-sets/tuples`. .. warning:: Auch wenn ihr in einem Dictionary verschiedene Schlüsseltypen verwenden könnt, solltet ihr das vermeiden, da dadurch nicht nur die Lesbarkeit sondern auch die Sortierung erschwert wird. -Werte können alle Arten von Objekten sein, -einschließlich veränderlicher Typen wie :doc:`lists` und :doc:`dicts`. Wenn ihr -versucht, auf den Wert eines Schlüssels zuzugreifen, der nicht im Dictionary -enthalten ist, wird eine ``KeyError``-Exception ausgelöst. Um diesen Fehler zu -vermeiden, gibt die Dictionary-Methode ``get`` optional einen -benutzerdefinierten Wert zurück, wenn ein Schlüssel nicht in einem Wörterbuch -enthalten ist. +Werte können alle Arten von Objekten sein, einschließlich veränderlicher Typen +wie :doc:`sequences-sets/lists` und :doc:`dicts`. + +.. code-block:: pycon + + >>> dict = { + ... "2022-01-31": -0.751442, + ... "2022-02-01": 0.816935, + ... "2022-02-02": -0.272546, + ... } + >>> dict["2022-02-03"] = -0.268295 + +Wenn ihr versucht, auf den Wert eines Schlüssels zuzugreifen, der nicht im +Dictionary enthalten ist, wird eine ``KeyError``-:doc:`/control-flow/exceptions` +ausgelöst. Um diesen Fehler zu vermeiden, gibt die Dictionary-Methode ``get`` +optional einen benutzerdefinierten Wert zurück, wenn ein Schlüssel nicht in +einem Wörterbuch enthalten ist. + +.. code-block:: pycon + + >>> dict["2022-02-03"] + -0.268295 + >>> dict["2022-02-04"] + Traceback (most recent call last): + File "", line 1, in + dict["2022-02-04"] + ~~~~^^^^^^^^^^^^^^ + KeyError: '2022-02-04' + >>> dict.get("2022-02-03", "Messwert nicht vorhanden") + -0.268295 + >>> dict.get("2022-02-04", "Messwert nicht vorhanden") + 'Messwert nicht vorhanden' + +Weitere Dict-Methoden +--------------------- + +Die in Dicts eingebaute Funktion ``len`` gibt die Anzahl der +Schlüssel-Wert-Paare zurück. Die ``del``-Anweisung kann zum Löschen eines +Schlüssel-Wert-Paares verwendet werden. Wie bei :doc:`sequences-sets/lists` sind +mehrere Dictionary-Methoden (:py:meth:`clear `, :py:meth:`copy +`, :py:meth:`get `, :py:meth:`items `, +:py:meth:`keys `, :py:meth:`update ` und +:py:meth:`values `) verfügbar. + +Die Methoden :py:meth:`keys `, :py:meth:`values ` und +:py:meth:`items ` geben keine Listen zurück, sondern +Dictionary-View-Objekte, die sich wie Sequenzen verhalten, aber dynamisch +aktualisiert werden, wenn sich das Dictionary ändert. Aus diesem Grund müsst ihr +die Funktion ``list`` verwenden, damit sie in diesen Beispielen zu einer Liste +werden: + +.. code-block:: pycon + + >>> list(dict.keys()) + ['2022-01-31', '2022-02-01', '2022-02-02', '2022-02-03'] + +Ab Python 3.6 behalten Dictionaries die Reihenfolge bei, in der die Schlüssel +erstellt wurden, und sie werden mit :py:meth:`keys ` auch in dieser +Reihenfolge zurückgegeben. + +Dicts zusammenführen +~~~~~~~~~~~~~~~~~~~~ + +Mit der :py:meth:`dict.update`-Methode könnt ihr zwei Dictionaries zu einem +einzigen Dictionary zusammenfügen: + +.. code-block:: pycon + + >>> titles = {7.0: "Data Types", 7.1: "Lists", 7.2: "Tuples"} + >>> new_titles = {7.0: "Data types", 7.3: "Sets"} + >>> titles.update(new_titles) + >>> titles + {7.0: 'Data types', 7.1: 'Lists', 7.2: 'Tuples', 7.3: 'Sets'} + +.. note:: + Die Reihenfolge der Operanden ist wichtig, da ``7.0`` dupliziert wird und der + Wert des letzten Schlüssel den vorhergehenden überschreibt. ``setdefault`` --------------- +~~~~~~~~~~~~~~ :py:meth:`setdefault ` kann verwendet werden, um Zähler für die Schlüssel eines Dicts bereitzustellen, :abbr:`z.B. (zum Beispiel)`: @@ -67,24 +115,6 @@ die Schlüssel eines Dicts bereitzustellen, :abbr:`z.B. (zum Beispiel)`: >>> collections.Counter(titles) Counter({'Lists': 2, 'Data types': 1, 'Sets': 1}) -Dictionaries zusammenführen ---------------------------- - -Ihr könnt zwei Dictionaries zu einem einzigen Dictionary zusammenfügen mit der -:py:meth:`dict.update`-Methode: - -.. code-block:: pycon - - >>> titles = {7.0: "Data Types", 7.1: "Lists", 7.2: "Tuples"} - >>> new_titles = {7.0: "Data types", 7.3: "Sets"} - >>> titles.update(new_titles) - >>> titles - {7.0: 'Data types', 7.1: 'Lists', 7.2: 'Tuples', 7.3: 'Sets'} - -.. note:: - Die Reihenfolge der Operanden ist wichtig, da ``7.0`` dupliziert wird und der - Wert des letzten Schlüssel den vorhergehenden überschreibt. - Erweiterungen ------------- @@ -112,6 +142,9 @@ Checks ``("Veit", "Tim", "Monique")`` * Ihr könnt ein :doc:`Dictionary ` verwenden, und das wie ein - Sheet einer Tabellenkalkulation verwenden, indem ihr :doc:`/types/tuples` als - Schlüssel Zeilen- und Spaltenwerte verwendet. Schreibt Beispielcode, um Werte - hinzuzufügen und wieder abzufragen. + Sheet einer Tabellenkalkulation verwenden, indem ihr + :doc:`/types/sequences-sets/tuples` als Schlüssel Zeilen- und Spaltenwerte + verwendet. Schreibt Beispielcode, um Werte hinzuzufügen und wieder abzufragen. + +* Wie könnt ihr alle Dubletten aus einer Liste entfernen ohne die Reihenfolge + der Elemente in der Liste zu ändern? diff --git a/docs/types/files.rst b/docs/types/files.rst deleted file mode 100644 index 2b7b31fb..00000000 --- a/docs/types/files.rst +++ /dev/null @@ -1,402 +0,0 @@ -Dateien -======= - -Öffnen von Dateien ------------------- - -In Python öffnet und lest ihr eine Datei, indem ihr die eingebaute Funktion -:func:`python3:open` und verschiedene eingebaute Leseoperationen verwendet. Das -folgende kurze Python-Programm liest eine Zeile aus einer Textdatei namens -:samp:`{myfile.txt}` ein: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r") - >>> line = f.readline() - -:func:`python3:open` liest nichts aus der Datei, sondern gibt ein :abbr:`sog. -(sogenanntes)` Datei-Objekt zurück, mit dem ihr auf die geöffnete Datei -zugreifen könnt. Es behält den Überblick über eine Datei und darüber, wie viel -von der Datei gelesen oder geschrieben wurde. Alle Dateieingaben in Python -werden mit Dateiobjekten und nicht mit Dateinamen durchgeführt. - -Der erste Aufruf von :mod:`python3:readline` gibt die erste Zeile des -Datei-Objekts zurück, also alles bis einschließlich des ersten Zeilenumbruchs -oder die gesamte Datei, wenn es keinen Zeilenumbruch in der Datei gibt; der -nächste Aufruf von ``readline`` gibt die zweite Zeile zurück, wenn sie -existiert, :abbr:`usw (und so weiter)`. - -Das erste Argument der Funktion ``open`` ist ein Pfadname. Im vorigen Beispiel -öffnet ihr eine Datei, von der ihr annehmt, dass sie sich im aktuellen -Arbeitsverzeichnis befindet. Das folgende Beispiel öffnet eine Datei an einem -absoluten Speicherort – :samp:`{C:\Meine Dokumente\\myfile.txt}`: - -.. code-block:: pycon - - >>> import os - >>> pathname = os.path.join("C:/", "Users", "Veit", "Documents", "myfile.txt") - >>> with open(pathname, "r") as f: - ... line = f.readline() - ... - -.. note:: - - In diesem Beispiel wird das Schlüsselwort ``with`` verwendet, :abbr:`d.h. - (das heißt)`, dass die Datei mit einem Kontextmanager geöffnet wird, der - in :doc:`/control-flows/with` näher erläutert wird. Diese Art des Öffnens - von Dateien verwaltet mögliche I/O-Fehler besser und sollte im Allgemeinen - bevorzugt werden. - -Schließen von Dateien ---------------------- - -Nachdem alle Daten aus einem Datei-Objekt gelesen oder in dieses geschrieben -wurden, sollte das Datei-Pbjekt wieder geschlossen werden damit Systemressourcen -freigegeben werden, das Lesen oder Schreiben der zugrunde liegenden Datei durch -anderen Code ermöglicht wird und das Programm insgesamt zuverlässiger wird. Bei -kleinen Skripten hat dies in der Regel keine großen Auswirkungen, da -Dateiobjekte werden automatisch geschlossen, wenn das Skript oder Programm -beendet wird. Bei größeren Programmen können zu viele offene Datei-Objekte -jedoch die Systemressourcen erschöpfen, was zum Abbruch des Programms führen. -Ihr schließt ein Dateiobjekt mit der ``close``-Methode, wenn das Datei-Objekt -nicht mehr benötigt wird: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r") - >>> line = f.readline() - >>> f.close() - -Die Verwendung eines :doc:`/control-flows/with` bleibt meist jedoch die bessere -Möglichkeit, um Dateien automatisch zu schließen, wenn ihr fertig seid: - -.. code-block:: pycon - - >>> with open("docs/types/myfile.txt", "r") as f: - ... line = f.readline() - ... - -Öffnen von Dateien im Schreib- oder anderen Modi ------------------------------------------------- - -Das zweite Argument des Befehls :func:`python3:open` ist eine Zeichenkette, die -angibt, wie die Datei geöffnet werden soll. ``"r"`` öffnet die Datei zum Lesen -(engl. *read*), ``"w"`` öffnet die Datei zum Schreiben (engl. *write*) und -``"a"`` offnet die Datei zum Anhängen (engl. *attach*). Wenn ihr die Datei zum -Lesen öffnen wollen, könnt ihr das zweite Argument weglassen, da ``"r"`` der -Standardwert ist. Das folgende kurze Programm schreibt :samp:`Hi, Pythonistas!` -in eine Datei: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "w") - >>> f.write("Hi, Pythonistas!\n") - 17 - >>> f.close() - -Je nach Betriebssystem kann :func:`python3:open` auch Zugang zu weiteren -Dateimodi haben. Diese Modi sind jedoch für die meisten Zwecke nicht notwendig. - -``open`` kann ein optionales drittes Argument annehmen, das definiert, wie -Lese- oder Schreibvorgänge für diese Datei gepuffert werden. Beim Puffern werden -Daten so lange im Speicher gehalten, bis genügend Daten angefordert oder -geschrieben wurden, um die Zeitaufwände für einen Plattenzugriff zu -rechtfertigen. Andere Parameter für ``open`` steuern die Kodierung für -Textdateien und die Behandlung von Zeilenumbrüchen in Textdateien. Auch hier -gilt, dass ihr euch in der Regel keine Gedanken über diese Funktionen machen -müsst, aber wenn ihr mit Python fortgeschrittener werdet, solltet ihr euch -vielleicht darüber informieren. - -Lese- und Schreib-Funktionen ----------------------------- - -Die häufigste Funktion zum Lesen von Textdateien, :mod:`python3:readline`, habe -ich bereits vorgestellt. Diese Funktion liest eine einzelne Zeile aus einem -Datei-Objekt und gibt sie zurück, einschließlich aller Zeilenumbrüche am Ende -der Zeile. Wenn es nichts mehr zu lesen gibt, gibt readline einen leeren String -zurück, was es einfach macht, :abbr:`z.B. (zum Beispiel)` die Anzahl der Zeilen -in einer Datei zu ermitteln: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r") - >>> lc = 0 - >>> while f.readline() != "": - ... lc = lc + 1 - ... - >>> print(lc) - 2 - >>> f.close() - -Ein kürzerer Weg, alle Zeilen zu zählen, gibt es mit der ebenfalls eingebauten -``readlines``-Methode, die alle Zeilen einer Datei liest und sie als Liste von -Strings mit einen String pro Zeile zurückgibt: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r") - >>> print(len(f.readlines())) - 1 - >>> f.close() - -Wenn ihr alle Zeilen einer großen Datei zählt, kann diese Methode dazu führen, -dass der Speicher vollläuft, weil die gesamte Datei auf einmal geliesen wird. Es -ist auch möglich, dass der Speicher mit :mod:`python3:readline` überläuft, wenn -ihr versucht, eine Zeile aus einer großen Datei zu lesen, die keine -Zeilenumbruchzeichen enthältist. Um mit solchen Situationen besser umgehen zu -können, haben beide Methoden ein optionales Argument, das die Menge der zu einem -Zeitpunkt gelesenen Daten beeinflusst. Eine andere Möglichkeit, über alle Zeilen -einer Datei zu iterieren, besteht darin, das Dateiobjekt als Iterator in einer -:ref:`for-loop` zu behandeln: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r") - >>> lc = 0 - >>> for l in f: - ... lc = lc + 1 - ... - >>> print(lc) - 1 - >>> f.close() - -Diese Methode hat den Vorteil, dass die Zeilen je nach Bedarf in den Speicher -eingelesen werden, so dass selbst bei großen Dateien kein Speicherplatzmangel zu -befürchten ist. Der andere Vorteil dieser Methode ist, dass sie einfacher und -lesbarer ist. - -Ein mögliches Problem mit der Lesemethode kann jedoch entstehen, wenn auf -Windows- und macOS Übersetzungen im Textmodus erfolgen, wenn ihr den Befehl -:func:`open` im Textmodus verwenden, :abbr:`d.h. (das heißt)` ohne ein ``b`` -anzuhängen. Im Textmodus wird auf macOS jedes ``\r`` in ``\n`` umgewandelt, -während unter Windows ``\r\n``-Paare in ``\n`` umgewandelt werden. Ihr könnt die -Behandlung von Zeilenumbrüchen festlegen, indem ihr beim Öffnen der Datei den -Parameter ``newline`` verwendet und ``newline="\n"``, ``\r`` oder ``\r\n`` -angebt, wodurch nur diese Zeichenfolge als Zeilenumbruch verwendet wird: - -.. code-block:: pycon - - >>> f = open("docs/types/myfile.txt", "r", newline="\r\n") - -In diesem Beispiel wird nur ``\n`` als Zeilenumbruch gewertet. Wenn die Datei -jedoch im Binärmodus geöffnet wurde, ist der Parameter ``newline`` nicht -erforderlich, da alle Bytes genau so zurückgegeben werden, wie sie in der Datei -stehen. - -Die Schreibmethoden, die den Methoden ``readline`` und ``readlines`` -entsprechen, sind ``write`` und ``writelines``. Beachtet, dass es keine -``writeline``-Funktion gibt. ``write`` schreibt eine einzelne Zeichenkette, die -sich über mehrere Zeilen erstrecken kann, wenn Zeilenumbruchzeichen in die -Zeichenkette eingebettet sind, wie im folgenden Beispiel: - -.. code-block:: python - - f.write("Hi, Pythinistas!\n\n") - -Die Methode ``writelines`` ist jedoch verwirrend, weil sie nicht unbedingt -mehrere Zeilen schreibt; sie nimmt eine Liste von Zeichenketten als Argument und -schreibt sie nacheinander in das angegebene Datei-Objekt, ohne Zeilenumbrüche -zwischen den Listenelementen einzufügen; nur wenn die Zeichenketten in der Liste -Zeilenumbrüchen enthalten, kommen Zeilenumbrüche im Datei-Objekt hinzu; -andernfalls werden sie aneinandergereiht. ``writelines`` ist damit die genaue -Umkehrung von ``readlines``, da sie auf die von ``readlines`` zurückgegebene -Liste angewendet werden kann, um eine Datei zu schreiben, die identisch mit der Ausgangsdatei ist. Unter der Annahme, dass myfile.txt existiert und eine -Textdatei ist, erzeugt das folgende Beispiel eine exakte Kopie von -:file:`myfile.txt` mit dem Namen :file:`myfile2.txt`: - -.. code-block:: pycon - - >>> input_file = open("myfile.txt", "r") - >>> lines = input_file.readlines() - >>> input_file.close() - >>> output_file = open("myfile2.txt", "w") - >>> output_file.writelines(lines) - >>> output_file.close() - -Verwendung des Binärmodus -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Wenn ihr alle Daten in einer Datei in ein einziges Byte-Objekt (partiell) -einlesen und in den Speicher übertragen möchtet um sie als Byte-Sequenz -behandeln zu können, könnt ihr die ``read``-Methode verwenden. Ohne ein Argument -liest sie die gesamte Datei ab der aktuellen Position ein und gibt die Daten als -Bytes-Objekt zurück. Mit einem ganzzahligen Argument liest sie maximal diese -Anzahl von Bytes und gibt ein Bytes-Objekt der angegebenen Größe zurück: - -.. code-block:: pycon - :linenos: - - >>> f = open("myfile.txt", "rb") - >>> head = f.read(16) - >>> print(head) - b'Hi, Pythonistas!' - >>> body = f.read() - >>> print(body) - b'\n\n' - >>> f.close() - -Zeile 1 - öffnet eine Datei zum Lesen im Binärmodus -Zeile 2 - liest die ersten 16 Bytes als ``head``-String -Zeile 3 - gibt den ``head``-String aus -Zeile 5 - liest den Rest der Datei - -.. note:: - - Dateien, die im Binärmodus geöffnet werden, arbeiten nur mit Bytes und nicht - mit Zeichenketten. Um die Daten als Zeichenketten zu verwenden, müsst ihr - alle Byte-Objekte in String-Objekte dekodieren. Dieser Punkt ist oft wichtig - im Umgang mit Netzwerkprotokollen, wo sich Datenströme oft wie Dateien - verhalten, aber als Bytes und nicht als Strings interpretiert werden müssen. - -Eingebaute Module für Dateien ------------------------------ - -Die Python-Standardbibliothek enthält eine Reihe eingebauter Module, mit denen -ihr Dateien managen könnt: - -.. _file-modules: - -+-----------------------------------+-------------------------------------------------------------------------------+ -| Modul | Beschreibung | -+===================================+===============================================================================+ -| :py:mod:`os.path` | führt allgemeine Pfadnamenmanipulationen durch | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`pathlib` | manipuliert Pfadnamen | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`fileinput` | iteriert über mehrere Eingabedateien | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`filecmp` | vergleicht Dateien und Verzeichnisse | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`tempfile` | erzeugt temporäre Dateien und Verzeichnisse | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`glob`, | verwenden UNIX-ähnlicher Pfad- und Dateinamensmuster | -| :py:mod:`fnmatch` | | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`linecache` | greift zufällig auf Textzeilen zu | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`shutil` | führt Dateioperationen auf höherer Ebene aus | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`mimetypes` | Zuordnung von Dateinamen zu MIME-Typen | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`pickle`, | aktivieren von Python-Objektserialisierung und -persistenz, :abbr:`s.a. (siehe| -| :py:mod:`shelve` | auch)` :doc:`../save-data/pickle` | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`csv` | liest und schreibt CSV-Dateien | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`json` | JSON-Kodierer und -Dekodierer | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`sqlite3` | bietet eine DB-API 2.0-Schnittstelle für SQLite-Datenbanken, :abbr:`s.a. | -| | (siehe auch)` :doc:`../save-data/sqlite` | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`xml`, | liest und schreibt XML-Dateien, :abbr:`s.a. (siehe auch)` | -| :py:mod:`xml.parsers.expat`, | :doc:`../save-data/xml` | -| :py:mod:`xml.dom`, | | -| :py:mod:`xml.sax`, | | -| :py:mod:`xml.etree.ElementTree` | | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`html.parser`, | Parsen von HTML und XHTML | -| :py:mod:`html.entities` | | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`configparser` | liest und schreibt Windows-ähnliche Konfigurationsdateien (``.ini``) | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`base64`, | Kodierung/Dekodierung von Dateien oder Streams | -| :py:mod:`binhex`, | | -| :py:mod:`binascii`, | | -| :py:mod:`quopri`, | | -| :py:mod:`uu` | | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`struct` | konvertiert zwischen Python-Werten und C-Strukturen, die als | -| | als Python-Bytes-Objekte dargestellt werden. | -+-----------------------------------+-------------------------------------------------------------------------------+ -| :py:mod:`zlib`, | für das Arbeiten mit Archivdateien und Komprimierungen | -| :py:mod:`gzip`, | | -| :py:mod:`bz2`, | | -| :py:mod:`zipfile`, | | -| :py:mod:`tarfile` | | -+-----------------------------------+-------------------------------------------------------------------------------+ - -.. seealso:: - * :doc:`Python4DataScience:data-processing/pandas-io` - * Beispiele für die Serialisierungsformate :doc:`CSV - `, - :doc:`JSON - `, - :doc:`Excel - `, - :doc:`XML/HTML - `, - :doc:`YAML - `, - :doc:`TOML - ` - und :doc:`Pickle - `. - -Checks ------- - -* Verwendet die Funktionen des :mod:`python3:os`-Moduls, um einen Pfad zu einer - Datei namens :file:`example.log` zu nehmen und einen neuen Dateipfad im selben - Verzeichnis für eine Datei namens :file:`example.log1` zu erstellen. - -* Welche Bedeutung hat das Hinzufügen von ``b`` als Parameter von - :func:`python3:open`? - -* Öffnet eine Datei :file:`my_file.txt` und fügt zusätzlichen Text am Ende der - Datei ein. Welchen Befehl würdet ihr verwenden, um :file:`my_file.txt` zu - öffnen? Welchen Befehl würdet ihr verwenden, um die Datei erneut zu öffnen und - von Anfang an zu lesen? - -* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das - :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten - nützlich wäre? - -* Warum könnte :doc:`pickle ` für die folgenden - Anwendungsfälle geeignet sein oder auch nicht: - - #. Speichern einiger Zustandsvariablen von einem Durchlauf zum nächsten - #. Aufbewahren von Auswertungsergebnissen - #. Speichern von Benutzernamen und Passwörtern - #. Speichern eines großen Wörterbuchs mit englischen Begriffen - -* Wenn ihr euch die `Manpage für das wc-Dienstprogramm - `_ anseht, seht ihr zwei - Befehlszeilenoptionen: - - ``-c`` - zählt die Bytes in der Datei - ``-m`` - zählt die Zeichen, die im Falle einiger Unicode-Zeichen zwei oder mehr - Bytes lang sein können - - Außerdem sollte unser Modul, wenn eine Datei angegeben wird, aus dieser Datei - lesen und sie verarbeiten, aber wenn keine Datei angegeben wird, sollte es aus - ``stdin`` lesen und verarbeiten. - -* Schreibt eure Version des :mod:`wc`-Dienstprogramms so um, dass es sowohl die - Unterscheidung zwischen Bytes und Zeichen als auch die Möglichkeit, aus - Dateien und von der Standardeingabe zu lesen, implementiert. - -* Wenn ein Kontext-Manager in einem Skript verwendet wird, das mehrere Dateien - liest und/oder schreibt, welche der folgenden Ansätze wäre eurer Meinung nach - am besten? - - #. Legt das gesamte Skript in einen Block, der von einer ``with``-Anweisung - verwaltet wird. - #. Verwendet eine ``with``-Anweisung für alle Lesevorgänge und eine weitere - für alle Schreibvorgänge. - #. Verwendet jedes Mal eine ``with``-Anweisung, wenn ihr eine Datei lest oder - schreibt, :abbr:`d.h. (das heißt)` für jede Zeile. - #. Verwendet für jede Datei, die ihr lest oder schreibt, eine - ``with``-Anweisung. - -* Archiviert :file:`*.txt`-Dateien aus dem aktuellen Verzeichnis im Verzeichnis - :file:`archive` als :file:`*.zip`-Dateien mit dem aktuellen Datum als - Dateiname. - - * Welche Module benötigt ihr hierfür? - * Schreibt eine mögliche Lösung. diff --git a/docs/types/index.rst b/docs/types/index.rst index 1dc05d1e..e7e83b87 100644 --- a/docs/types/index.rst +++ b/docs/types/index.rst @@ -1,12 +1,31 @@ Datentypen ========== -Python verfügt über mehrere eingebaute Datentypen, wie :abbr:`z.B. (zum -Beispiel)` :doc:`numbers` (Ganzzahlen, Gleitkommazahlen, komplexe Zahlen), -:doc:`strings`, :doc:`lists`, :doc:`tuples`, :doc:`dicts`, :doc:`sets` und -:doc:`files`. Diese Datentypen können mit Hilfe von Sprachoperatoren, -eingebauten Funktionen, Bibliotheksfunktionen oder den eigenen Methoden eines -Datentyps manipuliert werden. +Python hat mehrere eingebaute Datentypen, von Skalaren wie Zahlen und boolschen +Werten bis hin zu komplexeren Strukturen wie Sequenzen, Sets, Dictionaries und +Strings. + ++-----------------------+-----------------------------------------------+ +| Datentyp | Beispiele | ++=======================+===============================================+ +| Numerische Typen | :class:`int`, :class:`float`, :class:`complex`| ++-----------------------+-----------------------------------------------+ +| Boolescher Typ | :class:`bool` | ++-----------------------+-----------------------------------------------+ +| Sequenzen | :class:`list`, :class:`tuple` | ++-----------------------+-----------------------------------------------+ +| Sets | :class:`set`, :class:`frozenset` | ++-----------------------+-----------------------------------------------+ +| Mappings | :class:`dict` | ++-----------------------+-----------------------------------------------+ +| Strings | :class:`str` | ++-----------------------+-----------------------------------------------+ +| Dateien | :func:`open ` | ++-----------------------+-----------------------------------------------+ + +Diese Datentypen können mit Hilfe von Sprachoperatoren, eingebauten Funktionen, +Bibliotheksfunktionen oder den eigenen Methoden eines Datentyps manipuliert +werden. Ihr könnt auch eure eigenen Klassen definieren und eigene Klasseninstanzen erstellen. Für diese Klasseninstanzen könnt ihr Methoden definieren sowie mit @@ -19,18 +38,15 @@ speziellen Methodenattribute definiert habt, bearbeitet werden. viele andere Sprachen als Klasseninstanzen bezeichnen würden. Das liegt daran, dass alle Python-Objekte Instanzen der einen oder anderen Klasse sind. -Python hat mehrere eingebaute Datentypen, von Skalaren wie Zahlen und boolschen -Werten bis hin zu komplexeren Strukturen wie Listen, Dictionaries und Dateien. +.. seealso:: + * :doc:`python3:library/stdtypes` .. toctree:: :titlesonly: :hidden: - numbers - lists - tuples - sets + numbers/index + sequences-sets/index dicts - strings - files + strings/index none diff --git a/docs/types/none.rst b/docs/types/none.rst index 09121a63..1e2e968b 100644 --- a/docs/types/none.rst +++ b/docs/types/none.rst @@ -1,13 +1,14 @@ None ==== -Zusätzlich zu den Standardtypen wie :doc:`strings` und :doc:`numbers` verfügt -Python über einen speziellen Datentyp, der ein einziges spezielles Datenobjekt -namens ``None`` definiert. Wie der Name schon sagt, wird ``None`` verwendet, um -einen leeren Wert darzustellen. Er taucht in verschiedenen Formen in Python auf. +Zusätzlich zu den Standardtypen wie :doc:`strings/index` und +:doc:`numbers/index` verfügt Python über einen speziellen Datentyp, der ein +einziges spezielles Datenobjekt namens ``None`` definiert. Wie der Name schon +sagt, wird ``None`` verwendet, um einen leeren Wert darzustellen. Er taucht in +verschiedenen Formen in Python auf. ``None`` ist in der alltäglichen Python-Programmierung oft als Platzhalter -nützlich, um eine Datenstruktur zu kennzeichnen, an der irgendwann sinnvolle +nützlich, um eine Datenstruktur zu kennzeichnen, in der irgendwann sinnvolle Daten gefunden werden können, auch wenn diese Daten noch nicht berechnet wurden. Das Vorhandensein von ``None`` lässt sich leicht überprüfen, da es in Python @@ -20,10 +21,10 @@ dasselbe Objekt), und ``None`` ist nur mit sich selbst identisch: >>> MyType() is None True -:class:`None` ist *falsy* -------------------------- +:class:`None` ist ``False`` +--------------------------- -In Python verlassen wir uns oft darauf, dass :class:`None` *falsy* ist: +In Python verlassen wir uns oft darauf, dass :class:`None` ``False`` ist: .. code-block:: pycon @@ -31,7 +32,7 @@ In Python verlassen wir uns oft darauf, dass :class:`None` *falsy* ist: False So können wir :abbr:`z.B. (zum Beispiel)` in einer :doc:`if-Anweisung -<../control-flows/if-elif-else>` überprüfen, ob :doc:`../types/strings` leer +<../control-flow/conditional>` überprüfen, ob :doc:`../types/strings/index` leer sind: .. code-block:: pycon @@ -52,8 +53,8 @@ sind: >>> print(third_title) None -Der Standardrückgabewert einer Funktion ist :class:`None` ---------------------------------------------------------- +Der Standard-Rückgabewert einer Funktion ist :class:`None` +---------------------------------------------------------- Eine Prozedur in Python ist beispielsweise nur eine Funktion, die nicht explizit einen Wert zurückgibt, was bedeutet, dass sie standardmäßig ``None`` zurückgibt: diff --git a/docs/types/numbers/bool.rst b/docs/types/numbers/bool.rst new file mode 100644 index 00000000..783c1152 --- /dev/null +++ b/docs/types/numbers/bool.rst @@ -0,0 +1,33 @@ +Boolesche Werte +=============== + +In den folgenden Beispielen werden Boolesche Werte verwendet: + +.. code-block:: pycon + + >>> x = False + >>> x + False + >>> not x + True + +.. code-block:: pycon + + >>> y = True * 2 + >>> y + 2 + +Abgesehen von ihrer Darstellung als ``True`` und ``False`` verhalten sich +Boolesche Werte wie die Zahlen ``1`` (``True``) und ``0`` (``False``). + +Checks +------ + +* Entscheidet, ob die folgenden Aussagen wahr oder falsch sind: + + * ``1`` + * ``0`` + * ``-1`` + * ``[0]`` + * ``1 and 0`` + * ``1 > 0 or []`` diff --git a/docs/types/numbers/complex.rst b/docs/types/numbers/complex.rst new file mode 100644 index 00000000..a2bfd3fc --- /dev/null +++ b/docs/types/numbers/complex.rst @@ -0,0 +1,89 @@ +Komplexe Zahlen +=============== + +Komplexe Zahlen bestehen aus einem Realteil und einem +`Imaginärteil `_, der in +Python den Suffix ``j`` erhält. + +.. code-block:: pycon + + >>> 7 + 2j + (7+2j) + +.. note:: + + Python drückt die resultierende komplexe Zahl in Klammern aus, um + anzuzeigen, dass die Ausgabe den Wert eines einzelnen Objekts darstellt: + +.. code-block:: pycon + + >>> (7 + 2j) - (4 + 4j) + (3-2j) + +.. code-block:: pycon + + >>> 2j * 4j + (-8+0j) + +.. note:: + + Die Berechnung von ``2j * 4j`` ergibt die erwartete Antwort von ``-8``, aber + das Ergebnis bleibt ein Python-Objekt für komplexe Zahlen. Komplexe Zahlen + werden nie automatisch in entsprechende reelle oder ganzzahlige Objekte + umgewandelt. Ihr könnt aber leicht auf ihre realen und imaginären Teile mit + ``real`` und ``imag`` zugreifen. + +.. code-block:: pycon + + >>> x = 2j * 4j + >>> x + (-8+0j) + >>> x.real + -8.0 + >>> x.imag + 0.0 + +.. note:: + + Die Real- und Imaginärteile einer komplexen Zahl werden immer als + Fließkommazahlen zurückgegeben. + +Erweiterte Funktionen +--------------------- + +Die Funktionen im Modul :doc:`math ` sind nicht auf +komplexe Zahlen anwendbar; einer der Gründe hierfür dürfte sein, dass die +Quadratwurzel aus ``-1`` einen Fehler erzeugen soll. Daher wurden ähnliche +Funktionen für komplexe Zahlen im +:doc:`cmath `-Modul bereitgestellt: + +:func:`python3:cmath.acos`, :func:`python3:cmath.acosh`, :func:`python3:cmath.asin`, :func:`python3:cmath.asinh`, :func:`python3:cmath.atan`, :func:`python3:cmath.atanh`, :func:`python3:cmath.cos`, :func:`python3:cmath.cosh`, :func:`python3:cmath.e`, :func:`python3:cmath.exp`, :func:`python3:cmath.log`, :func:`python3:cmath.log10`, :func:`python3:cmath.pi`, :func:`python3:cmath.sin`, :func:`python3:cmath.sinh`, :func:`python3:cmath.sqrt`, :func:`python3:cmath.tan`, :func:`python3:cmath.tanh`. + +Um im Code deutlich zu machen, dass es sich bei diesen Funktionen um spezielle +Funktionen für komplexe Zahlen handelt, und um Namenskonflikte mit den +normaleren Äquivalenten zu vermeiden, empfiehlt sich der einfache Import des +Moduls um bei der Verwendung der Funktion ausdrücklich auf das ``cmath``-Modul +zu verweisen, :abbr:`z.B. (zum Beispiel)`: + +.. code-block:: pycon + + >>> import cmath + >>> cmath.sqrt(-2) + 1.4142135623730951j + +.. warning:: + + Nun wird auch verständlicher, weswegen wir nicht den Import aller Funktionen + eines Moduls empfehlen mit :samp:`from {MODULE} import \*`. Wenn ihr damit + zuerst das Modul ``math`` und dann das Modul ``cmath`` importieren würdet, + hätten die Funktionen in ``cmath`` Vorrang vor denen von ``math``. Außerdem + ist es beim Verstehen des Codes viel mühsamer, die Quelle der verwendeten + Funktionen herauszufinden. + +Checks +------ + +* Ladet das Modul :mod:`math` und probiert einige der Funktionen aus. Ladet dann + auch das Modul :mod:`cmath` und macht dasselbe. + +* Wie könnt ihr die Funktionen des :mod:`math`-Moduls wiederherstellen? diff --git a/docs/types/numbers.rst b/docs/types/numbers/index.rst similarity index 67% rename from docs/types/numbers.rst rename to docs/types/numbers/index.rst index ea01e593..2f867966 100644 --- a/docs/types/numbers.rst +++ b/docs/types/numbers/index.rst @@ -67,55 +67,12 @@ Beispiele: Floating-Point Arithmetic `_ -Komplexe Zahlen ---------------- +.. toctree:: + :titlesonly: + :hidden: -Komplexe Zahlen bestehen aus einem Realteil und einem -`Imaginärteil `_, der in -Python den Suffix ``j`` erhält. - -.. code-block:: pycon - - >>> 7 + 2j - (7+2j) - -.. note:: - - Python drückt die resultierende komplexe Zahl in Klammern aus, um - anzuzeigen, dass die Ausgabe den Wert eines einzelnen Objekts darstellt: - -.. code-block:: pycon - - >>> (7 + 2j) - (4 + 4j) - (3-2j) - -.. code-block:: pycon - - >>> 2j * 4j - (-8+0j) - -.. note:: - - Die Berechnung von ``2j * 4j`` ergibt die erwartete Antwort von ``-8``, aber - das Ergebnis bleibt ein Python-Objekt für komplexe Zahlen. Komplexe Zahlen - werden nie automatisch in entsprechende reelle oder ganzzahlige Objekte - umgewandelt. Ihr könnt aber leicht auf ihre realen und imaginären Teile mit - ``real`` und ``imag`` zugreifen. - -.. code-block:: pycon - - >>> x = 2j * 4j - >>> x - (-8+0j) - >>> x.real - -8.0 - >>> x.imag - 0.0 - -.. note:: - - Die Real- und Imaginärteile einer komplexen Zahl werden immer als - Fließkommazahlen zurückgegeben. + complex + bool Built-in numerische Funktionen ------------------------------ @@ -144,8 +101,8 @@ Mehrere eingebaute Funktionen können mit Zahlen arbeiten: gibt das größte Element in einem :term:`python3:iterable` oder das größte von zwei oder mehr Argumenten zurück. :func:`python3:min` - gibt das kleinste Element in einem Iterable oder das kleinste von zwei oder - mehr Argumenten zurück. + gibt das kleinste Element in einem :term:`python3:iterable` oder das + kleinste von zwei oder mehr Argumenten zurück. :func:`python3:oct` konvertiert eine Integer-Zahl in eine oktale Zeichenkette mit dem Präfix ``0o``. Das Ergebnis ist ein gültiger Python-Ausdruck. Wenn ``x`` kein @@ -154,37 +111,16 @@ Mehrere eingebaute Funktionen können mit Zahlen arbeiten: :func:`python3:pow` gibt *base* als Potenz von *exp* zurück. :func:`python3:round` - gibt eine Zahl zurück, die auf *ndigits* nach dem Dezimalpunkt gerundet ist. - Wird *ndigits* weggelassen oder ist *None*, wird die nächstgelegene Ganzzahl - zur Eingabe zurückgegeben. - -Boolsche Werte --------------- - -In den folgenden Beispielen werden Boolesche Werte verwendet: - -.. code-block:: pycon - - >>> x = False - >>> x - False - >>> not x - True - -.. code-block:: pycon - - >>> y = True * 2 - >>> y - 2 - -Abgesehen von ihrer Darstellung als ``True`` und ``False`` verhalten sich -Boolesche Werte wie die Zahlen ``1`` (``True``) und ``0`` (``False``). + gibt eine Zahl zurück, die auf :samp:`{N}` Stellen nach dem Dezimalpunkt + gerundet ist. + Wenn :samp:`{N}` weggelassen wird oder :doc:`../none` ist, wird die + nächstgelegene Ganzzahl zur Eingabe zurückgegeben. Erweiterte numerische Funktionen -------------------------------- Fortgeschrittenere numerische Funktionen wie Trigonometrie sowie einige -nützliche Konstanten sind nicht in Python integriert, sondern werden in einem +nützliche Variablen sind nicht in Python integriert, sondern werden in einem Standardmodul namens :doc:`math ` bereitgestellt. :doc:`Module ` werden später noch ausführlicher erklärt. Für den Moment genügt, dass ihr die mathematischen Funktionen in diesem Abschnitt @@ -226,39 +162,7 @@ Das ``math``-Modul bietet :abbr:`u.a. (unter anderem)` :func:`python3:math.sin`, * die hyperbolischen Funktionen :func:`python3:math.cosh`, :func:`python3:math.sinh` und :func:`python3:math.tanh` -* und die Konstanten :data:`python3:math.e` und :data:`python3:math.pi`. - -Erweiterte Funktionen für komplexe Zahlen ------------------------------------------ - -Die Funktionen im Modul :doc:`math ` sind nicht auf -komplexe Zahlen anwendbar; einer der Gründe hierfür dürfte sein, dass die -Quadratwurzel aus ``-1`` einen Fehler erzeugen soll. Daher wurden ähnliche -Funktionen für komplexe Zahlen arbeiten im -:doc:`cmath `-Modul bereitgestellt: - -:func:`python3:cmath.acos`, :func:`python3:cmath.acosh`, :func:`python3:cmath.asin`, :func:`python3:cmath.asinh`, :func:`python3:cmath.atan`, :func:`python3:cmath.atanh`, :func:`python3:cmath.cos`, :func:`python3:cmath.cosh`, :func:`python3:cmath.e`, :func:`python3:cmath.exp`, :func:`python3:cmath.log`, :func:`python3:cmath.log10`, :func:`python3:cmath.pi`, :func:`python3:cmath.sin`, :func:`python3:cmath.sinh`, :func:`python3:cmath.sqrt`, :func:`python3:cmath.tan`, :func:`python3:cmath.tanh`. - -Um im Code deutlich zu machen, dass es sich bei diesen Funktionen um spezielle -Funktionen für komplexe Zahlen handelt, und um Namenskonflikte mit den -normaleren Äquivalenten zu vermeiden, empfiehlt sich der einfache Import des -Moduls um bei der Verwendung der Funktion ausdrücklich auf das ``cmath``-Paket -zu verweisen, :abbr:`z.B. (zum Beispiel)`: - -.. code-block:: pycon - - >>> import cmath - >>> cmath.sqrt(-2) - 1.4142135623730951j - -.. warning:: - - Nun wird auch verständlicher, weswegen wir nicht den Import aller Funktionen - eines Moduls empfehlen mit :samp:`from {MODULE} import \*`. Wenn ihr damit - zuerst das Modul ``math`` und dann das Modul ``cmath`` importieren würdet, - hätten die Funktionen in ``cmath`` Vorrang vor denen von ``math``. Außerdem - ist es beim Verstehen des Codes viel mühsamer, die Quelle der verwendeten - Funktionen herauszufinden. +* und die Variablen :data:`python3:math.e` und :data:`python3:math.pi`. Kaufmännisch runden ------------------- @@ -280,16 +184,6 @@ vermeiden. Für das `kaufmännische Runden werden >>> rounded Decimal('3') -Numerische Berechnungen ------------------------ - -Die Python-Standardinstallation eignet sich aufgrund von -Geschwindigkeitseinschränkungen nicht gut für intensive numerische Berechnungen. -Aber die leistungsstarke Python-Erweiterung -:doc:`Python4DataScience:workspace/numpy/index` bieten hocheffiziente -Implementierungen vieler fortgeschrittener numerischer Operationen. Der Schwerpunkt liegt dabei auf Array-Operationen, einschließlich mehrdimensionaler Matrizen -und fortgeschrittener Funktionen wie der schnellen Fourier-Transformation. - Eingebaute Module für Zahlen ---------------------------- @@ -322,23 +216,14 @@ ihr Zahlen managen könnt: | :py:mod:`operator` | für Standardoperatoren als Funktionen | +-----------------------+-------------------------------------------------------------------------------+ +.. _end-number-modules: + +.. seealso:: + * :doc:`Python4DataScience:workspace/numpy/index` + Checks ------ * Erstellt einige Zahlenvariablen (Ganzzahlen, Gleitkommazahlen und komplexe Zahlen). Experimentiert ein wenig damit, was passiert, wenn ihr Operationen - mit ihnen durchführt, auch typübergreifend. - -* Ladet das Modul :mod:`math` und probiert einige der Funktionen aus. Ladet dann - auch das Modul :mod:`cmath` und macht dasselbe. - -* Wie könnt ihr die Funktionen des :mod:`math`-Moduls wiederherstellen? - -* Entscheidet, ob die folgenden Aussagen wahr oder falsch sind: - - * ``1`` - * ``0`` - * ``-1`` - * ``[0]`` - * ``1 and 0`` - * ``1 > 0 or []`` + mit ihnen durchführt, auch Typ-übergreifend. diff --git a/docs/types/sequences-sets/index.rst b/docs/types/sequences-sets/index.rst new file mode 100644 index 00000000..95a13cc4 --- /dev/null +++ b/docs/types/sequences-sets/index.rst @@ -0,0 +1,27 @@ +Sequenzen und Sets +================== + +Sequenzen (:doc:`lists` und :doc:`tuples`) und Sets (:ref:`set` und +:ref:`frozenset`) unterscheiden sich im Wesentlichen durch folgende +Eigenschaften: + ++---------------+-----------------------+---------------+---------------+---------------+ +| Datentyp | :term:`veränderlich | geordnet | indiziert | Duplikate | +| | ` | | | | ++===============+=======================+===============+===============+===============+ +| Liste | ✅ | ✅ | ✅ | ✅ | ++---------------+-----------------------+---------------+---------------+---------------+ +| Tuple | ❌ | ✅ | ✅ | ✅ | ++---------------+-----------------------+---------------+---------------+---------------+ +| Set | ✅ | ❌ | ❌ | ❌ | ++---------------+-----------------------+---------------+---------------+---------------+ +| Frozenset | ❌ | ❌ | ❌ | ❌ | ++---------------+-----------------------+---------------+---------------+---------------+ + +.. toctree:: + :titlesonly: + :hidden: + + lists + tuples + sets diff --git a/docs/types/lists.rst b/docs/types/sequences-sets/lists.rst similarity index 86% rename from docs/types/lists.rst rename to docs/types/sequences-sets/lists.rst index 30654f6a..9a92db18 100644 --- a/docs/types/lists.rst +++ b/docs/types/sequences-sets/lists.rst @@ -4,18 +4,18 @@ Listen Eine Liste in Python ist ähnlich wie ein Array in Java oder C: eine geordnete Kollektion von Objekten. Anders als Listen in vielen anderen Sprachen können Python-Listen jedoch verschiedene Arten von Elementen enthalten; ein -Listenelement kann ein beliebiges Python-Objekt sein, darunter :doc:`strings`, -:doc:`tuples`, :doc:`lists`, :doc:`dicts`, :doc:`../functions/index`, -:doc:`files` und jede Art von :doc:`numbers`. Ihr erstellt eine Liste, indem ihr -kein oder durch Komma getrennte Elemente in eckige Klammern einschließt, etwa -so: +Listenelement kann ein beliebiges Python-Objekt sein, darunter +:doc:`../strings/index`, :doc:`tuples`, :doc:`lists`, :doc:`../dicts`, +:doc:`../../functions/index`, :doc:`../../save-data/files` und jede Art von +:doc:`../numbers/index`. Ihr erstellt eine Liste, indem ihr kein oder durch Komma +getrennte Elemente in eckige Klammern einschließt, etwa so: .. code-block:: python :linenos: - [] - [1] - [1, "2.", 3.0, ["4a", "4b"], (5.1, 5.2)] + [] + [1] + [1, "2.", 3.0, ["4a", "4b"], (5.1, 5.2)] .. tip:: Ich empfehle euch, **nicht** den in Python verfügbaren @@ -30,17 +30,17 @@ Indizes Elemente können aus einer Python-Liste extrahiert werden, indem eine Notation verwendet wird, die der Array-Indizierung in C ähnelt und mit ``0`` beginnt; die Frage nach Element ``0`` gibt das erste Element der Liste zurück, die Frage nach -Element ``1`` gibt das zweite Element zurück :abbr:`u.s.w. (und so weiter)`. +Element ``1`` gibt das zweite Element zurück :abbr:`usw (und so weiter)`. Hier sind ein paar Beispiele: .. code-block:: pycon :linenos: - >>> x = [1, "2.", 3.0, ["4a", "4b"], (5.1, 5.2)] - >>> x[0] - '1' - >>> x[1] - '2.' + >>> x = [1, "2.", 3.0, ["4a", "4b"], (5.1, 5.2)] + >>> x[0] + '1' + >>> x[1] + '2.' Eine Liste kann von vorne oder hinten indiziert werden. Ihr könnt euch auch auf ein Teilsegment einer Liste beziehen, indem ihr die *Slice*-Notation verwendet: @@ -48,20 +48,20 @@ ein Teilsegment einer Liste beziehen, indem ihr die *Slice*-Notation verwendet: .. code-block:: pycon :lineno-start: 6 - >>> x[-1] - (5.1, 5.2) - >>> x[-2] - ['4a', '4b'] - >>> x[1:-1] - ['2.', 3.0, ['4a', '4b']] - >>> x[0:3] - [1, '2.', 3.0] - >>> x[:3] - [1, '2.', 3.0] - >>> x[-4:-1] - ['2.', 3.0, ['4a', '4b']] - >>> x[-4:] - ['2.', 3.0, ['4a', '4b'], (5.1, 5.2)] + >>> x[-1] + (5.1, 5.2) + >>> x[-2] + ['4a', '4b'] + >>> x[1:-1] + ['2.', 3.0, ['4a', '4b']] + >>> x[0:3] + [1, '2.', 3.0] + >>> x[:3] + [1, '2.', 3.0] + >>> x[-4:-1] + ['2.', 3.0, ['4a', '4b']] + >>> x[-4:] + ['2.', 3.0, ['4a', '4b'], (5.1, 5.2)] Zeilen 2 und 4 Index von vorne unter Verwendung positiver Indizes beginnend mit ``0`` als @@ -92,7 +92,7 @@ so weiter)`: >>> x[1::2] ['zweitens', (5.1, 5.2)] -Der *Stride*-Wert kann auch negativ sein. Ein ``-1``-*Stride* bedeutet, von +Der *Stride*-Wert kann auch negativ sein. Ein ``-1``-*Stride* bedeutet, dass von rechts nach links gezählt wird: .. code-block:: pycon @@ -113,6 +113,11 @@ Zeile 3 Zeile 5 Ein *Stride* von ``-1`` kehrt die Reihenfolge um. + .. tip:: + Zum Umkehren der Reihenfolge dürfte jedoch :func:`list.reverse` besser + lesbar sein als ein *Stride* von ``-1``, :abbr:`s.a. (siehe auch)` + :ref:`list.reverse() `. + .. seealso:: * :doc:`Daten auswählen und filtern mit pandas ` @@ -152,6 +157,8 @@ Zeile 11 Einige Funktionen der Slice-Notation können auch mit speziellen Operationen ausgeführt werden, wodurch die Lesbarkeit des Codes verbessert wird: +.. _reverse: + .. code-block:: pycon :linenos: @@ -266,7 +273,7 @@ um zu bestimmen, wie die Elemente einer Liste sortiert werden sollen. Die Standard-``key``-Methode, die von :meth:`python3:list.sort` verwendet wird, erfordert jedoch, dass alle Elemente in der Liste von vergleichbarem Typ sind. In einer Liste, die sowohl Zahlen als auch Zeichenketten enthält, wird daher -eine :class:`python3:Exception` ausgelöst: +eine :term:`Exception` ausgelöst: .. code-block:: pycon @@ -281,9 +288,9 @@ Benutzerdefinierte Sortierung ::::::::::::::::::::::::::::: .. note:: - Für eine benutzerdefinierte Sortierung müsst ihr :doc:`../functions/index` - definieren können. Und auch die Verarbeitung von :doc:`strings` wird später - noch ausführlicher behandelt. + Für eine benutzerdefinierte Sortierung müsst ihr :doc:`../../functions/index` + definieren können. Und auch die Verarbeitung von :doc:`../strings/index` wird + später noch ausführlicher behandelt. Üblicherweise sortiert Python Wörter lexikografisch – Großbuchstaben vor Kleinbuchstaben. Wir möchten jedoch stattdessen eine Liste von Wörtern @@ -308,11 +315,11 @@ Die Funktion ``sorted`` Listen haben eine eingebaute Methode, um sich selbst zu sortieren :meth:`python3:list.sort`. Andere *Iterables* in Python, wie :abbr:`z.B. (zum -Beispiel)` die Schlüssel von :doc:`dicts`, haben jedoch keine Sortiermethode. +Beispiel)` die Schlüssel von :doc:`../dicts`, haben jedoch keine Sortiermethode. Python bietet hierfür jedoch die eingebaute Funktion :func:`python3:sorted` an, die eine sortierte Liste aus einer beliebigen *Iterables* zurückgibt. -:func:`python3:sorted` verwendet die gleichen :doc:`../functions/params` ``key`` -und ``reverse`` wie die Methode :meth:`python3:list.sort`: +:func:`python3:sorted` verwendet die gleichen :doc:`../../functions/params` +``key`` und ``reverse`` wie die Methode :meth:`python3:list.sort`: .. code-block:: pycon @@ -363,7 +370,7 @@ Fällen ``append`` vorziehen, um die Liste zu Beginn des Programms zu vergröße [None, None, None, None] Der Operator für ``list``-Multiplikationen ``*`` wiederholt das Kopieren der -Elemnete einer Liste die angegebene Zahl und fügt alle Kopien zu einer neuen +Elemente einer Liste die angegebene Zahl und fügt alle Kopien zu einer neuen Liste zusammen. Dabei wird üblicherweise eine Liste mit einer einzelnen Instanz von :doc:`/types/none` für die Listenmultiplikation verwendet, aber die Liste kann alles sein: @@ -380,9 +387,10 @@ Minimum oder Maximum einer Liste Ihr könnt :func:`max` und :func:`min` verwenden, um das größte und kleinste Element einer Liste zu finden. Wahrscheinlich werdet ihr :func:`max` und -:func:`min` vor allem bei :doc:`numerischen ` Listen verwenden, -aber ihr könnt sie auch bei Listen mit beliebigen Elementen einsetzen; Dwenn der -Vergleich dieser Typen jedoch keinen Sinn ergibt, führt dies zu einem Fehler: +:func:`min` vor allem bei :doc:`numerischen ` Listen +verwenden, aber ihr könnt sie auch bei Listen mit beliebigen Elementen +einsetzen; wenn der Vergleich dieser Typen jedoch keinen Sinn ergibt, führt dies +zu einem Fehler: .. code-block:: pycon @@ -398,7 +406,7 @@ Vergleich dieser Typen jedoch keinen Sinn ergibt, führt dies zu einem Fehler: TypeError: '>' not supported between instances of 'str' and 'int' Beim Vergleich komplexer Objekte werden die Teillisten zuerst nach dem ersten -Element und dann nach dem zweiten Element :abbr:`u.s.w. /und so weiter)` +Element und dann nach dem zweiten Element :abbr:`usw. (und so weiter)` analysiert. .. code-block:: pycon @@ -537,14 +545,7 @@ ihr hat Auswirkungen auf die Originalliste: >>> sup [[0], 1] -Zusammenfassung ---------------- - -+---------------+---------------+---------------+---------------+---------------+ -| Datentyp | veränderlich | geordnet | indiziert | Duplikate | -+===============+===============+===============+===============+===============+ -| Liste | ✅ | ✅ | ✅ | ✅ | -+---------------+---------------+---------------+---------------+---------------+ +.. _check-list: Checks ------ @@ -567,23 +568,12 @@ Checks * ``max([1, 2, "3"])`` * ``[1,2,3].count("1")`` -* ``max([1, 2, "3"])``, da Strings und Ganzzahlen nicht verglichen werden - können; daher ist es unmöglich, einen Maximalwert zu erhalten. - * Wenn ihr eine Liste ``l`` habt, wie könnt ihr daraus einen bestimmten Wert ``i`` entfernen? - .. code-block:: pycon - - >>> if i in l: - ... l.remove(i) - ... - * Wenn ihr eine verschachtelte Liste ``ll`` habt, wie könnt ihr eine Kopie ``nll`` dieser Liste erhalten, in der ihr die Elemente ändern könnt, ohne den Inhalt von ``ll`` zu verändern? -.. _check-list: - * Stellt sicher, dass das Objekt ``my_collection`` eine Liste ist, bevor ihr versucht, daran Daten anzuhängen. diff --git a/docs/types/sequences-sets/sets.rst b/docs/types/sequences-sets/sets.rst new file mode 100644 index 00000000..a02ee206 --- /dev/null +++ b/docs/types/sequences-sets/sets.rst @@ -0,0 +1,146 @@ +Sets +==== + +Sets in Python sind eine ungeordnete Sammlung von Objekten, die in Situationen +verwendet werden, in denen die Zugehörigkeit und Einzigartigkeit zur Menge die +wichtigsten Informationen des Objekts sind. Der ``in``-Operator läuft bei Sets +schneller als bei :doc:`lists`: + +.. _set: + +``set`` +------- + +Sets erstellen +~~~~~~~~~~~~~~ + +Ihr könnt Sets erstellen, indem ihr :class:`set` auf eine Sequenz anwendet, +:abbr:`z.B. (zum Beispiel)` auf eine :doc:`Liste `. + +.. code-block:: pycon + + >>> sequences = set(["list", "tuple", "tuple"]) + >>> sequences + {'tuple', 'list'} + +Wenn eine Sequenz zu einem Set gemacht wird, werden Duplikate entfernt, +allerdings geht dann auch die Reihenfolge verloren. + +Auch können einzelne Elemente nicht mit Slicing ausgewählt werden: + +.. code-block:: pycon + + >>> sequences[0] + Traceback (most recent call last): + File "", line 1, in + sequences[0] + ~~~~~~~~~^^^ + TypeError: 'set' object is not subscriptable + +Werte überprüfen +~~~~~~~~~~~~~~~~ + +Das Schlüsselwort ``in`` wird verwendet, um die Zugehörigkeit eines Objekts zu +einer Menge zu prüfen. + +.. code-block:: pycon + + >>> "list" in sequences + True + >>> "set" in sequences + False + +Werte hinzufügen und löschen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mit ``add`` und ``remove`` könnt ihr Werte hinzufügen und löschen. + +.. code-block:: pycon + + >>> quantities = sequences.add("set") + >>> quantities + {'list', 'tuple', 'set'} + >>> quantities.remove("set") + >>> quantities + {'list', 'tuple'} + +Die Elemente sind ungeordnet, :abbr:`d.h. (das heißt)` die Werte innerhalb +einer Sequenz können sich verschieben, wenn neue Elemente hinzugefügt werden. + +Mengenbildung +~~~~~~~~~~~~~ + +Vereinigungsmenge + .. code-block:: pycon + + x = {4, 2, 3, 2, 1} + y = {3, 4, 5} + >>> x.union(y) + {0, 1, 2, 3, 4, 5} + +Schnittmenge + .. code-block:: pycon + + >>> x.intersection(y) + {3} + +Differenz- oder Restmenge + .. code-block:: pycon + + >>> x.difference(y) + {0, 1, 2} + +.. _frozenset: + +``frozenset`` +------------- + +Neben ``set`` gibt es noch ``frozenset``, einen :term:`unveränderlichen +` Datentyp. Damit können sie auch Mitglieder anderer Mengen +sein: + +.. code-block:: pycon + :linenos: + + >>> sequences = frozenset(["list", "tuple", "set", "tuple"]) + >>> sequences + frozenset({'list', 'tuple', 'set'}) + >>> dicts = {"dict"} + >>> sequences.add(dicts) + Traceback (most recent call last): + File "", line 1, in + sequences.add(dicts) + ^^^^^^^^^^^^^ + AttributeError: 'frozenset' object has no attribute 'add' + >>> dicts.add(sequences) + >>> dicts + {frozenset({'list', 'tuple', 'set'}), 'dict'} + +Performance +----------- + +Sets sind sehr schnell bei der Überprüfung, ob Elemente in einer Menge enthalten +sind. Auch zum Auffinden von gemeinsamen und eindeutigen Werten zweier Mengen +ist die Mengenarithmetik von Sets gut geeignet. Hierfür kann es sinnvoll sein, +:doc:`lists` oder :doc:`tuples` in Sets umzuwandeln. + +Reihenfolge +----------- + +Der Geschwindigkeitsvorteil hat jedoch auch ihren Preis: Sets halten die +Elemente nicht in der richtigen Reihenfolge, während :doc:`lists` und +:doc:`tuples` dies tun. Wenn die Reihenfolge für euch wichtig ist, solltet ihr +nur für bestimmte Operationen die Elemente in ein Set umwandeln, :abbr:`z.B. +(zum Beispiel)` um zu überprüfen, ob die Elemente einer Liste eindeutig sind mit + +.. code-block:: pycon + + >>> sequences = ["list", "tuple", "set", "tuple"] + >>> len(sequences) == len(set(sequences)) + False + +Checks +------ + +* Wieviele Elemente hat ein Set, wenn es aus der folgenden Liste + ``[4, 2, 3, 2, 1]`` gebildet wird? diff --git a/docs/types/tuples.rst b/docs/types/sequences-sets/tuples.rst similarity index 83% rename from docs/types/tuples.rst rename to docs/types/sequences-sets/tuples.rst index ddd17d7d..41833f29 100644 --- a/docs/types/tuples.rst +++ b/docs/types/sequences-sets/tuples.rst @@ -3,7 +3,7 @@ Tupel Tupel ähneln :doc:`lists`, können jedoch nicht geändert sondern nur erstellt werden. Tupel haben die wichtige Aufgabe, effizient :abbr:`z.B. (zum Beispiel)` -Schlüssel für :doc:`dicts` zu erstellen. +Schlüssel für :doc:`../dicts` zu erstellen. Tupel werden ähnlich wie die Listen erstellt: einer Variablen wird eine Folge von Werten zugewiesen, die jedoch nicht in eckige sondern in runde Klammern @@ -36,7 +36,7 @@ wurde, kann es ähnlich wie die eine Liste verwendet werden: Die Operatoren (:ref:`in, not in `, ``+`` und ``*``) und die eingebauten Funktionen (``len``, ``max`` und ``min``) arbeiten mit Tupeln auf die gleiche Weise wie mit :doc:`lists`, da keine dieser Funktionen das Original -verändert. Es gibt jedoch nur zwei Tupelmethoden: ``count`` und ``index``. +verändert. Es gibt jedoch nur zwei Tupel-Methoden: ``count`` und ``index``. Mit den ``+``- und ``*``-Operatoren könnt ihr Tupel aus bestehenden Tupeln erstellen: @@ -112,7 +112,7 @@ Beispiel: >>> w '2.' -Dieses Beispiel kann noch weiter einfacht werden, da Python Tupel in einem +Dieses Beispiel kann noch weiter vereinfacht werden, da Python Tupel in einem Zuweisungskontext auch ohne die runden Klammern erkennt: .. code-block:: pycon @@ -146,7 +146,7 @@ Elementen aufzunehmen, die nicht zu den sonstigen Elementen passen: Konvertieren zwischen Listen und Tupeln --------------------------------------- -Eine Liste kann mit Hilfe der eingebauten Funktion ``tuple`` in ein Tupel +Eine Liste kann mit Hilfe der eingebauten Funktion :func:`tuple` in ein Tupel umgewandelt werden: .. code-block:: pycon @@ -155,7 +155,7 @@ umgewandelt werden: >>> tuple(x) (1, 2, 3, 5) -Umgekehrt kann ein Tupel mit Hilfe der eingebauten Funktion list in eine Liste +Umgekehrt kann ein Tupel mit Hilfe der eingebauten Funktion :func:`list` in eine Liste umgewandelt werden: .. code-block:: pycon @@ -173,17 +173,8 @@ Die Vorteile von Tupeln gegenüber :doc:`lists` sind: * Tupel können nicht verändert werden und sind daher *schreibgeschützt*. -* Tupel können als Schlüssel in :doc:`dicts` und Werte in :doc:`sets` verwendet - werden. - -Zusammenfassung ---------------- - -+---------------+---------------+---------------+---------------+---------------+ -| Datentyp | veränderlich | geordnet | indiziert | Duplikate | -+===============+===============+===============+===============+===============+ -| Tuple | ❌ | ✅ | ✅ | ✅ | -+---------------+---------------+---------------+---------------+---------------+ +* Tupel können als Schlüssel in :doc:`../dicts` und Werte in :doc:`sets` + verwendet werden. Checks ------ diff --git a/docs/types/sets.rst b/docs/types/sets.rst deleted file mode 100644 index 7536744e..00000000 --- a/docs/types/sets.rst +++ /dev/null @@ -1,100 +0,0 @@ -Sets -==== - -Sets in Python sind eine ungeordnete Sammlung von Objekten, die in Situationen -verwendet werden, in denen die Zugehörigkeit und Einzigartigkeit zur Menge die -wichtigsten Informationen des Objekts sind. Der ``in``-Operator läuft bei Sets -schneller als bei :doc:`lists`: - -``set`` -------- - -.. code-block:: pycon - :linenos: - - >>> x = set([4, 2, 3, 2, 1]) - >>> x - {1, 2, 3, 4} - >>> 1 in x - True - >>> 5 in x - False - >>> x.add(0) - >>> x - {0, 1, 2, 3, 4} - >>> x.remove(4) - >>> x - {0, 1, 2, 3} - >>> y = set([3, 4, 5]) - >>> x | y - {0, 1, 2, 3, 4, 5} - >>> x & y - {3} - >>> x ^ y - {0, 1, 2, 4, 5} - >>> x.update(y) - >>> x - {0, 1, 2, 3, 4, 5} - -Zeile 1 - Ihr könnt ein Set erstellen, indem ihr ``set`` auf eine Sequenz anwendet, - :abbr:`z.B. (zum Beispiel)` auf eine :doc:`Liste `. -Zeile 3 - Wenn eine Sequenz zu einem Set gemacht wird, werden Duplikate entfernt. -Zeilen 4–7 - Das Schlüsselwort ``in`` wird verwendet, um die Zugehörigkeit eines Objekts - zu einer Menge zu prüfen. -Zeilen 8–13 - Mit ``add`` und ``remove`` könnt ihr die Elemente in ``set`` ändern. -Zeile 15 - ``|`` wird verwendet, um die Vereinigung oder Kombination von zwei Mengen zu - erhalten. -Zeile 17 - ``&`` wird verwendet, um die Schnittmenge zu erhalten. -Zeile 19 - ``^`` wird verwendet, um die symmetrische Differenz zu finden, :abbr:`d.h. - (das heißt)` Elemente, die in der einen oder der anderen Menge enthalten - sind, aber nicht in beiden. - -``frozenset`` -------------- - -Neben ``set`` gibt es noch ``frozenset``, einen unveränderlichen Datentyp. Damit -können sie auch Mitglieder anderer Mengen sein: - -.. code-block:: pycon - :linenos: - - >>> x = set([4, 2, 3, 2, 1]) - >>> z = frozenset(x) - >>> z - frozenset({1, 2, 3, 4}) - >>> z.add(5) - Traceback (most recent call last): - File "", line 1, in - AttributeError: 'frozenset' object has no attribute 'add' - >>> x.add(z) - >>> x - {1, 2, 3, 4, frozenset({1, 2, 3, 4})} - -Zusammenfassung ---------------- - -Der Geschwindigkeitsvorteil hat jedoch auch ihren Preis: Sets halten die -Elemente nicht in der richtigen Reihenfolge, während :doc:`lists` und -:doc:`tuples` dies tun. Wenn die Reihenfolge für euch wichtig ist, solltet ihr -eine Datenstruktur verwenden, die sich die Reihenfolge merkt. - -+---------------+---------------+---------------+---------------+---------------+ -| Datentyp | veränderlich | geordnet | indiziert | Duplikate | -+===============+===============+===============+===============+===============+ -| Sets | ✅ | ❌ | ❌ | ❌ | -+---------------+---------------+---------------+---------------+---------------+ -| Frozensets | ❌ | ❌ | ❌ | ❌ | -+---------------+---------------+---------------+---------------+---------------+ - -Checks ------- - -* Wieviele Elemente hat ein Set, wenn es aus der folgenden Liste - ``[4, 2, 3, 2, 1]`` gebildet wird? diff --git a/docs/types/strings.rst b/docs/types/strings.rst deleted file mode 100644 index 54236d77..00000000 --- a/docs/types/strings.rst +++ /dev/null @@ -1,997 +0,0 @@ -Zeichenketten -============= - -Die Verarbeitung von Zeichenketten ist eine der Stärken von Python. Es gibt -viele Optionen zur Begrenzung von Zeichenketten: - -.. code-block:: python - - "Eine Zeichenfolge in doppelten Anführungszeichen kann 'einfache Anführungszeichen' enthalten." - 'Eine Zeichenfolge in einfachen Anführungszeichen kann "doppelte Anführungszeichen" enthalten.' - """\tEine Zeichenkette, die mit einem Tabulator beginnt und mit einem Zeilenumbruchzeichen endet.\n""" - """Dies ist eine Zeichenkette in dreifach doppelten Anführungszeichen, die - einzige Zeichenkette, die echte Zeilenumbrüche enthält.""" - -Zeichenketten können durch einfache (``' '``), doppelte (``" "``), dreifache -einfache (``''' '''``) oder dreifache doppelte (``""" """``) Anführungszeichen -getrennt werden. - -Eine normale Zeichenkette kann nicht auf mehrere Zeilen aufgeteilt werden. Der -folgende Code wird also nicht funktionieren: - -.. code-block:: - - "Dies ist ein fehlerhafter Versuch, einen einen Zeilenumbruch in - eine Zeichenkette einzufügen, ohne \n zu verwenden." - -Sie können auch Tabulator- (``\t``) und *Newline*-Zeichen (``\n``) enthalten. -Allgemein können Backslashes ``\`` als Escape-Zeichen verwendet werden. So kann -:abbr:`z.B. (zum Beispiel)` ``\\`` für einen einzelnen Backslash und ``\'`` für -ein einfaches Anführungszeichen verwendet werden, wodurch es die -Zeichenfolge nicht beendet: - -.. blacken-docs:off - -.. code-block:: python - - "You don't need a backslash here." - 'However, this wouldn\'t work without a backslash.' - -.. blacken-docs:on - -Python bietet jedoch auch Zeichenketten in dreifachen Anführungszeichen -(``"""``), die dies ermöglichen und einfache und doppelte Anführungszeichen ohne -Backslashes ``\`` als Escape-Zeichen enthalten können. - -Sonderzeichen und Escape-Sequenzen ----------------------------------- - -``\n`` steht für das *Newline*-Zeichen und ``\t`` für das Tabulatorzeichen. -Zeichenfolgen, die mit einem Backslash beginnen und zur Darstellung anderer -Zeichen verwendet werden, werden Escape-Sequenzen genannt. Escape-Sequenzen -werden in der Regel verwendet, um Sonderzeichen darzustellen, :abbr:`d.h. (das -heißt)` Zeichen, für die es keine einstellige druckbare Darstellung gibt. - -Hier sind weitere Zeichen, die ihr mit dem Escape-Zeichen erhalten könnt: - -+--------------------------+--------------------------+--------------------------+ -| Escape-Sequenz | Ausgabe | Erläuterung | -+==========================+==========================+==========================+ -| ``\\`` | ``\`` | Backslash | -+--------------------------+--------------------------+--------------------------+ -| ``\'`` | ``'`` | einfaches | -| | | Anführungszeichen | -+--------------------------+--------------------------+--------------------------+ -| ``\"`` | ``"`` | doppeltes | -| | | Anführungszeichen | -+--------------------------+--------------------------+--------------------------+ -| ``\b`` | | Backspace (``BS``) | -+--------------------------+--------------------------+--------------------------+ -| ``\n`` | | ASCII Linefeed ``(LF``) | -+--------------------------+--------------------------+--------------------------+ -| ``\r`` | | ASCII Carriage Return | -| | | (``CR``) | -+--------------------------+--------------------------+--------------------------+ -| ``\t`` | | Tabulator (``TAB``) | -+--------------------------+--------------------------+--------------------------+ -| :samp:`\u{00B5}` | ``µ`` | Unicode 16 bit | -+--------------------------+--------------------------+--------------------------+ -| :samp:`\U{000000B5}` | ``µ`` | Unicode 32 bit | -+--------------------------+--------------------------+--------------------------+ -| :samp:`\N{{SNAKE}}` | ``🐍`` | Unicode Emoji name | -+--------------------------+--------------------------+--------------------------+ - -Zeilen 1–7 - Der ASCII-Zeichensatz, der von Python verwendet wird und der - Standardzeichensatz auf fast allen Computern ist, definiert eine ganze Reihe - weiterer Sonderzeichen. -Zeilen 8–9 - Unicode-Escape-Sequenzen. -Zeile 10 - Unicode-Namen zur Angabe eines Unicode-Zeichens. - -Operatoren und Funktionen -------------------------- - -Die Operatoren und Funktionen, die mit Zeichenketten arbeiten, geben neue, vom -Original abgeleitete Zeichenketten zurück. Die Operatoren (``in``, ``+`` und -``*``) und eingebauten Funktionen (``len``, ``max`` und ``min``) arbeiten mit -Zeichenketten genauso wie mit Listen und Tupeln. - -.. code-block:: pycon - - >>> welcome = "Hello pythonistas!\n" - >>> 2 * welcome - 'Hello pythonistas!\nHello pythonistas!\n' - >>> welcome + welcome - 'Hello pythonistas!\nHello pythonistas!\n' - >>> "python" in welcome - True - >>> max(welcome) - 'y' - >>> min(welcome) - '\n' - -Indizierung und Slicing ------------------------ - -Die Index- und Slice-Notation funktioniert auf die gleiche Weise, um einzelne -Elemente oder Slices zu erhalten: - -.. code-block:: pycon - - >>> welcome[0:5] - 'Hello' - >>> welcome[6:-1] - 'pythonistas!' - -Die Index- und Slice-Notation kann jedoch nicht verwendet werden, um Elemente -hinzuzufügen, zu entfernen oder zu ersetzen, da Zeichenketten unveränderlich -sind: - -.. code-block:: pycon - - >>> welcome[6:-1] = "everybody!" - Traceback (most recent call last): - File "", line 1, in - TypeError: 'str' object does not support item assignment - -String-Methoden ---------------- - -Die meisten der Python-:ref:`String-Methoden ` sind im -:ref:`str `-Typ integriert, so dass alle ``str``-Objekte -automatisch über sie verfügen: - -.. code-block:: pycon - - >>> welcome = "hello pythonistas!\n" - >>> welcome.isupper() - False - >>> welcome.isalpha() - False - >>> welcome[0:5].isalpha() - True - >>> welcome.capitalize() - 'Hello pythonistas!\n' - >>> welcome.title() - 'Hello Pythonistas!\n' - >>> welcome.strip() - 'Hello pythonistas!' - >>> welcome.split(" ") - ['hello', 'pythonistas!\n'] - >>> chunks = [snippet.strip() for snippet in welcome.split(" ")] - >>> chunks - ['hello', 'pythonistas!'] - >>> " ".join(chunks) - 'hello pythonistas!' - >>> welcome.replace("\n", "") - 'hello pythonistas!' - -Im Folgenden findet ihr einen Überblick über die häufigsten -:ref:`String-Methoden `: - -+---------------------------+---------------------------------------------------------------+ -| Methode | Beschreibung | -+===========================+===============================================================+ -| :py:meth:`str.count` | gibt die Anzahl der sich nicht überschneidenden Vorkommen der | -| | Zeichenkette zurück. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.endswith` | gibt ``True`` zurück, wenn die Zeichenkette mit dem Suffix | -| | endet. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.startswith` | gibt ``True`` zurück, wenn die Zeichenkette mit dem Präfix | -| | beginnt. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.join` | verwendet die Zeichenkette als Begrenzer für die Verkettung | -| | einer Folge anderer Zeichenketten. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.index` | gibt die Position des ersten Zeichens in der Zeichenkette | -| | zurück, wenn es in der Zeichenkette gefunden wurde; löst einen| -| | ``ValueError`` aus, wenn es nicht gefunden wurde. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.find` | gibt die Position des ersten Zeichens des ersten Vorkommens | -| | der Teilzeichenkette in der Zeichenkette zurück; wie | -| | ``index``, gibt aber ``-1`` zurück, wenn nichts gefunden | -| | wurde. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.rfind` | Rückgabe der Position des ersten Zeichens des letzten | -| | Vorkommens der Teilzeichenkette in der Zeichenkette; gibt | -| | ``-1`` zurück, wenn nichts gefunden wurde. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.replace` | ersetzt Vorkommen einer Zeichenkette durch eine andere | -| | Zeichenkette. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.strip`, | schneiden Leerzeichen ab, einschließlich Zeilenumbrüchen. | -| :py:meth:`str.rstrip`, | | -| :py:meth:`str.lstrip` | | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.split` | zerlegt eine Zeichenkette in eine Liste von Teilzeichenketten | -| | unter Verwendung des übergebenen Trennzeichens. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.lower` | konvertiert alphabetische Zeichen in Kleinbuchstaben. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.upper` | konvertiert alphabetische Zeichen in Großbuchstaben. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.casefold` | konvertiert Zeichen in Kleinbuchstaben und konvertiert alle | -| | regionsspezifischen variablen Zeichenkombinationen in eine | -| | gemeinsame vergleichbare Form. | -+---------------------------+---------------------------------------------------------------+ -| :py:meth:`str.ljust`, | linksbündig bzw. rechtsbündig; füllt die gegenüberliegende | -| :py:meth:`str.rjust` | Seite der Zeichenkette mit Leerzeichen (oder einem anderen | -| | Füllzeichen) auf, um eine Zeichenkette mit einer Mindestbreite| -| | zu erhalten. | -+---------------------------+---------------------------------------------------------------+ - -``str.split`` und ``str.join`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Während :meth:`python3:str.split` eine Liste von Zeichenfolgen zurückgibt, nimmt -:meth:`python3:str.join` eine Liste von Zeichenketten und fügt sie zu einer -einzigen Zeichenkette zusammen. Normalerweise verwendet -:meth:`python3:str.split` Leerraum als Begrenzungszeichen für die aufzuteilenden -Zeichenketten, aber ihr könnt dieses Verhalten mit einem optionalen -:doc:`../functions/params` ändern. - -.. warning:: - Die Verkettung von Zeichenketten mit ``+`` ist zwar nützlich, aber nicht - effizient, wenn es darum geht, eine große Anzahl von Zeichenketten zu einer - einzigen Zeichenkette zusammenzufügen, da jedes Mal, wenn ``+`` angewendet - wird, ein neues Zeichenketten-Objekt erstellt wird. :samp:`"Hello" + - "Pythonistas!"` erzeugt zwei Objekte, von denen eines sofort wieder verworfen - wird. - -Wenn ihr mit :meth:`python3:str.join` Zeichenfolgen zusammenführt, könnt ihr -zwischen die Zeichenfolgen beliebige Zeichen einfügen: - -.. code-block:: pycon - - >>> " :: ".join(["License", "OSI Approved"]) - 'License :: OSI Approved' - -Ihr könnt auch eine leere Zeichenkette, ``""``, verwenden, :abbr:`z.B. (zum -Beispiel)` für die CamelCase-Schreibweise von Python-Klassen: - -.. code-block:: pycon - - >>> "".join(["My", "Class"]) - 'MyClass' - -:meth:`python3:str.split` wird meist verwendet um Zeichenketten an Leerräumen zu -trennen. Ihr könnt eine Zeichenkette jedoch auch an einer bestimmten anderen -Zeichenfolge trennen, indem ihr einen optionalen :doc:`../functions/params` -übergebt: - -.. code-block:: pycon - - >>> example = "1. You can have\n\twhitespaces, newlines\n and tabs mixed in\n\tthe string." - >>> example.split() - ['1.', 'You', 'can', 'have', 'whitespaces,', 'newlines', 'and', 'tabs', 'mixed', 'in', 'the', 'string.'] - >>> license = "License :: OSI Approved" - >>> license.split(" :: ") - ['License', 'OSI Approved'] - -Manchmal ist es nützlich, dem letzten Feld in einer Zeichenkette zu erlauben, -beliebigen Text zu enthalten. Ihr könnt dies tun, indem ihr einen optionalen -zweiten :doc:`../functions/params` angebt, wie viele Teilungen durchgeführt -werden sollen: - -.. code-block:: pycon - - >>> example.split(" ", 1) - ['1.', 'You can have\n\twhitespaces, newlines\n and tabs mixed in\n\tthe string.'] - -Wenn ihr :meth:`python3:str.split` mit dem optionalen zweiten Argument verwendenwollt, müsst ihr zunächst ein erstes Argument angeben. Um zu erreichen, dass bei -allen Leerzeichen geteilt wird, verwendet :doc:`none` als erstes Argument: - -.. code-block:: pycon - - >>> example.split(None, 8) - ['1.', 'You', 'can', 'have', 'whitespaces,', 'newlines', 'and', 'tabs', 'mixed in\n\tthe string.'] - -.. tip:: - Ich verwende :meth:`python3:str.split` und :meth:`python3:str.join` - ausgiebig, meist für Textdateien, die von anderen Programmen erzeugt wurden. - Zum Schreiben von - :doc:`Python4DataScience:data-processing/serialisation-formats/csv/index`- - oder - :doc:`Python4DataScience:data-processing/serialisation-formats/json/index`-Dateien - verwende ich jedoch meist die zugehörigen Python-Bibliotheken. - -Leerraum entfernen -~~~~~~~~~~~~~~~~~~ - -:py:meth:`str.strip` gibt eine neue Zeichenkette zurück, die sich von der -ursprünglichen Zeichenkette nur dadurch unterscheidet, dass alle Leerzeichen am -Anfang oder Ende der Zeichenkette entfernt wurden. :py:meth:`str.lstrip` und -:py:meth:`str.rstrip` arbeiten ähnlich, entfernen jedoch nur die Leerzeichen am -linken :abbr:`bzw. (beziehungsweise)` rechten Ende der ursprünglichen -Zeichenkette: - -.. code-block:: pycon - - >>> example = " whitespaces, newlines \n\tand tabs. \n" - >>> example.strip() - 'whitespaces, newlines \n\tand tabs.' - >>> example.lstrip() - 'whitespaces, newlines \n\tand tabs. \n' - >>> example.rstrip() - ' whitespaces, newlines \n\tand tabs.' - -In diesem Beispiel werden die *Newlines* ``\n`` als Leerzeichen betrachtet. Die -genaue Zuordnung kann sich von Betriebssystem zu Betriebssystem unterscheiden. -Ihr könnt herausfinden, was Python als Leerzeichen betrachtet, indem ihr auf die -Konstante :py:data:`string.whitespace` zugreift. Bei mir wird das folgende -zurückgegeben: - -.. code-block:: pycon - - >>> import string - >>> string.whitespace - ' \t\n\r\x0b\x0c' - -Die im Hexadezimalformat (``\x0b``, ``\x0c``) angegebenen Zeichen stellen die -vertikalen Tabulator- und Vorschubzeichen dar. - -.. tip:: - Ändert nicht den Wert dieser Variablen um die Funktionsweise von - :py:meth:`str.strip` :abbr:`usw. (und so weiter)` zu beeinflussen. Welche - Zeichen diese Methoden entfernen, könnt ihr Zeichen als zusätzlichen - :doc:`../functions/params` übergeben: - - .. code-block:: pycon - - >>> url = "https://www.cusy.io/" - >>> url.strip("htps:/w.") - 'cusy.io' - -Suche in Zeichenketten -~~~~~~~~~~~~~~~~~~~~~~ - -:ref:`str `-Objekte bieten mehrere Methoden für die einfache -Suche nach Zeichenketten: Die vier grundlegenden Methoden für die Suche nach -Zeichenketten sind :py:meth:`str.find`, :py:meth:`str.rfind`, -:py:meth:`str.index` und :py:meth:`str.rindex`. Eine verwandte Methode, -:py:meth:`str.count`, zählt, wie oft eine Zeichenfolge in einer anderen -Zeichenfolge gefunden werden kann. - -:py:meth:`str.find` benötigt einen einzigen :doc:`../functions/params`: die -gesuchte Teilzeichenkette; zurückgegeben wird dann die Position des ersten -Vorkommens oder ``-1``, wenn es kein Vorkommen gibt: - -.. code-block:: pycon - - >>> hipy = "Hello Pythonistas!\n" - >>> hipy.find("\n") - 18 - -:py:meth:`str.find` kann auch ein oder zwei zusätzliche -:doc:`../functions/params` annehmen: - -``start`` - Zahl, der Zeichen am Anfang der zu durchsuchenden Zeichenkette, die - ignoriert werden soll. -``end`` - Zahl, der Zeichen am Ende der zu durchsuchenden Zeichenkette, die ignoriert - werden soll. - -Im Gegensatz zu :py:meth:`find` beginnt :py:meth:`rfind` die Suche am Ende der -Zeichenkette und gibt daher die Position des letzten Vorkommens zurück. - -:py:meth:`index` und :py:meth:`rindex` unterscheiden sich von :py:meth:`find` -und :py:meth:`rfind` dadurch, dass statt dem Rückgabewert ``-1`` eine -:class:`python3:ValueError`-Ausnahme ausgelöst wird. - -Ihr könnt zwei weitere :ref:`String-Methoden ` -verwenden, um Strings zu suchen: :py:meth:`str.startswith` und -:py:meth:`str.endswith`. Diese Methoden geben ``True``- oder ``False`` als -Ergebnis zurück, je nachdem, ob die Zeichenkette, auf die sie angewendet werden, -mit einer der als :doc:`../functions/params` angegebenen Zeichenketten beginnt -oder endet: - -.. code-block:: pycon - - >>> hipy.endswith("\n") - True - >>> hipy.endswith(("\n", "\r")) - True - -Darüber hinaus gibt es einige Methoden, mit denen die Eigenschaft einer -Zeichenkette überprüft werden kann: - -+---------------------------+---------------+---------------+---------------+---------------+---------------+ -| Methode | ``[!#$%…]`` | ``[a-zA-Z]`` | ``[¼½¾]`` | ``[¹²³]`` | ``[0-9]`` | -+===========================+===============+===============+===============+===============+===============+ -| :py:meth:`str.isprintable`| ✅ | ✅ | ✅ | ✅ | ✅ | -+---------------------------+---------------+---------------+---------------+---------------+---------------+ -| :py:meth:`str.isalnum` | ❌ | ✅ | ✅ | ✅ | ✅ | -+---------------------------+---------------+---------------+---------------+---------------+---------------+ -| :py:meth:`str.isnumeric` | ❌ | ❌ | ✅ | ✅ | ✅ | -+---------------------------+---------------+---------------+---------------+---------------+---------------+ -| :py:meth:`str.isdigit` | ❌ | ❌ | ❌ | ✅ | ✅ | -+---------------------------+---------------+---------------+---------------+---------------+---------------+ -| :py:meth:`str.isdecimal` | ❌ | ❌ | ❌ | ❌ | ✅ | -+---------------------------+---------------+---------------+---------------+---------------+---------------+ - -:py:meth:`str.isspace` prüft auf Leerzeichen. - -Zeichenketten ändern -~~~~~~~~~~~~~~~~~~~~ - -:ref:`str `-Objekte sind unveränderlich, aber sie verfügen über -mehrere Methoden, die eine modifizierte Version der ursprünglichen Zeichenkette -zurückgeben können. - -:py:meth:`str.replace` könnt ihr verwenden, um Vorkommen des ersten :doc:`../functions/params` durch den zweiten zu ersetzen, :abbr:`z.B. (zum Beispiel)`: - -.. code-block:: pycon - - >>> hipy.replace("\n", "\n\r") - 'Hello Pythonistas!\n\r' - -:py:meth:`str.maketrans` und :py:meth:`str.translate` können zusammen verwendet -werden, um Zeichen in Zeichenketten in andere Zeichen zu übersetzen, :abbr:`z.B. -(zum Beispiel)`: - -.. code-block:: pycon - :linenos: - - >>> hipy = "Hello Pythonistas!\n" - >>> trans_map = hipy.maketrans(" ", "-", "!\n") - >>> hipy.translate(trans_map) - 'Hello-Pythonistas' - -Zeile 2 - :py:meth:`str.maketrans` wird verwendet, um eine Übersetzungstabelle aus den - beiden Zeichenketten-Argumenten zu erstellen. Die beiden Argumente müssen - jeweils die gleiche Anzahl von Zeichen enthalten. Als drittes Argument - werden Zeichen übergeben, die nicht zurückgegeben werden sollen. -Zeile 3 - Die von :py:meth:`str.maketrans` erzeugte Tabelle wird an - :py:meth:`str.translate` übergeben. - -``re`` ------- - -Die Python-Standard-Bibliothek :doc:`re ` enthält ebenfalls -Funktionen für die Arbeit mit Zeichenketten. Dabei bietet ``re`` ausgefeiltere -Möglichkeiten zur Musterextraktion und -ersetzung als der -:ref:`str `-Typ. - -.. code-block:: pycon - - >>> import re - >>> re.sub("\n", "", welcome) - 'Hello pythonistas!' - -Hier wird der reguläre Ausdruck zunächst kompiliert und dann seine -:py:meth:`re.Pattern.sub`-Methode für den übergebenen Text aufgerufen. Ihr könnt -den Ausdruck selbst mit :py:func:`re.compile` kompilieren und so ein -wiederverwendbares ``regex``-Objekt bilden, das auf unterschiedliche -Zeichenketten angewendet die CPU-Zyklen verringert: - -.. code-block:: pycon - - >>> regex = re.compile("\n") - >>> regex.sub("", welcome) - 'Hello pythonistas!' - -Wenn ihr stattdessen eine Liste aller Muster erhalten möchtet, die dem -``regex``-Objekt entsprechen, könnt ihr die -:py:meth:`re.Pattern.findall`-Methode verwenden: - -.. code-block:: pycon - - >>> regex.findall(welcome) - ['\n'] - -.. note:: - Um das umständliche Escaping mit ``\`` in einem regulären Ausdruck zu - vermeiden, könnt ihr rohe String-Literale wie ``r'C:\PATH\TO\FILE'`` - anstelle des entsprechenden ``'C:\\PATH\\TO\\FILE'`` verwenden. - -:py:meth:`re.Pattern.match` und :py:meth:`re.Pattern.search` sind eng mit -:py:meth:`re.Pattern.findall` verwandt. Während ``findall`` alle -Übereinstimmungen in einer Zeichenkette zurückgibt, gibt ``search`` nur die -erste Übereinstimmung und ``match`` nur Übereinstimmungen am Anfang der -Zeichenkette zurück. Als weniger triviales Beispiel betrachten wir einen -Textblock und einen regulären Ausdruck, der die meisten E-Mail-Adressen -identifizieren kann: - -.. code-block:: pycon - - >>> addresses = """Veit - ... Veit Schiele - ... cusy GmbH - ... """ - >>> pattern = r"[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}" - >>> regex = re.compile(pattern, flags=re.IGNORECASE) - >>> regex.findall(addresses) - ['veit@cusy.io', 'veit.schiele@cusy.io', 'info@cusy.io'] - >>> regex.search(addresses) - - >>> print(regex.match(addresses)) - None - -``regex.match`` gibt ``None`` zurück, da das Muster nur dann passt, wenn es am -Anfang der Zeichenkette steht. - -Angenommen, ihr möchtet E-Mail-Adressen finden und gleichzeitig jede Adresse in -ihre drei Komponenten aufteilen: - -#. Personenname -#. Domänenname -#. Domänensuffix - -Dazu setzt ihr zunächst runde Klammern ``()`` um die zu segmentierenden Teile -des Musters: - -.. code-block:: pycon - - >>> pattern = r"([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})" - >>> regex = re.compile(pattern, flags=re.IGNORECASE) - >>> match = regex.match("veit@cusy.io") - >>> match.groups() - ('veit', 'cusy', 'io') - -:py:meth:`re.Match.groups` gibt ein :doc:`tuples` zurück, das alle Untergruppen -der Übereinstimmung enthält. - -:py:meth:`re.Pattern.findall` gibt eine Liste von Tupeln zurück, wenn das Muster -Gruppen enthält: - -.. code-block:: pycon - - >>> regex.findall(addresses) - [('veit', 'cusy', 'io'), ('veit.schiele', 'cusy', 'io'), ('info', 'cusy', 'io')] - -Auch in :py:meth:`re.Pattern.sub` können Gruppen verwendet werden wobei ``\1`` -für die erste übereinstimmende Gruppe steht, ``\2`` für die zweite :abbr:`usw. -(und so weiter)`: - -.. code-block:: pycon - - >>> regex.findall(addresses) - [('veit', 'cusy', 'io'), ('veit.schiele', 'cusy', 'io'), ('info', 'cusy', 'io')] - >>> print(regex.sub(r"Username: \1, Domain: \2, Suffix: \3", addresses)) - Veit - Veit Schiele - cusy GmbH - -Die folgende Tabelle enthält einen kurzen Überblick über Methoden für reguläre -Ausdrücke: - -+-------------------------------+-------------------------------------------------------------------------------+ -| Methode | Beschreibung | -+===============================+===============================================================================+ -| :py:func:`re.findall` | gibt alle sich nicht überschneidenden übereinstimmenden Muster in einer | -| | Zeichenkette als Liste zurück. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:func:`re.finditer` | wie ``findall``, gibt aber einen Iterator zurück. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:func:`re.match` | entspricht dem Muster am Anfang der Zeichenkette und segmentiert optional die | -| | Musterkomponenten in Gruppen; wenn das Muster übereinstimmt, wird ein | -| | ``match``-Objekt zurückgegeben, andernfalls keines. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:func:`re.search` | durchsucht die Zeichenkette nach Übereinstimmungen mit dem Muster; gibt in | -| | diesem Fall ein ``match``-Objekt zurück; im Gegensatz zu ``match`` kann die | -| | Übereinstimmung an einer beliebigen Stelle der Zeichenkette und nicht nur am | -| | Anfang stehen. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:func:`re.split` | zerlegt die Zeichenkette bei jedem Auftreten des Musters in Teile. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:func:`re.sub`, | ersetzt alle (``sub``) oder die ersten ``n`` Vorkommen (``subn``) des Musters | -| :py:func:`re.subn` | in der Zeichenkette durch einen Ersetzungsausdruck; verwendet die Symbole | -| | ``\1``, ``\2``, …, um auf die Elemente der Übereinstimmungsgruppe zu | -| | verweisen. | -+-------------------------------+-------------------------------------------------------------------------------+ -| :py:meth:`str.removeprefix` | In Python 3.9 kann dies verwendet werden, um das Suffix oder den Dateinamen | -| :py:meth:`str.removesuffix` | zu extrahieren. | -+-------------------------------+-------------------------------------------------------------------------------+ - - -.. seealso:: - * :doc:`../../appendix/regex` - * :doc:`python3:howto/regex` - * :doc:`python3:library/re` - -Konvertieren von Zeichenketten in Zahlen ----------------------------------------- - -Ihr könnt die Funktionen :class:`python3:int` und :class:`python3:float` -verwenden, um Zeichenketten in Ganzzahl- bzw. Fließkommazahlen zu konvertieren. -Wenn eine Zeichenkette übergeben wird, die nicht als Zahl des angegebenen Typs -interpretiert werden kann, lösen diese Funktionen eine -:class:`python3:ValueError`-Ausnahme aus. Ausnahmen werden in -:doc:`../control-flows/exceptions` ausführlicher erklärt. Darüber hinaus könnt -ihr :class:`python3:int` einen optionalen zweiten :doc:`../functions/params` -übergeben, der die numerische Basis angibt, die bei der Interpretation der -Zeichenfolge verwendet werden soll: - -.. code-block:: pycon - :linenos: - - >>> float("12.34") - 12.34 - >>> float("12e3") - 12000.0 - >>> int("1000") - 1000 - >>> int("1000", base=10) - 1000 - >>> int("1000", 8) - 512 - >>> int("1000", 2) - 8 - >>> int("1234", 2) - Traceback (most recent call last): - File "", line 1, in - ValueError: invalid literal for int() with base 2: '1234' - -Zeilen 5–8 - Wird kein zweiter :doc:`../functions/params` angegeben, rechnet - :class:`python3:int` mit einer Basis von ``10``. -Zeilen 9, 10 - ``1000`` wird als `Oktalzahl `_ - interpretiert. -Zeilen 11, 12 - ``1000`` wird als `Dualzahl `_ - interpretiert. -Zeilen 13–16 - ``1234`` kann nicht als Ganzzahl auf der Basis ``2`` angegeben werden. Daher - wird eine :class:`python3:ValueError`-Ausnahme ausgelöst. - -Ändern von Zeichenketten mit Listenmanipulationen -------------------------------------------------- - -Da :ref:`str `-Objekte unveränderlich sind, gibt es keine -Möglichkeit, sie direkt zu verändern wie :doc:`lists`. Ihr könnt sie jedoch in -Listen umwandeln: - -.. code-block:: pycon - - >>> palindromes = "lol level gag" - >>> palindromes_list = list(palindromes) - >>> palindromes_list.reverse() - >>> "".join(palindromes_list) - 'gag level lol' - -Objekte in Zeichenketten konvertieren -------------------------------------- - -In Python kann fast alles in eine Zeichenkette mit der eingebauten Funktion -:ref:`str ` umgewandelt werden: - -.. code-block:: pycon - - >>> data_types = [(7, "Data types", 19), (7.1, "Numbers", 19), (7.2, "Lists", 23)] - >>> ( - ... "The title of chapter " - ... + str(data_types[0][0]) - ... + " is «" - ... + data_types[0][1] - ... + "»." - ... ) - 'The title of chapter 7 is «Data types».' - -Das Beispiel verwendet :ref:`str `, um eine Ganzzahl aus der -Liste ``data_types`` in eine Zeichenkette umzuwandeln, die dann wieder -konkateniert werden, um die endgültige Zeichenkette zu bilden. - -.. note:: - Während :ref:`str ` meist verwendet wird, um für Menschen - lesbare Texte zu erzeugen, wird :func:`python3:repr` eher für - Debugging-Ausgaben oder Statusberichte verwendet, :abbr:`z.B. (zum - Beispiel)`, um Informationen über die eingebaute Python-Funktion - :func:`python3:len` zu erhalten: - - .. code-block:: pycon - - >>> repr(len) - '' - -``print()`` ------------ - -Die Funktion :func:`print` gibt Zeichenketten aus wobei andere Python-Datentypen -leicht in Strings umgewandelt und formatiert werden können, :abbr:`z.B. (zum -Beispiel)`: - -.. code-block:: pycon - - >>> import math - >>> pi = math.pi - >>> d = 28 - >>> u = pi * d - >>> print( - ... "Pi ist", - ... pi, - ... "und der Umfang bei einem Durchmesser von", - ... d, - ... "Zoll ist", - ... u, - ... "Zoll.", - ... ) - Pi ist 3.141592653589793 und der Umfang bei einem Durchmesser von 28 Zoll ist 87.96459430051421 Zoll. - -F-Strings -~~~~~~~~~ - -Mit F-Strings lassen sich die für einen Text zu detaillierten Zahlen kürzen: - -.. code-block:: pycon - - >>> print(f"Der Wert von Pi ist {pi:.3f}.") - Der Wert von Pi ist 3.142. - -In ``{pi:.3f}`` wird die Format-Spezifikation ``f`` verwendet, um die Zahl Pi -auf drei Nachkommastellen zu kürzen. - -In A/B-Testszenarien möchtet ihr oft die prozentuale Veränderung einer Kennzahl -darstellen. Mit F-Strings können sie verständlich formuliert werden: - -.. code-block:: pycon - - >>> metrics = 0.814172 - >>> print(f"Die AUC hat sich vergrößert auf {metrics:=+7.2%}") - Die AUC hat sich vergrößert auf +81.42% - -In diesem Beispiel wird die Variable ``metrics`` formatiert, wobei ``=`` die -Inhalte der Variable nach dem ``+`` übernimmt, wobei insgesamt sieben Zeichen -einschließlich des Vorzeichen, ``metrics`` und des Prozentzeichens angezeigt -werden. ``.2`` sorgt für zwei Dezimalstellen, während das ``%``-Symbol den -Dezimalwert in eine Prozentzahl umwandelt. So wird ``0.514172`` in ``+51.42%`` -umgewandelt. - -Werte lassen sich auch in binäre und hexadezimale Werte umrechnen: - -.. code-block:: pycon - - >>> block_size = 192 - >>> print(f"Binary block size: {block_size:b}") - Binary block size: 11000000 - >>> print(f"Hex block size: {block_size:x}") - Hex block size: c0 - -Es gibt auch Formatierungsangaben, die ideal geeignet sind für die :abbr:`CLI -(Command Line Interface)`-Ausgabe, :abbr:`z.B. (zum Beispiel)`: - -.. code-block:: pycon - - >>> data_types = [(7, "Data types", 19), (7.1, "Numbers", 19), (7.2, "Lists", 23)] - >>> for n, title, page in data_types: - ... print(f"{n:.1f} {title:.<25} {page: >3}") - ... - 7.0 Data types............... 19 - 7.1 Numbers.................. 19 - 7.2 Lists.................... 23 - -Allgemein sieht das Format folgendermaßen aus, wobei alle Angaben in eckigen -Klammern optional sind: - -:samp:`:[[FILL]ALIGN][SIGN][0b|0o|0x|d|n][0][WIDTH][GROUPING]["." PRECISION][TYPE]` - -In der folgenden Tabelle sind die Felder für die Zeichenkettenformatierung und -ihre Bedeutung aufgeführt: - -+-----------------------+-------------------------------------------------------+ -| Feld | Bedeutung | -+=======================+=======================================================+ -| :samp:`FILL` | Zeichen, das zum Ausfüllen von :samp:`ALIGN` verwendet| -| | wird. Der Standardwert ist ein Leerzeichen. | -+-----------------------+-------------------------------------------------------+ -| :samp:`ALIGN` | Textausrichtung und Füllzeichen: | -| | | -| | | ``<``: linksbündig | -| | | ``>``: rechtsbündig | -| | | ``^``: zentriert | -| | | ``=``: Füllzeichen nach :samp:`SIGN` | -+-----------------------+-------------------------------------------------------+ -| :samp:`SIGN` | Vorzeichen anzeigen: | -| | | -| | | ``+``: Vorzeichen bei positiven und negativen | -| | Zahlen anzeigen | -| | | ``-``: Standardwert, ``-`` nur bei negativen Zahlen | -| | oder Leerzeichen bei positiven Zahlen | -+-----------------------+-------------------------------------------------------+ -| :samp:`0b|0o|0x|d|n` | Vorzeichen für ganze Zahlen: | -| | | -| | | ``0b``: Binärzahlen | -| | | ``0o``: Oktalzahlen | -| | | ``0x``: Hexadezimalzahlen | -| | | ``d``: Standardwert, dezimale Ganzzahl zur Basis 10 | -| | | ``n``: verwendet die aktuelle | -| | ``locale``-Einstellung, um die entsprechenden | -| | Zahlentrennzeichen einzufügen | -+-----------------------+-------------------------------------------------------+ -| :samp:`0` | füllt mit Nullen auf | -+-----------------------+-------------------------------------------------------+ -| :samp:`WIDTH` | Minimale Feldbreite | -+-----------------------+-------------------------------------------------------+ -| :samp:`GROUPING` | Zahlentrennzeichen: [#]_ | -| | | -| | | ``,``: Komma als Tausendertrennzeichen | -| | | ``_``: Unterstrich für Tausendertrennzeichen | -+-----------------------+-------------------------------------------------------+ -| :samp:`.PRECISION` | | Bei Fließkommazahlen die Anzahl der Ziffern nach | -| | dem Punkt | -| | | bei nicht-numerischen Werten die maximale Länge | -+-----------------------+-------------------------------------------------------+ -| :samp:`TYPE` | Ausgabeformat als Zahlentyp oder Zeichenkette | -| | | -| | … für Ganzzahlen: | -| | | -| | | ``b``: Binärformat | -| | | ``c``: konvertiert die Ganzzahl in das | -| | entsprechende Unicode-Zeichen | -| | | ``d``: Standardwert, Dezimalzeichen | -| | | ``n``: dasselbe wie ``d``, mit dem Unterschied, | -| | dass es die aktuelle ``locale``-Einstellung | -| | verwendet, um die entsprechenden Zahlentrennzeichen | -| | einzufügen | -| | | ``o``: Oktalformat | -| | | ``x``: Hexadezimalformat zur Basis 16, wobei für | -| | die Ziffern über 9 Kleinbuchstaben verwendet werden | -| | | ``X``: Hexadezimalformat zur Basis 16, wobei für | -| | die Ziffern über 9 Großbuchstaben verwendet werden | -| | | -| | … für Fließkommazahlen: | -| | | -| | | ``e``: Exponent mit ``e`` als Trennzeichen zwischen | -| | Koeffizient und Exponent | -| | | ``E``: Exponent mit ``E`` als Trennzeichen zwischen | -| | Koeffizient und Exponent | -| | | ``g``: Standardwert für Fließkommazahlen, wobei der | -| | Exponent eine feste Breite für große und | -| | kleine Zahlen erhält | -| | | ``G``: Wie ``g``, wechselt aber zu ``E``, wenn | -| | die Zahl zu groß wird. Die Darstellungen von | -| | Unendlich und NaN werden ebenfalls in Großbuchstaben| -| | geschrieben | -| | | ``n``: Wie ``g`` mit dem Unterschied, dass es die | -| | aktuelle ``locale``-Einstellung verwendet, um die | -| | die entsprechenden Zahlentrennzeichen einzufügen | -| | | ``%``: Prozentsatz. Multipliziert die Zahl mit 100 | -| | und zeigt sie im festen Format ``f`` an, gefolgt | -| | von einem Prozentzeichen | -+-----------------------+-------------------------------------------------------+ - -.. [#] Der Formatbezeichner ``n`` formatiert eine Zahl in einer lokal angepassten - Weise, :abbr:`z.B. (zum Beispiel)`: - - .. code-block:: pycon - - >>> value = 635372 - >>> import locale - >>> locale.setlocale(locale.LC_NUMERIC, "en_US.utf-8") - 'en_US.utf-8' - >>> print(f"{value:n}") - 635,372 - -.. tip:: - Eine gute Quelle für F-Strings ist die Hilfe-Funktion: - - .. code-block:: pycon - - >>> help() - help> FORMATTING - ... - - Ihr könnt die Hilfe hier durchblättern und viele Beispiele finden. - - Mit :kbd:`:`–:kbd:`q` und :kbd:`⏎` könnt ihr die Hilfe-Funktion wieder - verlassen. - -.. seealso:: - * `PyFormat `_ - * :ref:`python3:f-strings` - * :pep:`498` - -Fehlersuche in F-Strings -:::::::::::::::::::::::: - -In Python 3.8 wurde ein Spezifizierer eingeführt, der bei der Fehlersuche in -F-String-Variablen hilft. Durch Hinzufügen eines Gleichheitszeichens ``=`` wird der -Code innerhalb des F-Strings aufgenommen: - -.. code-block:: pycon - - >>> uid = "veit" - >>> print(f"My name is {uid.capitalize()=}") - My name is uid.capitalize()='Veit' - -Formatierung von Datums-, Zeitformaten und IP-Adressen -:::::::::::::::::::::::::::::::::::::::::::::::::::::: - -:py:mod:`datetime` unterstützt die Formatierung von Zeichenketten mit der -gleichen Syntax wie die :py:meth:`strftime `-Methode -für diese Objekte. - -.. code-block:: pycon - - >>> import datetime - >>> today = datetime.date.today() - >>> print(f"Today is {today:%d %B %Y}.") - Today is 26 November 2023. - -Das :py:mod:`ipaddress`-Modul von Python unterstützt auch die Formatierung von -``IPv4Address``- und ``IPv6Address``-Objekten. - -Schließlich können Bibliotheken von Drittanbietern auch ihre eigene -Unterstützung für die Formatierung von Strings hinzufügen, indem sie eine -``__format__``-Methode zu ihren Objekten hinzufügen. - -.. seealso:: - * :ref:`format-codes` - * `Python strftime cheatsheet `_ - -Eingebaute Module für Zeichenketten ------------------------------------ - -Die Python-Standardbibliothek enthält eine Reihe eingebauter Module, mit denen -ihr Zeichenketten managen könnt: - -.. _string-modules: - -+-----------------------+-------------------------------------------------------------------------------+ -| Modul | Beschreibung | -+=======================+===============================================================================+ -| :py:mod:`string` | vergleicht mit Konstanten wie :py:data:`string.digits` oder | -| | :py:data:`string.whitespace` | -+-----------------------+-------------------------------------------------------------------------------+ -| :py:mod:`re` | sucht und ersetzt Text mit regulären Ausdrücken | -+-----------------------+-------------------------------------------------------------------------------+ -| :py:mod:`struct` | konvertiert zwischen Python-Werten und C-Strukturen, die als | -| | Python-Bytes-Objekte dargestellt werden. | -+-----------------------+-------------------------------------------------------------------------------+ -| :py:mod:`difflib` | hilft beim Berechnen von Deltas, beim Auffinden von Unterschieden zwischen | -| | Zeichenketten oder Sequenzen und beim Erstellen von Patches und Diff-Dateien | -+-----------------------+-------------------------------------------------------------------------------+ -| :py:mod:`textwrap` | umbricht und füllt Text, formatiert Text mit Zeilenumbrüchen oder Leerzeichen | -+-----------------------+-------------------------------------------------------------------------------+ - -.. seealso:: - * :doc:`Manipulation von Zeichenketten mit pandas - ` - -Checks ------- - -* Könnt ihr :abbr:`z.B. (zum Beispiel)` eine Zeichenkette mit einer ganzen Zahl - addieren oder multiplizieren, oder mit einer Gleitkommazahl oder einer - komplexen Zahl? - -* Wie könnt ihr eine Überschrift wie ``variables and expressions`` so abändern, - dass sie keine Leerzeichen mehr enthält und besser als Dateinamen verwendet - werden kann? - -* Welche der folgenden Zeichenketten können nicht in Zahlen umgewandelt werden - und warum? - - * ``int("1e2")`` - * ``int(1e+2)`` - * ``int("1+2")`` - * ``int("+2")`` - -* Wenn ihr überprüfen wollt, ob eine Zeile mit ``.. note::`` beginnt, welche - Methode würdet ihr verwenden? Gibt es auch noch andere Möglichkeiten? - -* Angenommen, ihr habt eine Zeichenkette mit Ausrufezeichen, Anführungszeichen - und Zeilenumbrruch. Wie können diese aus der Zeichenkette entfernt werden? - -* Wie könnt ihr **alle** Leerräume und Satzzeichen aus einer Zeichenfolge in - einen Bindestrich (``-``) ändern? - -* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das - :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten - nützlich wäre? - - * beim Lesen und Schreiben einer Binärdatei - * beim Lesen von einer externen Schnittstelle, wobei die Daten genau so - gespeichert werden sollen, wie sie übermittelt wurden - -* Welchen regulären Ausdruck würdet ihr verwenden, um Zeichenfolgen zu finden, - die die Zahlen zwischen -3 und +3 darstellen? - -* Welchen regulären Ausdruck würdet ihr verwenden, um Hexadezimalwerte zu - finden? diff --git a/docs/types/strings/built-in-modules/index.rst b/docs/types/strings/built-in-modules/index.rst new file mode 100644 index 00000000..e07e87aa --- /dev/null +++ b/docs/types/strings/built-in-modules/index.rst @@ -0,0 +1,38 @@ +Eingebaute Module für Zeichenketten +=================================== + +Die Python-Standardbibliothek enthält eine Reihe eingebauter Module, mit denen +ihr Zeichenketten managen könnt: + +.. _string-modules: + ++-----------------------+-------------------------------------------------------------------------------+ +| Modul | Beschreibung | ++=======================+===============================================================================+ +| :py:mod:`string` | vergleicht mit Variablen wie :py:data:`string.digits` oder | +| | :py:data:`string.whitespace` | ++-----------------------+-------------------------------------------------------------------------------+ +| :py:mod:`re` | sucht und ersetzt Text mit regulären Ausdrücken | ++-----------------------+-------------------------------------------------------------------------------+ +| :py:mod:`struct` | konvertiert zwischen Python-Werten und C-Strukturen, die als | +| | Python-Bytes-Objekte dargestellt werden. | ++-----------------------+-------------------------------------------------------------------------------+ +| :py:mod:`difflib` | hilft beim Berechnen von Deltas, beim Auffinden von Unterschieden zwischen | +| | Zeichenketten oder Sequenzen und beim Erstellen von Patches und Diff-Dateien | ++-----------------------+-------------------------------------------------------------------------------+ +| :py:mod:`textwrap` | umbricht und füllt Text, formatiert Text mit Zeilenumbrüchen oder Leerzeichen | ++-----------------------+-------------------------------------------------------------------------------+ + +.. _end-string-modules: + +.. seealso:: + * :doc:`Manipulation von Zeichenketten mit pandas + ` + * `humanize `_ + +.. toctree:: + :titlesonly: + :hidden: + + string + re diff --git a/docs/types/strings/built-in-modules/re.rst b/docs/types/strings/built-in-modules/re.rst new file mode 100644 index 00000000..1a91aa64 --- /dev/null +++ b/docs/types/strings/built-in-modules/re.rst @@ -0,0 +1,152 @@ +``re`` +====== + +Die Python-Standard-Bibliothek :doc:`re ` enthält ebenfalls +Funktionen für die Arbeit mit Zeichenketten. Dabei bietet ``re`` ausgefeiltere +Möglichkeiten zur Musterextraktion und -ersetzung als der +:ref:`str `-Typ. + +.. code-block:: pycon + + >>> import re + >>> re.sub("\n", "", welcome) + 'Hello pythonistas!' + +Hier wird der reguläre Ausdruck zunächst kompiliert und dann seine +:py:meth:`re.Pattern.sub`-Methode für den übergebenen Text aufgerufen. Ihr könnt +den Ausdruck selbst mit :py:func:`re.compile` kompilieren und so ein +wiederverwendbares ``regex``-Objekt bilden, das auf unterschiedliche +Zeichenketten angewendet die CPU-Zyklen verringert: + +.. code-block:: pycon + + >>> regex = re.compile("\n") + >>> regex.sub("", welcome) + 'Hello pythonistas!' + +Wenn ihr stattdessen eine Liste aller Muster erhalten möchtet, die dem +``regex``-Objekt entsprechen, könnt ihr die +:py:meth:`re.Pattern.findall`-Methode verwenden: + +.. code-block:: pycon + + >>> regex.findall(welcome) + ['\n'] + +.. note:: + Um das umständliche Escaping mit ``\`` in einem regulären Ausdruck zu + vermeiden, könnt ihr rohe String-Literale wie ``r'C:\PATH\TO\FILE'`` + anstelle des entsprechenden ``'C:\\PATH\\TO\\FILE'`` verwenden. + +:py:meth:`re.Pattern.match` und :py:meth:`re.Pattern.search` sind eng mit +:py:meth:`re.Pattern.findall` verwandt. Während ``findall`` alle +Übereinstimmungen in einer Zeichenkette zurückgibt, gibt ``search`` nur die +erste Übereinstimmung und ``match`` nur Übereinstimmungen am Anfang der +Zeichenkette zurück. Als weniger triviales Beispiel betrachten wir einen +Textblock und einen regulären Ausdruck, der die meisten E-Mail-Adressen +identifizieren kann: + +.. code-block:: pycon + + >>> addresses = """Veit + ... Veit Schiele + ... cusy GmbH + ... """ + >>> pattern = r"[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}" + >>> regex = re.compile(pattern, flags=re.IGNORECASE) + >>> regex.findall(addresses) + ['veit@cusy.io', 'veit.schiele@cusy.io', 'info@cusy.io'] + >>> regex.search(addresses) + + >>> print(regex.match(addresses)) + None + +``regex.match`` gibt ``None`` zurück, da das Muster nur dann passt, wenn es am +Anfang der Zeichenkette steht. + +Angenommen, ihr möchtet E-Mail-Adressen finden und gleichzeitig jede Adresse in +ihre drei Komponenten aufteilen: + +#. Personen-Name +#. Domänen-Name +#. Domänen-Suffix + +Dazu setzt ihr zunächst runde Klammern ``()`` um die zu segmentierenden Teile +des Musters: + +.. code-block:: pycon + + >>> pattern = r"([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})" + >>> regex = re.compile(pattern, flags=re.IGNORECASE) + >>> match = regex.match("veit@cusy.io") + >>> match.groups() + ('veit', 'cusy', 'io') + +:py:meth:`re.Match.groups` gibt ein :doc:`../../sequences-sets/tuples` zurück, +das alle Untergruppen der Übereinstimmung enthält. + +:py:meth:`re.Pattern.findall` gibt eine Liste von Tupeln zurück, wenn das Muster +Gruppen enthält: + +.. code-block:: pycon + + >>> regex.findall(addresses) + [('veit', 'cusy', 'io'), ('veit.schiele', 'cusy', 'io'), ('info', 'cusy', 'io')] + +Auch in :py:meth:`re.Pattern.sub` können Gruppen verwendet werden wobei ``\1`` +für die erste übereinstimmende Gruppe steht, ``\2`` für die zweite :abbr:`usw. +(und so weiter)`: + +.. code-block:: pycon + + >>> regex.findall(addresses) + [('veit', 'cusy', 'io'), ('veit.schiele', 'cusy', 'io'), ('info', 'cusy', 'io')] + >>> print(regex.sub(r"Username: \1, Domain: \2, Suffix: \3", addresses)) + Veit + Veit Schiele + cusy GmbH + +Die folgende Tabelle enthält einen kurzen Überblick über Methoden für reguläre +Ausdrücke: + ++-------------------------------+-------------------------------------------------------------------------------+ +| Methode | Beschreibung | ++===============================+===============================================================================+ +| :py:func:`re.findall` | gibt alle sich nicht überschneidenden übereinstimmenden Muster in einer | +| | Zeichenkette als Liste zurück. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:func:`re.finditer` | wie ``findall``, gibt aber einen Iterator zurück. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:func:`re.match` | entspricht dem Muster am Anfang der Zeichenkette und segmentiert optional die | +| | Musterkomponenten in Gruppen; wenn das Muster übereinstimmt, wird ein | +| | ``match``-Objekt zurückgegeben, andernfalls keines. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:func:`re.search` | durchsucht die Zeichenkette nach Übereinstimmungen mit dem Muster; gibt in | +| | diesem Fall ein ``match``-Objekt zurück; im Gegensatz zu ``match`` kann die | +| | Übereinstimmung an einer beliebigen Stelle der Zeichenkette und nicht nur am | +| | Anfang stehen. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:func:`re.split` | zerlegt die Zeichenkette bei jedem Auftreten des Musters in Teile. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:func:`re.sub`, | ersetzt alle (``sub``) oder die ersten ``n`` Vorkommen (``subn``) des Musters | +| :py:func:`re.subn` | in der Zeichenkette durch einen Ersetzungsausdruck; verwendet die Symbole | +| | ``\1``, ``\2``, …, um auf die Elemente der Übereinstimmungsgruppe zu | +| | verweisen. | ++-------------------------------+-------------------------------------------------------------------------------+ +| :py:meth:`str.removeprefix` | In Python 3.9 kann dies verwendet werden, um das Suffix oder den Dateinamen | +| :py:meth:`str.removesuffix` | zu extrahieren. | ++-------------------------------+-------------------------------------------------------------------------------+ + + +.. seealso:: + * :doc:`regex` + * :doc:`python3:howto/regex` + * :doc:`python3:library/re` + +Checks +------ + +* Welchen regulären Ausdruck würdet ihr verwenden, um Zeichenfolgen zu finden, + die die Zahlen zwischen -3 und +3 darstellen? +* Welchen regulären Ausdruck würdet ihr verwenden, um Hexadezimalwerte zu + finden? diff --git a/docs/appendix/regex.rst b/docs/types/strings/built-in-modules/regex.rst similarity index 99% rename from docs/appendix/regex.rst rename to docs/types/strings/built-in-modules/regex.rst index 7e983d9c..fd509042 100644 --- a/docs/appendix/regex.rst +++ b/docs/types/strings/built-in-modules/regex.rst @@ -1,3 +1,5 @@ +:orphan: + Reguläre Ausdrücke ================== diff --git a/docs/types/strings/built-in-modules/string.rst b/docs/types/strings/built-in-modules/string.rst new file mode 100644 index 00000000..10d836b2 --- /dev/null +++ b/docs/types/strings/built-in-modules/string.rst @@ -0,0 +1,332 @@ +``string`` +========== + +Die meisten der Python-:ref:`String-Methoden ` sind im +:ref:`str `-Typ integriert, so dass alle ``str``-Objekte +automatisch über sie verfügen: + +.. code-block:: pycon + + >>> welcome = "hello pythonistas!\n" + >>> welcome.isupper() + False + >>> welcome.isalpha() + False + >>> welcome[0:5].isalpha() + True + >>> welcome.capitalize() + 'Hello pythonistas!\n' + >>> welcome.title() + 'Hello Pythonistas!\n' + >>> welcome.strip() + 'Hello pythonistas!' + >>> welcome.split(" ") + ['hello', 'pythonistas!\n'] + >>> chunks = [snippet.strip() for snippet in welcome.split(" ")] + >>> chunks + ['hello', 'pythonistas!'] + >>> " ".join(chunks) + 'hello pythonistas!' + >>> welcome.replace("\n", "") + 'hello pythonistas!' + +Im Folgenden findet ihr einen Überblick über die häufigsten +:ref:`String-Methoden `: + ++---------------------------+---------------------------------------------------------------+ +| Methode | Beschreibung | ++===========================+===============================================================+ +| :py:meth:`str.count` | gibt die Anzahl der sich nicht überschneidenden Vorkommen der | +| | Zeichenkette zurück. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.endswith` | gibt ``True`` zurück, wenn die Zeichenkette mit dem Suffix | +| | endet. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.startswith` | gibt ``True`` zurück, wenn die Zeichenkette mit dem Präfix | +| | beginnt. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.join` | verwendet die Zeichenkette als Begrenzer für die Verkettung | +| | einer Folge anderer Zeichenketten. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.index` | gibt die Position des ersten Zeichens in der Zeichenkette | +| | zurück, wenn es in der Zeichenkette gefunden wurde; löst einen| +| | ``ValueError`` aus, wenn es nicht gefunden wurde. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.find` | gibt die Position des ersten Zeichens des ersten Vorkommens | +| | der Teil-Zeichenkette in der Zeichenkette zurück; wie | +| | ``index``, gibt aber ``-1`` zurück, wenn nichts gefunden | +| | wurde. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.rfind` | Rückgabe der Position des ersten Zeichens des letzten | +| | Vorkommens der Teil-Zeichenkette in der Zeichenkette; gibt | +| | ``-1`` zurück, wenn nichts gefunden wurde. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.replace` | ersetzt Vorkommen einer Zeichenkette durch eine andere | +| | Zeichenkette. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.strip`, | schneiden Leerzeichen ab, einschließlich Zeilenumbrüchen. | +| :py:meth:`str.rstrip`, | | +| :py:meth:`str.lstrip` | | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.split` | zerlegt eine Zeichenkette in eine Liste von Teil-Zeichenketten| +| | unter Verwendung des übergebenen Trennzeichens. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.lower` | konvertiert alphabetische Zeichen in Kleinbuchstaben. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.upper` | konvertiert alphabetische Zeichen in Großbuchstaben. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.casefold` | konvertiert Zeichen in Kleinbuchstaben und konvertiert alle | +| | regionsspezifischen variablen Zeichenkombinationen in eine | +| | gemeinsame vergleichbare Form. | ++---------------------------+---------------------------------------------------------------+ +| :py:meth:`str.ljust`, | linksbündig bzw. rechtsbündig; füllt die gegenüberliegende | +| :py:meth:`str.rjust` | Seite der Zeichenkette mit Leerzeichen (oder einem anderen | +| | Füllzeichen) auf, um eine Zeichenkette mit einer Mindestbreite| +| | zu erhalten. | ++---------------------------+---------------------------------------------------------------+ + +``str.split`` und ``str.join`` +------------------------------ + +Während :meth:`python3:str.split` eine Liste von Zeichenfolgen zurückgibt, nimmt +:meth:`python3:str.join` eine Liste von Zeichenketten und fügt sie zu einer +einzigen Zeichenkette zusammen. Normalerweise verwendet +:meth:`python3:str.split` Leerraum als Begrenzungszeichen für die aufzuteilenden +Zeichenketten, aber ihr könnt dieses Verhalten mit einem optionalen +:doc:`../../../functions/params` ändern. + +.. warning:: + Die Verkettung von Zeichenketten mit ``+`` ist zwar nützlich, aber nicht + effizient, wenn es darum geht, eine große Anzahl von Zeichenketten zu einer + einzigen Zeichenkette zusammenzufügen, da jedes Mal, wenn ``+`` angewendet + wird, ein neues Zeichenketten-Objekt erstellt wird. :samp:`"Hello" + + "Pythonistas!"` erzeugt zwei Objekte, von denen eines sofort wieder verworfen + wird. + +Wenn ihr mit :meth:`python3:str.join` Zeichenfolgen zusammenführt, könnt ihr +zwischen die Zeichenfolgen beliebige Zeichen einfügen: + +.. code-block:: pycon + + >>> " :: ".join(["License", "OSI Approved"]) + 'License :: OSI Approved' + +Ihr könnt auch eine leere Zeichenkette, ``""``, verwenden, :abbr:`z.B. (zum +Beispiel)` für die CamelCase-Schreibweise von Python-Klassen: + +.. code-block:: pycon + + >>> "".join(["My", "Class"]) + 'MyClass' + +:meth:`python3:str.split` wird meist verwendet um Zeichenketten an Leerräumen zu +trennen. Ihr könnt eine Zeichenkette jedoch auch an einer bestimmten anderen +Zeichenfolge trennen, indem ihr einen optionalen +:doc:`../../../functions/params` übergebt: + +.. code-block:: pycon + + >>> example = "1. You can have\n\twhitespaces, newlines\n and tabs mixed in\n\tthe string." + >>> example.split() + ['1.', 'You', 'can', 'have', 'whitespaces,', 'newlines', 'and', 'tabs', 'mixed', 'in', 'the', 'string.'] + >>> license = "License :: OSI Approved" + >>> license.split(" :: ") + ['License', 'OSI Approved'] + +Manchmal ist es nützlich, dem letzten Feld in einer Zeichenkette zu erlauben, +beliebigen Text zu enthalten. Ihr könnt dies tun, indem ihr einen optionalen +zweiten :doc:`../../../functions/params` angebt, wie viele Teilungen +durchgeführt werden sollen: + +.. code-block:: pycon + + >>> example.split(" ", 1) + ['1.', 'You can have\n\twhitespaces, newlines\n and tabs mixed in\n\tthe string.'] + +Wenn ihr :meth:`python3:str.split` mit dem optionalen zweiten Argument verwenden +wollt, müsst ihr zunächst ein erstes Argument angeben. Um zu erreichen, dass bei +allen Leerzeichen geteilt wird, verwendet :doc:`../../none` als erstes Argument: + +.. code-block:: pycon + + >>> example.split(None, 8) + ['1.', 'You', 'can', 'have', 'whitespaces,', 'newlines', 'and', 'tabs', 'mixed in\n\tthe string.'] + +.. tip:: + Ich verwende :meth:`python3:str.split` und :meth:`python3:str.join` + ausgiebig, meist für Textdateien, die von anderen Programmen erzeugt wurden. + Zum Schreiben von + :doc:`Python4DataScience:data-processing/serialisation-formats/csv/index`- + oder + :doc:`Python4DataScience:data-processing/serialisation-formats/json/index`-Dateien + verwende ich jedoch meist die zugehörigen Python-Bibliotheken. + +Leerraum entfernen +------------------ + +:py:meth:`str.strip` gibt eine neue Zeichenkette zurück, die sich von der +ursprünglichen Zeichenkette nur dadurch unterscheidet, dass alle Leerzeichen am +Anfang oder Ende der Zeichenkette entfernt wurden. :py:meth:`str.lstrip` und +:py:meth:`str.rstrip` arbeiten ähnlich, entfernen jedoch nur die Leerzeichen am +linken :abbr:`bzw. (beziehungsweise)` rechten Ende der ursprünglichen +Zeichenkette: + +.. code-block:: pycon + + >>> example = " whitespaces, newlines \n\tand tabs. \n" + >>> example.strip() + 'whitespaces, newlines \n\tand tabs.' + >>> example.lstrip() + 'whitespaces, newlines \n\tand tabs. \n' + >>> example.rstrip() + ' whitespaces, newlines \n\tand tabs.' + +In diesem Beispiel werden die *Newlines* ``\n`` als Leerzeichen betrachtet. Die +genaue Zuordnung kann sich von Betriebssystem zu Betriebssystem unterscheiden. +Ihr könnt herausfinden, was Python als Leerzeichen betrachtet, indem ihr auf die +Variable :py:data:`string.whitespace` zugreift. Bei mir wird das folgende +zurückgegeben: + +.. code-block:: pycon + + >>> import string + >>> string.whitespace + ' \t\n\r\x0b\x0c' + +Die im Hexadezimalformat (``\x0b``, ``\x0c``) angegebenen Zeichen stellen die +vertikalen Tabulator- und Vorschubzeichen dar. + +.. tip:: + Ändert nicht den Wert dieser Variablen um die Funktionsweise von + :py:meth:`str.strip` :abbr:`usw. (und so weiter)` zu beeinflussen. Welche + Zeichen diese Methoden entfernen, könnt ihr Zeichen als zusätzlichen + :doc:`../../../functions/params` übergeben: + + .. code-block:: pycon + + >>> url = "https://www.cusy.io/" + >>> url.strip("htps:/w.") + 'cusy.io' + +Suche in Zeichenketten +---------------------- + +:ref:`str `-Objekte bieten mehrere Methoden für die einfache +Suche nach Zeichenketten: Die vier grundlegenden Methoden für die Suche nach +Zeichenketten sind :py:meth:`str.find`, :py:meth:`str.rfind`, +:py:meth:`str.index` und :py:meth:`str.rindex`. Eine verwandte Methode, +:py:meth:`str.count`, zählt, wie oft eine Zeichenfolge in einer anderen +Zeichenfolge gefunden werden kann. + +:py:meth:`str.find` benötigt einen einzigen :doc:`../../../functions/params`: +die gesuchte Teil-Zeichenkette; zurückgegeben wird dann die Position des ersten +Vorkommens oder ``-1``, wenn es kein Vorkommen gibt: + +.. code-block:: pycon + + >>> hipy = "Hello Pythonistas!\n" + >>> hipy.find("\n") + 18 + +:py:meth:`str.find` kann auch ein oder zwei zusätzliche +:doc:`../../../functions/params` annehmen: + +``start`` + Zahl, der Zeichen am Anfang der zu durchsuchenden Zeichenkette, die + ignoriert werden soll. +``end`` + Zahl, der Zeichen am Ende der zu durchsuchenden Zeichenkette, die ignoriert + werden soll. + +Im Gegensatz zu :py:meth:`find` beginnt :py:meth:`rfind` die Suche am Ende der +Zeichenkette und gibt daher die Position des letzten Vorkommens zurück. + +:py:meth:`index` und :py:meth:`rindex` unterscheiden sich von :py:meth:`find` +und :py:meth:`rfind` dadurch, dass statt dem Rückgabewert ``-1`` eine +:class:`python3:ValueError`-Ausnahme ausgelöst wird. + +Ihr könnt zwei weitere :ref:`String-Methoden ` +verwenden, um Strings zu suchen: :py:meth:`str.startswith` und +:py:meth:`str.endswith`. Diese Methoden geben ``True``- oder ``False`` als +Ergebnis zurück, je nachdem, ob die Zeichenkette, auf die sie angewendet werden, +mit einer der als :doc:`../../../functions/params` angegebenen Zeichenketten +beginnt oder endet: + +.. code-block:: pycon + + >>> hipy.endswith("\n") + True + >>> hipy.endswith(("\n", "\r")) + True + +Darüber hinaus gibt es einige Methoden, mit denen die Eigenschaft einer +Zeichenkette überprüft werden kann: + ++---------------------------+---------------+---------------+---------------+---------------+---------------+ +| Methode | ``[!#$%…]`` | ``[a-zA-Z]`` | ``[¼½¾]`` | ``[¹²³]`` | ``[0-9]`` | ++===========================+===============+===============+===============+===============+===============+ +| :py:meth:`str.isprintable`| ✅ | ✅ | ✅ | ✅ | ✅ | ++---------------------------+---------------+---------------+---------------+---------------+---------------+ +| :py:meth:`str.isalnum` | ❌ | ✅ | ✅ | ✅ | ✅ | ++---------------------------+---------------+---------------+---------------+---------------+---------------+ +| :py:meth:`str.isnumeric` | ❌ | ❌ | ✅ | ✅ | ✅ | ++---------------------------+---------------+---------------+---------------+---------------+---------------+ +| :py:meth:`str.isdigit` | ❌ | ❌ | ❌ | ✅ | ✅ | ++---------------------------+---------------+---------------+---------------+---------------+---------------+ +| :py:meth:`str.isdecimal` | ❌ | ❌ | ❌ | ❌ | ✅ | ++---------------------------+---------------+---------------+---------------+---------------+---------------+ + +:py:meth:`str.isspace` prüft auf Leerzeichen. + +Zeichenketten ändern +-------------------- + +:ref:`str `-Objekte sind :term:`unveränderlich +`, aber sie verfügen über mehrere Methoden, die eine +modifizierte Version der ursprünglichen Zeichenkette zurückgeben können. + +:py:meth:`str.replace` könnt ihr verwenden, um Vorkommen des ersten +:doc:`../../../functions/params` durch den zweiten zu ersetzen, :abbr:`z.B. (zum +Beispiel)`: + +.. code-block:: pycon + + >>> hipy.replace("\n", "\n\r") + 'Hello Pythonistas!\n\r' + +:py:meth:`str.maketrans` und :py:meth:`str.translate` können zusammen verwendet +werden, um Zeichen in Zeichenketten in andere Zeichen zu übersetzen, :abbr:`z.B. +(zum Beispiel)`: + +.. code-block:: pycon + :linenos: + + >>> hipy = "Hello Pythonistas!\n" + >>> trans_map = hipy.maketrans(" ", "-", "!\n") + >>> hipy.translate(trans_map) + 'Hello-Pythonistas' + +Zeile 2 + :py:meth:`str.maketrans` wird verwendet, um eine Übersetzungstabelle aus den + beiden Zeichenketten-Argumenten zu erstellen. Die beiden Argumente müssen + jeweils die gleiche Anzahl von Zeichen enthalten. Als drittes Argument + werden Zeichen übergeben, die nicht zurückgegeben werden sollen. +Zeile 3 + Die von :py:meth:`str.maketrans` erzeugte Tabelle wird an + :py:meth:`str.translate` übergeben. + +Checks +------ + +* Wie könnt ihr eine Überschrift wie ``variables and expressions`` so abändern, + dass sie keine Leerzeichen mehr enthält und besser als Dateinamen verwendet + werden kann? + +* Wenn ihr überprüfen wollt, ob eine Zeile mit ``.. note::`` beginnt, welche + Methode würdet ihr verwenden? Gibt es auch noch andere Möglichkeiten? + +* Angenommen, ihr habt eine Zeichenkette mit Ausrufezeichen, Anführungszeichen + und Zeilenumbruch. Wie können diese aus der Zeichenkette entfernt werden? + +* Wie könnt ihr **alle** Leerräume und Satzzeichen aus einer Zeichenfolge in + einen Bindestrich (``-``) ändern? diff --git a/docs/appendix/encodings.rst b/docs/types/strings/encodings.rst similarity index 68% rename from docs/appendix/encodings.rst rename to docs/types/strings/encodings.rst index d7739e8f..2bcd8ea7 100644 --- a/docs/appendix/encodings.rst +++ b/docs/types/strings/encodings.rst @@ -1,6 +1,53 @@ Unicode und Zeichenkodierungen ============================== +Sonderzeichen und Escape-Sequenzen +---------------------------------- + +``\n`` steht für das *Newline*-Zeichen und ``\t`` für das Tabulator-Zeichen. +Zeichenfolgen, die mit einem Backslash beginnen und zur Darstellung anderer +Zeichen verwendet werden, werden Escape-Sequenzen genannt. Escape-Sequenzen +werden in der Regel verwendet, um Sonderzeichen darzustellen, :abbr:`d.h. (das +heißt)` Zeichen, für die es keine einstellige druckbare Darstellung gibt. + +Hier sind weitere Zeichen, die ihr mit dem Escape-Zeichen erhalten könnt: + ++--------------------------+--------------------------+--------------------------+ +| Escape-Sequenz | Ausgabe | Erläuterung | ++==========================+==========================+==========================+ +| ``\\`` | ``\`` | Backslash | ++--------------------------+--------------------------+--------------------------+ +| ``\'`` | ``'`` | einfaches | +| | | Anführungszeichen | ++--------------------------+--------------------------+--------------------------+ +| ``\"`` | ``"`` | doppeltes | +| | | Anführungszeichen | ++--------------------------+--------------------------+--------------------------+ +| ``\b`` | | Backspace (``BS``) | ++--------------------------+--------------------------+--------------------------+ +| ``\n`` | | ASCII Linefeed ``(LF``) | ++--------------------------+--------------------------+--------------------------+ +| ``\r`` | | ASCII Carriage Return | +| | | (``CR``) | ++--------------------------+--------------------------+--------------------------+ +| ``\t`` | | Tabulator (``TAB``) | ++--------------------------+--------------------------+--------------------------+ +| :samp:`\u{00B5}` | ``µ`` | Unicode 16 bit | ++--------------------------+--------------------------+--------------------------+ +| :samp:`\U{000000B5}` | ``µ`` | Unicode 32 bit | ++--------------------------+--------------------------+--------------------------+ +| :samp:`\N{{SNAKE}}` | ``🐍`` | Unicode Emoji name | ++--------------------------+--------------------------+--------------------------+ + +Zeilen 1–7 + Der ASCII-Zeichensatz, der von Python verwendet wird und der + Standard-Zeichensatz auf fast allen Computern ist, definiert eine ganze + Reihe weiterer Sonderzeichen. +Zeilen 8–9 + Unicode-Escape-Sequenzen. +Zeile 10 + Unicode-Namen zur Angabe eines Unicode-Zeichens. + Es gibt Dutzende von Zeichenkodierungen. Einen Überblick über die Encodings von Python erhaltet ihr in :ref:`python3:encodings-overview`. @@ -8,25 +55,22 @@ Das ``string``-Modul -------------------- Das :doc:`string `-Modul von Python unterscheidet die -folgenden String-Konstanten, die alle in den ASCII-Zeichensatz fallen: - -.. code-block:: python - - # Some strings for ctype-style character classification - whitespace = " \t\n\r\v\f" - ascii_lowercase = "abcdefghijklmnopqrstuvwxyz" - ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - ascii_letters = ascii_lowercase + ascii_uppercase - digits = "0123456789" - hexdigits = digits + "abcdef" + "ABCDEF" - octdigits = "01234567" - punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" - printable = digits + ascii_letters + punctuation + whitespace - -Die meisten dieser Konstanten sollten in ihrem Bezeichnernamen selbsterklärend -sein. ``hexdigits`` und ``octdigits`` beziehen sich auf die -Hexadezimal- :abbr:`bzw. (beziehungsweise)` Oktalwerte. Ihr könnt diese -Konstanten für alltägliche String-Manipulation verwenden: +folgenden String-Variablen, die alle in den ASCII-Zeichensatz fallen: + +* :py:data:`string.whitespace` ``= " \t\n\r\v\f"`` +* :py:data:`string.ascii_lowercase` ``= "abcdefghijklmnopqrstuvwxyz"`` +* :py:data:`string.ascii_uppercase` ``= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"`` +* :py:data:`string.ascii_letters` ``= ascii_lowercase + ascii_uppercase`` +* :py:data:`string.digits` ``= "0123456789"`` +* :py:data:`string.hexdigits` ``= digits + "abcdef" + "ABCDEF"`` +* :py:data:`string.octdigits` ``= "01234567"`` +* :py:data:`string.punctuation` ``= r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""`` +* :py:data:`string.printable` ``= digits + ascii_letters + punctuation + whitespace`` + +meisten dieser Variablen sollten in ihrem Bezeichnernamen selbsterklärend sein. +``hexdigits`` und ``octdigits`` beziehen sich auf die Hexadezimal- :abbr:`bzw. +(beziehungsweise)` Oktalwerte. Ihr könnt diese Variablen für alltägliche +String-Manipulation verwenden: .. code-block:: pycon @@ -70,12 +114,12 @@ Codepunkten und definiert mehrere verschiedene Kodierungen aus einem einzigen Zeichensatz. UTF-8 ist ein Kodierungsschema für die Darstellung von Unicode-Zeichen als Binärdaten mit einem oder mehreren Bytes pro Zeichen. -Kodierung und Dekodierung in Python 3 -------------------------------------- +Kodierung und Dekodierung +------------------------- Der :ref:`str `-Typ ist für die Darstellung von menschenlesbarem Text gedacht und kann alle Unicode-Zeichen enthalten. Der -:ref:`bytes-Typ ` hingegen repräsentiert Binärdaten, die +:ref:`bytes `-Typ hingegen repräsentiert Binärdaten, die nicht von vornherein mit einer Kodierung versehen sind. :meth:`python3:str.encode` und :meth:`python3:bytes.decode` sind die Methoden des Übergangs vom einen zum anderen: diff --git a/docs/types/strings/index.rst b/docs/types/strings/index.rst new file mode 100644 index 00000000..4a1cfac4 --- /dev/null +++ b/docs/types/strings/index.rst @@ -0,0 +1,86 @@ +Zeichenketten +============= + +Die Verarbeitung von Zeichenketten ist eine der Stärken von Python. Es gibt +viele Optionen zur Begrenzung von Zeichenketten: + +.. blacken-docs:off + +.. code-block:: python + + "Eine Zeichenfolge in doppelten Anführungszeichen kann 'einfache Anführungszeichen' enthalten." + 'Eine Zeichenfolge in einfachen Anführungszeichen kann "doppelte Anführungszeichen" enthalten.' + """\tEine Zeichenkette, die mit einem Tabulator beginnt und mit einem Zeilenumbruchzeichen endet.\n""" + """Dies ist eine Zeichenkette in dreifach doppelten Anführungszeichen, die + einzige Zeichenkette, die echte Zeilenumbrüche enthält.""" + +.. blacken-docs:on + +Zeichenketten können durch einfache (``' '``), doppelte (``" "``), dreifache +einfache (``''' '''``) oder dreifache doppelte (``""" """``) Anführungszeichen +getrennt werden. + +Eine normale Zeichenkette kann nicht auf mehrere Zeilen aufgeteilt werden. Der +folgende Code wird also nicht funktionieren: + +.. code-block:: + + "Dies ist ein fehlerhafter Versuch, einen Zeilenumbruch in + eine Zeichenkette einzufügen, ohne \n zu verwenden." + +Sie können auch Tabulator- (``\t``) und *Newline*-Zeichen (``\n``) enthalten. +Allgemein können Backslashes ``\`` als Escape-Zeichen verwendet werden. So kann +:abbr:`z.B. (zum Beispiel)` ``\\`` für einen einzelnen Backslash und ``\'`` für +ein einfaches Anführungszeichen verwendet werden, wodurch es die +Zeichenfolge nicht beendet: + +.. blacken-docs:off + +.. code-block:: python + + "You don't need a backslash here." + 'However, this wouldn\'t work without a backslash.' + +.. blacken-docs:on + +Python bietet jedoch auch Zeichenketten in dreifachen Anführungszeichen +(``"""``), die dies ermöglichen und einfache und doppelte Anführungszeichen ohne +Backslashes ``\`` als Escape-Zeichen enthalten können. + +.. toctree:: + :titlesonly: + :hidden: + + encodings + operators-functions + built-in-modules/index + print + input + +Checks +------ + +* Könnt ihr :abbr:`z.B. (zum Beispiel)` eine Zeichenkette mit einer ganzen Zahl + addieren oder multiplizieren, oder mit einer Gleitkommazahl oder einer + komplexen Zahl? + +* Wie könnt ihr eine Überschrift wie ``variables and expressions`` so abändern, + dass sie keine Leerzeichen mehr enthält und besser als Dateinamen verwendet + werden kann? + +* Wenn ihr überprüfen wollt, ob ein String mit ``.. note::`` beginnt, welche + Methode würdet ihr verwenden? Gibt es auch noch andere Möglichkeiten? + +* Angenommen, ihr habt eine Zeichenkette mit Ausrufezeichen, Anführungszeichen + und Zeilenumbruch. Wie können diese aus der Zeichenkette entfernt werden? + +* Wie könnt ihr **alle** Leerräume und Satzzeichen aus einer Zeichenfolge in + einen Bindestrich (``-``) ändern? + +* Welche Anwendungsfälle könnt ihr euch vorstellen, in denen das + :mod:`python3:struct`-Modul für das Lesen oder Schreiben von Binärdaten + nützlich wäre? + + * beim Lesen und Schreiben einer Binärdatei + * beim Lesen von einer externen Schnittstelle, wobei die Daten genau so + gespeichert werden sollen, wie sie übermittelt wurden diff --git a/docs/input.rst b/docs/types/strings/input.rst similarity index 98% rename from docs/input.rst rename to docs/types/strings/input.rst index b4207f6d..8c9ea733 100644 --- a/docs/input.rst +++ b/docs/types/strings/input.rst @@ -1,5 +1,5 @@ -Input -===== +``input()`` +=========== Ihr könnt die Funktion :func:`python3:input` verwenden, um Dateneingaben zu erhalten. Verwendet den Prompt-String, den ihr anzeigen möchtet, als Parameter diff --git a/docs/types/strings/operators-functions.rst b/docs/types/strings/operators-functions.rst new file mode 100644 index 00000000..94cd52c2 --- /dev/null +++ b/docs/types/strings/operators-functions.rst @@ -0,0 +1,159 @@ +Operatoren und Funktionen +========================= + +Die Operatoren und Funktionen, die mit Zeichenketten arbeiten, geben neue, vom +Original abgeleitete Zeichenketten zurück. Die Operatoren (``in``, ``+`` und +``*``) und eingebauten Funktionen (``len``, ``max`` und ``min``) arbeiten mit +Zeichenketten genauso wie mit :doc:`../sequences-sets/lists` and +:doc:`../sequences-sets/tuples`. + +.. code-block:: pycon + + >>> welcome = "Hello pythonistas!\n" + >>> 2 * welcome + 'Hello pythonistas!\nHello pythonistas!\n' + >>> welcome + welcome + 'Hello pythonistas!\nHello pythonistas!\n' + >>> "python" in welcome + True + >>> max(welcome) + 'y' + >>> min(welcome) + '\n' + +Indizierung und Slicing +----------------------- + +Auch die Index- und Slice-Notation funktioniert auf die gleiche Weise, um +einzelne Elemente zu erhalten: + +.. code-block:: pycon + + >>> welcome[0:5] + 'Hello' + >>> welcome[6:-1] + 'pythonistas!' + +Die Index- und Slice-Notation kann jedoch nicht verwendet werden, um Elemente +hinzuzufügen, zu entfernen oder zu ersetzen, da Zeichenketten +:term:`unveränderlich ` sind: + +.. code-block:: pycon + + >>> welcome[6:-1] = "everybody!" + Traceback (most recent call last): + File "", line 1, in + TypeError: 'str' object does not support item assignment + +Konvertierungen +--------------- + +Konvertieren von Zeichenketten in Zahlen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Ihr könnt die Funktionen :class:`python3:int` und :class:`python3:float` +verwenden, um Zeichenketten in Ganzzahl- bzw. Fließkommazahlen zu konvertieren. +Wenn eine Zeichenkette übergeben wird, die nicht als Zahl des angegebenen Typs +interpretiert werden kann, lösen diese Funktionen eine +:class:`python3:ValueError`-Ausnahme aus. Ausnahmen werden in +:doc:`../../control-flow/exceptions` ausführlicher erklärt. Darüber hinaus könnt +ihr :class:`python3:int` einen optionalen zweiten :doc:`../../functions/params` +übergeben, der die numerische Basis angibt, die bei der Interpretation der +Zeichenfolge verwendet werden soll: + +.. code-block:: pycon + :linenos: + + >>> float("12.34") + 12.34 + >>> float("12e3") + 12000.0 + >>> int("1000") + 1000 + >>> int("1000", base=10) + 1000 + >>> int("1000", 8) + 512 + >>> int("1000", 2) + 8 + >>> int("1234", 2) + Traceback (most recent call last): + File "", line 1, in + ValueError: invalid literal for int() with base 2: '1234' + +Zeilen 5–8 + Wird kein zweiter :doc:`../../functions/params` angegeben, rechnet + :class:`python3:int` mit einer Basis von ``10``. +Zeilen 9, 10 + ``1000`` wird als `Oktalzahl `_ + interpretiert. +Zeilen 11, 12 + ``1000`` wird als `Dualzahl `_ + interpretiert. +Zeilen 13–16 + ``1234`` kann nicht als Ganzzahl auf der Basis ``2`` angegeben werden. Daher + wird eine :class:`python3:ValueError`-Ausnahme ausgelöst. + +Ändern von Zeichenketten mit Listenmanipulationen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Da :ref:`str `-Objekte :term:`unveränderlich ` +sind, gibt es keine Möglichkeit, sie direkt zu verändern wie +:doc:`../sequences-sets/lists`. Ihr könnt sie jedoch in Listen umwandeln: + +.. code-block:: pycon + + >>> palindromes = "lol level gag" + >>> palindromes_list = list(palindromes) + >>> palindromes_list.reverse() + >>> "".join(palindromes_list) + 'gag level lol' + +Objekte in Zeichenketten konvertieren +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Python kann fast alles in eine Zeichenkette mit der eingebauten Funktion +:ref:`str ` umgewandelt werden: + +.. code-block:: pycon + + >>> data_types = [(7, "Data types", 19), (7.1, "Numbers", 19), (7.2, "Lists", 23)] + >>> ( + ... "The title of chapter " + ... + str(data_types[0][0]) + ... + " is «" + ... + data_types[0][1] + ... + "»." + ... ) + 'The title of chapter 7 is «Data types».' + +Das Beispiel verwendet :ref:`str `, um eine Ganzzahl aus der +Liste ``data_types`` in eine Zeichenkette umzuwandeln, die dann wieder +aneinanderhängt werden, um die endgültige Zeichenkette zu bilden. + +.. note:: + Während :ref:`str ` meist verwendet wird, um für Menschen + lesbare Texte zu erzeugen, wird :func:`python3:repr` eher für + Debugging-Ausgaben oder Statusberichte verwendet, :abbr:`z.B. (zum + Beispiel)`, um Informationen über die eingebaute Python-Funktion + :func:`python3:len` zu erhalten: + + .. code-block:: pycon + + >>> repr(len) + '' + +Checks +------ + +* Könnt ihr :abbr:`z.B. (zum Beispiel)` eine Zeichenkette mit einer ganzen Zahl + addieren oder multiplizieren, oder mit einer Gleitkommazahl oder einer + komplexen Zahl? + +* Welche der folgenden Zeichenketten können nicht in Zahlen umgewandelt werden + und warum? + + * ``int("1e2")`` + * ``int(1e+2)`` + * ``int("1+2")`` + * ``int("+2")`` diff --git a/docs/types/strings/print.rst b/docs/types/strings/print.rst new file mode 100644 index 00000000..f5e1c303 --- /dev/null +++ b/docs/types/strings/print.rst @@ -0,0 +1,236 @@ +``print()`` +=========== + +Die Funktion :func:`print` gibt Zeichenketten aus wobei andere Python-Datentypen +leicht in Strings umgewandelt und formatiert werden können, :abbr:`z.B. (zum +Beispiel)`: + +.. code-block:: pycon + + >>> import math + >>> pi = math.pi + >>> d = 28 + >>> u = pi * d + >>> print( + ... "Pi ist", + ... pi, + ... "und der Umfang bei einem Durchmesser von", + ... d, + ... "Zoll ist", + ... u, + ... "Zoll.", + ... ) + Pi ist 3.141592653589793 und der Umfang bei einem Durchmesser von 28 Zoll ist 87.96459430051421 Zoll. + +.. _f-strings: + +F-Strings +--------- + +Mit F-Strings lassen sich die für einen Text zu detaillierten Zahlen kürzen: + +.. code-block:: pycon + + >>> print(f"Der Wert von Pi ist {pi:.3f}.") + Der Wert von Pi ist 3.142. + +In ``{pi:.3f}`` wird die Format-Spezifikation ``f`` verwendet, um die Zahl Pi +auf drei Nachkommastellen zu kürzen. + +In A/B-Testszenarien möchtet ihr oft die prozentuale Veränderung einer Kennzahl +darstellen. Mit F-Strings können sie verständlich formuliert werden: + +.. code-block:: pycon + + >>> metrics = 0.814172 + >>> print(f"Die AUC hat sich vergrößert auf {metrics:=+7.2%}") + Die AUC hat sich vergrößert auf +81.42% + +In diesem Beispiel wird die Variable ``metrics`` formatiert, wobei ``=`` die +Inhalte der Variable nach dem ``+`` übernimmt, wobei insgesamt sieben Zeichen +einschließlich des Vorzeichen, ``metrics`` und des Prozentzeichens angezeigt +werden. ``.2`` sorgt für zwei Dezimalstellen, während das ``%``-Symbol den +Dezimalwert in eine Prozentzahl umwandelt. So wird ``0.514172`` in ``+51.42%`` +umgewandelt. + +Werte lassen sich auch in binäre und hexadezimale Werte umrechnen: + +.. code-block:: pycon + + >>> block_size = 192 + >>> print(f"Binary block size: {block_size:b}") + Binary block size: 11000000 + >>> print(f"Hex block size: {block_size:x}") + Hex block size: c0 + +Es gibt auch Formatierungsangaben, die ideal geeignet sind für die :abbr:`CLI +(Command Line Interface)`-Ausgabe, :abbr:`z.B. (zum Beispiel)`: + +.. code-block:: pycon + + >>> data_types = [(7, "Data types", 19), (7.1, "Numbers", 19), (7.2, "Lists", 23)] + >>> for n, title, page in data_types: + ... print(f"{n:.1f} {title:.<25} {page: >3}") + ... + 7.0 Data types............... 19 + 7.1 Numbers.................. 19 + 7.2 Lists.................... 23 + +Allgemein sieht das Format folgendermaßen aus, wobei alle Angaben in eckigen +Klammern optional sind: + +:samp:`:[[FILL]ALIGN][SIGN][0b|0o|0x|d|n][0][WIDTH][GROUPING]["." PRECISION][TYPE]` + +In der folgenden Tabelle sind die Felder für die Zeichenkettenformatierung und +ihre Bedeutung aufgeführt: + ++-----------------------+-------------------------------------------------------+ +| Feld | Bedeutung | ++=======================+=======================================================+ +| :samp:`FILL` | Zeichen, das zum Ausfüllen von :samp:`ALIGN` verwendet| +| | wird. Der Standardwert ist ein Leerzeichen. | ++-----------------------+-------------------------------------------------------+ +| :samp:`ALIGN` | Textausrichtung und Füllzeichen: | +| | | +| | | ``<``: linksbündig | +| | | ``>``: rechtsbündig | +| | | ``^``: zentriert | +| | | ``=``: Füllzeichen nach :samp:`SIGN` | ++-----------------------+-------------------------------------------------------+ +| :samp:`SIGN` | Vorzeichen anzeigen: | +| | | +| | | ``+``: Vorzeichen bei positiven und negativen | +| | Zahlen anzeigen | +| | | ``-``: Standardwert, ``-`` nur bei negativen Zahlen | +| | oder Leerzeichen bei positiven Zahlen | ++-----------------------+-------------------------------------------------------+ +| :samp:`0b|0o|0x|d|n` | Vorzeichen für ganze Zahlen: | +| | | +| | | ``0b``: Binärzahlen | +| | | ``0o``: Oktalzahlen | +| | | ``0x``: Hexadezimalzahlen | +| | | ``d``: Standardwert, dezimale Ganzzahl zur Basis 10 | +| | | ``n``: verwendet die aktuelle | +| | ``locale``-Einstellung, um die entsprechenden | +| | Zahlentrennzeichen einzufügen | ++-----------------------+-------------------------------------------------------+ +| :samp:`0` | füllt mit Nullen auf | ++-----------------------+-------------------------------------------------------+ +| :samp:`WIDTH` | Minimale Feldbreite | ++-----------------------+-------------------------------------------------------+ +| :samp:`GROUPING` | Zahlentrennzeichen: [#]_ | +| | | +| | | ``,``: Komma als Tausendertrennzeichen | +| | | ``_``: Unterstrich für Tausendertrennzeichen | ++-----------------------+-------------------------------------------------------+ +| :samp:`.PRECISION` | | Bei Fließkommazahlen die Anzahl der Ziffern nach | +| | dem Punkt | +| | | bei nicht-numerischen Werten die maximale Länge | ++-----------------------+-------------------------------------------------------+ +| :samp:`TYPE` | Ausgabeformat als Zahlentyp oder Zeichenkette | +| | | +| | … für Ganzzahlen: | +| | | +| | | ``b``: Binärformat | +| | | ``c``: konvertiert die Ganzzahl in das | +| | entsprechende Unicode-Zeichen | +| | | ``d``: Standardwert, Dezimalzeichen | +| | | ``n``: dasselbe wie ``d``, mit dem Unterschied, | +| | dass es die aktuelle ``locale``-Einstellung | +| | verwendet, um die entsprechenden Zahlentrennzeichen | +| | einzufügen | +| | | ``o``: Oktalformat | +| | | ``x``: Hexadezimalformat zur Basis 16, wobei für | +| | die Ziffern über 9 Kleinbuchstaben verwendet werden | +| | | ``X``: Hexadezimalformat zur Basis 16, wobei für | +| | die Ziffern über 9 Großbuchstaben verwendet werden | +| | | +| | … für Fließkommazahlen: | +| | | +| | | ``e``: Exponent mit ``e`` als Trennzeichen zwischen | +| | Koeffizient und Exponent | +| | | ``E``: Exponent mit ``E`` als Trennzeichen zwischen | +| | Koeffizient und Exponent | +| | | ``g``: Standardwert für Fließkommazahlen, wobei der | +| | Exponent eine feste Breite für große und | +| | kleine Zahlen erhält | +| | | ``G``: Wie ``g``, wechselt aber zu ``E``, wenn | +| | die Zahl zu groß wird. Die Darstellungen von | +| | Unendlich und NaN werden ebenfalls in Großbuchstaben| +| | geschrieben | +| | | ``n``: Wie ``g`` mit dem Unterschied, dass es die | +| | aktuelle ``locale``-Einstellung verwendet, um die | +| | die entsprechenden Zahlentrennzeichen einzufügen | +| | | ``%``: Prozentsatz. Multipliziert die Zahl mit 100 | +| | und zeigt sie im festen Format ``f`` an, gefolgt | +| | von einem Prozentzeichen | ++-----------------------+-------------------------------------------------------+ + +.. [#] Der Formatbezeichner ``n`` formatiert eine Zahl in einer lokal angepassten + Weise, :abbr:`z.B. (zum Beispiel)`: + + .. code-block:: pycon + + >>> value = 635372 + >>> import locale + >>> locale.setlocale(locale.LC_NUMERIC, "en_US.utf-8") + 'en_US.utf-8' + >>> print(f"{value:n}") + 635,372 + +.. tip:: + Eine gute Quelle für F-Strings ist die Hilfe-Funktion: + + .. code-block:: pycon + + >>> help() + help> FORMATTING + ... + + Ihr könnt die Hilfe hier durchblättern und viele Beispiele finden. + + Mit :kbd:`:`–:kbd:`q` und :kbd:`⏎` könnt ihr die Hilfe-Funktion wieder + verlassen. + +.. seealso:: + * `PyFormat `_ + * :ref:`python3:f-strings` + * :pep:`498` + +Fehlersuche in F-Strings +~~~~~~~~~~~~~~~~~~~~~~~~ + +In Python 3.8 wurde ein Spezifizierer eingeführt, der bei der Fehlersuche in +F-String-Variablen hilft. Durch Hinzufügen eines Gleichheitszeichens ``=`` wird der +Code innerhalb des F-Strings aufgenommen: + +.. code-block:: pycon + + >>> uid = "veit" + >>> print(f"My name is {uid.capitalize()=}") + My name is uid.capitalize()='Veit' + +Formatierung von Datums-, Zeitformaten und IP-Adressen +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:py:mod:`datetime` unterstützt die Formatierung von Zeichenketten mit der +gleichen Syntax wie die :py:meth:`strftime `-Methode +für diese Objekte. + +.. code-block:: pycon + + >>> import datetime + >>> today = datetime.date.today() + >>> print(f"Today is {today:%d %B %Y}.") + Today is 26 November 2023. + +Das :py:mod:`ipaddress`-Modul von Python unterstützt auch die Formatierung von +``IPv4Address``- und ``IPv6Address``-Objekten. + +Schließlich können Bibliotheken von Drittanbietern auch ihre eigene +Unterstützung für die Formatierung von Strings hinzufügen, indem sie eine +``__format__``-Methode zu ihren Objekten hinzufügen. + +.. seealso:: + * :ref:`format-codes` + * `Python strftime cheatsheet `_ diff --git a/docs/variables-expressions.rst b/docs/variables-expressions.rst index a60748eb..6056732e 100644 --- a/docs/variables-expressions.rst +++ b/docs/variables-expressions.rst @@ -13,7 +13,7 @@ lautet: >>> pi = 3.14159 In Python ist, anders als in vielen anderen Programmiersprachen, weder eine -Variablendeklaration noch ein Zeilenendebegrenzer notwendig. Die Zeile wird +Variablendeklaration noch ein Zeilenende-Begrenzer notwendig. Die Zeile wird durch das Ende der Zeile abgeschlossen. Variablen werden automatisch erstellt, wenn sie zum ersten Mal zugewiesen werden. @@ -32,7 +32,8 @@ wenn sie zum ersten Mal zugewiesen werden. >>> print(x) [4, 2, 3] - Variablen können sich jedoch auch auf Konstanten beziehen: + Variablen können jedoch auch auf :term:`unveränderliche ` + Objekte zeigen: .. code-block:: pycon @@ -44,9 +45,10 @@ wenn sie zum ersten Mal zugewiesen werden. 1 4 1 In diesem Fall verweisen nach der dritten Zeile ``x``, ``y`` und ``z`` alle - auf dasselbe unveränderlichee Integer-Objekt mit dem Wert ``1``. Die nächste - Zeile, ``y = 4``, bewirkt, dass ``y`` auf das Integer-Objekt ``4`` verweist, - dies ändert jedoch nicht die Referenzen von ``x`` oder ``z``. + auf dasselbe :term:`unveränderliche ` Integer-Objekt mit dem + Wert ``1``. Die nächste Zeile, ``y = 4``, bewirkt, dass ``y`` auf das + Integer-Objekt ``4`` verweist, dies ändert jedoch nicht die Referenzen von + ``x`` oder ``z``. Python-Variablen können auf jedes beliebige Objekt gesetzt werden, während in vielen anderen Sprachen Variablen nur im deklarierten Typ gespeichert werden @@ -56,36 +58,40 @@ Variablennamen unterscheiden Groß- und Kleinschreibung und können jedes alphanumerische Zeichen sowie Unterstriche enthalten, müssen aber mit einem Buchstaben oder Unterstrich beginnen. -.. note:: - Wenn ihr einen ``SyntaxError`` erhaltet, prüft, ob der Variablenname ein - Schlüsselwort ist. Schlüsselwörter sind für die Verwendung in - Python-Sprachkonstrukten reserviert, so dass ihr sie nicht zu Variablen - machen könnt. Nach dem Aufruf von :ref:`help` könnt ihr ``keywords`` - eingeben, um die Schlüsselworte zu erhalten: - - .. code-block:: pycon +Schlüsselwörter +--------------- - >>> help() - help> keywords +Wenn ihr einen ``SyntaxError`` erhaltet, prüft, ob der Variablenname ein +Schlüsselwort ist. Schlüsselwörter sind für die Verwendung in +Python-Sprachkonstrukten reserviert, so dass ihr sie nicht zu Variablen machen +könnt. Nach dem Aufruf von :ref:`help` könnt ihr ``keywords`` eingeben, um die +Schlüsselworte zu erhalten: - Here is a list of the Python keywords. Enter any keyword to get more help. - - False class from or - None continue global pass - True def if raise - and del import return - as elif in try - assert else is while - async except lambda with - await finally nonlocal yield - break for not +.. code-block:: pycon -.. note:: - Ihr könnt mit einem Variablennamen eingebaute (engl.: *built-in*) Funktionen, - Typen und andere Objekte überschreiben, sodass der Zugriff anschließend nur - noch über das :doc:`builtins `-Modul erfolgen kann. - Daher sollten diese Variablennamen nie verwendet werden. Eine Liste der - :mod:`__builtins__`-Objekte erhaltet ihr mit: + >>> help() + help> keywords + + Here is a list of the Python keywords. Enter any keyword to get more help. + + False class from or + None continue global pass + True def if raise + and del import return + as elif in try + assert else is while + async except lambda with + await finally nonlocal yield + break for not + +Built-in-Funktionen +------------------- + +Ihr könnt mit einem Variablennamen eingebaute (engl.: *built-in*) Funktionen, +Typen und andere Objekte überschreiben, sodass der Zugriff anschließend nur noch +über das :doc:`builtins `-Modul erfolgen kann. Daher +sollten diese Variablennamen nie verwendet werden. Eine Liste der +:mod:`__builtins__`-Objekte erhaltet ihr mit: .. code-block:: pycon diff --git a/pyproject.toml b/pyproject.toml index 5746a25e..c9d9648e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,9 @@ dependencies = [] [project.optional-dependencies] docs = [ "furo", + "ipython", + "ipywidgets", + "nbsphinx", "sphinxext.opengraph", # matplotlib is required for social cards "matplotlib", "sphinx_copybutton", @@ -39,4 +42,4 @@ dev = [ "Bug Tracker" = "https://github.com/veit/python-basics-tutorial-de/issues" [tool.codespell] -skip = "*.rst, *.svg" +skip = "*.ipynb, *.rst, *.svg"