<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Python Archives - Creatronix</title>
	<atom:link href="https://creatronix.de/category/software-engineering/python/feed/" rel="self" type="application/rss+xml" />
	<link>https://creatronix.de/category/software-engineering/python/</link>
	<description>My adventures in code &#38; business</description>
	<lastBuildDate>Fri, 06 Feb 2026 20:04:52 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Pytest Tutorial</title>
		<link>https://creatronix.de/pytest-tutorial/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 06 Feb 2026 20:00:23 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7846</guid>

					<description><![CDATA[<p>Motivation Tests sind ein zentraler Bestandteil moderner Entwicklung. Das Framework pytest macht das Schreiben, Organisieren und Ausführen von Tests einfach, flexibel und leistungsfähig. Tests schreiben und ausführen Installation Zuerst installierst du pytest mit: pip install pytest pytest ist kompatibel mit unittest-Tests und erfordert keine Testklassen. Namenskonventionen Damit pytest Tests automatisch findet, müssen Dateien und Funktionen&#8230;</p>
<p>The post <a href="https://creatronix.de/pytest-tutorial/">Pytest Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Tests sind ein zentraler Bestandteil moderner Entwicklung. Das Framework pytest macht das Schreiben, Organisieren und Ausführen von Tests einfach, flexibel und leistungsfähig.</p>
<h2>Tests schreiben und ausführen</h2>
<h3>Installation</h3>
<p>Zuerst installierst du pytest mit:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pytest</code></pre>
</div>
<p>pytest ist kompatibel mit unittest-Tests und erfordert keine Testklassen.</p>
<h3>Namenskonventionen</h3>
<p>Damit pytest Tests automatisch findet, müssen Dateien und Funktionen bestimmte Namen haben:</p>
<ul>
<li>Dateinamen haben test im Namen</li>
<li>Funktionsnamen beginnen mit test_</li>
</ul>
<p>Beispiel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_example():
    x = 5
    assert x == 5</code></pre>
</div>
<p>Mit pytest im Terminal führt pytest alle passenden Tests aus.</p>
<h3>Testauswahl</h3>
<p>Du kannst gezielt Tests ausführen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>pytest test_module.py::test_function
pytest -k "keyword"</code></pre>
</div>
<p>Optionen wie -v (verbose), -x (bei Fehler abbrechen) oder -s (print-Ausgaben anzeigen) sind sehr praktisch.</p>
<p>Um Optionen nicht immer per Hand eingeben zu müssen, können diese auch in einer pytest.ini angegeben werden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang="Plain Text"><code>[pytest]
pythonpath = .
addopts = -x --durations=0 -vv</code></pre>
</div>
<h2>Fortgeschrittene pytest-Techniken</h2>
<h3>Fixtures</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.fixture(scope="function")
def simple_fixture():
    return 42

def test_function(simple_fixture):
    assert simple_fixture == 42</code></pre>
</div>
<h3>Fixture scopes</h3>
<p>pytest-Fixtures haben einen Scope, der bestimmt, wie oft eine Fixture erzeugt und wiederverwendet wird.<br />
Der Standard-Scope ist function, d. h. die Fixture wird für jeden Test neu ausgeführt.</p>
<p>Weitere Scopes sind:</p>
<ul>
<li>class &#8211; einmal pro Testklasse</li>
<li>module &#8211; einmal pro Datei</li>
<li>package &#8211; einmal pro Paket</li>
<li>session &#8211; einmal pro gesamtem Testlauf</li>
</ul>
<p>Größere Scopes verbessern die Performance, bergen aber das Risiko von geteiltem Zustand zwischen Tests.<br />
Daher gilt: so klein wie möglich, so groß wie nötig.</p>
<h3>Setup and teardown</h3>
<p>Mit pytest können wir klassische setup / teardown Methoden mit fixtures nachbauen</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

def test_setup_and_teardown():
    assert True

@pytest.fixture(autouse=True, scope="function")
def setup_and_teardown():
    print("SetUp")
    # setup code goes here
    yield
    print("TearDown")
    # teardown code goes here</code></pre>
</div>
<h3>Parametrisierung</h3>
<p>Mit Parametrisierung lässt sich derselbe Test mit verschiedenen Daten ausführen. So umgehst du Duplikate:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.parametrize("data, expected", [
    ([1], 1),
    ([1, 2], 1.5),
    ([1, 2, 3], 2),
])
def test_calculate_mean(data, expected_mean):
    mean = calculate_mean(data)
    assert mean == expected_mean

def calculate_mean(data):
    return sum(data) / len(data)</code></pre>
</div>
<p>&nbsp;</p>
<p>Das definiert mehrere Testinstanzen automatisch.</p>
<h3>Marker</h3>
<p>Marker helfen, Gruppen von Tests zu steuern.<br />
Marker eignen sich z. B. zur Gruppierung nach Plattform, Kategorie oder Dauer.</p>
<p>Es gibt drei eingebaute Marker:</p>
<ul>
<li>skip</li>
<li>skipif</li>
<li>xfail</li>
</ul>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest
import sys

@pytest.mark.skip(reason="Feature not yet implemented")
def test_future():
    pass

@pytest.mark.skipif(sys.platform == "win32", reason="Linux only")
def test_linux_only():
    pass

@pytest.mark.xfail(reason="Bug #123")
def test_known_bug():
    pass</code></pre>
</div>
<h3>Eigene Marker</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.slow
def test_slow():
    pass</code></pre>
</div>
<p>In pytest.ini:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang="Plain Text"><code>[pytest]
...
markers =
    slow: mark slow tests</code></pre>
</div>
<p>Dann kannst du testen mit:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest -m slow</code></pre>
</div>
<p>Oder alle nicht langsamen Tests laufen lassen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest -m "not slow"</code></pre>
</div>
<h3>Nahezu gleiche Vergleiche</h3>
<p>Für Fließkommazahlen ist ein exakter Vergleich oft ungeeignet. pytest bietet das approx-Feature:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>assert 2.2 == pytest.approx(2.3, 0.1)</code></pre>
</div>
<p>Erlaubt eine Toleranz statt exakter Übereinstimmung.</p>
<h3>Exceptions testen</h3>
<p>Mit pytest kannst du das Auftreten von Ausnahmen elegant testen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0</code></pre>
</div>
<p>Das prüft, ob ein Fehler erwartet korrekt geworfen wird.</p>
<h2>Test-Reporting &amp; Coverage</h2>
<p>Testabdeckung mit pytest-cov</p>
<p>Mit dem Plugin pytest-cov lässt sich messen, wie viel deines Codes getestet wird:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pytest-cov

pytest --cov=my_app 10_app_test</code></pre>
</div>
<p>Ergebnis: Coverage-Statistiken zeigen an, wie viele Zeilen getestet wurden.</p>
<h3>HTML-Reports</h3>
<p>Du kannst mit demselben Plugin HTML-Berichte erzeugen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest --cov=my_app --cov-report=html</code></pre>
</div>
<p>Eine visuelle Übersicht über die Abdeckung hilft bei der Analyse.</p>
<h3>Integration mit Werkzeugen</h3>
<p>Coverage-Daten lassen sich in Tools wie SonarQube einbinden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest --cov-report=xml:coverage.xml</code></pre>
</div>
<p>Dadurch kannst du Metriken in CI/CD-Pipelines oder Dashboards verwenden.</p>
<h2>Assertions mit Hamcrest (optional, aber mächtig)</h2>
<p>Neben den eingebauten assert-Anweisungen von pytest kann man auch Hamcrest verwenden.<br />
Hamcrest ist eine Matcher-Bibliothek, die Tests lesbarer und ausdrucksstärker machen kann – besonders bei komplexen Datenstrukturen.</p>
<h3>Installation</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pyhamcrest</code></pre>
</div>
<p>Import in Tests:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from hamcrest import assert_that, equal_to</code></pre>
</div>
<h3>Warum Hamcrest?</h3>
<p>Vergleich:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_hamcrest_assert():
    result = 42
    assert_that(result, equal_to(42))</code></pre>
</div>
<p>Der Vorteil zeigt sich bei komplexeren Assertions:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_hamcrest_start_with():
    name = "Max Mustermann"
    assert_that(name, starts_with("Max"))

def test_hamcrest_has_length():
    items: Sized = ['apple', 'banana', 'cherry']
    assert_that(items, has_length(3))</code></pre>
</div>
<p>Tests lesen sich mehr wie Spezifikationen.</p>
<h3>Listen &amp; Collections</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from hamcrest import assert_that, has_items
from collections.abc import Sequence

def test_compare_list_of_items():
    actual_list_of_items: Sequence[str] = ['apple', 'banana', 'cherry']
    assert_that(actual_list_of_items, has_items("apple", "banana"))</code></pre>
</div>
<h2>Fazit</h2>
<p>pytest ist ein einfaches, doch mächtiges Framework zur Testautomatisierung in Python. Es kombiniert:</p>
<ul>
<li>geringe Einstiegshürde – einfache Tests schnell geschrieben</li>
<li>hohe Flexibilität – Marker, Parameter, Coverage</li>
<li>gute Tools-Integration – HTML-Reports oder CI-Coverage</li>
</ul>
<p>Damit eignet es sich sowohl für Einsteiger als auch für professionelle Test-Pipelines.</p>
<h2>Code</h2>
<p><a href="https://github.com/jboegeholz/pytest_tutorial">https://github.com/jboegeholz/pytest_tutorial</a></p>
<p>The post <a href="https://creatronix.de/pytest-tutorial/">Pytest Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So funktionieren structs in Python</title>
		<link>https://creatronix.de/so-funktionieren-structs-in-python/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sun, 16 Nov 2025 08:34:09 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Robotics]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7678</guid>

					<description><![CDATA[<p>Motivation Wenn man Daten über eine serielle Schnittstelle zwischen z.b Raspberry Pi und einem Arduino schicken will, ergibt es Sinn, sich über das Format Gedanken zu machen. Angenommen wir wollen einen Drehzahlwert von 60 RPM jeweils für die linke und rechte Seite übertragen. Standardmäßig verschickt eine Bibliothek wie pyserial ASCII-Zeichen, dass heißt eine Zahl wie&#8230;</p>
<p>The post <a href="https://creatronix.de/so-funktionieren-structs-in-python/">So funktionieren structs in Python</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Wenn man Daten über eine serielle Schnittstelle zwischen z.b Raspberry Pi und einem Arduino schicken will,<br />
ergibt es Sinn, sich über das Format Gedanken zu machen. Angenommen wir wollen einen Drehzahlwert von 60 RPM jeweils für die linke und rechte Seite übertragen.<br />
Standardmäßig verschickt eine Bibliothek wie pyserial ASCII-Zeichen, dass heißt eine Zahl wie 60 würde als zwei Byte ASCII übertragen. Das sind doppelt so viele Daten wie benötigt würden, wenn wir die Zahl als Integer übertragen. Hier kommen structs ins Spiel.</p>
<h2>struct for the rescue</h2>
<p>Mit dem struct Modul in Python können wir Binärdaten in einem bestimmten Format packen und entpacken.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_struct_b():
    assert struct.pack('b', 1) == b'\x01'
    assert struct.pack('b', 127) == b'\x7f'
    assert struct.pack('b', -128) == b'\x80'
</code></pre>
</div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_unpack_b():
    packed = struct.pack('b', 127)
    unpacked = struct.unpack('b', packed)[0]
    assert unpacked == 127
</code></pre>
</div>
<h2>Mehrere Variablen</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_struct_bb():
    msg = struct.pack('bb', int(60), int(60))
    assert msg == b'\x3C\x3C'
</code></pre>
</div>
<h2>Fallstricke</h2>
<p>Damit die Empfängerseite die Daten auch wieder korrekt entpacken kann, muss natürlich das Format beachtet werden.</p>
<p>Achtet dementsprechend auf gute Dokumentation und versioniert idelaerweise die Dateien für die Datenübertragung in den gleichen Commits.</p>
<h2>Datentypen</h2>
<p>In structs können verschiedene Datentypen verwendet werden:<br />
Tabelle der wichtigsten Datentypen:</p>
<table border="1">
<thead>
<tr>
<th>Zeichen</th>
<th>Datentyp</th>
<th>Wertebereich</th>
</tr>
</thead>
<tbody>
<tr>
<td>b</td>
<td>signed char (1 byte)</td>
<td>-128 bis 127</td>
</tr>
<tr>
<td>B</td>
<td>unsigned char (1 byte)</td>
<td>0 bis 255</td>
</tr>
<tr>
<td>h</td>
<td>signed short (2 bytes)</td>
<td>-32768 bis 32767</td>
</tr>
<tr>
<td>H</td>
<td>unsigned short (2 bytes)</td>
<td>0 bis 65535</td>
</tr>
<tr>
<td>i</td>
<td>signed int (4 bytes)</td>
<td>-2147483648 bis 2147483647</td>
</tr>
<tr>
<td>I</td>
<td>unsigned int (4 bytes)</td>
<td>0 bis 4294967295</td>
</tr>
<tr>
<td>f</td>
<td>float (4 bytes)</td>
<td>IEEE</td>
</tr>
<tr>
<td>d</td>
<td>double (8 bytes)</td>
<td>IEEE</td>
</tr>
</tbody>
</table>
<h2>Pretty hex</h2>
<p>Wenn man die Darstellung der Zahlen auf der Konsole oder im Log etwas aufhübschen will:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_pretty_hex():
    msg = struct.pack('&gt;I', 2864434397)
    pretty_hex = ' '.join(f'{b:02x}' for b in msg)
    assert pretty_hex == "aa bb cc dd"</code></pre>
</div>
<p>The post <a href="https://creatronix.de/so-funktionieren-structs-in-python/">So funktionieren structs in Python</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Autarke Skripte in Python</title>
		<link>https://creatronix.de/autarke-skripte-in-python/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sun, 04 May 2025 08:38:02 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7377</guid>

					<description><![CDATA[<p>Szenario Du entwickelst ein kleines Python-Skript, das externe Abhängigkeiten verwendet (z. B. requests, pandas oder andere Pakete aus PyPI). Möchtest du dieses Skript weitergeben, müssen Empfänger die erforderlichen Dependencies manuell installieren. Das ist nicht besonders benutzerfreundlich. Ein naheliegender Ansatz wäre es, das Skript mit pyinstaller in ein ausführbares Binary zu verpacken – allerdings ist das für&#8230;</p>
<p>The post <a href="https://creatronix.de/autarke-skripte-in-python/">Autarke Skripte in Python</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Szenario</h2>
<p>Du entwickelst ein kleines Python-Skript, das externe Abhängigkeiten verwendet (z. B. requests, pandas oder andere Pakete aus PyPI). Möchtest du dieses Skript weitergeben, müssen Empfänger die erforderlichen Dependencies manuell installieren. Das ist nicht besonders benutzerfreundlich.</p>
<p>Ein naheliegender Ansatz wäre es, das Skript mit pyinstaller in ein ausführbares Binary zu verpacken – allerdings ist das für kleine Tools oft überdimensioniert.</p>
<h2>Idee: &#8220;Bootstrapping&#8221; à la Münchhausen</h2>
<p>Angelehnt an die Geschichte des Baron Münchhausen, der sich selbst am eigenen Schopf aus dem Sumpf zog: Auch in Python kann sich ein Skript &#8220;selbst helfen&#8221;. Das Skript prüft beim Start, ob benötigte Pakete installiert sind. Falls nicht, installiert es diese zur Laufzeit mittels subprocess und versucht anschließend erneut den Import.</p>
<h2>Erster Wurf</h2>
<p>Hier ein Code-Beispiel mit dem Paket dotenv</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import subprocess
import sys
import importlib

try:
    import dotenv
    print("dotenv already installed.")
except ImportError:
    print("dotenv not installed: trying to install")
    result = subprocess.run([sys.executable, "-m", "pip", "install", "python-dotenv"])
    if result.returncode == 0:
        try:
            importlib.import_module("dotenv")
            print("dotenv is now available.")
        except ImportError:
            print("dotenv still cannot be imported after installation.")
    else:
        print("dotenv could not be installed")
</code></pre>
</div>
<h2>Funktion für mehrere Pakete</h2>
<p>Ein häufiges Problem besteht darin, dass der Name eines Pakets beim Import nicht mit dem Namen übereinstimmt, unter dem es via pip installiert wird.<br />
Im obigen Code siehst du bereits dass dotenv mit import dotenv eingebunden wird, aber über pip install python-dotenv installiert werden muss.</p>
<p>Dieses Mismatch lässt sich elegant lösen, indem man ein Dictionary definiert, das die Zuordnung zwischen Importnamen und pip-Paketnamen enthält.<br />
Anschließend kann man über dieses Dictionary iterieren, den Import jedes Moduls versuchen und – falls erforderlich – das zugehörige pip-Paket automatisch nachinstallieren.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import subprocess
import sys
import importlib

dict_of_packages = {
    "dotenv": "python-dotenv",
    "xlrd": "xlrd"
                    }
def ensure_packages(dict_of_packages):
    for package_name, package_pip_name in dict_of_packages.items():
        try:
            importlib.import_module(package_name)
            print(f"{package_name} already installed.")
        except ImportError:
            print(f"{package_name} not installed: trying to install")
            result = subprocess.run([sys.executable, "-m", "pip", "install", package_pip_name])
            if result.returncode == 0:
                try:
                    importlib.import_module(package_name)
                    print(f"{package_name} is now available.")
                except ImportError:
                    print(f"{package_name} still cannot be imported after installation.")
            else:
                print(f"{package_name} could not be installed")
</code></pre>
</div>
<h2>Fazit</h2>
<p>Dieser Ansatz funktioniert gut in kontrollierten Umgebungen (bspw. venv, Skripte für Kollegen, Schulungen).</p>
<p>Bei Deployment auf Produktivsystemen sollte Package-Management über requirements.txt und pip install -r bevorzugt werden.</p>
<p>Für komplexere Setups oder GUI-Tools sind Build-Systeme wie setuptools, pipx, shiv, pex oder pyinstaller besser geeignet.</p>
<p>The post <a href="https://creatronix.de/autarke-skripte-in-python/">Autarke Skripte in Python</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>match case in Python 3.10</title>
		<link>https://creatronix.de/match-case-in-python-3-10/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Thu, 17 Apr 2025 21:56:25 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7354</guid>

					<description><![CDATA[<p>🧠 Motivation Python gibt’s inzwischen seit über 30 Jahren – eine echte Veteranin unter den Programmiersprachen. Und ganz ehrlich: Bisher hatte ich nie das Gefühl, dass mir ein bestimmtes Sprachfeature gefehlt hätte. Alles irgendwie machbar. Aber dann kam Python 3.10 mit einer interessanten Neuerung um die Ecke: match&#8211;case, also eine Art switch-case, wie man’s aus&#8230;</p>
<p>The post <a href="https://creatronix.de/match-case-in-python-3-10/">match case in Python 3.10</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>🧠 Motivation</h2>
<p>Python gibt’s inzwischen seit über 30 Jahren – eine echte Veteranin unter den Programmiersprachen. Und ganz ehrlich: Bisher hatte ich nie das Gefühl, dass mir ein bestimmtes Sprachfeature gefehlt hätte. Alles irgendwie machbar.</p>
<p>Aber dann kam Python 3.10 mit einer interessanten Neuerung um die Ecke: <strong><code>match</code>&#8211;<code>case</code></strong>, also eine Art <code>switch-case</code>, wie man’s aus anderen Sprachen kennt – aber auf Python-Art natürlich, mit mehr Wumms.</p>
<h2>🧱 Klassisch: <code>if</code>&#8211;<code>elif</code>&#8211;<code>else</code></h2>
<p>Der Standard-Weg, Entscheidungen zu treffen, ist klar: <code>if</code>&#8211;<code>elif</code>&#8211;<code>else</code>. Nehmen wir mal ein Beispiel aus der Praxis:</p>
<p>Du bekommst einen Steuerschlüssel und sollst den in einen kompakten Suffix umwandeln – etwa <code>_9</code> für <code>19%</code>, <code>_7</code> für <code>7%</code>, um ihn dann an einen Kostenträger dranzuhängen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def convert_tax_code(tax_code, cost_carrier):
    if tax_code == "19%":
        cost_carrier += "_9"
    elif tax_code == "7%":
        cost_carrier += "_7"
    else:
        print("Error: unknown tax code")
    return cost_carrier
</code></pre>
</div>
<p>So weit, so simpel. Aber dann will der Kunde auch <code>"19"</code> und <code>"7"</code> (ohne <code>%</code>) unterstützt haben:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def convert_tax_code(tax_code, cost_carrier):
    if tax_code == "19%" or tax_code == "19":
        cost_carrier += "_9"
    elif tax_code == "7%" or tax_code == "7":
        cost_carrier += "_7"
    else:
        print("Error: unknown tax code")
    return cost_carrier
</code></pre>
</div>
<p>Und es hört natürlich nicht auf – jetzt sollen auch <code>"0,19"</code> und <code>"0,07"</code> funktionieren:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def convert_tax_code(tax_code, cost_carrier):
    if tax_code == "19%" or tax_code == "19" or tax_code == "0,19":
        cost_carrier += "_9"
    elif tax_code == "7%" or tax_code == "7" or tax_code == "0,07":
        cost_carrier += "_7"
    else:
        print("Error: unknown tax code")
    return cost_carrier
</code></pre>
</div>
<p>Und ja, das <strong>funktioniert</strong> – aber der Code wird langsam ziemlich unleserlich. Die Bedingung wird länger, <code>tax_code</code> wiederholt sich ständig, und elegant ist was anderes.</p>
<p>Spätestens hier wäre für mich der Punkt erreicht, nach einer besseren Lösung zu suchen:</p>
<h2>🗂️ Alternative: Dictionary als Lookup</h2>
<p>Eine super Alternative für solche Mappings: ein Dictionary.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>TAX_CODE_DICT = {
    "19%": "_9",
    "19": "_9",
    "0,19": "_9",
    "7%": "_7",
    "7": "_7",
    "0,07": "_7",
}

def convert_tax_code_dict(tax_code, cost_carrier):
    if tax_code in TAX_CODE_DICT:
        cost_carrier += TAX_CODE_DICT[tax_code]
    else:
        print("Error: unknown tax code")
    return cost_carrier
</code></pre>
</div>
<p>Das sieht schon viel sauberer aus. Aber: Es bringt ein paar (kleine) Nachteile mit sich:</p>
<ol>
<li><strong>Kohäsion</strong> – Das Dictionary lebt außerhalb der Funktion. Muss man mögen.</li>
<li><strong>Fehlerbehandlung</strong> – Der <code>else</code>-Zweig bleibt, um ungültige Eingaben abzufangen.</li>
</ol>
<h2>🎯 Endlich: <code>match case</code></h2>
<p>Jetzt kommt Python 3.10 ins Spiel – und mit ihm <code>match case</code>. Das liest sich nicht nur gut, es lässt sich auch gut gruppieren und ist super erweiterbar:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def convert_tax_code_match_case(cost_carrier, tax_code):
    match tax_code:
        case "19%" | "19" | "0,19":
            cost_carrier += "_9"
        case "7%" | "7" | "0,07":
            cost_carrier += "_7"
        case _:
            print("Error: unknown tax code")
    return cost_carrier
</code></pre>
</div>
<p><strong>Nice!</strong> Keine unnötige Wiederholung, alle verwandten Werte sind gruppiert, und das Ganze liest sich wie ein klar strukturierter Entscheidungsbaum.</p>
<h2>✅ Fazit</h2>
<p>Mit <code>match case</code> bekommst du eine moderne, saubere Alternative zum klassischen <code>if</code>-Konstrukt, ohne gleich auf externe Strukturen wie Dictionaries ausweichen zu müssen.<br />
Vor allem bei <strong>vielen, strukturierten Bedingungen</strong> kann das eine Erleichterung sein.</p>
<p>The post <a href="https://creatronix.de/match-case-in-python-3-10/">match case in Python 3.10</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Einführung in numpy</title>
		<link>https://creatronix.de/einfuhrung-in-numpy/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 04 Oct 2024 17:30:04 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7127</guid>

					<description><![CDATA[<p>Motivation numpy is das Arbeitspferd für Data Scientists und Machine Learning Engineers. Aber auch als Robotiker kann diese Bibliothek nützlich sein, wenn man zum Beispiel Bildverarbeitung macht. Es ist eine Bibliothek für numerische Berechnungen und bietet viele Funktionen, die das Arbeiten mit Zahlen erleichtern. numpy ist schneller als Python-Listen, weil es in C geschrieben ist.&#8230;</p>
<p>The post <a href="https://creatronix.de/einfuhrung-in-numpy/">Einführung in numpy</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2024/10/NumPy_logo_2020.svg_-300x135.png" alt="" width="300" height="135" class="alignnone wp-image-7122 size-medium" srcset="https://creatronix.de/wp-content/uploads/2024/10/NumPy_logo_2020.svg_-300x135.png 300w, https://creatronix.de/wp-content/uploads/2024/10/NumPy_logo_2020.svg_.png 768w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>numpy is das Arbeitspferd für Data Scientists und Machine Learning Engineers. Aber auch als Robotiker kann diese Bibliothek nützlich sein, wenn man zum Beispiel Bildverarbeitung macht.</p>
<p>Es ist eine Bibliothek für numerische Berechnungen und bietet viele Funktionen, die das Arbeiten mit Zahlen erleichtern.<br />
numpy ist schneller als Python-Listen, weil es in C geschrieben ist. Es ist auch effizienter, weil es homogene Datenstrukturen verwendet.<br />
Das bedeutet, dass alle Elemente in einem numpy-Array den gleichen Datentyp haben. numpy ist auch effizienter, weil es Vektorisierung verwendet.<br />
Das bedeutet, dass Operationen auf numpy-Arrays auf einmal auf alle Elemente angewendet werden können, anstatt sie einzeln zu durchlaufen.<br />
Das macht numpy ideal für mathematische Operationen, lineare Algebra, Statistik und maschinelles Lernen.</p>
<h2>Historie</h2>
<p>numpy wurde 2005 von Travis Oliphant entwickelt. (Damals noch unter dem Namen Numeric.)<br />
Es wurde später in numpy umbenannt, um Verwechslungen mit einem anderen Paket zu vermeiden.<br />
numpy ist Teil des SciPy-Stacks, einer Sammlung von Open-Source-Software für wissenschaftliches Rechnen in Python.<br />
Der SciPy-Stack enthält auch Bibliotheken wie pandas, matplotlib und scikit-learn.</p>
<h2>Installation</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install numpy
</code></pre>
</div>
<h2>Import</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="PYthon"><code>import numpy as np
</code></pre>
</div>
<p>Der `as`-Befehl definiert einen sogenannten Alias. Jetzt können Strukturen aus numpy via np referenziert werden.</p>
<h2>ndarray</h2>
<p>ndarray ist die wichtigste Datenstruktur in numpy. Sie steht für n-dimensionale Arrays.<br />
Ein Array kann mit der Methode `array` aus einer Liste erstellt werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
a
array([1, 2, 3])</code></pre>
</div>
<p>Ein Array kann auch wieder in eine Liste umgewandelt werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>b = a.tolist()
b
[1, 2, 3]
</code></pre>
</div>
<h3>Datentypen</h3>
<p>Im Gegensatz zu Python-Listen sind numpy-Arrays homogen, d.h. alle Elemente haben den gleichen Datentyp.<br />
Der Datentyp eines Arrays kann mit der Methode `dtype` abgefragt werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a.dtype
dtype('int64')
</code></pre>
</div>
<p>Wenn man ein Array mit einem anderen Datentyp erstellen möchte, kann man explizit den Datentyp angeben.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>b = np.array([1, 2, 3], dtype='float')
b
array([1., 2., 3., 4.], dtype=float32)
</code></pre>
</div>
<h3>Vektoren</h3>
<p>Wir schauen uns zuerst die Vektorarithmetik an:</p>
<h4>Addition / Subtraktion</h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
b = np.array([5, 6, 7])
a + b
</code></pre>
</div>
<p>Hier werden die Elemente der Arrays elementweise addiert.<br />
das ergibt array([6, 8, 10])</p>
<h4><img fetchpriority="high" decoding="async" src="https://creatronix.de/wp-content/uploads/2024/10/vector-add.png" alt="" width="500" height="256" class="alignnone size-full wp-image-7126" srcset="https://creatronix.de/wp-content/uploads/2024/10/vector-add.png 500w, https://creatronix.de/wp-content/uploads/2024/10/vector-add-300x154.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></h4>
<h4>Skalarmultiplikation</h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
3 * a</code></pre>
</div>
<p>Hier werden die Elemente des Arrays mit 3 multipliziert.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2024/10/scalar-mult.png" alt="" width="500" height="197" class="alignnone size-full wp-image-7123" srcset="https://creatronix.de/wp-content/uploads/2024/10/scalar-mult.png 500w, https://creatronix.de/wp-content/uploads/2024/10/scalar-mult-300x118.png 300w" sizes="(max-width: 500px) 100vw, 500px" /><br />
Hier können wir sehen, warum es charmant ist, numpy&#8217;s Array für diese Operation zu verwenden.<br />
Wenn wir das mit einer List comprehension machen, sieht das so aus:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>c = [1, 2, 3]
d = [x * 3 for x in c]
</code></pre>
</div>
<p>Das ist weniger intuitiv und auch langsamer.</p>
<h4>Skalarprodukt</h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
b = np.array([5, 6, 7])
a.dot(b)
</code></pre>
</div>
<p>Hier wird das Skalarprodukt berechnet, in dem alle Elemente miteinander multipliziert und dann summiert werden.<br />
Das ergibt in diesem Fall 38.</p>
<h4><img decoding="async" src="https://creatronix.de/wp-content/uploads/2024/10/scalar-prod.png" alt="" width="500" height="163" class="alignnone size-full wp-image-7121" srcset="https://creatronix.de/wp-content/uploads/2024/10/scalar-prod.png 500w, https://creatronix.de/wp-content/uploads/2024/10/scalar-prod-300x98.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></h4>
<h4>Hadamard-Produkt</h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
b = np.array([5, 6, 7])
a * b
</code></pre>
</div>
<p>Hier wird das Hadamard-Produkt berechnet, in dem die Elemente der Arrays elementweise multipliziert werden.<br />
Das ergibt</p>
<p>array([5, 12, 21])</p>
<h4>Kreuzprodukt</h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1, 2, 3])
b = np.array([5, 6, 7])
np.cross(a, b)
</code></pre>
</div>
<p>Hier wird das Kreuzprodukt berechnet.<br />
Das Ergebnis ist ein neues Array, das das Kreuzprodukt von a und b darstellt.</p>
<h3>Matrizen</h3>
<p>ndarray kann auch für Matrizen verwendet werden.</p>
<h4>Matrixmultiplikation</h4>
<p>Bei der Matrixmultiplikation ist es wichtig, dass die Anzahl der Spalten der ersten Matrix gleich der Anzahl der Zeilen der zweiten Matrix ist.<br />
Dann kann nach dem Schema &#8220;Zeile mal Spalte&#8221; multipliziert werden. Das kenne wir noch aus der Schulzeit.<br />
Dort haben wir es unter dem Namen Schema von Falk kennengelernt.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
a.dot(b)
</code></pre>
</div>
<p>Hier wird die Matrixmultiplikation durchgeführt.<br />
Das Ergebnis ist ein neues Array, das die Matrixmultiplikation von a und b darstellt.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2024/10/numpy-matmul.png" alt="" width="500" height="500" class="alignnone size-full wp-image-7124" srcset="https://creatronix.de/wp-content/uploads/2024/10/numpy-matmul.png 500w, https://creatronix.de/wp-content/uploads/2024/10/numpy-matmul-300x300.png 300w, https://creatronix.de/wp-content/uploads/2024/10/numpy-matmul-150x150.png 150w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>matmul() und dot() sind äquivalent, aber matmul() sollte bevorzugt werden, weil es klarer ist. Besser noch ist die @-Operator-Syntax.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>np.matmul(a, b)
a @ b
</code></pre>
</div>
<h3>Reshape</h3>
<p>Mit der Methode reshape kann ein Array in eine andere Form gebracht werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([1,2,3,4,5,6,7,8])
reshaped = a.reshape([2, 4])
reshaped
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
</code></pre>
</div>
<h3>einsum</h3>
<p>einsum ist eine Funktion, die es ermöglicht, die Summation über die Elemente eines Arrays zu steuern.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.array([[1, 2], [3, 4]])
np.einsum('ii', a)
</code></pre>
</div>
<p>Hier wird die Summe der Diagonalelemente berechnet.<br />
Das Ergebnis ist 5.</p>
<h2>ranges und linspace</h2>
<p>numpy hat auch Funktionen, um die Erstellung von Arrays zu erleichtern.</p>
<h3>arange</h3>
<p>arange erstellt ein Array mit einer Sequenz von Zahlen.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.arange(10)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
</code></pre>
</div>
<p>wie die range-Funktion in Python, kann auch arange mit einem Startwert, einem Endwert und einem Schritt verwendet werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>b = np.arange(1, 10, 2)
b
array([1, 3, 5, 7, 9])</code></pre>
</div>
<h3>linspace</h3>
<p>linspace erstellt ein Array mit einer Sequenz von Zahlen, die gleichmäßig verteilt sind.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.linspace(0, 1, 5)
a
array([0. , 0.25, 0.5 , 0.75, 1. ])
</code></pre>
</div>
<h2>Zufallszahlen</h2>
<p>numpy hat auch Funktionen, um Zufallszahlen zu generieren.</p>
<h3>rand</h3>
<p>rand erstellt ein Array mit Zufallszahlen zwischen 0 und 1.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.random.rand(3)
a
array([0.5488135 , 0.71518937, 0.60276338])
</code></pre>
</div>
<h3>randint</h3>
<p>randint erstellt ein Array mit Zufallszahlen zwischen einem Startwert und einem Endwert.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>a = np.random.randint(1, 10, 3)
a
array([6, 1, 1])
</code></pre>
</div>
<h2>Fazit</h2>
<p>NumPy ist eine leistungsstarke Bibliothek für numerische Berechnungen in Python.<br />
Es bietet viele Funktionen, die das Arbeiten mit Vektoren und Matrizen erleichtern.</p>
<p>The post <a href="https://creatronix.de/einfuhrung-in-numpy/">Einführung in numpy</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Letzen winlogon mit win32evtlog auslesen</title>
		<link>https://creatronix.de/letzen-winlogon-mit-win32evtlog-auslesen/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 13 Oct 2023 15:40:48 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=6822</guid>

					<description><![CDATA[<p>Motivation Unserer Zeiterfassungssoftware startet normalerweise beim Hochfahren des Rechners. Im Homeoffice kann es aber passieren, dass der VPN-Client nicht automatisch startet und die Zeiterfassung nicht funktioniert. Dann muss man die Arbeitszeit per Hand eintragen. Dazu schaue ich mir im Event Viewer das letzten winlogon-Event an. Um nicht mehr in der Ereignisanzeige suchen zu müssen, habe&#8230;</p>
<p>The post <a href="https://creatronix.de/letzen-winlogon-mit-win32evtlog-auslesen/">Letzen winlogon mit win32evtlog auslesen</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Unserer Zeiterfassungssoftware startet normalerweise beim Hochfahren des Rechners.<br />
Im Homeoffice kann es aber passieren, dass der VPN-Client nicht automatisch startet und die Zeiterfassung nicht funktioniert.<br />
Dann muss man die Arbeitszeit per Hand eintragen. Dazu schaue ich mir im Event Viewer das letzten winlogon-Event an.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2023/10/event_viewer.png" alt="" width="899" height="93" class="alignnone size-full wp-image-6821" srcset="https://creatronix.de/wp-content/uploads/2023/10/event_viewer.png 899w, https://creatronix.de/wp-content/uploads/2023/10/event_viewer-300x31.png 300w, https://creatronix.de/wp-content/uploads/2023/10/event_viewer-768x79.png 768w" sizes="(max-width: 899px) 100vw, 899px" /></p>
<p>Um nicht mehr in der Ereignisanzeige suchen zu müssen, habe ich mir ein kleines Script geschrieben, dass mir das letzten winlogon-Event anzeigt.</p>
<h2>Code</h2>
<p>Als Erstes installieren wir das Paket pywin32, welches uns den Zugriff auf die Windows-API ermöglicht.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pywin32
</code></pre>
</div>
<p>Mit der OpenEventLog-Methode können wir das eventlog öffnen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>handle = win32evtlog.OpenEventLog(SERVER, LOGTYPE)
</code></pre>
</div>
<p>Da wir die Events vom letzten Eintrag aus durchgehen wollen, setzen wir die flags auf EVENTLOG_BACKWARDS_READ.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>flags = win32evtlog.EVENTLOG_SEQUENTIAL_READ | win32evtlog.EVENTLOG_BACKWARDS_READ
</code></pre>
</div>
<p>Mit der ReadEventLog Methode können wir die Events auslesen.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>events = win32evtlog.ReadEventLog(handle, flags, 0)
</code></pre>
</div>
<p>Achtung: Es kommen nicht alle Events, sondern nur Chunks! Deshalb müssen wir iterieren.</p>
<p>Die Information, die wir suchen, steckt im SourceName und heißt &#8220;Microsoft-Windows-Winlogon&#8221;<br />
Wenn wir ein Event mit dem Namen gefunden haben, können wir den Timestamp aus der TimeGenerated Property auslesen.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>for event in events:
    if 'Microsoft-Windows-Winlogon' in event.SourceName:
        last_logon = event.TimeGenerated
        found = True  # we're only interested in the last
        break
</code></pre>
</div>
<p>Da uns nur der letzte Eintrag interessiert, brechen wir aus beiden Schleifen raus.</p>
<p>zu guter Letzt schließen wir das Eventlog noch sauber</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>win32evtlog.CloseEventLog(handle)
</code></pre>
</div>
<h2>Vollständiger Code</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import win32evtlog

def get_last_win_logon():
    last_logon = None
    handle = win32evtlog.OpenEventLog(None, 'System')
    flags = win32evtlog.EVENTLOG_SEQUENTIAL_READ | win32evtlog.EVENTLOG_BACKWARDS_READ
    found = False
    while not found:  # we need an outer loop because events will be retrieved as chunks
        events = win32evtlog.ReadEventLog(handle, flags, 0)
        if not events:
            break  # no more events

        for event in events:
            if 'Microsoft-Windows-Winlogon' in event.SourceName:
                last_logon = event.TimeGenerated
                found = True  # we're only interested in the last
                break

    win32evtlog.CloseEventLog(handle)
    return last_logon
</code></pre>
</div>
<p>The post <a href="https://creatronix.de/letzen-winlogon-mit-win32evtlog-auslesen/">Letzen winlogon mit win32evtlog auslesen</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Wie TDD die Welt rettet (und deine Nerven schont)</title>
		<link>https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Thu, 13 Apr 2023 08:09:43 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=6702</guid>

					<description><![CDATA[<p>Motivation In diesem Artikel möchte ich euch den Einsatz von TDD schmackhaft machen. Es gibt viel zu viel schlechte Software und aus meiner Sicht hängt das zu großen Teilen daran, dass diese Software nicht genug getestet wurde. Auch wenn ein Großteil der Tests oft von dedizierten Testern gemacht wird, könnt ihr als Entwickler euren Beitrag&#8230;</p>
<p>The post <a href="https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/">Wie TDD die Welt rettet (und deine Nerven schont)</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>In diesem Artikel möchte ich euch den Einsatz von TDD schmackhaft machen. Es gibt viel zu viel schlechte Software und aus meiner Sicht hängt das zu großen Teilen daran, dass diese Software nicht genug getestet wurde. Auch wenn ein Großteil der Tests oft von dedizierten Testern gemacht wird, könnt ihr als Entwickler euren Beitrag leisten, indem ihr für eine gute Testabdeckung auf der untersten Ebene sorgt. Als unterste Ebene in der Software-Entwicklung spricht man oft über eine &#8220;Unit&#8221;. Der Begriff ist nicht exakt definiert, hier kann eine Klasse, ein Modul, aber auch eine einzelne Funktion gemeint sein. Nichtsdestotrotz sind Unit-Tests ein wirksames Mittel, um die intrinsische Software-Qualität zu steigern. Ein Weg wie Unit-Tests entstehen können ist TDD.</p>
<h2>Was bedeutet TDD?</h2>
<p>TDD steht für Test Driven Development, also test-getriebene Entwicklung. In einer perfekten Welt entsteht damit keine Zeile Produktiv-Code ohne einen korrespondieren Testfall.<br />
Warum ist das gut? Aus mehreren Gründen. Bevor ihr den Code schreibt, macht ihr euch Gedanken über seine Verwendung, weil ihr im Test euren Code selbst aufrufen müsst.<br />
Das führt dazu, dass man sich mehr Gedanken um die Schnittstelle und die Modularisierung machen muss, damit man überhaupt etwas testbares bekommt. (Weg vom Spaghetti-Code)<br />
Dann implementiert man auch immer nur so viel Code, dass der Test erfolgreich durchläuft (Wir sagen &#8220;grün wird&#8221;)<br />
Das korrespondiert sehr gut mit dem agilen Konzept der &#8220;Definition of Done&#8221;: Ich mache mir vor der Arbeit Gedanken darüber, wann die Arbeit erledigt ist.<br />
Als Nächstes kann ich hemmungslos meinen Code refaktorn ohne mir Gedanken zu machen, ob ich ein einmal erfolgreich gebautes Feature wieder kaputtmache, weil mir die Tests unmittelbar verraten, wenn ich Mist mache.<br />
Aber lassen wir den Worten doch Taten folgen:</p>
<h2>Vorbereitung</h2>
<p>Für dieses Beispiel benötigt ihr eine halbwegs aktuelle Python-Installation und einen Editor eurer Wahl.<br />
Für TDD empfehle ich die Benutzung von pytest, da es weniger setup braucht, um erste Tests schreiben zu können und das Standardkommando auch das automatische Finden von Testfällen erleichtert.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pytest</code></pre>
</div>
<h2>Unser erster Test</h2>
<p>Wir legen eine neue Datei test_mean.py an und fügen den ersten Testfall hinzu:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_mean():
    assert False
</code></pre>
</div>
<p>Jetzt können wir pytest im Terminal starten, um sicherzugehen, dass der Test gefunden wird. pytest deutet alles was das Wort &#8220;test&#8221; im Namen hat als Test und versucht es auszuführen.</p>
<h2>Struktur schaffen</h2>
<p>Das Ziel der Übung soll sein, eine Funktion zu haben, die den Mittelwert einer Datenreihe berechnet.<br />
Dazu schreiben wir das Grundgerüst der Funktion und rufen diese in unserem Test auf.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_mean():
    assert calculate_mean([1]) == 1

def calculate_mean(data):
    pass
</code></pre>
</div>
<p>Der Test ist jetzt noch rot. Das ist aber richtig und wichtig, weil wir sicherstellen müssen, dass unser Test auch wirklich unsere Implementierung testet.<br />
Wäre er schon am Anfang grün, würden wir irgendetwas testen, nur nicht den Code, den wir ja jetzt eigentlich erst schreiben wollen.</p>
<h2>Implementieren bis Test grün</h2>
<p>Unsere Aufgabe ist es jetzt, den Test mit dem dümmst-möglichen Code auf grün zu bringen. In unserem Fall:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    return 1
</code></pre>
</div>
<p>Das ist so dumm, das tut schon fast weh. Aber: Fake it till you make it! Wir wissen, dass der Mittelwert einer Zahl sie selbst sein muss und deshalb ist der Code auch so korrekt.</p>
<p>Wenn wir pytest im Terminal ausführen, läuft der Test jetzt auch durch. Bingo!</p>
<h2>Neuer Testcase</h2>
<p>Wir wollen uns aber auch nicht zu lange ausruhen und wiederholen das Spiel mit folgendem Testfall:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_mean_2():
    assert calculate_mean([2]) == 2
</code></pre>
</div>
<p>Wenn wir pytest ausführen, ist der Test auch wieder rot.</p>
<h2>Implementieren bis Test grün</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    return data[0]
</code></pre>
</div>
<p>Jetzt können wir beide Testfälle erfüllen, indem wir das erste Element der Liste (Achtung Index 0!) zurückgeben,</p>
<h2>Neuer Testcase</h2>
<p>Ein weiterer Fall mit einem Element würde keinen Erkenntnisgewinn bringen, deshalb übergeben wir jetzt eine Liste mit zwei Elementen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_mean_two_elements():
    assert calculate_mean([1, 2]) == 1.5
</code></pre>
</div>
<h2>Implementieren bis Test grün</h2>
<p>Jetzt müssen wir eine Fallunterscheidung machen: wenn die Länge der Datenreihe zwei ist, addieren wir die Werte an Index 0 und 1 und teilen sie durch zwei.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    if len(data) == 2:
        return (data[0] + data[1]) / 2
    return data[0]
</code></pre>
</div>
<h2>Rule of Three</h2>
<p>Jetzt haben wir den Punkt erreicht, an dem wir dreimal das gleich getan haben. Wir haben dreimal einen ähnlichen Testfall geschrieben und deshalb führen wir jetzt parametrisierte Tests ein. parametrize ist ein schönes pytest-Feature. Ihr definiert Eingangsdaten und Erwartungswert und der decorator @pytest.mark.parametrize sorgt dafür, dass euer Test so oft ausgeführt wird, wie Datenzeilen vorhanden sind. Ihr spart damit eine Menge Schreibarbeit!</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.parametrize(
    "data, expected_mean",
    [
        ([1], 1),
        ([2], 2),
        ([1, 2], 1.5)
    ]
)
def test_mean(data, expected_mean):
    assert calculate_mean(data) == expected_mean
</code></pre>
</div>
<p>Wenn ihr den Test startet, sollte der auch noch grün sein, da wir die drei einzelnen Testfälle durch den einen parametrisierten Test ersetzt haben.</p>
<h2>Neuer Testcase</h2>
<p>Jetzt haben wir die Grundlage, um entspannt einen neuen Testfall mit drei Eingangswerten zu ergänzen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>@pytest.mark.parametrize(
    "data, expected_mean",
    [
        ([1], 1),
        ([2], 2),
        ([1, 2], 1.5),
        ([1, 2, 3], 2),
    ]
)
</code></pre>
</div>
<h2>Implementieren bis Test grün</h2>
<p>Wir wiederholen das Spiel für den Fall mit drei Elementen: Überprüfen der Länge, Addieren der Elemente und Teilen durch drei.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    if len(data) == 2:
        return (data[0] + data[1]) / 2
    if len(data) == 3:
        return (data[0] + data[1] + data[2]) / 3
    return data[0]
</code></pre>
</div>
<p>Der Test sollte jetzt auch wieder grün sein.</p>
<h2>Refactoring</h2>
<p>Mit einem grünen Test können wir jetzt gefahrlos refactorn.<br />
Die Implementierung aus Test 1 &amp; 2 passen wir vom Aussehen an, damit es sich im nächsten Schritt generalisieren lassen kann.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    if len(data) == 1:
        return data[0] / 1
    if len(data) == 2:
        return (data[0] + data[1]) / 2
    if len(data) == 3:
        return (data[0] + data[1] + data[2]) / 3
</code></pre>
</div>
<h2>Rule of Three &#8211; die Zwote</h2>
<p>Jetzt haben wir wieder einen Punkt erreicht, an dem wir wieder dreimal das gleich getan haben.<br />
In diesem Fall haben wir dreimal eine ähnliche Implementierung geschrieben. Der Unterschied besteht hier in der Länge der Datenreihe.</p>
<p>Deshalb refactorn wir erstmal die len(data)-Aufrufe und ziehen eine Variable raus:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    length = len(data)
    if length == 1:
        return data[0] / 1
    if length == 2:
        return (data[0] + data[1]) / 2
    if length == 3:
        return (data[0] + data[1] + data[2]) / 3
</code></pre>
</div>
<p>Als Nächstes können wir den Teiler durch die length-Variable ersetzen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    length = len(data)
    if length == 1:
        return data[0] / length
    if length == 2:
        return (data[0] + data[1]) / length
    if length == 3:
        return (data[0] + data[1] + data[2]) / length
</code></pre>
</div>
<p>Achtung: bitte immer nach jeder Änderung den Test / die Tests laufen lassen, damit wir frühzeitig herausfinden, ob wir etwas kaputtgemacht haben.</p>
<p>Jetzt geht es ans Eingemachte und es ist auch der größte Stretch: Wir müssen die Additionen loswerden. Dies können wir in einer Schleife machen. Wir benötigen eine Variable, die die Summe repräsentiert und am Anfang 0 ist. Dann addieren wir jedes Element aus unserer Datenreihe auf. Danach ersetzen wir die Additionen durch unsere neue Variable</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>if length == 3:
    sum = 0
    for elem in data:
        sum = sum + elem
    return sum / length
</code></pre>
</div>
<p>Die Tests laufen noch durch, also können wir weiter vereinfachen und die Abfragen für die unterschiedlichen Längen entfernen. Damit sieht unser Code nun so aus:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def calculate_mean(data):
    length = len(data)
    sum = 0
    for elem in data:
        sum = sum + elem
    return sum / length
</code></pre>
</div>
<p>Das kann sich doch schon sehen lassen!</p>
<h2>Just to be sure &#8211; neuer Testfall</h2>
<p>Wir testen jetzt, ob unsere Generalisierung auch für den Fall mit 4 Elementen funktioniert und fügen noch</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>    ([1, 2, 3, 4], 2.5),
</code></pre>
</div>
<p>hinzu. pytest gestartet, läuft noch!</p>
<h2>Corner Cases</h2>
<p>Um eine robuste Implementierung zu bekommen, müssen wir noch den Fall &#8220;leere Liste&#8221; abhandeln. Also fügen wir noch den Testfall</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>    ([], None),
</code></pre>
</div>
<p>hinzu. Das heißt wir erwarten, dass die Funktion None zurückgibt, wenn eine leere Liste übergeben wird. Das frühstücken wir mit einem</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>if length == 0:
    return
</code></pre>
</div>
<p>ab. Alternativ ließe sich natürlich auch eine Exception werfen.</p>
<p>Damit sind wir auch am Ende unserer kleinen TDD-Session.</p>
<h2>Finale Worte</h2>
<p>Dies ist ein Beispiel, wie sich TDD anwenden lässt. Die Anzahl der Zwischenschritte wirkt hier eventuell etwas künstlich hoch. Erfahrenere Entwickler würden wahrscheinlich dazu tendieren den Schritt mit der Summenberechnung direkt hinzuschreiben. Primär soll es aber darum gehen, die Abläufe im TDD zu üben (insbesondere die Rule of Three), daher ist die Implementierung die entstehen sollte bewusst einfach gewählt. Im Idealfall implementieren wir eine Mittelwertberechnung gar nicht selbst, sondern nutzen das statistics Modul.</p>
<h2>Kompletter Code</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.parametrize(
    "data, expected_mean",
    [
        ([], None),
        ([1], 1),
        ([2], 2),
        ([1, 2], 1.5),
        ([1, 2, 3], 2),
        ([1, 2, 3, 4], 2.5),
    ]
)
def test_mean(data, expected_mean):
    assert calculate_mean(data) == expected_mean


def calculate_mean(data):
    length = len(data)
    if length == 0:
        return
    sum = 0
    for elem in data:
        sum = sum + elem
    return sum / length
</code></pre>
</div>
<p>The post <a href="https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/">Wie TDD die Welt rettet (und deine Nerven schont)</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Pillow how to convert images to webp</title>
		<link>https://creatronix.de/pillow-how-to-convert-images-to-webp/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 30 Dec 2022 10:28:48 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=6548</guid>

					<description><![CDATA[<p>Motivation WebP is a modern image format that provides superior lossless and lossy compression for images on the web. It was developed by Google, and is designed to be smaller in size than other image formats, while still maintaining high image quality. One reason WebP is often considered better than JPEG is that it can&#8230;</p>
<p>The post <a href="https://creatronix.de/pillow-how-to-convert-images-to-webp/">Pillow how to convert images to webp</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>WebP is a modern image format that provides superior lossless and lossy compression for images on the web. It was developed by Google, and is designed to be smaller in size than other image formats, while still maintaining high image quality.</p>
<p>One reason WebP is often considered better than JPEG is that it can achieve significantly smaller file sizes for images with the same visual quality. This is because WebP uses more advanced compression techniques than JPEG, which allows it to reduce the size of the image data without sacrificing quality. This can be especially useful for websites, as smaller image sizes can lead to faster page load times and a better user experience.</p>
<p>In addition to its smaller file size, WebP also has other advantages over JPEG. It supports both lossless and lossy compression, so you can choose the right balance of file size and image quality for your needs. WebP also supports transparency, which can be useful for graphics and logos.</p>
<p>Overall, WebP is a modern image format that can provide improved compression and faster page load times, making it a good choice for use on the web.</p>
<h2>Conversion with Pillow</h2>
<p>Pillow supports webp as well. The cool thing is that the conversion can be done in a couple of lines:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_convert_to_webp():
    source = Path("pillow_webp-fi.jpg")
    destination = source.with_suffix(".webp")

    image = Image.open(source)
    image.save(destination, format="webp")

    path_webp = Path("pillow_webp-fi.webp")
    assert path_webp.exists()
</code></pre>
</div>
<h2>Bottom line</h2>
<p>After conversion the image size is cut in half (7.3 MB to 3.6MB) without any visual artifacts of compression.</p>
<h2>Further Reading</h2>
<p><a href="https://creatronix.de/how-to-create-youtube-thumbnails-pillow-tutorial/">How to create youtube thumbnails &#8211; pillow tutorial</a></p>
<p>The post <a href="https://creatronix.de/pillow-how-to-convert-images-to-webp/">Pillow how to convert images to webp</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to learn IDE Shortcuts with Key Promotor X</title>
		<link>https://creatronix.de/how-to-learn-ide-shortcuts-with-key-promotor-x/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 16 Dec 2022 07:34:09 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=6465</guid>

					<description><![CDATA[<p>Motivation The DRY (Don&#8217;t repeat yourself) principle does not only apply to code but to mouse clicks as well: When you are repeatedly clicking buttons you might want to figure out some keyboard shortcuts. In Jetbrains IDEs like PyCharm you can use the plugin Key Promotor X to learn these shortcuts. Installation Open the File&#8230;</p>
<p>The post <a href="https://creatronix.de/how-to-learn-ide-shortcuts-with-key-promotor-x/">How to learn IDE Shortcuts with Key Promotor X</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>The DRY (Don&#8217;t repeat yourself) principle does not only apply to code but to mouse clicks as well:<br />
When you are repeatedly clicking buttons you might want to figure out some keyboard shortcuts.</p>
<p>In Jetbrains IDEs like PyCharm you can use the plugin Key Promotor X to learn these shortcuts.</p>
<h2>Installation</h2>
<ul>
<li>Open the File menu</li>
<li>Choose Settings</li>
<li>Go to Plugins</li>
<li>Switch to marketplace</li>
<li>Search for &#8220;Key Promoter X&#8221;</li>
<li>Click &#8220;Install&#8221;</li>
<li>Restart your IDE (if necessary)</li>
</ul>
<h2><img decoding="async" src="https://creatronix.de/wp-content/uploads/2022/12/kpx_install.png" alt="" class="alignnone size-full wp-image-6463" width="955" height="856" srcset="https://creatronix.de/wp-content/uploads/2022/12/kpx_install.png 955w, https://creatronix.de/wp-content/uploads/2022/12/kpx_install-300x269.png 300w, https://creatronix.de/wp-content/uploads/2022/12/kpx_install-768x688.png 768w" sizes="(max-width: 955px) 100vw, 955px" /></h2>
<h2>Usage</h2>
<p>After Key Promotor X is correctly installed it will nag you in two different ways to get your shortcut game up and running</p>
<h3>Tool Window</h3>
<p>Under View -&gt; Tool windows you will find a new entry for Key Promotor X<br />
If you enable the tool window you will get a list of your missed shortcuts sorted after frequency.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2022/12/kpx_usage.png" alt="" class="alignnone size-full wp-image-6464" width="833" height="812" srcset="https://creatronix.de/wp-content/uploads/2022/12/kpx_usage.png 833w, https://creatronix.de/wp-content/uploads/2022/12/kpx_usage-300x292.png 300w, https://creatronix.de/wp-content/uploads/2022/12/kpx_usage-768x749.png 768w" sizes="(max-width: 833px) 100vw, 833px" /></p>
<h3>Alert</h3>
<p>For every action you take in your IDE you will now get a notification. It shows you if there already is a shortcut or let&#8217;s you create one.</p>
<h2><img decoding="async" src="https://creatronix.de/wp-content/uploads/2022/12/kpx_alerts.png" alt="" class="alignnone size-full wp-image-6462" width="392" height="175" srcset="https://creatronix.de/wp-content/uploads/2022/12/kpx_alerts.png 392w, https://creatronix.de/wp-content/uploads/2022/12/kpx_alerts-300x134.png 300w" sizes="(max-width: 392px) 100vw, 392px" /></h2>
<h2>Further Reading</h2>
<p>If you like to become more efficient please also read our articles about other shortcuts</p>
<p><a href="https://creatronix.de/my-most-used-keyboard-shortcuts-in-windows-10/">My most used keyboard shortcuts in Windows 10</a><a href="https://creatronix.de/my-most-used-keyboard-shortcuts-in-windows-10/"></a></p>
<p><a href="https://creatronix.de/shortcuts-for-screenshots-on-macos/">Shortcuts for Screenshots on macOS</a></p>
<p><a href="https://creatronix.de/keyboard-shortcuts-for-signals-in-the-macos-terminal/">Keyboard shortcuts for signals in the macOS terminal</a><a href="https://creatronix.de/keyboard-shortcuts-for-signals-in-the-macos-terminal/"></a></p>
<p><a href="https://creatronix.de/how-to-enable-spell-checking-for-german-in-pycharm/">How to enable spell checking for German in PyCharm</a></p>
<p>The post <a href="https://creatronix.de/how-to-learn-ide-shortcuts-with-key-promotor-x/">How to learn IDE Shortcuts with Key Promotor X</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Advent of Code 2022 Day 3</title>
		<link>https://creatronix.de/advent-of-code-2022-day-3/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sat, 10 Dec 2022 11:05:05 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=6448</guid>

					<description><![CDATA[<p>Challenge &#8211; Part 1 https://adventofcode.com/2022/day/3 Problem vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw The first rucksack contains the items vJrwpWtwJgWrhcsFMMfFFhFp, which means its first compartment contains the items vJrwpWtwJgWr, while the second compartment contains the items hcsFMMfFFhFp. The only item type that appears in both compartments is lowercase p. The second rucksack&#8217;s compartments contain jqHRNqRjqzjGDLGL and&#8230;</p>
<p>The post <a href="https://creatronix.de/advent-of-code-2022-day-3/">Advent of Code 2022 Day 3</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Challenge &#8211; Part 1</h2>
<p><a href="https://adventofcode.com/2022/day/3">https://adventofcode.com/2022/day/3</a></p>
<h3>Problem</h3>
<pre>vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw</pre>
<p>The first rucksack contains the items vJrwpWtwJgWrhcsFMMfFFhFp, which means its first compartment contains the items vJrwpWtwJgWr, while the second compartment contains the items hcsFMMfFFhFp. The only item type that appears in both compartments is lowercase p.<br />
The second rucksack&#8217;s compartments contain jqHRNqRjqzjGDLGL and rsFMfFZSrLrFZsSL. The only item type that appears in both compartments is uppercase L.<br />
The third rucksack&#8217;s compartments contain PmmdzqPrV and vPwwTWBwg; the only common item type is uppercase P.<br />
The fourth rucksack&#8217;s compartments only share item type v.<br />
The fifth rucksack&#8217;s compartments only share item type t.<br />
The sixth rucksack&#8217;s compartments only share item type s.</p>
<p>To help prioritize item rearrangement, every item type can be converted to a priority:</p>
<p>Lowercase item types a through z have priorities 1 through 26.<br />
Uppercase item types A through Z have priorities 27 through 52.</p>
<p>In the above example, the priority of the item type that appears in both compartments of each rucksack is 16 (p), 38 (L), 42 (P), 22 (v), 20 (t), and 19 (s); the sum of these is 157.</p>
<p>Find the item type that appears in both compartments of each rucksack. What is the sum of the priorities of those item types?</p>
<h3>Solution</h3>
<p>We put the sample data into a multi-line string:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>data="""vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw"""
</code></pre>
</div>
<p>We check if we can get the first rucksack:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_split_rucksacks():
    first_rucksack = data.split("\n")[0]
    assert first_rucksack == "vJrwpWtwJgWrhcsFMMfFFhFp"
</code></pre>
</div>
<p>Due to different length of each rucksack we need to determine the splitting point:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_split_rucksack_items():
    first_rucksack = data.split("\n")[0]
    <strong>split_point</strong> = len(first_rucksack)//2
    first_rucksack_item = first_rucksack[0:split_point]
    assert first_rucksack_item == "vJrwpWtwJgWr"
    second_rucksack_item = first_rucksack[split_point:]
    assert second_rucksack_item == "hcsFMMfFFhFp"
</code></pre>
</div>
<p>A neat solution for checking common characters is putting the strings into sets and logically and them</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_check_common_char():
    first_rucksack_item = "vJrwpWtwJgWr"
    second_rucksack_item = "hcsFMMfFFhFp"
    a = list(<strong>set(first_rucksack_item) &amp; set(second_rucksack_item)</strong>)
    assert a == ['p']
</code></pre>
</div>
<p>To get the corresponding value for the characters we use the built-in ascii_letters:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_lookup_char_value():
    chars = string.ascii_letters
    value = chars.index('p') + 1
    assert value == 16

    value = chars.index('L') + 1
    assert value == 38
</code></pre>
</div>
<p>Putting it all together:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_sum_priorities_data():
    with open("aoc_data_03.txt", "r") as f:
        data_from_file = f.read()
    sum_of_list = 0
    chars = string.ascii_letters
    rucksacks = data_from_file.split("\n")
    for rucksack in rucksacks:
        split_point = len(rucksack) // 2
        first_rucksack_item = rucksack[0:split_point]
        second_rucksack_item = rucksack[split_point:]
        common_char = list(set(first_rucksack_item) &amp; set(second_rucksack_item))
        value = chars.index(common_char[0]) + 1
        sum_of_list += value
    assert sum_of_list == 8202
</code></pre>
</div>
<p>The post <a href="https://creatronix.de/advent-of-code-2022-day-3/">Advent of Code 2022 Day 3</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
