ÜberblickToolingVerwalten von AbhängigkeitenVirtuelle UmgebungenErstellen von pip-PaketenGrundlagenSyntaxKommentareOperatorenVariablen und IdentitätKontrollstrukturenFunkionenMehrfachrückgabenVariable ParameteranzahlGlobale VariablenEin- und AusgabenDatentypenBoolesche AusdrückeListen und TupelnWörterbücherMengenPfadeDateienModuleImporte & Future-AnweisungenSuchpfade für ModuleÜbersetzungseinheitenPakete Relative ImporteDokumentationObjektorientierungAttributeDynamisches Erzeugen & LöschenSlotsMethodenKonstruktorDestruktorWeitere magische MethodenDatenkapselungEigenschaftenKonstantenVererbungHöhere Funktionenλ-AusdrückeFunktionsabschlussmap, filter und reduceDekorateureIteratorenGeneratorenKonventionenZeilenprogrammeAnwendungen mit QtBasisklassenSignale und SlotsKomponentenCython
Merkmal | — |
---|---|
Grundparadigma | imperativ: strukturiert, objektorientiert |
Nebenparadigmen | funktional, reflexiv |
Syntax | distinktive Einrückungen und Zeilenenden |
Speicherverwaltung | automatisch – Skriptsprache |
Typsystem | stark, dynamisch |
Übersetzungsmodell | interpretierend |
Dateiendungen | PY, PYC, PYD, PYO, PYW, PYZ |
Ersterscheinungsjahr | 1990 |
Erfinder | Guido van Rossum |
Herausgeber | Python Software Foundation |
Vorläufer | ABC (Syntax), Perl |
Abkömmlinge | Cython, Numba, RPython, Starlark, Xonsh (Befehlssprache) |
Standardisierungen | keine – Gemeinschaftsprojekt |
Referenzimplementierung | CPython |
alternative Umsetzungen | Cython, IronPython (.Net), MicroPython / CircuitPython, PyPy, Stackless Python |
Paketverwalter | pip |
gängige Werkzeuge | Anaconda, IPython, virtualenv / venv, PyInstaller |
Bibliotheken | Django, Kivy, Numba, NumPy, pandas, PySide, RxPY |
empfohlene IDE | Eclipse + PyDev, Eric, Geany, PyCharm, VS Code |
offizielle Webseite | python.org |
Datei | Erläuterung |
---|---|
*.py | Quelltext |
*.pyc | Vorkompilat des Quelltextes – Bytecode für die virtuelle Maschine |
*.pyo | Vorkompilat des Quelltextes mit Optimierungen |
*.pyd | Windows-spezifisches Format für eine dynamische Programmbibliothek (DLL) |
*.pyw | Quelltext eines Programms, welches über eine Benutzeroberfläche gesteuert wird |
*.pyz | ZIP-Archiv mit allen Dateien einer Konsolenanwendung |
*.pyzw | ZIP-Archiv mit allen Dateien einer grafischen Anwendung |
Die Referenzimplementierung CPython
kann von der offiziellen Webseite python.org oder über die jeweilige Paketquelle des Hostsystems bezogen werden:
Windows | choco install python pip |
Ubuntu | sudo apt-get install python3 python3-pip |
Python bietet den eigenen Paketverwalter pip
, um Bibliotheken lokal auf dem Hostsystem zu installieren:
> pip install <package>
Mit pip --help
wird eine Übersicht aller Befehle ausgegeben.
In einer Textdatei requirements.txt
sind alle benötigten Packages aufzulisten, welche nicht Teil der Standardbibliothek sind:
pymongo==3.3.0
psutil>=2.0
pywin32; sys_platform == 'win32'
Die Installation erfolgt im selben Verzeichnis mit:
> pip install -r requirements.txt
> python install <package>
xxxxxxxxxx
python3 -m venv /path/to/new/virtual/environment
Zum Erstellen von verteilbaren Python-Bibliotheken müssen die folgenden Werkzeuge installiert sein:
xxxxxxxxxx
pip install --upgrade pip setuptools wheel
pip install tqdm
pip install --user --upgrade twine
Statt Schlüsselwörter wie begin … end
oder Schweifklammern { … }
werden Anweisungsblöcke durch bloßes Einrücken wiedergegeben.
xxxxxxxxxx
KONSTRUKT:
ANWEISUNG
ANWEISUNG
…
Syntaktisch erfolgt nach dem einleitenden Konstrukt ein Doppelpunkt, um den Beginn des Anweisungsblocks zu markieren.
Ein Befehlsabschluss ;
am Zeilenende ist nicht notwendig und wird ignoriert. Sollen jedoch mehrere Anweisungen in einer Zeile stehen, sind diese mittels Strichpunkt voneinander zu trennen:
xKONSTRUKT: ANWEISUNG; ANWEISUNG
if True: print( "Hallo "); print("Welt!")
Verschachtelungen von Konstrukten innerhalb einer Zeile erlaubt Python hingegen nicht.
Zu beachten ist, dass entweder alle Anweisungen noch auf selbiger Zeile zum Konstrukt stehen, oder unter diesem beliebig – aber jeweils gleich – eingerückt erfolgen:
xxxxxxxxxx
for i in range(10): print("A")
print("B")
In diesem Beispiel geht der Interpreter davon aus, dass nur print "A"
zum Schleifenkonstrukt gehört, da sich diese Anweisung noch auf selbiger Zeile befindet. Demnach verursacht die nachfolgende Anweisung print "B"
als unerwartet eingerückt
einen Fehler.
Python unterscheidet zwischen Groß- und Kleinschreibung. Gültige Bezeichner müssen mit einem Buchstaben oder Unterstrich beginnen, gefolgt von selbigem oder einer Zahl.
xxxxxxxxxx
# Zeilenkommentar
"""
Blockkommentar / mehrzeilige Zeichenkette
"""
Tatsächlich weist Python keine eigene Syntax für mehrzeilige Kommentare vor. Jedoch ignoriert der Interpreter unzugewiesene mehrzeilige Zeichenketten.
Benutzereigene Operatoren sind nicht möglich.
a + b |
Addition |
a - b |
Subtraktion |
a * b |
Multiplikation |
a ** b |
Exponentiation |
a / b |
Division |
a // b |
abrundende Division |
a % b |
Modulo |
xxxxxxxxxx
>>> 6 / 4 # automatisches Umwandeln zur Gleitkommazahl
1.5
>>> 6 // 4 # auromatisches Abrunden zur Ganzzahl
1
>>> 6.0 / 4
1.5
>>> 6.0 // 4 # abgerundete Gleitkommazahl als Ergebnis
1.0
a == b |
gleich |
a != b |
ungleich |
a > b |
größer |
a >= b |
größer gleich |
a < b |
kleiner |
a <= b |
kleiner gleich |
Vergleichsoperatoren lassen sich miteinander verketten:
xxxxxxxxxx
>>> 5 < 8 < 9 < 10 # entspricht '5 < 8 and 8 < 9 and 9 < 10'
True
a is b |
wahr, wenn beide Variablen auf das selbe Objekt verweisen |
a is not b |
wahr, wenn beide Variablen unterschiedliche Objekte referenzieren |
a in b |
wahr, wenn a in b enthalten ist |
a not in b |
wahr, wenn a nicht in b enthalten ist |
not a |
wenn a falsch, dann True , sonst False |
a and b |
wenn a falsch, dann a, sonst b |
a or b |
wenn a falsch, dann b, sonst a |
Bei Objekten, welche keine Wahrheitswerte darstellen, geben logische Operatoren einen der beiden Operanden zurück:
xxxxxxxxxx
>>> 22 and 333/12 or 1
27
>>> 333/12 if 22 else 1 # empfohlen!
27
Seit Version 2.5 bietet Python bedingte Ausdrücke, sodass derartige Zweckentfremdungen logischer Operatoren vermieden werden sollten.
Operation | Name | Beschreibung |
---|---|---|
a & b |
AND | setze Bit auf 1, wenn beide Bits 1 sind |
a | b |
OR | setze Bit auf 1, wenn zumindest ein Bit 1 ist |
a ^ b |
XOR | setze Bit auf 1, wenn nur ein Bit 1 ist |
~a |
NOT | invertiere alle Bits |
a << b |
Linksverschiebung | |
a >> b |
Rechtsverschiebung |
xxxxxxxxxx
a = 0b111100 # binär für 60
b = 0b1000101 # 257
c = 0o5017 # oktal für 2575
d = 0x92AF0 # hexadezimal für 600816
print(bin(a & b))
print(bin(a | b))
print(bin(a ^ b))
print(bin(~a))
print(bin(b << 2))
print(bin(b >> 2))
print(oct(c))
print(hex(d))
= |
Zuweisung des rechten Ausdrucks |
#= |
kombinierte Zuweisung mit # ∈ { + , - , * , / , % , // , ** , & , | , ^ , >> , << } |
Eine Variable ist ein Name, mit welchem auf ein Objekt zugegriffen werden kann:
xxxxxxxxxx
# NAME = AUSDRUCK
x = 9
print("x ist vom Wert ", x)
y = x
x = x + 1
print(x, y)
a, b, c = 0.1, 100, 'string' # Mehrfachzuweisung
Ob zwei Variablen das selbe Objekt referenzieren, lässt sich durch Ermitteln der Identität mit der Funktion id
herausfinden:
xxxxxxxxxx
>>> x = 1
>>> y = 2
>>> id(x)
94874271540632
>>> id(y)
94874271540608
>>> y = x
>>> id(y)
94874271540632
Diese Identität muss für ein Objekt während der gesamten Lebensdauer eindeutig und konstant sein.
Jeder Variablen ist eine bestimmte Datenart bzw Klasse zugeordnet, welche sich zur Laufzeit ändern kann:
xxxxxxxxxx
>>> a = 59
>>> type(a) # Abfrage der Datenart
<type 'int'>
>>> a = "Hallo"
>>> type(a)
<type 'str'>
>>> a = [1, 4, 5]
>>> type(a)
<type 'list'>
>>> x = (1, 'TEXT')
>>> isinstance(x, tuple) # prüfe, ob x Objekt der Klasse 'tuple' ist
True
>>> x = '10'
>>> isinstance(x, (int, float)) # prüfe Typ auf 'int' und 'float'
False
>>> x = int(x)
>>> isinstance(x, (int, float))
True
Alle Kontrollstrukturen sind als Anweisungen umgesetzt.
Python bietet nur ein allgemeines Konstrukt für bedingte Ausführungen:
xxxxxxxxxx
a = 9
b = 9
# bedingte Anweisung
if b < a: print("b ist größer als a") # if BEDINGUNG: ANWEISUNG
# Verzweigung ohne Alternative:
if b > a:
print("b ist größer als a")
elif a == b:
print("a und b sind gleich")
# Verzweigung mit Alternative:
if b > a:
print("b ist größer als a")
elif a == b:
print("a und b sind gleich")
else:
print("b ist kleiner als a")
# bedingter Ausdruck / ternärer Operator:
x, y = 10, 20
min = x if x < y else y # AUSDRUCK if BEDINGUNG else AUSDRUCK
Bei Verzweigungen wird lediglich der erste zutreffende Block ausgeführt.
Musterabgleiche wie in anderen Sprachen, zumeist mit switch
oder match
eingeleitet, sind in Python nicht vorhanden.
Neben kopfgesteuerten Dauerschleifen bestehen auch Zählschleifen, welche zugleich Iteratoren darstellen:
xxxxxxxxxx
# Dauerschleife:
zähler = 0
while (zähler < 3):
zähler = zähler + 1
print("Zählerstand:", zähler)
# Zählschleife / Iterator:
for x in range(10): print(x) # 'range' liefert eine Zahlensequenz
# Zählschleifen mit Sprungbefehlen:
früchte = ["Apfel", "Birne", "Banane", "Kirsche"]
for x in früchte:
print(x)
if x == "Banane": break # vorzeitiger Schleifenabbruch
for x in früchte:
if x == "Banane":
continue # Sprungbefehl: vorzeitig zum nächsten Durchlauf
print(x)
# unvollständige Zählschleife:
for i in range(10): print("3 <", i,"< 9") if (3 < i < 9) else None
Mit der Sprunganweisung break
sind Schleifen vorzeitig beendbar; continue
veranlasst den nächsten Durchlauf.
Eine Funktion ist ein Programmabschnitt, welcher nur bei Aufruf ausgeführt wird.
xxxxxxxxxx
# def FUNKTIONSNAME(PARAMETERLISTE):
# ANWEISUNGEN
# return AUSDRUCK
def Hallo(dein_name):
return "Hallo " + dein_name + "!"
def ausgeben(text):
print(text)
19900130 # Ausdruck wird ignoriert
def macht_nüscht(text):
return
print(text)
def leere_Funktion(x):
pass # Interpreter ignoriert den Block und gibt None zurück
print(leere_Funktion("Hallo"))
ausgeben(Hallo("Rico"))
print(ausgeben("TEST")) # Rückgabe von 'None' nach print-Anweisung
macht_nüscht("Tu was!")
print(macht_nüscht("Immer noch nicht!")) # → 'None'
print(leere_Funktion("Hallo")) # → 'None'
In Python müssen Funktionen nicht zwingend einen Rückgabewert definieren. Fehlt aber die return-Anweisung, wird davon ausgegangen, dass etwaige Ergebnisse keine Rolle spielen und stattdessen None
zurückzuliefern ist. Das selbe trifft ebenso auf die leere Anweisung pass
zu. Auch kann return
ohne Ergebnis verwendet werden, um einen Anweisungsblock vorzeitig zu beenden.
Innerhalb der Parameterliste können Standardwerte festgelegt werden:
xxxxxxxxxx
def V(h, r=1):
"""Anmerkung: Volumen; Einheitskreis als Grundfläche, falls kein Radius angegeben"""
return (3.14159 * (r ** 2)) * h
print(V(r=2, h=5)) # Reihenfolge spielt bei Nennungen keine Rolle
print(V.__doc__)
Zu beachten ist, dass zwischen Vorwert, Gleichheitszeichen und Parameternamen keine Leerzeichen stehen dürfen.
Parameter ohne Vorwert müssen stets als erstes erfolgen; def V(r=1, h)
würde hingegen einen Fehler verursachen.
Funktionen vermögen stets nur ein Objekt zurückzuliefern; jedoch kann diese Beschränkung inform von zusammengesetzten Objekten wie Tupeln umgangen werden:
xxxxxxxxxx
def f():
return "zum Bleistift", 10; # das gleiche wie
# 'return ("zum Bleistift", 10)'
a, b = f() # einzelne Entgegennahme
c = f()
print("f:", f())
print(a)
print(b)
print(c)
Python fasst mehrere durch Komma voneinander getrennte Rückgabewerte automatisch zu eine Tupel zusammengefasst, sodass die umschließenden Rundklammern entfallen dürfen. Über Mehrfachzuweisungen – mittels Kommatrennung vor dem Zuweisungszeichen =
– lassen sich einzelne Elemente einer Tupel als unabhängige Variablen binden.
Mit einem vorangestellten Sternchens *
am Parameternamen wird ausdrücken, dass beliebig viele Werte entgegenzunehmen sind (→ variadische Funktionen, en variadic functions
):
xxxxxxxxxx
def f(x, *argv):
print(x)
for arg in argv: print (arg)
f('A', 'B', 'C', '...')
Intern fasst Python die Argumente des variablen Parameters als Tupel zusammen.
Ebenfalls kann eine Liste übergeben werden:
xxxxxxxxxx
args = ["Ich", "bin", "so", "glücklich!"]
f(*args)
f(*["Ich", "bin", "so", "glücklich!"]) # oder literal
Ohne angefügtes Sternchen *
wird die Liste als ein Objekt behandelt.
en kwargs
Bei doppeltem Sternchen **
ist zu jedem Argument auch ein Schlüsselwort mit anzugeben:
xxxxxxxxxx
def f(**x):
for key, value in x.items(): print("%s: %s" % (key, value))
f(Subjekt='Ich', Verb='bin', Objekt='Deutscher')
Gleich einem Wörterbuch spielt die Reihenfolge beim Durchlaufen der Argumente keine Rolle.
Python erlaubt das Verändern von äußeren Werten nur, wenn im Funktionsrumpf der Zugriff auf eine globale Variable explizit angegeben wird:
xxxxxxxxxx
x = 1
def incr():
global x
x = x + 1
print("x =", x)
incr()
print("x =", x)
Ohne die Deklaration mit dem Schlüsselwort global
geht Python von einer lokalen Variable aus.
Das neuere Schlüsselwort nonlocal
verfolgt den gleichen Zweck, beschränkt sich aber auf die nächsthöherere Ebene innerhalb von Verschachtelungen.
In Python wird alles als Objekt einer Klasse aufgefasst:
Zahlentypen | int float complex |
Wahrheitstyp | bool |
Sequenztypen | str list tuple range |
binäre Sequenztypen | bytes bytearray memoryview |
Mengentypen | set frozenset |
Zuordnungstyp | dict |
Funktionstypen | function builtin_function_or_method NoneType |
Klassen- / Instanztyp | classobj instance |
Neben diesen grundlegenden Datenarten zählt die Spezifikation auch Module, Klassen, Instanzen, Funktionen, … zu den vordefinierte Standardtypen.
xxxxxxxxxx
"Hallo Welt!" # str
20 # int
20.5 # float
1j # complex
["Apfel", "Banane", "Kirsche"] # list
("Apfel", "Banane", "Kirsche") # tuple
range(6) # range
{"name" : "Max", "alter" : 36} # dict
{"Apfel", "Banane", "Kirsche"} # set
frozenset({"Apfel", "Banane", "Kirsche"}) # frozenset
True # bool
b"Hello" # bytes
bytearray(5) # bytearray
memoryview(bytes(5)) # memoryview
Mit type()
kann der Datentyp – oder genauer gesagt die Klasse – eines Literals bzw Objekts abgefragt werden. Dabei sei zum Verständnis angemerkt, dass Typen und Klassen in Python vereinheitlicht wurden.
Trotz dynamischer Typisierung werden – mit Ausnahme von Ganzzahlen in Gleitkommazahlen – keine automatischen Konvertierungen vorgenommen. Stattdessen bestehen Funktionen wie int()
, float()
oder auch str()
(benannt nach dem Zieldatentypen) für explizite Umwandlungen. Dementsprechend achtet Python streng auf Typverletzungen:
xxxxxxxxxx
>>> "TEXT" + 7
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
Ähnlich wie in C werden False
und True
als 0
und 1
gedeutet:
xxxxxxxxxx
>>> not (True or False)
False
>>> True + 7
8
>>> 5 * False
0
Die Funktion bool()
konvertiert ein Objekt ausdrücklich zu einem Wahrheitswert:
xxxxxxxxxx
>>> bool("Hallo!")
True
Abgesehen der folgenden Werte gibt bool()
stets True
zurück:
False
und None
leere Sequenzen wie ()
, []
, …
0
, 0.0
oder 0j
Objekte, für welche gilt:
obj.__bool__()
→ False
obj.__len__()
→ 0
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
Da Windows den rechten Schrägstrich \
(backslash
) als Trennzeichen für Pfade verwendet, tritt eine Verwechslung mit Unicode-Escape-Sequenzen auf:
xxxxxxxxxx
>>> xy = "C:\Users\XY\Projects"
File "<stdin>", line 1
xy = "C:\Users\XY\Projects"
^
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
>>> xy = r"C:\Users\XY\Projects"
>>> xy = "C:\\Users\\XY\\Projects"
>>>
Die Lösung für dieses Problem besteht entweder im Maskieren mit einem weiteren Schrägstrich oder im Markieren der Zeichenkette als roh
(en raw string
).
Module bezeichnen in andere Dateien einbindbare Objekte mit beliebig vielen Attributen. Python unterscheidet zwischen Bibliotheken sowie lokalen Modulen; erstere werden zwischen verschiedenen Programmen als Abhängigkeit geteilt, während letztere auf das hiesige Projektverzeichnis beschränkt bleiben.
Der Inhalt eines mit import
eingebundenen Moduls ist nur über den Modulbezeichner als Namensraumszusatz verfügbar,
xxxxxxxxxx
>>> import math
>>> math.pi
3.141592653589793
sofern nicht mittels from … import …
der Namensraum übergangen wird,
xxxxxxxxxx
>>> from math import pi # 'math.pi' hingegen ist unbekannt!
>>> pi
3.141592653589793
oder mit as …
umbenannt:
xxxxxxxxxx
>>> import math as num
>>> num.pi
3.141592653589793
Die Anweisung from … import *
übernimmt alles in den lokalen Namensraum.
Zu Unterscheiden von Importen ist die Future-Anweisung (en future statement
), um neue Syntax oder Semantik freizuschalten, welche andernfalls erst in zukünftigen Versionen direkt verfügbar ist:
xxxxxxxxxx
from __feature__ import snake_case, true_property
Mit dieser Anweisung kann in PySide6 auf Member und Klasseneigentum in der von Python typischen Weise zugegriffen werden. So wird addWidget
zu add_widget
und anstelle von Gettern und Settern lassen sich die Attribute direkt aufrufen.
Beim Import sucht der Interpreter
nach einem gleichnamigen Standardmodul, andernfalls
im aktuellen Verzeichnis, oder in
PYTHONPATH
falls PYTHONPATH nicht gesetzt ist, wird installationsabhängig gesucht,
unter Linux: /usr/lib/python3.8
xxxxxxxxxx
>>> import sys
>>> for dir in sys.path: print(dir)
...
/usr/lib/python38.zip
/usr/lib/python3.8
/usr/lib/python3.8/lib-dynload
/home/enrik/.local/lib/python3.8/site-packages
/usr/local/lib/python3.8/dist-packages
/usr/lib/python3/dist-packages
Modulart | Endung |
---|---|
– in Python geschriebenes Modul | py, pyc |
– dynamisch geladenes C-Modul | dll, pyd, so, sl, … |
– mit dem Interpreter verlinkte C-Module |
xxxxxxxxxx
>>> import sys # Python 3.8
>>> print(sys.builtin_module_names)
('_abc', '_ast', '_bisect', '_blake2', …
Tatsächlich ist ein Modul in Python nichts anderes als eine gewöhnliche Quelldatei name.py
, welche Definitionen und Anweisungen enthält; wobei der Dateinahme zugleich den Modulnamen vorgibt:
xxxxxxxxxx
# ./fib.py
__all__ = ['out', 'name']
def out(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print("\n")
def printer():
print("module name:", __name__) # __name__ :: str
def name():
printer()
print("module", __name__, "is initialized!", "\n")
xxxxxxxxxx
# ./main.py
import fib
if __name__ == "__main__":
fib.out(3)
fib.name() # Ausgabe des Modulnamens
oder interaktiv:
xxxxxxxxxx
>>> import fib # oder 'from fib import *'
module fib is initialized!
>>>
Modulanweisungen – beispielsweise für etwaige Konfigurationen – werden mit import
ausgeführt.
Um ein Modul gleichzeitig als Bibliothek und ausführbares Script zu verwenden, sind die jeweiligen Programmanweisungen in einem bedingten Block zusammenzufassen:
xxxxxxxxxx
# ./test.py
import sys
import fib
def n10(): fib.out(10)
if __name__ == "__main__":
fib.out(int(sys.argv[1]))
fib.name()
'''
if __name__ == "test": # Anweisungsblock wird immer ausgeführt
n10()
fib.name()
'''
xxxxxxxxxx
> python test.py 6
module fib is initialized!
0 1 1 2 3 5
module name: fib
Beim Einlesen eines Scripts setzt der Interpreter den Modulnamen als __main__
fest. Bezieht sich der Vergleich des bedingten Anweisungsblocks hingegen auf den Dateinamen, wird folglich der Blockinhalt immer ausgeführt, jedoch beim Interpretieren des Quelltextes als Skript ignoriert.
Als Paket (en package
) wird in Python die Gesamtheit aller Module eines Verzeichnisses zusammen mit einer sog Initialisierungsdatei
__init__.py
bezeichnet. Die Initialisierungsdatei kann im einfachsten Fall leer sein, oder aber Importanweisungen enthalten, sodass sich Module über den Namen des Verzeichnisses in gewohnter Punktnotation aufrufen lassen:
xxxxxxxxxx
# ./test/__init__.py
from test import a
xxxxxxxxxx
# ./test/a.py
__all__ = ['hallo']
def hallo(name):
print("Hallo " + name + "!")
xxxxxxxxxx
#! /usr/bin/python
import test
if __name__ == '__main__':
test.a.hallo("Rico")
Ohne die Anweisung from test import a
in der Initialisierungsdatei würde die Fehlermeldung auftreten:
xxxxxxxxxx
AttributeError: module 'test' has no attribute 'a'
Über die spezielle Variable __all__
kann in der Initialisierungsdatei genau spezifiziert werden, welche Module gemeinsam importierbar sein sollen:
xxxxxxxxxx
# ./test/__init__.py
__all__ = ['a', …]
xxxxxxxxxx
#! /usr/bin/python
import test
from test import * # importiere alle Module
if __name__ == '__main__':
a.hallo("Rico")
Ist die Variable __all__
nicht definiert, werden bei Verwenden des Sternchens *
alle Module eines Verzeichnisses importiert.
Unter ~/.local/lib/pythonX.X/site-packages/ installierte Pakete sind genauso wie die Standardbibliothek übergreifend verfügbar.
Absolute Importe können unter Umstände äußerst lang sein:
from package.subpackage.subsubpackage.module import …
Module des selben Packages lassen sich aber auch vereinfacht aufrufen:
import .module
oder beziehend auf das übergeordnete Package (Elternverzeichnis):
import ..module
Beim direkten Ausführen von Untermodulen kann der Interpreter die Pfade der Importanweisungen nur auflösen, wenn die betreffende Eingabe mit der Option -m
als Modulname markiert wird:
> python -m package.subpackage.module
Wird hingegen das Modul über den Verzeichnispfad aufgerufen oder ohne Flag -m
, deutet der Interpreter die Datei als bloßes Skript und sucht in der Folge nicht nach projektinternen Modulen:
xxxxxxxxxx
> python package/subpackage/module.py
…
ImportError: attempted relative import with no known parent package
Der Paketname eines Modules ist mit __package__
abfragbar.
Mit sog docstrings
bietet Python ein Konzept, um Funktionen, Klassen sowie Module durch einen Kommentar in den ersten Zeilen zu beschreiben:
xxxxxxxxxx
# ./modulname.py
def f():
"""Dies ist ein dokumentierender Kommentar."""
return 0
print(f.__doc__)
help = print(help(f))
Die Dokumentation eines Objekts ist über das allgemeine Attribut __doc__
ansprechbar und kann mit der Funktion help
in der interaktiven Umgebung auch unmittelbar ausgegeben werden:
xxxxxxxxxx
>>> import modulname
>>> help(modulname.f)
Durch Eintippen von q
wird die Help-Ausgabe verlassen.
Definition von objektorientierte Programmierung nach Alan Kay:
Merkmale eines Objekts:
Zustand
→ Variablen sind nach außen abgekapselt bzw zugriffsbeschränkt.
Identität
→ Jedes erzeugte Objekt ist eindeutig bestimmbar.
Lebenszeit
→ Objekte werden entweder automatisch oder auf Befehl gelöscht.
Obgleich Python auf Klassen beruht, entspricht die Herangehensweise im Kern der prototypenbasierten Programmierung: Jedem Objekt sind nachträglich Attribute sowie Methoden zuweisbar, sogar dynamisch zur Laufzeit. Demnach lassen sich neue Objekte entweder durch Instanziierung einer Klasse oder durch Kopieren (Klonen) eines bestehenden Objekts erzeugen:
xxxxxxxxxx
from copy import copy, deepcopy
class Produkt: pass # leere Klasse
a = Produkt() # Instanziierungen
x = a # bloße Referenz
y = copy(a) # Kopieren des Objekts a (Prototyp)
print("ID[a]:", id(a))
print("ID[x]:", id(x)) # selbe Objekt im Speicher
print("ID[y]:", id(y))
Als Instanz wird ein Objekt im Speicher bezeichnet.
Attribute liegen entweder als Klassenattribute für alle Instanzen vor, oder sind spezifisch für eine Instanz festgelegt (→ Instanzattribute):
xxxxxxxxxx
class Produkt: pass # leere Klasse als Vorlage für Prototypen
a = Produkt()
b = Produkt()
# Klassenattribute:
Produkt.standort = "Magdeburg"
print(b.standort)
# Festlegen von spezifischen Attributen:
a.name = "Staubsauger Superfix"
a.linie = "RT-X"
a.baujahr = 2019
b.name = "Staubsauger Hyperfix"
b.linie = "RT-X2"
b.baujahr = 2020
b.standort = 99099 # PLZ; Überschreiben des Klassenattributs
# jeweils eigenständige Speicherobjekte:
print(id(a))
print(id(b))
print(a.__dict__)
print(b.__dict__)
print(b.standort)
Eine Instanz greift auf ein Klassenattribut nur dann zu, wenn kein gleichnamiges Instanzattribut bereits im Wörterbuch aufgelistet ist.
Intern werden Attribute von Instanzen ähnlich einem Wörterbuch verwaltet.
Nachfolgend eine Übersicht der wichtigsten Funktionen im Umgang mit Attributen:
Funktion | Wirkung |
---|---|
vars(OBJ) | Anzeige alle Instanzattribute als Wörterbuch |
dir(OBJ) | Anzeige von Instanz- und Klassenattribute |
getattr(OBJ, STR) | Abfragen des Attributwertes |
hasattr(OBJ, STR) | Prüfen, ob Attribut existiert |
setattr(OBJ, STR, VAL) | Attributzuweisung |
delattr(OBJ, STR) | Löschen eines Attributs |
Auch Instanzen von Funktions- und Modulklassen lassen sich Attribute dynamisch zuordnen:
xxxxxxxxxx
import math
# Erweitern des Moduls 'math'
math.Mathematiker = ["Carl Friedrich Gauß", "David Hilbert"]
# Funktionsattribute als Ersatz für statische Variablen:
def f(x):
if hasattr(f, "aufrufe"): f.aufrufe += 1
else: f.aufrufe = 1 # falls Attribut noch nicht vorhanden
return x + 9
for i in range(10): f(i)
print(f.aufrufe)
def g(x):
g.aufrufe = getattr(g, "aufrufe", 0) + 1
# 'getattr' als Alternative zur Punktnotation
return x + 3
for i in range(20): g(i)
print(g.aufrufe)
Über das spezielle Attribut __slots__
kann genau angegeben werden, welche Eigenschaften eine Instanz vorweisen darf:
xxxxxxxxxx
class X:
__slots__ = 'a', 'b'
instanz = X()
instanz.a = 10
instanz.b = "Hallo Welt!"
instanz.c = True
# AttributeError: 'X' object has no attribute 'c'
Methoden in Python sind an eine Instanz gebundene Funktionsobjekte, deren erster Parameter auf die zugehörige Instanz verweist (Referenz):
xxxxxxxxxx
# declaring class
class Zeuge:
def geständnis(self): print("Ich war es!")
A = Zeuge()
# Aufruf über Namensraum der Klasse
Zeuge.geständnis(A)
# Aufruf über Namensraum der Instanz → autom Übergabe als Argument
A.geständnis()
Tatsächlich ist self
kein verbindliches Schlüsselwort, um auf das Objekt selbst zu verweisen; jeder andere Bezeichner ist denkbar.
Der Methodenaufruf erfolgt in gängiger Punktnotation.
Der Dekorateur @staticmethod
erzeugt eine Methode, deren erster Parameter bei Aufruf in Punktnotation nicht implizit als ein Objekt selbiger Klasse angenommen wird:
xxxxxxxxxx
>>> class A:
... x = 3
... def f(self): return self.x * self.x
...
>>> class B:
... x = 3
... @staticmethod
... def f(self): return self.x * self.x
...
>>> a = A()
>>> A.f(a)
9
>>> a.f()
9
>>> b = B()
>>> A.f(b)
9
>>> B.f(a)
9
>>> b.f() #
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'self'
>>>
Magische Methoden weisen jeweils links und rechts angefügte doppelte Unterstriche __f__
vor. Durch das Implementieren von magischen Methoden lassen sich Klassen um besondere Funktionalitäten erweitern, welche nicht anderweitig programmierbar sind (spezielle Syntax).
xxxxxxxxxx
print(dir()) # Auflisten aller vordefinierter Methoden
implementierbar als __init__
xxxxxxxxxx
class Person: pass
a = Person()
xxxxxxxxxx
class Person:
def __init__(self, vorname, nachname, geb, geb_ort):
self.vorname = vorname
self.nachname = nachname
self.geb = geb
self.geb_ort = geb_ort
p = Person("Richard", "Rupin", "14.01.2000", "Erfurt")
print(p.nachname)
Konvention: __init__
als erste Methode
implementierbar als __del__
Der Speicherplatz von Objekten wird automatisch bei Verlassen des Gültigkeitsbereichs freigegeben. Dabei ruft der Übersetzer eigenmächtig die Methode __del__
, sofern für eine Klasse definiert, am Ende des Lebenszyklus auf:
xxxxxxxxxx
class Obj:
def __init__(obj, bezeichner): # initializer
obj.bezeichner = bezeichner
print(bezeichner, "ist im Speicher abgelegt.")
def __del__(obj): # finalizer
print(obj.bezeichner, "wird zur Löschung freigegeben")
instanz = Obj("X")
print("Programmende")
Mit del
kann der Programmierer die Löschung vorzeitig veranlassen:
xxxxxxxxxx
class Obj:
def __init__(obj, bezeichner): # initializer
obj.bezeichner = bezeichner
print(bezeichner, "ist im Speicher abgelegt.")
def __del__(obj): # finalizer
print(obj.bezeichner, "wird zur Löschung freigegeben")
instanz = Obj("X")
del(instanz)
print("Programmende")
Die Methode __del__
löscht nicht das Objekt, sondern leitet nur den eigentlichen Destruktor ein, vorausgesetzt, keine weiteren Referenzen verweisen auf die betreffende Instanz:
xxxxxxxxxx
class Obj:
def __init__(obj, bezeichner): # initializer
obj.bezeichner = bezeichner
print(bezeichner, "ist im Speicher abgelegt.")
def __del__(obj): # finalizer
print(obj.bezeichner, "wird zur Löschung freigegeben")
instanz = Obj("X")
ref = instanz
del(instanz)
print(ref.bezeichner)
print("Programmende")
__str__
und __repr__
xxxxxxxxxx
class Person:
name = ""
alter = 0
def __init__(self, name, alter):
self.name = name
self.alter = alter
def __repr__(self):
return str({'Name': self.name, 'Alter': self.alter})
def __str__(self):
return 'Person(name=' + self.name + ', alter=' + str(self.alter) + ')'
p = Person("Hugo", 50)
# __str__()
print(p)
print(p.__str__())
# __repr__()
print(p.__repr__())
print(type(p.__repr__()))
print(repr(p))
__str__()
gibt eine Zeichenkettenrepräsentation des Objekts zurück.__repr__()
statt __str__()
, wenn letztere nicht implementiert ist.__str__()
ist überflüssig, wenn __repr__()
eine Zeichenkette zurückliefert.Mit Datenkapslung ist der Schutz von Attributen (Daten) vor einem unmittelbaren Zugriff gemeint, indem Veränderungen nur über spezielle Methoden zugelassen werden.
Python verfügt über keinen Mechanismus, um den Zugriff auf interne Variablen sowie Funktionen einzuschränken; alle Bestandteile (en members
) sind grundsätzlich öffentlich (en public
). Stattdessen schreibt nur eine Konvention vor, geschützte (protected
) Werte durch einen vorangestellten einfachen und private Bestandteile mittels eines doppelten Unterstrich zu kennzeichnen.
xxxxxxxxxx
class Klasse:
def __init__(obj, a):
obj.__Attribut = a # als zugriffsgeschützt markiert
def __Methode(self): return self.__Attribut
Instanz = Klasse(True)
print(Instanz.__Attribut)
# AttributeError: 'Klasse' object has no attribute '__Attribut'
print(Instanz._Klasse__Attribut) # Umgehen des Zugriffsschutzes
print(Instanz._Klasse__Methode())
Im Hintergrund löst Python als privat gekennzeichnete Bestandteile zu Instanz._Klasse__Attribut
auf.
OOP | Python | Bedeutung |
---|---|---|
public | name | von Außen les- und schreibbar |
protected | _name | von Außen les- und schreibbar, jedoch als nicht zu benutzen gekennzeichnet (Einhaltung obliegt dem Programmierer) |
private | __name | weder von Außen sichtbar noch zu benutzen |
en property
Die Funktion property
erlaubt das Definieren von öffentlich zugänglichen Klassenattributen, welche automatisch die angegebenen Änderungs- sowie Abfragemethoden aufrufen:
xxxxxxxxxx
# property(getter, setter, deconstructor, docstring)
class Celsius:
def __init__(self, temperature = 0):
self.set_temp(temperature)
def kelvin(self):
return self.get_temp() + 273.15
def fahrenheit(self):
return (self.get_temp() * 1.8) + 32
def get_temp(self): # Abfragemethoden (en „getter“)
return self.__temp
def set_temp(self, value): # Änderungsmethoden (en „setter“)
if value < -273:
raise ValueError("Eine Temperatur niedriger als -273 ist nicht möglich.")
self.__temp = value
temp = property(get_temp, set_temp)
# t = Celsius(-424)
# ValueError: Temperatur niedriger als -273 ist nicht möglich.
t = Celsius(8)
t.temp = -12
t.set_temp(10)
print(t.__dict__)
print(t.fahrenheit())
print(t.kelvin())
Wird die Funktion property
ohne Argumente aufgerufen, verhält sich das Attribut wie gehabt.
xxxxxxxxxx
class Person:
def __init__(self, vorname, nachname, geschlecht, geb, geb_ort):
self.__vorname = vorname
self.__nachname = nachname
self.__geschlecht(geschlecht)
self.__geb = geb
self.__geb_ort = geb_ort
# Abfragemethoden (en „getter“):
def info(self):
print(
"Person:", self.__vorname, self.__nachname,
"(" + self.__geschlecht + "),",
"geboren am", self.__geb, "in", self.__geb_ort
)
def gib_name(self): return self.__vorname + " " + self.__nachname
def __geschlecht(self, x): # private Methode
if x == "männlich":
self.__geschlecht = "männlich"
elif x == "weiblich":
self.__geschlecht = "weiblich"
else:
print("Fehler: Uneindeutige Angabe zum Geschlecht.")
self.__geschlecht = "unbestimmt"
# Änderungsmethoden (en „setter“):
def geschlechtsumwandlung(self, neuer_name):
if self.__geschlecht == "unbestimmt":
print("Fehler: Keine Umwandlung vorgenommen, da unbestimmtes Geschlecht!")
else:
self.__geschlecht = ("männlich"
if self.__geschlecht == "weiblich"
else "weiblich") # Klammern für mehrzeiligen Ausdruck
self.__vorname = neuer_name
def neuer_name(self, name):
self.__vorname, self.__nachname = name.split()
name = property(gib_name, neuer_name)
p = Person("Maximilia", "Mustermann", "weiblich", "01.04.1999", "Buxtehude")
p.info()
print(p.gib_name())
p.geschlechtsumwandlung("Max")
p.info()
p.name = "Max Schulze"
p.info()
xxxxxxxxxx
class __Const: # als privat gekennzeichnete Klasse
def e(self):
return 2.71828
const = __Const() #
print(const.e)
const.e = 9 # AttributeError: can't set attribute
xxxxxxxxxx
def constant(cls):
# Replace a class's attributes with properties and itself with an instance of its doppelganger.
is_special = lambda name: (name.startswith("__") and name.endswith("__"))
class_contents = {n: getattr(cls, n) for n in vars(cls) if not is_special(n)}
def unbind(value):
# Get the value out of the lexical closure.
return lambda self: value
propertified_contents = {name: property(unbind(value))
for (name, value) in class_contents.items()}
receptor = type(cls.__name__, (object,), propertified_contents)
return receptor() # Replace with an instance, so properties work.
class paths(object):
home = "/home"
null = "/dev/null"
print(paths.home)
paths.home = "~" # → AttributeError: can't set attribute
Vererbung definiert eine Beziehung zwischen einer allgemeinen Klasse (auch Basis- / Eltern- oder Superklasse) und einer spezialisierten Klasse (Kind- oder Subklasse): Oberklasse ⊇ Unterklasse
xxxxxxxxxx
class Person:
def __init__(self, vorname, nachname):
self.vorname = vorname
self.nachname = nachname
def __str__(self):
return "Name: " + self.vorname + " " + self.nachname
class Angestellter(Person):
def __init__(p, vorname, nachname, personalnr):
Person.__init__(p, vorname, nachname)
# alternativ: super().__init__(vorname, nachname)
p.personalnr = personalnr
def __str__(p):
return Person.__str__(p) + ", Personal-Nr: " + str(p.personalnr)
# return super().__str__() + ", Personal-Nr: " + str(self.personalnr)
p = Person("Hugo", "Humpel")
print(p)
a = Angestellter("Max", "Mustermann", 30)
print(a)
Durch Kopieren eines bestehendes Objekts entsteht eine neue Instanz:
xxxxxxxxxx
Mit sog docstrings
bietet Python ein Konzept, um Funktionen, Klassen sowie Module durch einen Kommentar in den ersten Zeilen zu beschreiben:
xxxxxxxxxx
# ./modulname.py
def f():
"""Dies ist ein dokumentierender Kommentar."""
return 0
print(f.__doc__)
help = print(help(f))
Die Dokumentation eines Objekts ist über das allgemeine Attribut __doc__
ansprechbar und kann mit der Funktion help
in der interaktiven Umgebung auch unmittelbar ausgegeben werden:
xxxxxxxxxx
>>> import modulname
>>> help(modulname.f)
Durch Eintippen von q
wird die Help-Ausgabe verlassen.
In Python sind Funktionen First-Class-Objekte – im Englischen auch allgemein first-class citizen
genannt –, was soviel bedeutet, dass Funktionen als Referenz einer anderen Funktion übergeben oder von dieser zurückgeliefert werden können:
xxxxxxxxxx
def großschreiben(txt): return txt.upper()
def kleinschreiben(txt): return txt.lower()
def gruß(ausgabe):
begrüßung = ausgabe("Hallo, Welt!")
print(begrüßung)
gruß(großschreiben) # Übergabe als Objektreferenz (keine Rundklammern)
gruß(kleinschreiben)
Zudem sieht die Definition von First-Class-Objekten vor, dass diese auch einer Variable zuweisbar sind:
xxxxxxxxxx
out = print
out("Hallo, Welt!")
Python unterstützt funktionale Ansätze nur nebensächlich, sodass einzig und allein dem Programmierer das Einhalten folgender Designprinzipien obliegt:
side effects)
Bei Befolgen dieser Ansätze wird der Programmierer mit
belohnt.
Python unterstützt das Teilanwenden von Funktionen (en partial function application
) nur indirekt mittels der Hilfsfunktion partial
aus dem Modul functools
der Standardbibliothek:
xxxxxxxxxx
from functools import partial
import inspect
def f4(a, b, c, x):
return 1000*a + 100*b + 10*c + x
print("f4: ", inspect.signature(f4)) # Ausgabe der Signatur
# partielle Anwendung von f:
f1 = partial(f4, 3, 1, 4) # a, b und c werden umgesetzt, x bleibt unerfüllt
print("f1: ", inspect.signature(f1))
g1 = partial(f4, b=1, c=4, a=3) # oder über Schlüsselwortargumente
print("g1: ", inspect.signature(g1))
print(list(map(partial((lambda a, b: a * b), b=2), [11, 64, 7, 30, 2])))
en lambda expressions
– auch Funktionsliterale genannt
xxxxxxxxxx
>>> (lambda a, b: a + b)(2, 5) # lambda ARGS: AUSDRUCK
7
>>> for i in range(10): (lambda a: a * 2)(i)
0
2
...
>>> quadrat = lambda x: x * x
>>> quadrat(122)
14884
>>> def abb(f, liste):
... x = []
... for elem in liste:
... x.append(f(elem))
... return x
...
>>> print(abb((lambda x: x/2), range(10)))
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
Lambdaausdrücke müssen nicht zwangsläufig parametrisch sein:
xxxxxxxxxx
>>> output = lambda: print("Hallo, Welt!")
>>> output()
Hallo, Welt!
en closure
Ein Funktionsabschluss liegt vor, wenn eine Funktion Zugang zu lokalen Variablen eines umschließenden, bereits abgearbeiteten Gültigkeitsbereichs besitzt, was eine äußere Funktion sein kann oder allg der Erstellungskontext:
xxxxxxxxxx
def gib_echo(ruf):
verstärker = "!!!" # von echo unabhängig bestehender globaler Wert
def echo():
print(ruf + verstärker, ruf + verstärker)
return echo
echo = gib_echo('Hallo')
echo()
Bei Aufruf einer übergeordneten Funktion gib_echo
wird ein neuer Rahmen (en stack frame
) mit dem kompilierten Programmtext der inneren Funktion echo auf dem Stapelspeicher abgelegt. Lokale Werte, auf welche die innere Funktion zugreift, bleiben erhalten.
Von einem Funktionsabschluss wird nur gesprochen, wenn die innere Funktion den lokalen Zustand speichert.
xxxxxxxxxx
def gib_echo(ruf):
def echo(ruf=ruf): # optionaler Parameter
verstärker = "!!!" # neu angelegt mit jedem Aufruf von echo
print(ruf + verstärker, ruf + verstärker)
return echo # kein Funktionsabschluss
Python bietet bereits von Haus aus einige grundlegende höhere Funktionen:
xxxxxxxxxx
# map(f, seq)
print(list(map((lambda a: 3 * a), [11, 64, 7, 30, 2])))
# filter(f, list) mit f → Bool
fib = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
ungerade = list(filter(lambda x: x % 2, fib))
print(ungerade)
# reduce(f, seq)
from functools import reduce
print(reduce(lambda a, b: a + b, fib))
Durch map
entstandene Objekte müssen zur Ausgabe in eine Liste umgewandelt werden. Ferner ist map
auf einstellige Funktionen beschränkt.
Die Funktion reduce
wurde in das Modul functools
ausgelagert.
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
Der offizielle Style Guide for Python Code
empfiehlt 4 Leerzeichen zum Einrücken. Diese Entscheidung rührt aus der Vergangenheit, als Texteditoren noch nicht mit Tabulatoren umgehen konnten. Jedoch spricht aus heutiger Sicht absolut nichts gegen das Verwenden von Tabulatoren, ganz im Gegenteil:
In jedem Fall gilt, Leerzeichen und Tabulatoren auf keinen Fall zu mischen!
Auch sollten Zeile nicht länger als 120 Zeichen sein.
Typ | Konvention | Beispiel |
---|---|---|
Variable, Funktion | durchgängig klein mit Unterstrichen | count |
Konstante | durchgängig groß mit Unterstrichen | EULER |
Klasse | großgeschrieben mit Binnenmajuskel | BackgroundColor |
Modul, Paket | durchgängig klein mit Unterstrichen | ui_main |
Jedoch hält sich die Standardbibliothek selbst nicht an diese empfohlenen Regeln, und schreibt Konstanten wie math.pi
durchgängig klein.
Im Zusammenhang mit dem Paketsystem hat sich der folgende Standard etabliert:
Project Name/ | Wurzelverzeichnis des Projekts |
---|---|
./bin | ausführbare Dateien (ohne Dateiendung) |
./doc | Dokumentation für Anwender |
./spec | Programmspezifikation für Entwickler |
./project_name | alle Programmdateien des Projekts als ein Package |
./project_name/__res__ | Ressourcen wie Bilder |
./project_name/test | Testmodule (→ en unit tests) |
./project_name/… | weitere Subpackages |
./project_name/__init__.py | Initialisierungsdatei des Projektpakets |
./project_name/__main__.py | ausführbare Programmdatei des Projektpakets |
./project_name.py | sofern das Projekt nur aus einer Quelldatei besteht |
./LICENSE {.md , .rst , .txt } | Lizenz als Textdatei (vornehmlich ohne Endung) |
./README.md | Textdatei mit wichtigen Informationen |
./requirements.txt | Liste aller Abhängigkeiten |
./setup.py | Einstellungen für das Verteilen mittels pip |
Alle Importe sollten zu Beginn der Quelldatei in sortierter Weise erfolgen:
xxxxxxxxxx
# Standard library imports
import datetime
import os
# Third party imports / pip
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
# Local application imports
from local_module import local_class
from local_package import local_function
xxxxxxxxxx
Installation mittels:
> pip install PySide2
Unter Linux ist zu beachten, dass Python 3 statt Version 2 standardmäßig aufgerufen wird. Um dies zu gewährleisten, kann das folgende Paket installiert werden:
> sudo apt-get install python-is-python3
Jede Qt-Anwendung läuft in einer Ereignisschleife (en event loop
) ab. Bei Interaktion mit der GUI werden ausgelöste Ereignisse – beispielsweise durch Klicken von Schaltflächen – in einer Warteschlange (en event queue
) abgelegten. Mit jedem Durchlauf erfolgt eine Abfrage der Warteschlange, um im Anschluss jedes Ereignis zusammen mit dem Kontrollfluss an den zugehörigen Event Handler weiterzureichen. Nach Abarbeitung gibt der Event Handler die Kontrolle an die Ereignisschleife zurück.
xxxxxxxxxx
from PySide2.QtWidgets import QApplication, QWidget
import sys
app = QApplication(sys.argv)
# define the event loop as an instance of QApplication
window = QWidget()
# create a Qt widget, which will be our window.
window.show()
# windows are hidden by default.
app.exec_()
# start the event loop
# application won't reach here until you exit to stop the event loop
Jedes Widget kann als Fenster auf oberster Ebene auftreten (→ en top level widget
); ist somit nicht auf ein anderes Widget oder Layout angewiesen. Folglich kann ein Fenster aus jedem beliebigen Widget erstellt werden.
Die QMainWindow-Klasse beschreibt Widgets, welche viele Standardfunktionen wie Menüs und Leisten bereitstellen.
Durch Vererbung werden neue benutzerdefinierte Fenster abgleitet:
xxxxxxxxxx
import sys
from PySide2.QtCore import QSize, Qt
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton
# Subclass QMainWindow to customize the main window:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
self.setFixedSize(QSize(400, 300))
self.setCentralWidget(button)
# Set the central widget of the Window.
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
In Python kann jede Funktion bzw Methode als Slot fungieren und Daten empfangen. Viele Qt-Widgets weisen auch eigene Slots vor, um sich direkt miteinander verknüpfen zu lassen.
xxxxxxxxxx
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton
from PySide2.QtCore import Qt
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
button = QPushButton("Drücken!")
button.setCheckable(True)
button.clicked.connect(lambda: print("Klick"))
button.clicked.connect(lambda checked: print("Test:", checked))
self.setCentralWidget(button)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
In Qt werden Komponenten der UI als Widget bezeichnet. Neben einer ganzen Reihe bestehender Widgets sind auch eigene definierbar.