Code-Smells und Anti-Patterns#
Siehe auch
Funktionen, die Objekte sein sollten#
Python unterstützt neben der objektorientierten auch die prozedurale Programmierung mithilfe von Funktionen und vererbbaren Klassen. Beide Paradigmen sollten jedoch auf die passenden Probleme angewendet werden.
Typische Symptome von funktionalem Code, der in Klassen umgestaltet werden sollte, sind
ähnliche Argumente über Funktionen hinweg
hohe Anzahl eindeutiger Halstead-Operanden
Mix aus mutable und immutable Funktionen
So können z.B. drei Funktionen mit unklarer Verwendung so reorganisiert werden,
dass load_image()
durch .__init__()
ersetzt wird, crop()
eine
Klassenmethode wird und get_thumbnail()
eine Eigenschaft:
class Image(object):
thumbnail_resolution = 128
def __init__(self, path):
...
def crop(self, width, height):
...
@property
def thumbnail(self):
...
return thumb
Objekte, die Funktionen sein sollten#
Manchmal sollte jedoch auch objektorientierter Code besser in Funktionen
aufgelöst werden, z.B. wenn in einer Klasse außer .__init__()
nur eine
weitere Methode oder nur statische Methoden enthalten sind.
Bemerkung
Ihr müsst nicht händisch nach solchen Klassen suchen, sondern es gibt eine pylint-Regel dafür:
$ pipenv run pylint --disable=all --enable=R0903 requests
************* Module requests.auth
requests/auth.py:72:0: R0903: Too few public methods (1/2) (too-few-public-methods)
requests/auth.py:100:0: R0903: Too few public methods (1/2) (too-few-public-methods)
************* Module requests.models
requests/models.py:60:0: R0903: Too few public methods (1/2) (too-few-public-methods)
-----------------------------------
Your code has been rated at 9.99/10
Dies zeigt uns, dass in auth.py
zwei Klassen mit nur einer öffentlichen
Methode definiert wurden und zwar in den Zeilen 72ff. und 100ff. Auch in
models.py
gibt es ab Zeile 60 eine Klasse mit nur einer öffentlichen
Methode.
Verschachtelter Code#
«Flat is better than nested.»
– Tim Peters, Zen of Python
Verschachtelter Code erschwert das Lesen und Verstehen. Ihr müsst die Bedingungen verstehen und merken, wenn ihr durch die Zweige geht. Objektiv erhöht sich die zyklomatische Komplexität bei steigender Anzahl der Code-Verzweigungen.
Ihr könnt verschachtelte Methoden mit mehreren ineinandergesteckten
if
-Anweisungen reduzieren, indem ihr Ebenen durch Methoden ersetzt, die ggf.
False
zurückgeben. Anschließend könnt ihr mit .count()
überprüfen, ob
die Anzahl der Fehler > 0
ist.
Eine andere Möglichkeit besteht in der Verwendung von List Comprehensions. So kann der Code
results = []
for item in iterable:
if item == match:
results.append(item)
ersetzt werden durch:
results = [item for item in iterable if item == match]
Bemerkung
Die itertools der Python-Standardbibliothek sind häufig ebenfalls gut geeignet, um die Verschachtelungstiefe zu reduzieren indem Funktionen zum Erstellen von Iteratoren aus Datenstrukturen erstellt werden.
Bemerkung
Zudem könnt ihr mit den itertools auch filtern, z.B. mit filterfalse:
>>> from itertools import filterfalse
>>> from math import isnan
>>> from statistics import median
>>> data = [20.7, float('NaN'),19.2, 18.3, float('NaN'), 14.4]
>>> sorted(data)
[20.7, nan, 14.4, 18.3, 19.2, nan]
>>> median(data)
16.35
>>> sum(map(isnan, data))
2
>>> clean = list(filterfalse(isnan, data))
>>> clean
[20.7, 19.2, 18.3, 14.4]
>>> sorted(clean)
[14.4, 18.3, 19.2, 20.7]
>>> median(clean)
18.75
Query-Tools für komplexe Dicts#
JMESPath, glom, asq und flupy können die Abfrage von Dicts in Python deutlich vereinfachen.
Code reduzieren mit dataclasses
und attrs
#
dataclasses wurde in
Python 3.7 eingeführt und es gibt auch einen Backport für Python 3.6. Sie sollen
die Definition von Klassen vereinfachen, die hauptsächlich zum Speichern von
Werten erstellt werden, und auf die dann über die Attributsuche zugegriffen
werden kann. Einige Beispiele sind collections.namedtuple()
,
typing.NamedTuple
, Rezepte zu Records [1] und Verschachtelte Dicts
[2]. Datenklassen ersparen Euch das Schreiben und Verwalten dieser Methoden.
Siehe auch
PEP 557 – Data Classes
attrs ist ein Python-Paket, das es schon
viel länger als dataclasses
gibt, umfangreicher ist und auch mit älteren
Versionen von Python verwendet werden kann.