Kapitel 4. Python in einem Netzwerkkontext lernen
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Als Netzwerktechniker/in gab es nie einen besseren Zeitpunkt für dich, um zu lernen, wie man automatisiert und Code schreibt. Wie wir in Kapitel 1 dargelegt haben, verändert sich die Netzwerkbranche grundlegend. Es ist eine Tatsache, dass sich die Netzwerkbranche von den späten 1990er Jahren bis etwa 2010 sowohl architektonisch als auch betrieblich nicht viel verändert hat. In dieser Zeitspanne hast du als Netzwerkingenieur zweifellos hunderte, wenn nicht tausende Male dieselben CLI-Befehle eingegeben, um Netzwerkgeräte zu konfigurieren und Fehler zu beheben. Warum dieser Wahnsinn?
Gerade wenn es um den Betrieb eines Netzwerks geht, macht es Sinn, zu lernen, wie man Code liest und schreibt. Das Schreiben von Skripten oder ein paar Codezeilen, um Informationen über das Netzwerk zu sammeln oder Änderungen vorzunehmen, ist eigentlich nichts Neues. Das wird schon seit Jahren gemacht. Es gibt Ingenieure, die dieses Kunststück vollbracht haben - sie haben in der Sprache ihrer Wahl programmiert und gelernt, mit Rohtext zu arbeiten, indem sie komplexe Parser und reguläre Ausdrücke verwendet und SNMP-MIBs in einem Skript abgefragt haben. Wenn du das selbst schon einmal versucht hast, weißt du aus erster Hand, dass es möglich ist, aber die Arbeit mit regulären Ausdrücken und das Parsen von Text ist zeitaufwändig und mühsam.
Glücklicherweise bewegen sich die Dinge langsam in die richtige Richtung und die Einstiegshürde für die Netzwerkautomatisierung ist so niedrig wie nie zuvor. Es gibt Fortschritte bei den Netzwerkanbietern, aber auch bei den Open-Source-Tools, die für die Automatisierung des Netzwerks zur Verfügung stehen und die wir in diesem Buch behandeln. Zum Beispiel gibt es jetzt Netzwerkgeräte-APIs, hersteller- und community-unterstützte Python-Bibliotheken und frei verfügbare Open-Source-Tools, die dir und allen anderen Netzwerktechnikern Zugang zu einem wachsenden Ökosystem geben, mit dem du deine Reise in die Netzwerkautomatisierung beginnen kannst. Das bedeutet letztlich, dass du weniger Code schreiben musst als in der Vergangenheit, und weniger Code bedeutet schnellere Entwicklung und weniger Bugs.
Bevor wir in die Grundlagen von Python eintauchen, wollen wir noch eine wichtige Frage klären, die in Gesprächen unter Netzwerktechnikern immer wieder auftaucht: Sollten Netzwerktechniker/innen programmieren lernen?
Sollten Netzwerktechniker/innen programmieren lernen?
Leider bekommst du von uns weder ein definitives Ja noch ein definitives Nein. Natürlich haben wir ein ganzes Kapitel über Python und viele andere Beispiele im Buch, wie man Python verwendet, um mit Netzwerkgeräten über Netzwerk-APIs zu kommunizieren und DevOps-Plattformen wie Ansible, Salt und Puppet zu erweitern. Wir sind also definitiv der Meinung, dass das Erlernen der Grundlagen jeder Programmiersprache wertvoll ist. Wir sind außerdem der Meinung, dass diese Fähigkeit noch wertvoller wird, da sich die Netzwerk- und IT-Branche weiterhin so schnell verändert, und wir sind der Meinung, dass Python eine ziemlich gute erste Wahl ist.
Hinweis
Es ist wichtig zu betonen, dass wir Python keine Technologie-Religion sind. Wir sind jedoch der Meinung, dass Python aus mehreren Gründen eine gute erste Wahl ist, wenn es um Netzwerkautomatisierung geht. Erstens ist Python eine dynamisch typisierte Sprache, die es dir ermöglicht, Python-Objekte (z. B. Variablen und Funktionen) bei Bedarf zu erstellen und zu verwenden, d. h. sie müssen nicht definiert werden, bevor du sie verwendest. Das vereinfacht den Einstieg in die Sprache. Zweitens ist Python auch sehr gut lesbar. Es ist üblich, bedingte Anweisungen wie if device in device_list:
zu sehen, und in dieser Anweisung kann man leicht erkennen, dass wir einfach prüfen, ob ein Gerät in einer bestimmten Liste von Geräten ist. Ein weiterer Grund ist, dass Netzwerkanbieter und Open-Source-Projekte eine Vielzahl von Bibliotheken und Tools mit Python entwickeln. Das macht es noch interessanter, mit Python zu programmieren.
Die eigentliche Frage ist jedoch, ob jeder Netzwerktechniker wissen sollte, wie man ein grundlegendes Skript liest und schreibt? Die Antwort auf diese Frage ist ein klares Ja. Sollte also jeder Netzwerktechniker ein Softwareentwickler werden? Auf keinen Fall. Viele Ingenieurinnen und Ingenieure werden sich eher zu einer Disziplin hingezogen fühlen als zu einer anderen, und vielleicht werden einige Netzwerkingenieurinnen und -ingenieure zu Entwicklerinnen und Entwicklern, aber alle Arten von Ingenieurinnen und Ingenieuren, nicht nur Netzwerkingenieurinnen und -ingenieure, sollten keine Angst davor haben, sich in Python oder Ruby oder sogar in fortgeschritteneren Sprachen wie C oder Go einzulesen. Systemadministratoren und -administratorinnen haben bereits gute Erfahrungen mit Skripten gemacht, die es ihnen ermöglichen, ihre Arbeit effizienter zu erledigen, indem sie Bash-Skripte, Python, Ruby und PowerShell verwenden.
Für Netzwerkadministratoren war das bisher nicht der Fall (was ein wichtiger Grund für dieses Buch ist!). Mit dem Fortschritt der Branche und der Weiterentwicklung der Ingenieure ist es sehr realistisch, dass du als Netzwerktechniker eher DevOps-orientiert bist und irgendwo in der Mitte landest - nicht als Entwickler, aber auch nicht als traditioneller Netzwerktechniker, der nur mit CLI arbeitet. Du könntest Open-Source-Konfigurationsmanagement- und Automatisierungstools verwenden und dann bei Bedarf ein wenig Code hinzufügen, um die Arbeitsabläufe und Aufgaben in deiner spezifischen Umgebung zu erledigen und zu automatisieren.
Hinweis
Es ist nicht üblich und auch nicht empfehlenswert, für alles eine eigene Software zu schreiben und eine eigene Automatisierungsplattform aufzubauen, es sei denn, dein Unternehmen ist aufgrund seiner Größe, seines Umfangs, der Einhaltung von Vorschriften oder der Kontrolle dazu berechtigt. Das ist keine effiziente Zeitverwendung. Empfehlenswert ist, dass du die Komponenten der Programmierung und Softwareentwicklung verstehst und vor allem die Grundlagen wie die Kerndatentypen, die in allen Tools und Sprachen vorkommen.
Wir wissen also, dass sich die Branche verändert, dass Geräte APIs haben und dass es sinnvoll ist, sich auf den Weg zu machen und zu lernen, Code zu schreiben. Dieses Kapitel gibt dir die Bausteine an die Hand, mit denen du von 0 auf 60 kommst und deine Python-Reise beginnen kannst.
Im weiteren Verlauf dieses Kapitels behandeln wir die folgenden Themen:
-
Den interaktiven Python-Interpreter verwenden
-
Python-Datentypen verstehen
-
Hinzufügen von bedingter Logik zu deinem Code
-
Eindämmung verstehen
-
Schleifen in Python verwenden
-
Funktionen
-
Arbeiten mit Dateien
-
Python-Programme erstellen
-
Mit Python-Modulen arbeiten
Mach dich bereit - wir werden gleich loslegen und ein bisschen Python lernen!
Hinweis
Dieses Kapitel bietet eine Einführung in die grundlegenden Konzepte von Python für Netzwerktechniker/innen, die Python lernen möchten, um ihre vorhandenen Kenntnisse zu erweitern. Es ist nicht als umfassende Ausbildung für Vollzeitentwickler gedacht, die Python-Software in Produktionsqualität schreiben wollen.
Bitte beachte außerdem, dass die in diesem Kapitel behandelten Konzepte auch außerhalb von Python von großer Bedeutung sind. Du musst zum Beispiel Konzepte wie Schleifen und Datentypen verstehen - die wir hier erkunden -, um mit Tools wie Ansible, Salt, Puppet und StackStorm arbeiten zu können.
Den interaktiven Python-Interpreter verwenden
Der interaktive Python-Interpreter ist nicht immer denjenigen bekannt, die gerade erst mit dem Programmieren anfangen oder die bereits in anderen Sprachen entwickelt haben, aber wir denken, dass es ein Werkzeug ist, das jeder kennen und lernen sollte, bevor er versucht, eigenständige ausführbare Skripte zu erstellen.
Der Interpreter ist ein Werkzeug, das für Entwickler aller Erfahrungsstufen wichtig ist. Der interaktive Python-Interpreter, der auch als Python-Shell bekannt ist, wird von Anfängern als Lernplattform genutzt, aber auch von erfahrenen Entwicklern, um zu testen und Echtzeit-Feedback zu erhalten, ohne ein vollständiges Programm oder Skript schreiben zu müssen.
Die Python-Shell bzw. der Python-Interpreter ist auf fast allen nativen Linux-Distributionen sowie auf vielen modernen Netzwerkbetriebssystemen von Herstellern wie Cisco, HP, Juniper, Cumulus und Arista zu finden, aber nicht nur dort.
Um den interaktiven Python-Interpreter aufzurufen, öffnest du einfach ein Linux-Terminalfenster oder verbindest dich per SSH mit einem modernen Netzwerkgerät, gibst den Befehl python
ein und drückst auf Enter.
Hinweis
Alle Beispiele in diesem Kapitel, die einen Linux-Terminalbefehl bezeichnen, beginnen mit $
. Wenn du dich in der Python-Shell befindest, beginnen alle Zeilen und Befehle mit >>>
. Außerdem wurden alle Beispiele auf einem System mit Ubuntu 14.04 LTS und Python 2.7.6 gezeigt.
Nachdem du den Befehl python
eingegeben und die Eingabetaste gedrückt hast, gelangst du direkt in die Shell. In der Shell kannst du sofort mit dem Schreiben von Python-Code beginnen! Es gibt keinen Texteditor, keine IDE und keine Voraussetzungen, um loszulegen.
$ python Python 2.7.6 (default, Mar 22 2014, 22:59:56) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
Obwohl wir im Laufe dieses Kapitels noch viel mehr über Python erfahren werden, schauen wir uns jetzt kurz ein paar Beispiele an, um die Leistungsfähigkeit des Python-Interpreters zu sehen.
Das folgende Beispiel erstellt eine Variable namens hostname
und weist ihr den Wert ROUTER_1
zu.
>>>
hostname
=
'ROUTER_1'
>>>
Beachte, dass du die Variable nicht erst deklarieren oder festlegen musst, dass hostname
vom Typ string
sein soll. Dies ist ein Unterschied zu einigen Programmiersprachen wie C und Java und ein Grund, warum Python als dynamische Sprache bezeichnet wird.
Lass uns die Variable hostname
ausdrucken.
>>>
(
hostname
)
ROUTER_1
>>>
>>>
hostname
'ROUTER_1'
>>>
Wenn du die Variable erstellt hast, kannst du sie ganz einfach mit dem Befehl print
ausdrucken. In der Shell kannst du aber auch den Wert von hostname
oder einer anderen Variablen anzeigen, indem du einfach den Namen der Variablen eingibst und die Eingabetaste drückst. Ein Unterschied zwischen diesen beiden Methoden besteht darin, dass bei Verwendung der Anweisung print
Zeichen wie das Zeilenende (oder \n
) interpretiert werden, nicht aber, wenn du die Anweisung print
nicht verwendest.
Wenn du zum Beispiel print
verwendest, wird \n
interpretiert und eine neue Zeile gedruckt. Wenn du aber nur den Variablennamen in die Shell eingibst und Enter drückst, wird \n
nicht interpretiert und nur im Terminal angezeigt.
>>>
banner
=
"
\n\n
WELCOME TO ROUTER_1
\n\n
"
>>>
>>>
(
banner
)
WELCOME
TO
ROUTER_1
>>>
>>>
banner
'
\n\n
WELCOME TO ROUTER_1
\n\n
'
>>>
Siehst du den Unterschied?
Wenn du validieren oder testen willst, ist die Python-Shell ein großartiges Werkzeug. In den vorangegangenen Beispielen hast du vielleicht bemerkt, dass sowohl einfache als auch doppelte Anführungszeichen verwendet wurden. Jetzt fragst du dich vielleicht, ob sie zusammen in einer Zeile verwendet werden können? Lass uns nicht darüber spekulieren, sondern die Python-Shell benutzen, um es auszuprobieren.
>>>
hostname
=
'ROUTER_1"
File
"<stdin>"
,
line
1
hostname
=
'ROUTER_1"
^
SyntaxError
:
EOL
while
scanning
string
literal
>>>
Und schon haben wir festgestellt, dass Python sowohl einfache als auch doppelte Anführungszeichen unterstützt, aber gelernt, dass sie nicht zusammen verwendet werden können.
Die meisten Beispiele in diesem Kapitel verwenden weiterhin den Python-Interpreter - du kannst ihnen gerne folgen und sie nach und nach ausprobieren.
Wir werden weiterhin den Python-Interpreter verwenden, um die verschiedenen Python-Datentypen mit besonderem Schwerpunkt auf Netzwerken kennenzulernen.
Python-Datentypen verstehen
Dieser Abschnitt von bietet einen Überblick über verschiedene Python-Datentypen wie Strings, Zahlen (Ganzzahlen und Fließkommazahlen), Boolesche Werte, Listen und Wörterbücher und geht auch auf Tupel und Sets ein.
Die Abschnitte über Strings, Listen und Dictionaries sind in zwei Teile unterteilt. Der erste Teil ist eine Einführung in den Datentyp und der zweite Teil behandelt einige der eingebauten Methoden. Wie du sehen wirst, sind die Methoden ein fester Bestandteil von Python und machen es Entwicklern extrem leicht, die jeweiligen Datentypen zu manipulieren und mit ihnen zu arbeiten.
Eine Methode namens upper
, die eine Zeichenkette in Großbuchstaben umwandelt, kann zum Beispiel mit der Anweisung "router1".upper()
ausgeführt werden, die ROUTER1
zurückgibt. Wir werden in diesem Kapitel noch viele weitere Beispiele für die Verwendung von Methoden zeigen.
Die Abschnitte über ganze Zahlen und Boolesche Ausdrücke geben dir einen Überblick darüber, wie du mathematische Operatoren und boolesche Ausdrücke beim Schreiben von Code in Python verwenden kannst.
Zum Abschluss des Abschnitts über Datentypen geben wir eine kurze Einführung in Tupel und Sets. Das sind zwar fortgeschrittenere Datentypen, aber wir waren der Meinung, dass sie in einer Einführung in Python dennoch behandelt werden sollten.
Tabelle 4-1 beschreibt und markiert jeden Datentyp, den wir in diesem Kapitel behandeln werden. Sie dient als Referenz für das gesamte Kapitel.
Datentyp | Beschreibung | Kurzname (Typ) | Zeichen | Beispiel |
---|---|---|---|---|
String | Reihe beliebiger Zeichen, die von Anführungszeichen umgeben sind | str |
"" |
hostname="nycr01" |
Integer | Ganze Zahlen, die ohne Anführungszeichen dargestellt werden | int |
k.A. | eos_qty=5 |
Schwimmer | Fließkommazahl (Dezimalzahlen) | float |
k.A. | cpu_util=52.33 |
Boolesche | Entweder Wahr oder Falsch (keine Anführungszeichen) | bool |
k.A. | is_switchport=True |
Liste | Geordnete Folge von Werten. Die Werte können von jedem Datentyp sein. | list |
[] |
vendors=['cisco', 'juniper', 'arista', 'cisco'] |
Wörterbuch | Ungeordnete Liste von Schlüssel-Werte-Paaren | dict |
{} |
facts={"vendor":"cisco", "platform":"catalyst", "os":"ios"} |
Set | Ungeordnete Sammlung von eindeutigen Elementen | set |
set() |
set(vendors)=>['cisco', 'juniper', 'arista'] |
Tupel | Geordnete und unveränderliche Abfolge von Werten | tuple |
() |
ipaddr=(10.1.1.1, 24) |
Fangen wir an und werfen einen Blick auf Python-Strings.
Lernen, Strings zu verwenden
Strings sind eine Folge von Zeichen, die von Anführungszeichen umschlossen sind und sind wohl der bekannteste Datentyp, den es in allen Programmiersprachen gibt.
Zu Beginn des Kapitels haben wir uns ein paar grundlegende Beispiele für die Erstellung von Variablen vom Typ string
angesehen. Sehen wir uns nun an, was du sonst noch wissen musst, wenn du anfängst, Strings zu verwenden.
Zuerst definieren wir zwei neue Variablen, die beide Zeichenketten sind: final
und ipaddr
.
>>>
final
=
'The IP address of router1 is: '
>>>
>>>
ipaddr
=
'1.1.1.1'
>>>
Tipp
Du kannst die eingebaute Funktion namens type
verwenden, um den Datentyp eines beliebigen Objekts in Python zu überprüfen.
>>>
type
(
final
)
<
type
'str'
>
>>>
So kannst du den Typ eines Objekts leicht überprüfen, was bei der Fehlersuche im Code oft hilfreich ist, vor allem, wenn es sich um Code handelt, den du nicht selbst geschrieben hast.
Als Nächstes schauen wir uns unter an, wie man Strings kombiniert, addiert oder verkettet.
>>>
final
+
ipaddr
'The IP address of router1 is: 1.1.1.1'
In diesem Beispiel wurden zwei neue Variablen erstellt: final
und ipaddr
. Jede ist eine Zeichenkette. Nachdem beide Variablen erstellt wurden, haben wir sie mit dem Operator +
verkettet und schließlich ausgedruckt. Ziemlich einfach, oder?
Das Gleiche könnte auch gemacht werden, wenn final
kein vordefiniertes Objekt wäre:
>>>
(
'The IP address of router1 is: '
+
ipaddr
)
The
IP
address
of
router1
is
:
1.1
.
1.1
>>>
Eingebaute Methoden von Strings verwenden
Um die verfügbaren eingebauten Methoden für Strings anzuzeigen, verwendest du die eingebaute Funktion dir()
in der Python-Shell. Du erstellst zunächst eine beliebige Variable, die eine Zeichenkette ist, oder verwendest den formalen Datentypnamen str
und übergibst sie als Argument an dir()
, um die verfügbaren Methoden anzuzeigen.
Hinweis
dir()
kann auf jedes Python-Objekt angewendet werden, nicht nur auf Strings, wie wir im Laufe dieses Kapitels zeigen werden.
>>>
>>>
dir
(
str
)
# output has been omitted
[
'__add__'
,
'__class__'
,
'__contains__'
,
'__delattr__'
,
'__doc__'
,
'endswith'
,
'expandtabs'
,
'find'
,
'format'
,
'index'
,
'isalnum'
,
'isalpha'
,
'isdigit'
,
'islower'
,
'isspace'
,
'istitle'
,
'isupper'
,
'join'
,
'lower'
,
'lstrip'
,
'replace'
,
rstrip
', '
split
', '
splitlines
', '
startswith
',
'strip'
,
'upper'
]
>>>
Um zu wiederholen, was wir bereits gesagt haben, ist es möglich, der Funktion dir()
eine beliebige Zeichenkette zu übergeben, um die gleiche Ausgabe wie oben zu erhalten. Wenn du z. B. eine Variable wie hostname = 'ROUTER'
definiert hast, kann hostname
an dir()
- also dir(hostname)
- übergeben werden, was die gleiche Ausgabe wie dir(str)
ergibt, um festzustellen, welche Methoden für Strings verfügbar sind.
Tipp
Die Verwendung von dir()
kann ein Lebensretter sein, um zu überprüfen, welche Methoden für einen bestimmten Datentyp verfügbar sind, also vergiss das nicht.
Alles mit einem einfachen oder doppelten Unterstrich aus der vorherigen Ausgabe wird in diesem Buch nicht behandelt, da unser Ziel eine praktische Einführung in Python ist, aber es lohnt sich, darauf hinzuweisen, dass die Methoden mit Unterstrichen von den Interna von Python verwendet werden.
Werfen wir einen Blick auf einige der String-Methoden, darunter count
, endswith
, startswith
, format
, isdigit
, join
, lower
, upper
und strip
.
Tipp
In kannst du mit der eingebauten Funktion help()
lernen, wie du eine bestimmte Methode, die du in der Ausgabe von dir()
siehst, verwenden kannst. Um die eingebaute Hilfefunktion zu nutzen, gibst du das Objekt (oder die Variable) und die angegebene Methode an. Die folgenden Beispiele zeigen zwei Möglichkeiten, wie du help()
verwenden kannst und wie du die Methode upper
nutzen kannst:
>>>
help
(
str
.
upper
)
>>>
>>>
help
(
hostname
.
upper
)
>>>
Die Ausgabe der beiden ist die folgende:
Help on method_descriptor: upper(
...)
S.upper()
-> string Return a copy of the string S converted to uppercase.(
END)
Wenn du fertig bist, gibst du ein Q
ein, um die integrierte Hilfe zu beenden.
Bei der Überprüfung jeder Methode gibt es zwei wichtige Fragen, die du dir stellen solltest. Welcher Wert wird von der Methode zurückgegeben? Und welche Aktion führt die Methode mit dem ursprünglichen Objekt durch?
Verwendung der Methoden upper() und lower()
Die Methoden , upper()
und lower()
sind hilfreich, wenn du Zeichenketten vergleichen musst, bei denen die Groß- und Kleinschreibung nicht beachtet werden muss. Du musst z.B. eine Variable akzeptieren, die der Name einer Schnittstelle ist, wie "Ethernet1/1", möchtest aber auch zulassen, dass der Benutzer "ethernet1/1" eingibt. Am besten vergleichst du diese beiden Angaben mit upper()
oder lower()
.
>>>
interface
=
'Ethernet1/1'
>>>
>>>
interface
.
lower
()
'ethernet1/1'
>>>
>>>
interface
.
upper
()
'ETHERNET1/1'
>>>
Du kannst sehen, dass das Format bei der Verwendung einer Methode darin besteht, den Objektnamen oder in diesem Fall den String einzugeben und dann .methodname()
anzuhängen.
Nach der Ausführung von interface.lower()
wurde ethernet1/1
auf dem Terminal ausgegeben. Das sagt uns, dass ethernet1/1
zurückgegeben wurde, als lower()
ausgeführt wurde. Das Gleiche gilt für upper()
. Wenn etwas zurückgegeben wird, hast du auch die Möglichkeit, es als Wert einer neuen oder bestehenden Variablen zuzuweisen.
>>>
intf_lower
=
interface
.
lower
()
>>>
>>>
(
intf_lower
)
ethernet1
/
1
>>>
In diesem Beispiel kannst du sehen, wie du die Methode verwendest, aber auch die zurückgegebenen Daten einer Variablen zuweist.
Was ist mit der ursprünglichen Variable namens interface
? Schauen wir mal, was, wenn überhaupt, sich mit interface
geändert hat.
>>>
(
interface
)
Ethernet1
/
1
>>>
Da dies das erste Beispiel ist, ist vielleicht noch nicht klar, wonach wir suchen, um zu sehen, ob sich in der ursprünglichen Variable interface
etwas geändert hat, aber wir wissen, dass sie immer noch den Wert von Ethernet1/1
enthält und sich nichts geändert hat. Keine Sorge, wir werden im Laufe dieses Kapitels noch viele Beispiele dafür sehen, wann das ursprüngliche Objekt verändert wird.
Verwendung der Methoden startswith() und endswith()
Wie du wahrscheinlich erraten kannst, wird startswith()
verwendet, um zu überprüfen, ob eine Zeichenkette mit einer bestimmten Folge von Zeichen beginnt, und endswith()
wird verwendet, um zu überprüfen, ob eine Zeichenkette mit einer bestimmten Folge von Zeichen endet.
>>>
ipaddr
=
'10.100.20.5'
>>>
>>>
ipaddr
.
startswith
(
'10'
)
True
>>>
>>>
ipaddr
.
startswith
(
'100'
)
False
>>>
>>>
ipaddr
.
endswith
(
'.5'
)
True
>>>
In den vorherigen Beispielen, die die Methoden lower()
und upper()
verwendet haben, wurde ein String zurückgegeben, und dieser String war ein geänderter String mit allen Klein- oder Großbuchstaben.
Im Fall von startswith()
wird kein String zurückgegeben, sondern ein boolesches (bool
) Objekt. Wie du später in diesem Kapitel lernen wirst, sind die booleschen Werte True
und False
. Die Methode startswith()
gibt True
zurück, wenn die übergebene Zeichenfolge mit der jeweiligen Start- oder Endsequenz des Objekts übereinstimmt. Andernfalls gibt sie False
zurück.
Hinweis
Beachte, dass boolesche Werte entweder True
oder False
sind, dass für Boolesche Werte keine Anführungszeichen verwendet werden und dass der erste Buchstabe groß geschrieben werden muss. Boolesche Werte werden später in diesem Kapitel ausführlicher behandelt.
Die Verwendung dieser Methoden erweist sich als nützlich, wenn du den Anfang oder das Ende einer Zeichenkette überprüfen willst. Vielleicht geht es darum, das erste oder vierte Oktett einer IPv4-Adresse zu überprüfen oder einen Schnittstellennamen zu verifizieren, wie im vorherigen Beispiel mit lower()
. Anstatt davon auszugehen, dass der Benutzer eines Skripts den vollständigen Namen eingeben würde, ist es von Vorteil, die ersten beiden Zeichen zu überprüfen, damit der Benutzer "ethernet1/1", "eth1/1" und "et1/1" eingeben kann.
Für diese Prüfung zeigen wir, wie man Methoden kombiniert oder den Rückgabewert einer Methode als Basis-Stringobjekt für die zweite Methode verwendet.
>>>
interface
=
'Eth1/1'
>>>
>>>
interface
.
lower
()
.
startswith
(
'et'
)
True
>>>
Wie in diesem Code zu sehen ist, stellen wir sicher, dass es sich um eine Ethernet-Schnittstelle handelt, indem wir zuerst lower()
ausführen, das eth1/1
zurückgibt, und dann die boolesche Prüfung durchführen, um zu sehen, ob "eth1/1" mit "et" beginnt. Und das ist eindeutig der Fall.
Natürlich gibt es noch andere Dinge, die über das "eth" in einem Interface-String-Objekt hinaus ungültig sein könnten, aber der Punkt ist, dass die Methoden einfach zusammen verwendet werden können.
Verwendung der Methode strip()
Viele Netzwerkgeräte haben immer noch keine Anwendungsprogrammierschnittstellen (APIs). Wenn du ein Skript schreiben willst, wirst du es mit ziemlicher Sicherheit irgendwann auf einem älteren CLI-basierten Gerät ausprobieren. Wenn du das tust, wirst du mit Sicherheit auf Klumpen von Rohtext stoßen, die von dem Gerät zurückkommen - das kann das Ergebnis eines beliebigen show
Befehls sein, von der Ausgabe von show interfaces
bis hin zu einem vollständigen show running-config
.
Wenn du etwas speichern oder einfach nur ausdrucken musst, möchtest du vielleicht nicht, dass das Objekt, das du verwenden oder sehen willst, von Leerzeichen umgeben ist. Um mit den vorherigen Beispielen übereinzustimmen, könnte dies eine IP-Adresse sein.
Was ist, wenn das Objekt, mit dem du arbeitest, den Wert " 10.1.50.1 "
einschließlich der Leerzeichen hat? Die Methoden startswith()
oder endswith()
funktionieren wegen der Leerzeichen nicht. In diesen Fällen wird strip()
verwendet, um die Leerzeichen zu entfernen.
>>>
ipaddr
=
' 10.1.50.1 '
>>>
>>>
>>>
ipaddr
.
strip
()
'10.1.50.1'
>>>
Mit strip()
wird das Objekt ohne Leerzeichen auf beiden Seiten zurückgegeben. Für lstrip()
und rstrip()
werden keine Beispiele gezeigt, aber das sind zwei andere eingebaute Methoden für Strings, die Leerzeichen speziell auf der linken oder rechten Seite eines String-Objekts entfernen.
Verwendung der Methode isdigit()
Es) kann vorkommen, dass du mit Strings arbeitest, aber überprüfen musst, ob das String-Objekt eine Zahl ist. Technisch gesehen sind Ganzzahlen ein anderer Datentyp (der im nächsten Abschnitt behandelt wird), aber Zahlen können trotzdem Werte in Strings sein.
Mit isdigit()
kannst du ganz einfach feststellen, ob es sich bei dem Zeichen oder der Zeichenkette tatsächlich um eine Ziffer handelt.
>>>
ten
=
'10'
>>>
>>>
ten
.
isdigit
()
True
>>>
>>>
bogus
=
'10a'
>>>
>>>
bogus
.
isdigit
()
False
Genau wie startswith()
gibt auch isdigit()
einen booleschen Wert zurück. Er gibt True
zurück, wenn der Wert eine ganze Zahl ist, andernfalls False
.
Verwendung der Methode count()
Stell dir vor, du arbeitest mit einer Binärzahl - vielleicht, um eine IP-Adresse oder eine Subnetzmaske zu berechnen. Es gibt zwar einige eingebaute Bibliotheken, die Binärzahlen in Dezimalzahlen umwandeln können, aber was ist, wenn du einfach nur zählen willst, wie viele 1en oder 0en in einer bestimmten Zeichenkette enthalten sind? Du kannst count()
verwenden, um dies zu tun.
>>>
octet
=
'11111000'
>>>
>>>
octet
.
count
(
'1'
)
5
Das Beispiel zeigt, wie einfach es ist, die Methode count()
zu verwenden. Diese Methode gibt jedoch im Gegensatz zu den vorherigen Beispielen eine int
(Ganzzahl) zurück.
Wenn du count()
verwendest, bist du nicht darauf beschränkt, ein einzelnes Zeichen als Parameter zu senden.
>>>
octet
.
count
(
'111'
)
1
>>>
>>>
test_string
=
"Don't you wish you started programming a little earlier?"
>>>
>>>
test_string
.
count
(
'you'
)
2
Verwendung der Methode format()
Wir haben bereits gesehen, wie man Zeichenketten miteinander verknüpft. Stell dir vor, du musst einen Satz oder besser noch einen Befehl an ein Netzwerkgerät senden, der aus mehreren Strings oder Variablen besteht. Wie würdest du die Zeichenfolge oder den CLI-Befehl formatieren?
Nehmen wir ping
als Beispiel und gehen wir davon aus, dass der Befehl, der erstellt werden muss, der folgende ist:
ping 8.8.8.8 vrf management
Hinweis
In den Beispielen in diesem Kapitel werden nur allgemeine Netzwerk-CLI-Befehle verwendet, da keine tatsächlichen Geräteverbindungen hergestellt werden. Sie sind daher keinem bestimmten Anbieter zugeordnet, da es sich um "Standardbeispiele" handelt, die mit verschiedenen Anbietern wie Cisco IOS, Cisco NXOS, Arista EOS und vielen anderen funktionieren.
Wenn du ein Skript schreibst, ist es mehr als wahrscheinlich, dass die IP-Adresse, an die du ICMP-Echo-Anfragen senden willst, und die virtuelle Weiterleitung (VRF) beides Eingabeparameter sind. In diesem Beispiel bedeutet das, dass '8.8.8.8'
und 'management'
die Eingabeargumente (Parameter) sind.
Eine Möglichkeit, den String zu erstellen, ist, mit dem Folgenden zu beginnen:
>>>
ipaddr
=
'8.8.8.8'
>>>
vrf
=
'management'
>>>
>>>
ping
=
'ping'
+
ipaddr
+
'vrf'
+
vrf
>>>
>>>
(
ping
)
ping8
.
8.8
.
8
vrfmanagement
Du siehst, dass die Abstände nicht stimmen, also gibt es zwei Möglichkeiten: Füge Leerzeichen zu deinen Eingabeobjekten oder innerhalb des ping
Objekts hinzu. Sehen wir uns an, wie wir sie innerhalb von ping
hinzufügen.
>>>
ping
=
'ping'
+
' '
+
ipaddr
+
' '
+
'vrf '
+
vrf
>>>
>>>
(
ping
)
ping
8.8
.
8.8
vrf
management
Wie du siehst, funktioniert das ganz gut und ist nicht allzu kompliziert, aber wenn die Strings oder Befehle länger werden, kann es ziemlich unübersichtlich werden, wenn man mit all den Anführungszeichen und Leerzeichen zu tun hat. Die Methode format()
kann dies vereinfachen.
>>>
ping
=
'ping {} vrf {}'
.
format
(
ipaddr
,
vrf
)
>>>
>>>
(
ping
)
ping
8.8
.
8.8
vrf
management
Die Methode format()
nimmt eine Reihe von Argumenten entgegen, die zwischen den geschweiften Klammern ({}
) in der Zeichenkette eingefügt werden. Beachte, dass die Methode format()
im Gegensatz zu den vorherigen Beispielen auf einen rohen String angewendet wird.
Hinweis
Es ist möglich, jede der String-Methoden sowohl auf Variablen als auch auf rohe Strings anzuwenden. Das gilt auch für jeden anderen Datentyp und seine eingebauten Methoden.
Das nächste Beispiel zeigt die Verwendung der Methode format()
mit einem zuvor erstellten String-Objekt (Variable) im Gegensatz zum vorherigen Beispiel, in dem sie auf einen rohen String angewendet wurde.
>>>
ping
=
'ping {} vrf {}'
>>>
>>>
command
=
ping
.
format
(
ipaddr
,
vrf
)
>>>
>>>
(
command
)
ping
8.8
.
8.8
vrf
management
Dieses Szenario ist wahrscheinlicher, da du einen vordefinierten Befehl in einem Python-Skript hast, in das der Benutzer zwei Argumente eingibt, und die Ausgabe ist der endgültige Befehlsstring, der an ein Netzwerkgerät gesendet wird.
Verwendung der Methoden join() und split()
Diese sind die letzten Methoden für Strings, die in diesem Kapitel behandelt werden. Wir haben sie für den Schluss aufgehoben, da sie die Arbeit mit einem anderen Datentyp namens list
beinhalten.
Hinweis
Listen werden zwar erst später im Kapitel behandelt, aber wir wollen hier eine kurze Einführung geben, um die Methoden join()
und split()
für String-Objekte zu zeigen.
Listen sind genau das, wonach sie klingen. Sie sind eine Liste von Objekten - jedes Objekt wird als Element bezeichnet, und jedes Element hat den gleichen oder einen anderen Datentyp. Es ist nicht vorgeschrieben, dass alle Elemente in einer Liste denselben Datentyp haben müssen.
Wenn du eine Umgebung mit fünf Routern hast, hast du vielleicht eine Liste mit Hostnamen.
>>>
hostnames
=
[
'r1'
,
'r2'
,
'r3'
,
'r4'
,
'r5'
]
Du kannst auch eine Liste von Befehlen erstellen, die du an ein Netzwerkgerät senden kannst, um eine Konfigurationsänderung vorzunehmen. Das nächste Beispiel ist eine Liste von Befehlen zum Herunterfahren einer Ethernet-Schnittstelle auf einem Switch.
>>>
commands
=
[
'config t'
,
'interface Ethernet1/1'
,
'shutdown'
]
Es ist durchaus üblich, eine solche Liste zu erstellen, aber wenn du ein herkömmliches CLI-basiertes Netzwerkgerät verwendest, kannst du ein list
Objekt möglicherweise nicht direkt an das Gerät senden. Das Gerät kann verlangen, dass Strings (oder einzelne Befehle) gesendet werden.
join()
ist eine solche Methode, die aus einer Liste eine Zeichenkette erstellen kann, aber bei Bedarf die erforderlichen Zeichen dazwischen einfügt.
Erinnere dich daran, dass \n
das Zeichen für das Zeilenende (EOL) ist. Wenn du Befehle an ein Gerät sendest, musst du eventuell ein \n
zwischen den Befehlen einfügen, damit das Gerät eine neue Zeile für den nächsten Befehl erstellen kann.
Wenn wir commands
aus dem vorherigen Beispiel nehmen, sehen wir, wie wir join()
nutzen können, um eine einzelne Zeichenkette mit einem \n
zwischen den einzelnen Befehlen zu erstellen.
>>>
'
\n
'
.
join
(
commands
)
'config t
\n
interface Ethernet1/1
\n
shutdown'
>>>
Ein weiteres praktisches Beispiel ist die Verwendung einer API wie der NX-API, die es auf den Cisco Nexus Switches gibt. Cisco bietet die Möglichkeit, eine Reihe von Befehlen zu senden, die jedoch durch ein Semikolon (;
) getrennt werden müssen.
Dafür würdest du den gleichen Ansatz verwenden.
>>>
' ; '
.
join
(
commands
)
'config t ; interface Ethernet1/1 ; shutdown'
>>>
In diesem Beispiel haben wir ein Leerzeichen vor und nach dem Semikolon eingefügt, aber im Großen und Ganzen ist es derselbe Ansatz.
Hinweis
In den gezeigten Beispielen wurden ein Semikolon und ein EOL-Zeichen als Trennzeichen verwendet, aber du solltest wissen, dass du überhaupt keine Zeichen verwenden musst. Es ist auch möglich, die Elemente in der Liste zu verketten, ohne irgendwelche Zeichen einzufügen, etwa so: ''.join(list)
.
Du hast gelernt, wie du mit join()
eine Zeichenkette aus einer Liste erstellen kannst. Aber was wäre, wenn du genau das Gegenteil machen und eine Liste aus einer Zeichenkette erstellen müsstest? Eine Möglichkeit ist, die Methode split()
zu verwenden.
Im nächsten Beispiel beginnen wir mit der zuvor erzeugten Zeichenkette und wandeln sie wieder in eine Liste um.
>>>
commands
=
'config t ; interface Ethernet1/1 ; shutdown'
>>>
>>>
cmds_list
=
commands
.
split
(
' ; '
)
>>>
>>>
(
cmds_list
)
[
'config t'
,
'interface Ethernet1/1'
,
'shutdown'
]
>>>
Das zeigt, wie einfach es ist, ein String-Objekt zu nehmen und daraus eine Liste zu erstellen. Ein weiteres gängiges Beispiel für Netzwerke ist die IP-Adresse (String), die mit split()
in eine Liste umgewandelt wird, die aus vier Elementen besteht - ein Element pro Oktett.
>>>
ipaddr
=
'10.1.20.30'
>>>
>>>
ipaddr
.
split
(
'.'
)
[
'10'
,
'1'
,
'20'
,
'30'
]
>>>
Das waren die Grundlagen der Arbeit mit Python-Strings. Kommen wir nun zum nächsten Datentyp, den Zahlen.
Lernen mit Zahlen umzugehen
Wir verbringen nicht viel Zeit mit den verschiedenen Zahlentypen wie Floats (Dezimalzahlen) oder imaginären Zahlen, aber wir schauen uns kurz den Datentyp int
an, besser bekannt als Integer. Ehrlich gesagt, liegt das daran, dass die meisten Menschen Zahlen verstehen und es keine eingebauten Methoden gibt, die an dieser Stelle sinnvoll sind. Anstatt die eingebauten Methoden für Ganzzahlen zu behandeln, sehen wir uns die Verwendung mathematischer Operatoren in der Python-Shell an.
Hinweis
Du solltest dir auch bewusst sein, dass Dezimalzahlen in Python als Floats bezeichnet werden. Erinnere dich daran, dass du den Datentyp jederzeit mit der eingebauten Funktion type()
überprüfen kannst:
>>>
cpu
=
41.3
>>>
>>>
type
(
cpu
)
<
type
'float'
>
>>>
>>>
Ausführen von mathematischen Operationen
Wenn du Zahlen addieren musst, ist nichts Ausgefallenes nötig: füge sie einfach hinzu.
>>>
5
+
3
8
>>>
a
=
1
>>>
b
=
2
>>>
a
+
b
3
Es kann vorkommen, dass du einen Zähler brauchst, wenn du eine Schleife durch eine Reihe von Objekten durchläufst. Du möchtest vielleicht counter = 1
sagen, eine Art von Operation durchführen und dann counter = counter + 1
tun. Das ist zwar durchaus funktional und funktioniert, aber in Python ist es idiomatischer, die Operation als counter += 1
auszuführen. Dies wird im nächsten Beispiel gezeigt.
>>>
counter
=
1
>>>
counter
=
counter
+
1
>>>
counter
2
>>>
>>>
counter
=
5
>>>
counter
+=
5
>>>
>>>
counter
10
Ähnlich wie bei der Addition gibt es auch bei der Subtraktion nichts Besonderes. Wir fangen gleich mit einem Beispiel an.
>>>
100
-
90
10
>>>
count
=
50
>>>
count
-
20
30
>>>
Beim Multiplizieren gibt es wiederum keinen Unterschied. Hier ist ein kurzes Beispiel.
>>>
100
*
50
5000
>>>
>>>
(
2
*
25
)
50
>>>
Das Schöne an dem Multiplikationsoperator (*
) ist, dass man ihn auch auf Strings anwenden kann. Vielleicht möchtest du etwas formatieren und hübsch machen.
>>>
(
'*'
*
50
)
**************************************************
>>>
>>>
(
'='
*
50
)
==================================================
>>>
Das vorangehende Beispiel ist extrem einfach und gleichzeitig extrem mächtig. Da du nicht weißt, dass dies möglich ist, könntest du versucht sein, eine Zeile nach der anderen zu drucken und eine Zeichenkette mit dem Befehl print(*******************)
zu drucken, aber nachdem du diesen und ein paar andere Tipps gelernt hast, die später in diesem Kapitel behandelt werden, wird das Schönschreiben von Textdaten viel einfacher.
Wenn du in den letzten Jahren keine Mathematik von Hand gemacht hast, mag dir die Division wie ein Albtraum vorkommen. Erwartungsgemäß unterscheidet sie sich aber nicht von den drei zuvor besprochenen Rechenoperationen. Naja, irgendwie schon.
Es gibt keinen Unterschied darin, wie du eingibst, was du erreichen willst. Um eine Operation auszuführen, verwendest du nach wie vor 10 / 2
oder 100 / 50
, und so weiter:
>>>
100
/
50
2
>>>
>>>
10
/
2
5
>>>
Diese Beispiele sind wahrscheinlich das, was du erwartet hast.
Der Unterschied ist, was zurückgegeben wird, wenn es einen Rest gibt:
>>>
12
/
10
1
>>>
Wie du unter weißt, geht die Zahl 10 einmal durch 12. Das ist der so genannte Quotient, also ist der Quotient hier gleich 1. Was nicht angezeigt oder zurückgegeben wird, ist der Rest. Um den Rest in Python zu sehen, musst du die %
oder die Modulus-Operation verwenden.
>>>
12
%
10
2
>>>
Das bedeutet, dass zur vollständigen Berechnung des Ergebnisses einer Divisionsaufgabe sowohl die Operatoren /
als auch %
verwendet werden.
Das war ein kurzer Blick darauf, wie man in Python mit Zahlen arbeitet. Wir gehen jetzt zu den Booleschen Zahlen über.
Lernen, Boolesche Werte zu verwenden
Boolesche Objekte, die in Python auch als Objekte vom Typ bool
bekannt sind, sind ziemlich einfach. Schauen wir uns zunächst die Grundlagen der allgemeinen booleschen Logik an, indem wir uns eine Wahrheitstabelle ansehen(Tabelle 4-2).
A | B | A und B | A oder B | Nicht A |
---|---|---|---|---|
Falsch | Falsch | Falsch | Falsch | Wahr |
Falsch | Wahr | Falsch | Wahr | Wahr |
Wahr | Falsch | Falsch | Wahr | Falsch |
Wahr | Wahr | Wahr | Wahr | Falsch |
Beachte, dass alle Werte in der Tabelle entweder True
oder False
sind. Das liegt daran, dass in der booleschen Logik alle Werte entweder auf True
oder False
reduziert werden. Das macht die Boolesche Logik einfach zu verstehen.
Da boolesche Werte nur True
oder False
sein können, ergeben alle Ausdrücke entweder True
oder False
. Du kannst in der Tabelle sehen, dass BEIDE Werte, für A und B, True
sein müssen, damit "A und B" True
ergibt. Und "A oder B" ergibt True
, wenn JEDER Wert (A oder B) True
ist. Du kannst auch sehen, dass das NICHT eines booleschen Wertes den Kehrwert dieses Wertes berechnet. Das ist deutlich zu sehen, denn "NOT False" ergibt True und "NOT True" ergibt False
.
Aus Sicht von Python hat sich nichts geändert. Wir haben immer noch nur zwei boolesche Werte, und zwar True
und False
. Um einen dieser Werte in Python einer Variablen zuzuweisen, musst du ihn genau so eingeben, wie du ihn siehst (mit großem Anfangsbuchstaben und ohne Anführungszeichen).
>>>
exists
=
True
>>>
>>>
exists
True
>>>
>>>
exists
=
true
Traceback
(
most
recent
call
last
):
File
"<stdin>"
,
line
1
,
in
<
module
>
NameError
:
name
'true'
is
not
defined
>>>
Wie du in diesem Beispiel sehen kannst, ist es ganz einfach. Anhand des Echtzeit-Feedbacks des Python-Interpreters können wir sehen, dass die Verwendung eines kleinen t nicht funktioniert, wenn wir versuchen, den Wert von True
einer Variablen zuzuweisen.
Hier sind ein paar weitere Beispiele für die Verwendung boolescher Ausdrücke im Python-Interpreter.
>>>
True
and
True
True
>>>
>>>
True
or
False
True
>>>
>>>
False
or
False
False
>>>
Im nächsten Beispiel werden dieselben Bedingungen ausgewertet, wobei den Variablen boolesche Werte zugewiesen werden.
>>>
value1
=
True
>>>
value2
=
False
>>>
>>>
value1
and
value2
False
>>>
>>>
value1
or
value2
True
>>>
Beachte, dass boolesche Ausdrücke auch nicht auf zwei Objekte beschränkt sind.
>>>
value3
=
True
>>>
value4
=
True
>>>
>>>
value1
and
value2
and
value3
and
value4
False
>>>
>>>
value1
and
value3
and
value4
True
>>>
Wenn du Informationen aus einem Netzwerkgerät extrahierst, ist es üblich, Boolesche Werte für eine schnelle Überprüfung zu verwenden. Ist die Schnittstelle ein gerouteter Port? Ist die Verwaltungsschnittstelle konfiguriert? Ist das Gerät erreichbar? Auch wenn es eine komplexe Operation gibt, um jede dieser Fragen zu beantworten, wird das Ergebnis als True
oder False
gespeichert.
Die Antwort auf diese Fragen wäre: Ist die Schnittstelle ein Switched Port oder ist das Gerät nicht erreichbar? Es wäre nicht sinnvoll, für jede Frage Variablen oder Objekte zu haben, aber wir könnten den not
Operator verwenden, da wir wissen, dass die Operation not
den Kehrwert eines booleschen Wertes zurückgibt.
Schauen wir uns die Verwendung von not
in einem Beispiel an.
>>>
not
False
>>>
True
>>>
>>>
is_layer3
=
True
>>>
not
is_layer3
False
>>>
In diesem Beispiel gibt es eine Variable namens is_layer3
. Sie ist auf True
gesetzt und zeigt an, dass die Schnittstelle ein Layer 3 Port ist. Wenn wir die not
von is_layer3
nehmen, wissen wir, ob es sich um einen Layer 2 Port handelt.
Wir werden uns später in diesem Kapitel mit den Conditionals (if-else
Anweisungen) beschäftigen, aber je nach der benötigten Logik musst du vielleicht wissen, ob eine Schnittstelle tatsächlich Layer 3 ist. Wenn dies der Fall ist, würdest du etwas wie if is_layer3:
verwenden, aber wenn du eine Aktion ausführen musst, wenn die Schnittstelle Layer 2 ist, würdest du if not is_layer3:
verwenden.
In werden zusätzlich zu den Operanden and
und or
die Ausdrücke equal to ==
und does not equal to !=
verwendet, um ein boolesches Objekt zu erzeugen. Mit diesen Ausdrücken kannst du einen Vergleich oder eine Prüfung durchführen, um festzustellen, ob zwei oder mehr Objekte gleich sind (oder nicht).
>>>
True
==
True
True
>>>
>>>
True
!=
False
True
>>>
>>>
'network'
==
'network'
True
>>>
>>>
'network'
==
'no_network'
False
>>>
Nachdem wir einen kurzen Blick auf die Arbeit mit booleschen Objekten, Operanden und Ausdrücken geworfen haben, sind wir bereit für die Arbeit mit Python-Listen.
Lernen, wie man Python-Listen verwendet
Du hattest eine kurze Einführung in Listen, als wir die eingebauten String-Methoden namens join()
und split()
behandelt haben. Listen werden jetzt etwas ausführlicher behandelt.
Listen sind ein Objekttyp namens list
und sind auf ihrer grundlegendsten Ebene eine geordnete Folge von Objekten. Die Beispiele vom Anfang des Kapitels, als wir uns die Methode join()
mit Strings angesehen haben, werden hier noch einmal aufgeführt, um zu zeigen, wie man eine Liste erstellt. In diesen Beispielen ging es um Listen mit Strings, aber es ist auch möglich, Listen mit anderen Datentypen zu erstellen, wie wir gleich sehen werden.
>>>
hostnames
=
[
'r1'
,
'r2'
,
'r3'
,
'r4'
,
'r5'
]
>>>
commands
=
[
'config t'
,
'interface Ethernet1/1'
,
'shutdown'
]
>>>
Das nächste Beispiel zeigt eine Liste von Objekten, bei der jedes Objekt ein anderer Datentyp ist!
>>>
new_list
=
[
'router1'
,
False
,
5
]
>>>
>>>
(
new_list
)
[
'router1'
,
False
,
5
]
>>>
Du weißt jetzt, dass Listen eine geordnete Folge von Objekten sind und von Klammern umschlossen werden. Eine der häufigsten Aufgaben, wenn du mit Listen arbeitest, ist der Zugriff auf ein einzelnes Element der Liste.
Lass uns eine neue Liste von Schnittstellen erstellen und zeigen, wie man ein einzelnes Element einer Liste ausdruckt.
>>>
interfaces
=
[
'Eth1/1'
,
'Eth1/2'
,
'Eth1/3'
,
'Eth1/4'
]
>>>
Die Liste wird erstellt und nun werden drei Elemente der Liste nacheinander gedruckt.
>>>
(
interfaces
[
0
])
Eth1
/
1
>>>
>>>
(
interfaces
[
1
])
Eth1
/
2
>>>
>>>
(
interfaces
[
2
])
Eth1
/
3
>>>
Um auf die einzelnen Elemente innerhalb einer Liste zuzugreifen, verwendest du den Indexwert des Elements, der in Klammern eingeschlossen ist. Es ist wichtig zu wissen, dass der Index bei 0 beginnt und bei der "Länge der Liste minus 1" endet. Das bedeutet in unserem Beispiel, dass du für den Zugriff auf das erste Element interfaces[0]
und für den Zugriff auf das letzte Element interfaces[3]
verwendest.
Im Beispiel können wir leicht sehen, dass die Länge der Liste vier ist, aber was ist, wenn du die Länge der Liste nicht kennst?
Glücklicherweise bietet Python eine eingebaute Funktion namens len()
, die dabei hilft.
>>>
len
(
interfaces
)
4
>>>
Eine andere Möglichkeit, auf das letzte Element in einer Liste zuzugreifen, ist: list[-1]
.
>>>
interfaces
[
-
1
]
'Eth1/4'
>>>
Hinweis
Auf werden die Begriffe Funktion und Methode oft synonym verwendet, aber bis jetzt haben wir uns hauptsächlich mit Methoden und nicht mit Funktionen beschäftigt. Der kleine Unterschied ist, dass eine Funktion aufgerufen wird, ohne auf ein übergeordnetes Objekt zu verweisen. Wie du gesehen hast, wird eine eingebaute Methode eines Objekts mit der Syntax object
aufgerufen.method()
, und wenn du Funktionen wie len()
verwendest, rufst du sie direkt auf. Trotzdem ist es sehr üblich, eine Methode als Funktion zu bezeichnen.
Eingebaute Methoden von Python-Listen verwenden
Um die verfügbaren eingebauten Methoden für Listen anzuzeigen, wird die Funktion dir()
genauso verwendet, wie wir es zuvor bei der Arbeit mit String-Objekten gezeigt haben. Du kannst eine beliebige Variable erstellen, die eine Liste ist, oder den formalen Datentypnamen list
verwenden und ihn als Argument an dir()
übergeben. Wir werden die Liste interfaces
dafür verwenden.
>>>
dir
(
interfaces
)
[
'append'
,
'count'
,
'extend'
,
'index'
,
'insert'
,
'pop'
,
'remove'
,
'reverse'
,
'sort'
]
Hinweis
Um die Ausgabe sauber zu halten und das Beispiel zu vereinfachen, haben wir alle Objekte, die mit Unterstrichen beginnen und enden, entfernt.
Werfen wir einen Blick auf einige dieser eingebauten Methoden.
Verwendung der Methode append()
Das Tolle an diesen Methodennamen ist, wie du noch sehen wirst, dass sie für Menschen lesbar und größtenteils intuitiv sind. Die Methode append()
wird verwendet, um ein Element an eine bestehende Liste anzuhängen.
Das wird im nächsten Beispiel gezeigt, aber beginnen wir mit der Erstellung einer leeren Liste. Das tust du, indem du einem Objekt leere Klammern zuweist.
>>>
vendors
=
[]
>>>
Fügen wir dieser Liste die Adresse vendors
hinzu.
>>>
vendors
.
append
(
'arista'
)
>>>
>>>
(
vendors
)
[
'arista'
]
>>>
>>>
vendors
.
append
(
'cisco'
)
>>>
>>>
(
vendors
)
[
'arista'
,
'cisco'
]
>>>
Du siehst, dass append()
das Element an die letzte Position in der Liste setzt. Im Gegensatz zu vielen anderen Methoden für Strings gibt diese Methode nichts zurück, sondern verändert die ursprüngliche Variable bzw. das Objekt.
Verwendung der Methode insert()
Eher als nur ein Element an eine Liste anzuhängen, musst du vielleicht ein Element an einer bestimmten Stelle einfügen. Dies geschieht mit der Methode insert()
.
Um insert()
zu verwenden, musst du ihm zwei Argumente übergeben. Das erste Argument ist die Position bzw. der Index, an dem das neue Element gespeichert wird, und das zweite Argument ist das eigentliche Objekt, das in die Liste eingefügt wird.
Im nächsten Beispiel geht es darum, eine Liste von Befehlen zu erstellen.
>>>
commands
=
[
'interface Eth1/1'
,
'ip address 1.1.1.1/32'
]
Hinweis
Zur Erinnerung: Die Befehle in diesen Beispielen sind allgemein gehalten und beziehen sich nicht auf einen bestimmten Anbieter oder eine bestimmte Plattform.
Nehmen wir nun an, dass wir der Liste ['interface Eth1/1', 'ip address 1.1.1.1/32']
zwei weitere Befehle hinzufügen müssen. Der Befehl, der als erstes Element hinzugefügt werden muss, ist config t
und derjenige, der kurz vor der IP-Adresse hinzugefügt werden muss, ist no switchport
.
>>>
commands
=
[
'interface Eth1/1'
,
'ip address 1.1.1.1/32'
]
>>>
>>>
commands
.
insert
(
0
,
'config t'
)
>>>
>>>
(
commands
)
[
'config t'
,
'interface Eth1/1'
,
'ip address 1.1.1.1/32'
]
>>>
>>>
commands
.
insert
(
2
,
'no switchport'
)
>>>
>>>
(
commands
)
[
'config t'
,
'interface Eth1/1'
,
'no switchport'
,
'ip address 1.1.1.1/32'
]
>>>
Verwendung der Methode count()
Wenn du eine Bestandsaufnahme der Gerätetypen im gesamten Netzwerk durchführst, kannst du eine Liste erstellen, die mehr als ein Objekt innerhalb einer Liste enthält. Um das Beispiel von vorhin zu erweitern, könnte deine Liste wie folgt aussehen:
>>>
vendors
=
[
'cisco'
,
'cisco'
,
'juniper'
,
'arista'
,
'cisco'
,
'hp'
,
'cumulus'
,
'arista'
,
'cisco'
]
>>>
Mit der Methode count()
kannst du zählen, wie viele Instanzen eines bestimmten Objekts gefunden werden. In unserem Beispiel lässt sich so feststellen, wie viele Cisco- oder Arista-Geräte es in der Umgebung gibt.
>>>
vendors
.
count
(
'cisco'
)
4
>>>
>>>
vendors
.
count
(
'arista'
)
2
>>>
Beachte, dass count()
ein int
oder eine Ganzzahl zurückgibt und das bestehende Objekt nicht verändert, wie insert()
, append()
und einige andere, die in den nächsten Beispielen behandelt werden.
Verwendung der Methoden pop() und index()
Die meisten der bisherigen Methoden haben entweder das ursprüngliche Objekt verändert oder etwas zurückgegeben. pop()
macht beides.
>>>
hostnames
=
[
'r1'
,
'r2'
,
'r3'
,
'r4'
,
'r5'
]
>>>
Das vorangegangene Beispiel enthält eine Liste von Hostnamen. Lass uns r5
löschen, denn dieses Gerät wurde gerade aus dem Netzwerk genommen.
>>>
hostnames
.
pop
()
'r5'
>>>
>>>
(
hostnames
)
[
'r1'
,
'r2'
,
'r3'
,
'r4'
]
>>>
Wie du siehst, wird das gepoppte Element zurückgegeben und die ursprüngliche Liste wird ebenfalls geändert.
Du solltest auch bemerkt haben, dass kein Element- oder Indexwert übergeben wurde. Du kannst also sehen, dass pop()
standardmäßig das letzte Element in der Liste ausgibt.
Was, wenn du "r2"
ausblenden musst? Es stellt sich heraus, dass du einen Indexwert des Elements, das du ausblenden möchtest, übergeben musst, um ein Element auszublenden, das nicht das letzte Element ist. Aber wie findest du den Indexwert eines bestimmten Elements? An dieser Stelle kommt die Methode index()
ins Spiel.
Um den Indexwert eines bestimmten Elements zu finden, verwendest du die Methode index()
.
>>>
hostnames
.
index
(
'r2'
)
1
>>>
Hier siehst du, dass der Index des Wertes "r2"
1
ist.
Um "r2"
zu öffnen, müssen wir also Folgendes tun:
>>>
hostnames
.
pop
(
1
)
'r2'
>>>
>>>
(
hostnames
)
[
'r1'
,
'r3'
,
'r4'
]
>>>
Es hätte auch in einem einzigen Schritt gemacht werden können:
hostnames.pop(hostnames.index('r2'))
Verwendung der Methode sort()
Die letzte eingebaute Methode, die wir uns für Listen ansehen werden, ist sort()
. Wie du vielleicht schon vermutet hast, wird sort()
verwendet, um eine Liste zu sortieren.
Im nächsten Beispiel haben wir eine Liste von IP-Adressen in nicht-sequentieller Reihenfolge, und sort()
wird verwendet, um das ursprüngliche Objekt zu aktualisieren. Beachte, dass nichts zurückgegeben wird.
>>>
available_ips
[
'10.1.1.1'
,
'10.1.1.9'
,
'10.1.1.8'
,
'10.1.1.7'
,
'10.1.1.4'
]
>>>
>>>
>>>
available_ips
.
sort
()
>>>
>>>
available_ips
[
'10.1.1.1'
,
'10.1.1.4'
,
'10.1.1.7'
,
'10.1.1.8'
,
'10.1.1.9'
]
Hinweis
Beachte, dass die Sortierung aus dem vorherigen Beispiel IP-Adressen als Strings sortiert hat.
In fast allen Beispielen, die wir mit Listen behandelt haben, waren die Elemente der Liste vom gleichen Objekttyp, d.h. sie waren alle Befehle, IP-Adressen, Anbieter oder Hostnamen. Es wäre jedoch kein Problem, wenn du eine Liste erstellen müsstest, die verschiedene Arten von Kontextobjekten (oder sogar Datentypen) enthält.
Ein gutes Beispiel für die Speicherung verschiedener Objekte ist die Speicherung von Informationen über ein bestimmtes Gerät. Vielleicht möchtest du den Hostnamen, den Hersteller und das Betriebssystem speichern. Eine Liste zum Speichern dieser Geräteattribute würde etwa so aussehen:
>>>
device
=
[
'router1'
,
'juniper'
,
'12.2'
]
>>>
Da die Elemente einer Liste durch eine ganze Zahl indiziert werden, musst du dir merken, welcher Index welchem bestimmten Attribut zugeordnet ist. In diesem Beispiel mag das nicht schwer sein, aber was wäre, wenn es 10, 20 oder 100 Attribute gäbe, auf die du zugreifen musst? Selbst wenn es Zuordnungen gäbe, könnte es extrem schwierig werden, da Listen geordnet sind. Das Ersetzen oder Aktualisieren eines Elements in einer Liste müsste sehr sorgfältig durchgeführt werden.
Wäre es nicht schön, wenn du die einzelnen Elemente einer Liste mit ihrem Namen referenzieren könntest und dich nicht so sehr um die Reihenfolge der Elemente kümmern müsstest? Anstatt also den Hostnamen mit device[0]
aufzurufen, könntest du ihn wie device['hostname']
aufrufen.
Wie es der Zufall will, kommen genau hier die Python-Wörterbücher ins Spiel, die wir als nächsten Datentyp in diesem Kapitel behandeln.
Lernen, Python-Wörterbücher zu verwenden
Wir haben nun einige der gebräuchlichsten Datentypen wie Strings, Integer, Booleans und Listen besprochen, die es in allen Programmiersprachen gibt. In diesem Abschnitt werfen wir einen Blick auf das Wörterbuch, das ein Python-spezifischer Datentyp ist. In anderen Sprachen sind sie als assoziative Arrays, Maps oder Hash Maps bekannt.
Wörterbücher sind ungeordnete Listen, auf deren Werte über Namen, die so genannten Schlüssel, und nicht über einen Index (Integer) zugegriffen wird. Wörterbücher sind einfach eine Sammlung von ungeordneten Schlüssel-Wert-Paaren, die Items genannt werden.
Mit diesem Beispiel haben wir den vorherigen Abschnitt über Listen abgeschlossen:
>>>
device
=
[
'router1'
,
'juniper'
,
'12.2'
]
>>>
Wenn auf diesem Beispiel aufbaut und die Liste device
in ein Wörterbuch umwandelt, würde es so aussehen:
>>>
device
=
{
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
,
'os'
:
'12.1'
}
>>>
Die Notation für ein Wörterbuch ist eine geschweifte Klammer ({
), dann Schlüssel, Doppelpunkt und Wert, für jedes Schlüssel-Wert-Paar getrennt durch ein Komma (,
), und dann schließt es mit einer weiteren geschweiften Klammer (}
) ab.
Sobald das dict
Objekt erstellt ist, kannst du auf den gewünschten Wert zugreifen, indem du dict
[key
].
>>>
(
device
[
'hostname'
])
router1
>>>
>>>
(
device
[
'os'
])
12.1
>>>
>>>
(
device
[
'vendor'
])
juniper
>>>
Wie bereits erwähnt, sind Wörterbücher nicht geordnet - im Gegensatz zu Listen, die geordnet sind. Das siehst du daran, dass die Schlüssel-Wert-Paare von device
im folgenden Beispiel in einer anderen Reihenfolge ausgedruckt werden, als sie ursprünglich erstellt wurden.
>>>
(
device
)
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
Es ist erwähnenswert, dass es möglich ist, das gleiche Wörterbuch aus dem vorherigen Beispiel auf verschiedene Arten zu erstellen. Diese werden in den nächsten beiden Codeblöcken gezeigt.
>>>
device
=
{}
>>>
device
[
'hostname'
]
=
'router1'
>>>
device
[
'vendor'
]
=
'juniper'
>>>
device
[
'os'
]
=
'12.1'
>>>
>>>
(
device
)
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
>>>
device
=
dict
(
hostname
=
'router1'
,
vendor
=
'juniper'
,
os
=
'12.1'
)
>>>
>>>
(
device
)
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
Eingebaute Methoden von Python-Wörterbüchern verwenden
Python Wörterbücher haben ein paar eingebaute Methoden, die es wert sind, dass wir sie uns ansehen.
Genau wie bei den anderen Datentypen schauen wir uns zunächst alle verfügbaren Methoden an, abzüglich derer, die mit Unterstrichen beginnen und enden.
>>>
dir
(
dict
)
[
'clear'
,
'copy'
,
'fromkeys'
,
'get'
,
'has_key'
,
'items'
,
'iteritems'
,
'iterkeys'
,
'itervalues'
,
'keys'
,
'pop'
,
'popitem'
,
'setdefault'
,
'update'
,
'values'
,
'viewitems'
,
'viewkeys'
,
'viewvalues'
]
>>>
Verwendung der Methode get()
Wir haben bereits gesehen, wie man auf ein Schlüssel-Wert-Paar eines Wörterbuchs zugreift, indem man die Notation dict
[key
]. Das ist ein sehr beliebter Ansatz, allerdings mit einer Einschränkung. Wenn der Schlüssel nicht existiert, wird ein KeyError ausgelöst, da der Schlüssel nicht existiert.
>>>
device
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
>>>
(
device
[
'model'
])
Traceback
(
most
recent
call
last
):
File
"<stdin>"
,
line
1
,
in
<
module
>
KeyError
:
'model'
>>>
Die Methode get()
bietet einen anderen Ansatz, der wohl sicherer ist, es sei denn, du willst einen Fehler auslösen.
Schauen wir uns zunächst ein Beispiel mit get()
an, wenn der Schlüssel existiert.
>>>
device
.
get
(
'hostname'
)
'router1'
>>>
Und nun ein Beispiel, wenn ein Schlüssel nicht existiert:
>>>
device
.
get
(
'model'
)
>>>
Wie du aus dem vorangegangenen Beispiel ersehen kannst, wird absolut nichts zurückgegeben, wenn der Schlüssel nicht im Wörterbuch enthalten ist. Aber es kommt noch besser. get()
ermöglicht es dem Benutzer auch, einen Wert zu definieren, der zurückgegeben wird, wenn der Schlüssel nicht existiert! Schauen wir uns das mal an.
>>>
device
.
get
(
'model'
,
False
)
False
>>>
>>>
device
.
get
(
'model'
,
'DOES NOT EXIST'
)
'DOES NOT EXIST'
>>>
>>>
>>>
device
.
get
(
'hostname'
,
'DOES NOT EXIST'
)
'router1'
>>>
Ziemlich einfach, oder? Du siehst, dass der Wert rechts vom Schlüssel nur dann zurückgegeben wird, wenn der Schlüssel im Wörterbuch nicht vorhanden ist.
Verwendung der Methoden keys() und values()
Dictionaries sind eine ungeordnete Liste von Schlüssel-Wert-Paaren. Mit den eingebauten Methoden keys()
und values()
hast du die Möglichkeit, auf die Listen der einzelnen Wörterbücher zuzugreifen. Beim Aufruf jeder Methode erhältst du eine Liste der Schlüssel bzw. Werte zurück, aus denen das Wörterbuch besteht.
>>>
device
.
keys
()
[
'os'
,
'hostname'
,
'vendor'
]
>>>
>>>
device
.
values
()
[
'12.1'
,
'router1'
,
'juniper'
]
>>>
Verwendung der Methode pop()
Wir haben eine eingebaute Methode namens pop()
schon früher im Kapitel gesehen, als wir uns mit Listen beschäftigt haben. Zufälligerweise haben auch Wörterbücher eine pop()
Methode, die ganz ähnlich verwendet wird. Anstatt der Methode einen Indexwert zu übergeben, wie wir es bei Listen getan haben, übergeben wir ihr einen Schlüssel.
>>>
device
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
>>>
device
.
pop
(
'vendor'
)
'juniper'
>>>
>>>
device
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
}
>>>
Im Beispiel kannst du sehen, dass pop()
das ursprüngliche Objekt verändert und den Wert zurückgibt, der gepoppt wird.
Verwendung der update()-Methode
Es kann der Zeitpunkt kommen, an dem du Geräteinformationen wie Hostname, Hersteller und Betriebssystem extrahierst und sie in einem Python-Wörterbuch speicherst. Später musst du dann ein weiteres Wörterbuch hinzufügen oder aktualisieren, das andere Attribute über ein Gerät enthält.
Im Folgenden siehst du zwei verschiedene Wörterbücher.
>>>
device
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
}
>>>
>>>
oper
=
dict
(
cpu
=
'5%'
,
memory
=
'10%'
)
>>>
>>>
oper
{
'cpu'
:
'5%'
,
'memory'
:
'10%'
}
>>>
Die Methode update()
kann nun verwendet werden, um eines der Wörterbücher zu aktualisieren, d.h. ein Wörterbuch dem anderen hinzuzufügen. Fügen wir also oper
zu device
hinzu.
>>>
device
.
update
(
oper
)
>>>
>>>
(
device
)
{
'os'
:
'12.1'
,
'hostname'
:
'router1'
,
'vendor'
:
'juniper'
,
'cpu'
:
'5%'
,
'memory'
:
'10%'
}
>>>
Beachte, dass bei update()
nichts zurückgegeben wurde. Nur das zu aktualisierende Objekt, oder in diesem Fall device
, wurde geändert.
Verwendung der Methode items()
Wenn du mit Wörterbüchern arbeitest, wirst du sehen, dass items()
häufig verwendet wird.
Wir haben gesehen, wie man mit get()
auf einzelne Werte zugreift und wie man mit den Methoden keys()
und values()
eine Liste aller Schlüssel und Werte erhält.
Wie wäre es, wenn du gleichzeitig auf ein bestimmtes Schlüssel-Wert-Paar eines bestimmten Eintrags zugreifst oder über alle Einträge iterierst? Wenn du durch ein Wörterbuch iterieren (oder eine Schleife bilden) und gleichzeitig auf Schlüssel und Werte zugreifen musst, ist items()
ein großartiges Werkzeug für deinen Werkzeuggürtel.
Hinweis
Eine formale Einführung in Schleifen gibt es später in diesem Kapitel, aber da items()
häufig mit einer for
Schleife verwendet wird, zeigen wir hier ein Beispiel mit einer for
Schleife. Solange Schleifen noch nicht formell behandelt werden, ist es wichtig zu wissen, dass du bei der Verwendung der for
Schleife mit items()
gleichzeitig auf einen Schlüssel und einen Wert eines bestimmten Elements zugreifen kannst.
Das einfachste Beispiel ist das Durchlaufen eines Wörterbuchs mit einer for
Schleife und das Ausdrucken des Schlüssels und des Wertes für jedes Element. Auch hier werden Schleifen später im Kapitel behandelt, aber dies soll nur eine grundlegende Einführung in items()
sein.
>>>
for
key
,
value
in
device
.
items
():
...
(
key
+
': '
+
value
)
...
os
:
12.1
hostname
:
router1
vendor
:
juniper
cpu
:
5
%
memory
:
10
%
>>>
Es lohnt sich, darauf hinzuweisen, dass key
und value
in der for
Schleife benutzerdefiniert sind und alles sein können, wie du im folgenden Beispiel sehen kannst.
>>>
for
my_attribute
,
my_value
,
in
device
.
items
():
...
(
my_attribute
+
': '
+
my_value
)
...
os
:
12.1
hostname
:
router1
vendor
:
juniper
cpu
:
5
%
memory
:
10
%
>>>
Wir haben jetzt die wichtigsten Datentypen in Python kennengelernt. Du solltest jetzt wissen, wie du mit Zeichenketten, Zahlen, Booleschen Werten, Listen und Wörterbüchern arbeiten kannst. Wir werden nun eine kurze Einführung in zwei weitere Datentypen geben, nämlich Sets und Tupel, die etwas fortgeschrittener sind als die zuvor behandelten Datentypen.
Lernen über Python Mengen und Tupel
Die nächsten beiden Datentypen müssen nicht unbedingt in einer Einführung in Python behandelt werden, aber wie wir zu Beginn des Kapitels gesagt haben, wollten wir sie der Vollständigkeit halber kurz zusammenfassen. Diese Datentypen sind set
und tuple
.
Wenn du Listen verstehst, wirst du auch Sets verstehen. Mengen sind eine Liste von Elementen, aber es kann nur ein einziges Element in einer Menge geben, und außerdem können Elemente nicht indiziert werden (oder durch einen Indexwert wie eine Liste angesprochen werden).
Du kannst sehen, dass ein Set wie eine Liste aussieht, aber von set()
umgeben ist:
>>>
vendors
=
set
([
'arista'
,
'cisco'
,
'arista'
,
'cisco'
,
'juniper'
,
'cisco'
])
>>>
Das vorangegangene Beispiel zeigt, wie eine Menge mit mehreren gleichen Elementen erstellt wird. Ein ähnliches Beispiel haben wir verwendet, als wir die Methode count()
für Listen verwenden wollten, um zu zählen, wie viele Exemplare eines bestimmten Anbieters es gibt. Was aber, wenn du nur wissen willst, wie viele und welche Anbieter es in einer Umgebung gibt? Dann kannst du eine Menge verwenden.
>>>
vendors
=
set
([
'arista'
,
'cisco'
,
'arista'
,
'cisco'
,
'juniper'
,
'cisco'
])
>>>
>>>
vendors
set
([
'cisco'
,
'juniper'
,
'arista'
])
>>>
>>>
len
(
vendors
)
3
>>>
Beachte, dass vendors
nur drei Elemente enthält.
Das nächste Beispiel zeigt, was passiert, wenn du versuchst, auf ein Element innerhalb einer Menge zuzugreifen. Um auf Elemente in einer Menge zuzugreifen, musst du sie durchlaufen, zum Beispiel mit einer for
Schleife.
>>>
vendors
[
0
]
Traceback
(
most
recent
call
last
):
File
"<stdin>"
,
line
1
,
in
<
module
>
TypeError
:
'set'
object
does
not
support
indexing
>>>
Es bleibt dem Leser überlassen, die eingebauten Methoden für sets
zu erkunden.
Das Tupel ist ein interessanter Datentyp und auch am besten zu verstehen, wenn man es mit einer Liste vergleicht. Es ist wie eine Liste, kann aber nicht verändert werden. Wir haben gesehen, dass Listen veränderbar sind, d.h. sie können aktualisiert, erweitert und verändert werden. Tupel hingegen sind unveränderlich und können nach ihrer Erstellung nicht mehr geändert werden. Wie bei Listen ist es auch bei Tupeln möglich, auf einzelne Elemente zuzugreifen.
>>>
description
=
tuple
([
'ROUTER1'
,
'PORTLAND'
])
>>>
>>>
>>>
description
(
'ROUTER1'
,
'PORTLAND'
)
>>>
>>>
>>>
(
description
[
0
])
ROUTER1
>>>
Und sobald das variable Objekt description
erstellt ist, kann es nicht mehr geändert werden. Du kannst keines der Elemente ändern oder neue Elemente hinzufügen. Das kann hilfreich sein, wenn du ein Objekt erstellen musst und sicherstellen willst, dass keine andere Funktion oder ein anderer Benutzer es ändern kann. Das nächste Beispiel zeigt, dass du ein Tupel nicht verändern kannst und dass ein Tupel keine Methoden wie update()
oder append()
hat.
>>>
description
[
1
]
=
'trying to modify one'
Traceback
(
most
recent
call
last
):
File
"<stdin>"
,
line
1
,
in
<
module
>
TypeError
:
'tuple'
object
does
not
support
item
assignment
>>>
>>>
dir
(
tuple
)
[
'__add__'
,
'__class__'
,
'__contains__'
,
'__delattr__'
,
'__doc__'
,
'__eq__'
,
'__format__'
,
'__ge__'
,
'__getattribute__'
,
'__getitem__'
,
'__getnewargs__'
,
'__getslice__'
,
'__gt__'
,
'__hash__'
,
'__init__'
,
'__iter__'
,
'__le__'
,
'__len__'
,
'__lt__'
,
'__mul__'
,
'__ne__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__rmul__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'count'
,
'index'
]
>>>
Um den Vergleich zwischen Listen, Tupeln und Mengen zu erleichtern, haben wir diese Übersicht zusammengestellt:
-
Listen sind veränderbar, sie können geändert werden, auf einzelne Elemente kann direkt zugegriffen werden, und sie können doppelte Werte haben.
-
Mengen sind veränderbar, sie können geändert werden, auf einzelne Elemente kann nicht direkt zugegriffen werden und sie können keine doppelten Werte haben.
-
Tupel sind unveränderlich, d.h. sie können nicht aktualisiert oder verändert werden, wenn sie einmal erstellt wurden, auf einzelne Elemente kann direkt zugegriffen werden und sie können doppelte Werte haben.
Damit ist der Abschnitt über Datentypen abgeschlossen. Du solltest jetzt ein gutes Verständnis der behandelten Datentypen haben, darunter Zeichenketten, Zahlen, Boolesche Werte, Listen, Wörterbücher, Mengen und Tupel.
Jetzt schalten wir einen Gang höher und beginnen mit der Verwendung von Konditionalen(if then logic) in Python.
Hinzufügen bedingter Logik zu deinem Code
Unter solltest du jetzt ein solides Verständnis für die Arbeit mit verschiedenen Arten von Objekten haben . Die Schönheit des Programmierens kommt ins Spiel, wenn du anfängst, diese Objekte zu nutzen, indem du in deinem Code Logik anwendest, z. B. eine Aufgabe ausführst oder ein Objekt erstellst, wenn eine bestimmte Bedingung wahr (oder nicht wahr!) ist.
Bedingungen sind ein wichtiger Teil der Logik in deinem Code, und das Verständnis von Bedingungen beginnt mit dem Verständnis der if
Anweisung.
Beginnen wir mit einem einfachen Beispiel, das den Wert einer Zeichenkette überprüft.
>>>
hostname
=
'NYC'
>>>
>>>
if
hostname
==
'NYC'
:
...
(
'The hostname is NYC'
)
...
The
hostname
is
NYC
>>>
Selbst wenn du Python nicht verstehst, bevor du mit diesem Kapitel beginnst, ist es wahrscheinlich, dass du weißt, was in dem vorherigen Beispiel gemacht wurde. Das ist ein Teil des Wertes der Arbeit in Python - es versucht, so lesbar wie möglich zu sein.
Es gibt zwei Dinge, die du in Bezug auf die Syntax beachten musst, wenn du mit einer if
Anweisung arbeitest. Erstens: Alle if
Anweisungen enden mit einem Doppelpunkt (:
). Zweitens ist der Code, der ausgeführt wird , wenn deine Bedingung wahr ist, Teil eines eingerückten Codeblocks - der Einzug sollte vier Leerzeichen betragen, aber das spielt technisch gesehen keine Rolle. Alles, was technisch wichtig ist, ist, dass du konsistent bist.
Tipp
Allgemein ist es gute Praxis, beim Schreiben von Python-Code einen Einzug von vier Leerzeichen zu verwenden. Dies wird von der Python-Gemeinschaft weitgehend als Norm für das Schreiben von idiomatischem Python-Code akzeptiert. Das macht das Teilen von Code und die Zusammenarbeit viel einfacher.
Das nächste Beispiel zeigt einen vollständig eingerückten Codeblock.
>>>
if
hostname
==
'NYC'
:
...
(
'This hostname is NYC'
)
...
(
len
(
hostname
))
...
(
'The End.'
)
...
This
hostname
is
NYC
3
The
End
.
>>>
Da du nun weißt, wie du eine grundlegende if
Anweisung erstellen kannst, wollen wir sie erweitern.
Was wäre, wenn du prüfen müsstest, ob der Hostname zusätzlich zu "NYC" auch "NJ" lautet? Um dies zu erreichen, führen wir die else if-Anweisung ein, oder elif
.
>>>
hostname
=
'NJ'
>>>
>>>
if
hostname
==
'NYC'
:
...
(
'This hostname is NYC'
)
...
elif
hostname
==
'NJ'
:
...
(
'This hostname is NJ'
)
...
This
hostname
is
NJ
>>>
Sie ist der Anweisung if
sehr ähnlich, da sie immer noch mit einem Doppelpunkt enden muss und der zugehörige Codeblock, der ausgeführt werden soll, eingerückt werden muss. Du solltest auch sehen können, dass die Anweisung elif
an der Anweisung if
ausgerichtet werden muss.
Was ist, wenn NYC
und NJ
die einzigen gültigen Hostnamen sind, du aber einen Codeblock ausführen musst, wenn ein anderer Hostname verwendet wird? In diesem Fall verwenden wir die Anweisung else
.
>>>
hostname
=
'DEN_CO'
>>>
>>>
if
hostname
==
'NYC'
:
...
(
'This hostname is NYC'
)
...
elif
hostname
==
'NJ'
:
...
(
'This hostname is NJ'
)
...
else
:
...
(
'UNKNOWN HOSTNAME'
)
...
UNKNOWN
HOSTNAME
>>>
Die Verwendung von else
unterscheidet sich nicht von if
und elif
. Sie benötigt einen Doppelpunkt (:
) und einen eingerückten Codeblock darunter, um ausgeführt zu werden.
Wenn Python bedingte Anweisungen ausführt, wird der bedingte Block verlassen, sobald es eine Übereinstimmung gibt. Wenn zum Beispiel hostname
gleich 'NYC'
wäre, gäbe es eine Übereinstimmung in der ersten Zeile des bedingten Blocks, die print
Anweisung print('This hostname is NYC')
würde ausgeführt und dann würde der Block verlassen werden (keine weiteren elif
oder else
würden ausgeführt).
Im Folgenden findest du ein Beispiel für einen Fehler, der bei einem Einrückungsfehler auftritt. Das Beispiel hat zusätzliche Leerzeichen vor elif
, die dort nicht sein sollten.
>>>
if
hostname
==
'NYC'
:
...
(
'This hostname is NYC'
)
...
elif
hostname
==
'NJ'
:
File
"<stdin>"
,
line
3
elif
hostname
==
'NJ'
:
^
IndentationError
:
unindent
does
not
match
any
outer
indentation
level
>>>
Und hier ist ein Beispiel für einen Fehler, der durch einen fehlenden Doppelpunkt verursacht wird.
>>>
if
hostname
==
'NYC'
File
"<stdin>"
,
line
1
if
hostname
==
'NYC'
^
SyntaxError
:
invalid
syntax
>>>
Der Punkt ist, selbst wenn du einen Tippfehler in deinem Code hast, wenn du gerade erst anfängst, mach dir keine Sorgen; du wirst ziemlich intuitive Fehlermeldungen sehen.
Du wirst in den kommenden Beispielen weitere Konditionale sehen, darunter auch das nächste, in dem das Konzept des Containments eingeführt wird.
Verständnis von Containment
Wenn wir auf von " Containment" sprechen, meinen wir damit die Möglichkeit zu prüfen, ob ein Objekt ein bestimmtes Element oder Objekt enthält. Wir werden uns die Verwendung von in
genauer ansehen und dabei auf dem aufbauen, was wir gerade mit Konditionalen gelernt haben.
Obwohl dieser Abschnitt nur in
behandelt, sollte man nicht unterschätzen, wie mächtig diese Funktion von Python ist.
Wenn wir die Variable vendors
verwenden, die in den vorherigen Beispielen verwendet wurde, wie würdest du dann überprüfen, ob ein bestimmter Verkäufer existiert? Eine Möglichkeit ist, die gesamte Liste zu durchlaufen und den gesuchten Verkäufer mit jedem Objekt zu vergleichen. Das ist sicherlich möglich, aber warum nicht einfach in
verwenden?
Die Verwendung von Containment ist nicht nur lesbar, sondern vereinfacht auch die Überprüfung, ob ein Objekt das hat, wonach du suchst.
>>>
vendors
=
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
]
>>>
>>>
'arista'
in
vendors
True
>>>
Du kannst sehen, dass die Syntax recht einfach ist und eine bool
zurückgegeben wird. Es ist erwähnenswert, dass diese Syntax eine weitere der Ausdrücke ist, die als idiomatischer Python-Code gilt.
Dies kann nun einen Schritt weiter gehen und in eine bedingte Anweisung eingefügt werden.
>>>
if
'arista'
in
vendors
:
...
(
'Arista is deployed.'
)
...
'Arista is deployed.'
>>>
Im nächsten Beispiel wird geprüft, ob ein Teil einer Zeichenkette in einer anderen Zeichenkette enthalten ist, im Vergleich zur Prüfung, ob ein Element in einer Liste enthalten ist. Die Beispiele zeigen einen einfachen booleschen Ausdruck und dann die Verwendung des Ausdrucks in einer bedingten Anweisung.
>>>
version
=
"CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M),
Version
16.3
.
1
,
RELEASE
"
>>>
>>>
"16.3.1"
in
version
True
>>>
>>>
if
"16.3.1"
in
version
:
...
(
"Version is 16.3.1!!"
)
...
Version
is
16.3
.
1
!!
>>>
>>>
Wie wir bereits erwähnt haben, ist Containment in Kombination mit Conditionals eine einfache, aber leistungsstarke Methode, um zu prüfen, ob ein Objekt oder ein Wert innerhalb eines anderen Objekts existiert. Wenn du gerade erst anfängst, kommt es häufig vor, dass du sehr lange und komplexe bedingte Anweisungen erstellst, aber eigentlich brauchst du eine effizientere Methode, um die Elemente eines bestimmten Objekts auszuwerten.
Eine dieser Möglichkeiten ist die Verwendung von Schleifen bei der Arbeit mit Objekten wie Listen und Wörterbüchern. Die Verwendung von Schleifen vereinfacht die Arbeit mit diesen Arten von Objekten. Das wird bald klarer werden, denn im nächsten Abschnitt werden Schleifen offiziell eingeführt.
Schleifen in Python verwenden
Unter sind wir endlich bei den Schleifen angekommen. Wenn Objekte weiter wachsen, vor allem solche, die viel größer sind als unsere bisherigen Beispiele, sind Schleifen absolut notwendig. Denke über Listen von Geräten, IP-Adressen, VLANs und Schnittstellen nach. Wir brauchen effiziente Möglichkeiten, um Daten zu durchsuchen oder die gleiche Operation für jedes Element in einer Datenmenge durchzuführen (als Beispiele). Hier zeigen Schleifen ihren Wert.
Wir behandeln zwei Haupttypen von Schleifen - die for
Schleife und die while
Schleife.
Aus der Sicht eines Netzwerkingenieurs, der sich mit der Automatisierung von Netzwerkgeräten und der allgemeinen Infrastruktur befasst, kannst du fast immer eine for
Schleife verwenden. Natürlich hängt es davon ab, was du genau machst, aber im Allgemeinen sind for
Schleifen in Python ziemlich genial, also heben wir sie uns für den Schluss auf.
Die while-Schleife verstehen
Die allgemeine Prämisse hinter einer while
Schleife ist, dass ein bestimmter Code ausgeführt wird , während eine Bedingung erfüllt ist. Im folgenden Beispiel wird die Variable counter
auf 1
gesetzt und dann, solange sie kleiner ist als 5
, wird die Variable gedruckt und dann um 1 erhöht.
Die erforderliche Syntax ähnelt der, die wir beim Erstellen von if-elif-else
Anweisungen verwendet haben. Die while
Anweisung wird mit einem Doppelpunkt (:
) abgeschlossen und der auszuführende Code wird ebenfalls um vier Leerzeichen eingerückt.
>>>
counter
=
1
>>>
>>>
while
counter
<
5
:
...
(
counter
)
...
counter
+=
1
...
1
2
3
4
>>>
Aus Sicht der Einführung ist dies alles, was wir über die while
Schleife behandeln werden, da wir die for
Schleife in den meisten Beispielen verwenden werden.
Die for-Schleife verstehen
for
Schleifen in Python sind großartig, denn wenn du sie verwendest, durchläufst du in der Regel eine Schleife über eine Reihe von Objekten, z. B. in einer Liste, einer Zeichenkette oder einem Wörterbuch. for
Schleifen in anderen Programmiersprachen erfordern immer die Angabe eines Index und eines Inkrementwertes, was in Python nicht der Fall ist.
Beginnen wir mit der sogenannten for-in oder for-each-Schleife, die in Python die häufigste Form der for
Schleife ist.
Wie in den vorangegangenen Abschnitten gehen wir zunächst auf einige grundlegende Beispiele ein.
Die erste besteht darin, jedes Objekt innerhalb einer Liste zu drucken. Wie du im folgenden Beispiel siehst, ist die Syntax einfach und ähnelt dem, was wir bei der Verwendung von Konditionalen und der while
Schleife gelernt haben. Die erste Anweisung oder der Beginn der for
Schleife muss mit einem Doppelpunkt (:
) enden und der auszuführende Code muss eingerückt werden.
>>>
vendors
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
]
>>>
>>>
for
vendor
in
vendors
:
...
(
'VENDOR: '
+
vendor
)
...
VENDOR
:
arista
VENDOR
:
juniper
VENDOR
:
big_switch
VENDOR
:
cisco
>>>
Wie bereits erwähnt, wird diese Art von for
Schleife oft als for-in oder for-each Schleife bezeichnet, weil du über jedes Element in einem bestimmten Objekt iterierst.
In diesem Beispiel ist der Name des Objekts vendor
völlig willkürlich und kann vom Benutzer festgelegt werden. Bei jeder Iteration ist vendor
gleich dem jeweiligen Element. In diesem Beispiel ist vendor
in der ersten Iteration gleich arista
, in der zweiten Iteration gleich juniper
, und so weiter.
Um zu zeigen, dass vendor
beliebig benannt werden kann, benennen wir es in network_vendor
um.
>>>
for
network_vendor
in
vendors
:
...
(
'VENDOR: '
+
network_vendor
)
...
VENDOR
:
arista
VENDOR
:
juniper
VENDOR
:
big_switch
VENDOR
:
cisco
>>>
Kombinieren wir nun ein paar der bisher gelernten Dinge mit Containment, Konditionalen und Schleifen.
Im nächsten Beispiel wird eine neue Liste von vendors
definiert. Eine davon ist ein großartiges Unternehmen, das aber nicht als Netzwerkanbieter geeignet ist! Dann wird approved_vendors
definiert, die im Grunde die richtigen oder zugelassenen Anbieter für einen bestimmten Kunden enthält. In diesem Beispiel werden die Anbieter in einer Schleife überprüft, um sicherzustellen, dass sie alle zugelassen sind, und falls nicht, wird eine entsprechende Meldung auf dem Terminal ausgegeben.
>>>
vendors
=
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
,
'oreilly'
]
>>>
>>>
approved_vendors
=
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
]
>>>
>>>
for
vendor
in
vendors
:
...
if
vendor
not
in
approved_vendors
:
...
(
'NETWORK VENDOR NOT APPROVED: '
+
vendor
)
...
NETWORK
VENDOR
NOT
APPROVED
:
oreilly
>>>
Du siehst, dass not
in Verbindung mit in
verwendet werden kann, was es sehr leistungsfähig und einfach zu lesen macht, was passiert.
Jetzt sehen wir uns ein anspruchsvolleres Beispiel an, bei dem wir in einer Schleife durch ein Wörterbuch laufen und dabei Daten aus einem anderen Wörterbuch extrahieren und sogar einige integrierte Methoden verwenden, die du in diesem Kapitel gelernt hast.
Zur Vorbereitung auf das nächste Beispiel erstellen wir ein Wörterbuch, das CLI-Befehle zur Konfiguration bestimmter Funktionen eines Netzwerkgeräts speichert:
>>>
COMMANDS
=
{
...
'description'
:
'description {}'
,
...
'speed'
:
'speed {}'
,
...
'duplex'
:
'duplex {}'
,
...
}
>>>
>>>
(
COMMANDS
)
{
'duplex'
:
'duplex {}'
,
'speed'
:
'speed {}'
,
'description'
:
'description {}'
}
>>>
Wir sehen, dass wir ein Wörterbuch mit drei Elementen (Schlüssel-Wert-Paaren) haben. Der Schlüssel jedes Elements ist ein zu konfigurierendes Netzwerkmerkmal und der Wert jedes Elements ist der Anfang eines Befehlsstrings, mit dem das jeweilige Merkmal konfiguriert wird. Zu den Funktionen gehören Geschwindigkeit, Duplex und Beschreibung. Die Werte des Wörterbuchs haben geschweifte Klammern ({}
), weil wir die format()
Methode für Strings verwenden, um Variablen einzufügen.
Nachdem wir nun das Wörterbuch COMMANDS
erstellt haben, legen wir ein zweites Wörterbuch mit dem Namen CONFIG_PARAMS
an, in dem wir festlegen, welche Befehle ausgeführt werden und welcher Wert für jeden in COMMANDS
definierten Befehlsstring verwendet werden soll.
>>>
CONFIG_PARAMS
=
{
...
'description'
:
'auto description by Python'
,
...
'speed'
:
'10000'
,
...
'duplex'
:
'auto'
...
}
>>>
Wir werden nun eine for
Schleife verwenden, um CONFIG_PARAMS()
mit der eingebauten Methode items
für Wörterbücher zu durchlaufen. Beim Durchlaufen verwenden wir den Schlüssel von CONFIG_PARAMS
und nutzen ihn, um get
den richtigen Wert oder Befehlsstring von COMMANDS
zu geben. Das ist möglich, weil beide Wörterbücher mit der gleichen Schlüsselstruktur erstellt wurden. Der Befehlsstring wird mit geschweiften Klammern zurückgegeben, aber sobald er zurückgegeben wird, verwenden wir die Methode format()
, um den richtigen Wert einzufügen, der zufällig der Wert in CONFIG_PARAMS
ist.
Schauen wir uns das mal an.
>>>
commands_list
=
[]
>>>
>>>
for
feature
,
value
in
CONFIG_PARAMS
.
items
():
...
command
=
COMMANDS
.
get
(
feature
)
.
format
(
value
)
...
commands_list
.
append
(
command
)
...
>>>
commands_list
.
insert
(
0
,
'interface Eth1/1'
)
>>>
>>>
(
commands_list
)
[
'interface Eth1/1'
,
'duplex auto'
,
'speed 10000'
,
'description auto description by Python'
]
>>>
Jetzt gehen wir das Ganze noch ausführlicher durch. Nimm dir Zeit und probiere es selbst aus, indem du den interaktiven Python-Interpreter verwendest.
In der ersten Zeile commands_list
wird eine leere Liste []
erstellt. Diese wird benötigt, um später append()
zu dieser Liste hinzuzufügen.
Anschließend verwenden wir die eingebaute Methode items()
, um eine Schleife durch CONFIG_PARAMS
zu ziehen. Diese Methode wurde weiter oben in diesem Kapitel schon kurz behandelt, aber items()
gibt dir als Netzwerkentwickler gleichzeitig Zugriff auf den Schlüssel und den Wert eines bestimmten Schlüssel-Wert-Paares. In diesem Beispiel werden drei Schlüssel-Werte-Paare durchlaufen, nämlich description/auto description by Python, speed/10000 und duplex/auto.
Bei jeder Iteration - d.h. für jedes Schlüssel-Wert-Paar, auf das die Variablen feature
und value
verweisen - wird ein Befehl aus dem Wörterbuch COMMANDS
gezogen. Wenn du dich erinnerst, wird die Methode get()
verwendet, um den Wert eines Schlüssel-Wert-Paares zu erhalten, wenn du den Schlüssel angibst. In diesem Beispiel ist der Schlüssel das Objekt feature
. Der zurückgegebene Wert ist description {}
für description
, speed {}
für speed
und duplex {}
für duplex
. Wie du siehst, sind alle zurückgegebenen Objekte Zeichenketten, so dass wir die Methode format()
verwenden können, um value
aus CONFIG_PARAMS
einzufügen, denn wir haben bereits gesehen, dass mehrere Methoden zusammen in einer Zeile verwendet werden können!
Sobald der Wert eingefügt ist, wird der Befehl an commands_list
angehängt. Sobald die Befehle erstellt sind, fügen wir insert()
Eth1/1
ein. Das hätte man auch zuerst machen können.
Wenn du dieses Beispiel verstehst, bist du schon an einem sehr guten Punkt, um Python zu verstehen!
Du hast jetzt einige der häufigsten Arten von for
Schleifen gesehen, mit denen du über Listen und Wörterbücher iterieren kannst. Jetzt sehen wir uns eine weitere Möglichkeit an, eine for
Schleife zu konstruieren und zu verwenden.
Verwendung der Funktion enumerate()
Gelegentlich musst du auf einen Indexwert verfolgen, während du eine Schleife durch ein Objekt durchläufst. Wir zeigen das nur ganz kurz, da die meisten Beispiele, die wir besprechen, den bereits behandelten Beispielen ähneln.
enumerate()
wird verwendet, um die Liste aufzuzählen und einen Indexwert zu geben, und ist oft praktisch, um die genaue Position eines bestimmten Elements zu bestimmen.
Das nächste Beispiel zeigt, wie du enumerate()
innerhalb einer for
Schleife verwendest. Du wirst feststellen, dass der Anfang der for
Schleife wie die Wörterbuchbeispiele aussieht, nur dass im Gegensatz zu items()
, das einen Schlüssel und einen Wert zurückgibt, enumerate()
einen Index, beginnend bei 0, und das Objekt aus der Liste, das du aufzählst, zurückgibt.
Das Beispiel gibt sowohl den Index als auch den Wert aus, damit du verstehst, was es tut:
>>>
vendors
=
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
]
>>>
>>>
for
index
,
each
in
enumerate
(
vendors
):
...
(
index
+
' '
+
each
)
...
0
arista
1
juniper
2
big_switch
3
cisco
>>>
Vielleicht musst du nicht alle Indizes und Werte ausdrucken. Vielleicht brauchst du nur den Index für einen bestimmten Lieferanten. Das wird im nächsten Beispiel gezeigt.
>>>
for
index
,
each
in
enumerate
(
vendors
):
...
if
each
==
'arista'
:
...
(
'arista index is: '
+
index
)
...
arista
index
is
:
0
>>>
Wir haben bisher eine ganze Menge über Python gelernt, von Datentypen über Bedingungen bis hin zu Schleifen. Allerdings haben wir noch nicht behandelt, wie man Code durch die Verwendung von Funktionen effizient wiederverwenden kann. Das werden wir als Nächstes behandeln.
Python-Funktionen verwenden
Weil du dieses Buch liest, hast du wahrscheinlich schon einmal von Funktionen gehört, aber falls nicht, keine Sorge - wir haben alles für dich! Bei Funktionen geht es darum, redundanten und doppelten Code zu vermeiden und die Wiederverwendung von Code zu erleichtern. Ehrlich gesagt, sind Funktionen das Gegenteil von dem, was Netzwerktechniker/innen täglich tun.
Täglich konfigurieren Netzwerktechniker/innen VLANs wieder und wieder. Und sie sind wahrscheinlich stolz darauf, wie schnell sie die gleichen CLI-Befehle immer wieder in ein Netzwerkgerät oder einen Switch eingeben können. Wenn du ein Skript mit Funktionen schreibst, musst du nicht immer wieder denselben Code eingeben.
Nehmen wir an, du musst ein paar VLANs für eine Reihe von Switches erstellen. Bei einem Gerät von Cisco oder Arista könnten die erforderlichen Befehle etwa so aussehen:
vlan
10
name
USERS
vlan
20
name
VOICE
vlan
30
name
WLAN
Stell dir vor, du musst 10, 20 oder 50 Geräte mit denselben VLANs konfigurieren! Es ist sehr wahrscheinlich, dass du diese sechs Befehle für so viele Geräte wie du in deiner Umgebung hast, eingeben würdest.
Das ist eigentlich die perfekte Gelegenheit, um eine Funktion zu erstellen und ein kleines Skript zu schreiben. Da wir uns noch nicht mit Skripten beschäftigt haben, werden wir noch mit der Python-Shell arbeiten.
Für unser erstes Beispiel beginnen wir mit einer grundlegenden print()
Funktion und kommen dann direkt auf das VLAN-Beispiel zurück.
>>>
def
print_vendor
(
net_vendor
):
...
(
net_vendor
)
...
>>>
>>>
vendors
=
[
'arista'
,
'juniper'
,
'big_switch'
,
'cisco'
]
>>>
>>>
for
vendor
in
vendors
:
...
print_vendor
(
vendor
)
...
arista
juniper
big_switch
cisco
>>>
Im vorangegangenen Beispiel ist print_vendor()
eine Funktion, die mit def
erstellt und definiert wird. Wenn du Variablen (Parameter) an deine Funktion übergeben willst, schließt du sie in Klammern neben dem Funktionsnamen ein. Dieses Beispiel erhält einen Parameter und wird als vendor
referenziert, während die Funktion print_vendor()
heißt. Wie Konditionale und Schleifen, enden auch Funktionsdeklarationen mit einem Doppelpunkt (:
). Innerhalb der Funktion gibt es einen eingerückten Codeblock, der eine einzige Anweisung enthält - er gibt einfach den empfangenen Parameter aus.
Sobald die Funktion erstellt ist, kann sie sofort verwendet werden, auch wenn sie sich im Python-Interpreter befindet.
In diesem ersten Beispiel haben wir sichergestellt, dass vendors
erstellt wurde und dann eine Schleife durchlaufen. Bei jeder Iteration der Schleife haben wir das Objekt, das eine Zeichenkette mit dem Namen des Anbieters ist, an print_vendor()
übergeben.
Beachte, dass die Variablen unterschiedliche Namen haben, je nachdem, wo sie verwendet werden. Das bedeutet, dass wir vendor
übergeben, sie aber in der Funktion als net_vendor
empfangen und referenziert wird. Es ist nicht erforderlich, dass die Variablen innerhalb der Funktion denselben Namen haben, aber es ist kein Problem, wenn du es so machen willst.
Da wir jetzt wissen, wie man eine grundlegende Funktion erstellt, kehren wir zum VLAN-Beispiel zurück.
Wir werden zwei Funktionen erstellen, um die VLAN-Bereitstellung zu automatisieren.
Die erste Funktion mit der Bezeichnung get_commands()
ruft die erforderlichen Befehle ab, die an ein Netzwerkgerät gesendet werden sollen. Sie akzeptiert zwei Parameter: die VLAN-ID mit dem Parameter vlan
und den VLAN-NAMEN mit dem Parameter name
.
Die zweite Funktion mit dem Namen push_commands()
gibt die aktuellen Befehle, die unter get_commands
gesammelt wurden, an eine bestimmte Liste von Geräten weiter. Auch diese Funktion nimmt zwei Parameter entgegen: device
Das ist das Gerät, an das die Befehle gesendet werden sollen, und commands
, die Liste der zu sendenden Befehle. In Wirklichkeit wird in dieser Funktion nicht gepusht, sondern es werden Befehle auf dem Terminal ausgedruckt, um die Befehlsausführung zu simulieren.
>>>
def
get_commands
(
vlan
,
name
):
...
commands
=
[]
...
commands
.
append
(
'vlan '
+
vlan
)
...
commands
.
append
(
'name '
+
name
)
...
...
return
commands
...
>>>
>>>
def
push_commands
(
device
,
commands
):
...
(
'Connecting to device: '
+
device
)
...
for
cmd
in
commands
:
...
(
'Sending command: '
+
cmd
)
>>>
Um diese Funktionen nutzen zu können, brauchen wir zwei Dinge: eine Liste der zu konfigurierenden Geräte und die Liste der zu sendenden VLANs.
Die Liste der zu konfigurierenden Geräte sieht folgendermaßen aus:
>>>
devices
=
[
'switch1'
,
'switch2'
,
'switch3'
]
>>>
Um ein einzelnes Objekt zu erstellen, das die VLANs repräsentiert, haben wir eine Liste von Wörterbüchern erstellt. Jedes Wörterbuch hat zwei Schlüssel-Wert-Paare, ein Paar für die VLAN-ID und eines für den VLAN-Namen.
>>>
vlans
=
[{
'id'
:
'10'
,
'name'
:
'USERS'
},
{
'id'
:
'20'
,
'name'
:
'VOICE'
},
{
'id'
:
'30'
,
'name'
:
'WLAN'
}]
>>>
Wenn du dich erinnerst, gibt es mehr als eine Möglichkeit, ein Wörterbuch zu erstellen. Jede dieser Möglichkeiten hätte hier verwendet werden können.
Der nächste Abschnitt des Codes zeigt eine Möglichkeit, diese Funktionen zu nutzen. Der folgende Code läuft in einer Schleife durch die Liste vlans
. Erinnere dich daran, dass jedes Element in vlans
ein Wörterbuch ist. Für jedes Element oder Wörterbuch werden id
und name
mit Hilfe der Methode get()
ermittelt. Es gibt zwei print
Anweisungen, und dann wird die erste Funktion, get_commands()
, aufgerufen -id
und name
sind Parameter, die an die Funktion gesendet werden, und dann wird eine Liste von Befehlen zurückgegeben und commands
zugewiesen.
Sobald wir die Befehle für ein bestimmtes VLAN haben, werden sie auf jedem Gerät ausgeführt, indem wir eine Schleife durch devices
ziehen. Bei diesem Prozess wird push_commands()
für jedes Gerät und jedes VLAN aufgerufen.
Du kannst den zugehörigen Code und die erzeugte Ausgabe hier sehen:
>>>
for
vlan
in
vlans
:
...
id
=
vlan
.
get
(
'id'
)
...
name
=
vlan
.
get
(
'name'
)
...
(
'
\n
'
)
...
(
'CONFIGURING VLAN:'
+
id
)
...
commands
=
get_commands
(
id
,
name
)
...
for
device
in
devices
:
...
push_commands
(
device
,
commands
)
...
(
'
\n
'
)
...
>>>
CONFIGURING VLAN: 10 Connecting to device: switch1 Sending command: vlan 10 Sending command: name USERS Connecting to device: switch2 Sending command: vlan 10 Sending command: name USERS Connecting to device: switch3 Sending command: vlan 10 Sending command: name USERS CONFIGURING VLAN: 20 Connecting to device: switch1 Sending command: vlan 20 Sending command: name VOICE Connecting to device: switch2 Sending command: vlan 20 Sending command: name VOICE Connecting to device: switch3 Sending command: vlan 20 Sending command: name VOICE CONFIGURING VLAN: 30 Connecting to device: switch1 Sending command: vlan 30 Sending command: name WLAN Connecting to device: switch2 Sending command: vlan 30 Sending command: name WLAN Connecting to device: switch3 Sending command: vlan 30 Sending command: name WLAN >>>
Hinweis
Erinnere dich daran, dass nicht alle Funktionen Parameter benötigen und dass nicht alle Funktionen einen Wert zurückgeben.
Du solltest jetzt ein grundlegendes Verständnis für das Erstellen und Verwenden von Funktionen haben und wissen, wie sie mit und ohne Parameter aufgerufen und definiert werden und wie es möglich ist, Funktionen innerhalb von Schleifen aufzurufen.
Als Nächstes behandeln wir, wie man in Python Daten aus Dateien liest und schreibt.
Arbeiten mit Dateien
In diesem Abschnitt von wird dir gezeigt, wie du Daten aus Dateien lesen und schreiben kannst. Wir konzentrieren uns auf die Grundlagen und zeigen dir so viel, dass du problemlos ein komplettes Python-Buch von O'Reilly in die Hand nehmen kannst, um die Arbeit mit Dateien weiter zu lernen.
Lesen aus einer Datei
Für unser Beispiel haben wir ein Konfigurations-Snippet, das sich in demselben Verzeichnis befindet, in dem wir den Python-Interpreter eingegeben haben.
Der Dateiname heißt vlans.cfg und sieht so aus:
vlan 10 name USERS vlan 20 name VOICE vlan 30 name WLAN vlan 40 name APP vlan 50 name WEB vlan 60 name DB
Mit nur zwei Zeilen in Python können wir die Datei öffnen und lesen.
>>>
vlans_file
=
open
(
'vlans.cfg'
,
'r'
)
>>>
>>>
vlans_file
.
read
()
'vlan 10
\n
name USERS
\n
vlan 20
\n
name VOICE
\n
vlan 30
\n
name
WLAN
\nvlan
40
\n
name
APP
\nvlan
50
\n
name
WEB
\nvlan
60
\n
name
DB
'
>>>
>>>
vlans_file
.
close
()
>>>
In diesem Beispiel wird die vollständige Datei als komplettes str
Objekt eingelesen, indem die read()
Methode für Dateiobjekte verwendet wird.
Das nächste Beispiel liest die Datei und speichert jede Zeile als Element in einer Liste, indem es die Methode readlines()
für Dateiobjekte verwendet.
>>>
vlans_file
=
open
(
'vlans.cfg'
,
'r'
)
>>>
>>>
vlans_file
.
readlines
()
[
'vlan 10
\n
'
,
' name USERS
\n
'
,
'vlan 20
\n
'
,
' name VOICE
\n
'
,
'vlan 30
\n
'
,
' name WLAN
\n
'
,
'vlan 40
\n
'
,
' name APP
\n
'
,
'vlan 50
\n
'
,
' name WEB
\n
'
,
'vlan 60
\n
'
,
' name DB'
]
>>>
>>>
vlans_file
.
close
()
>>>
Wir öffnen die Datei erneut, speichern den Inhalt als String und manipulieren ihn dann, um die VLANs als Wörterbuch zu speichern, ähnlich wie wir das vlans
Objekt im Beispiel aus dem Abschnitt über Funktionen verwendet haben.
>>>
vlans_file
=
open
(
'vlans.cfg'
,
'r'
)
>>>
>>>
vlans_text
=
vlans_file
.
read
()
>>>
>>>
vlans_list
=
vlans_text
.
splitlines
()
>>>
>>>
vlans_list
[
'vlan 10'
,
' name USERS'
,
'vlan 20'
,
' name VOICE'
,
'vlan 30'
,
' name WLAN'
,
'vlan 40'
,
' name APP'
,
'vlan 50'
,
' name WEB'
,
'vlan 60'
,
' name DB'
]
>>>
>>>
vlans
=
[]
>>>
for
item
in
vlans_list
:
...
if
'vlan'
in
item
:
...
temp
=
{}
...
id
=
item
.
strip
()
.
strip
(
'vlan'
)
.
strip
()
...
temp
[
'id'
]
=
id
...
elif
'name'
in
item
:
...
name
=
item
.
strip
()
.
strip
(
'name'
)
.
strip
()
...
temp
[
'name'
]
=
name
...
vlans
.
append
(
temp
)
...
>>>
>>>
vlans
[{
'id'
:
'10'
,
'name'
:
'USERS'
},
{
'id'
:
'20'
,
'name'
:
'VOICE'
},
{
'id'
:
'30'
,
'name'
:
'WLAN'
},
{
'id'
:
'40'
,
'name'
:
'APP'
},
{
'id'
:
'50'
,
'name'
:
'WEB'
},
{
'id'
:
'60'
,
'name'
:
'DB'
}]
>>>
>>>
vlans_file
.
close
()
>>>
In diesem Beispiel wird die Datei gelesen und der Inhalt der Datei wird als String in vlans_text
gespeichert. Eine eingebaute Methode für Strings namens splitlines()
wird verwendet, um eine Liste zu erstellen, in der jedes Element der Liste jede Zeile in der Datei ist. Diese neue Liste heißt vlans_list
und hat eine Länge, die der Anzahl der Befehle in der Datei entspricht.
Sobald die Liste erstellt ist, wird sie in einer for
Schleife durchlaufen. Die Variable item
wird verwendet, um jedes Element in der Liste zu repräsentieren, während sie durchlaufen wird. Bei der ersten Iteration wird item
zu 'vlan 10'
, bei der zweiten Iteration wird item
zu ' name users'
und so weiter. In der Schleife for
wird schließlich eine Liste von Wörterbüchern erstellt, wobei jedes Element in der Liste ein Wörterbuch mit zwei Schlüssel-Wert-Paaren ist: id
und name
. Dazu verwenden wir ein temporäres Wörterbuch namens temp
, fügen ihm beide Schlüssel-Wert-Paare hinzu und fügen es erst nach dem Anhängen des VLAN-Namens an die endgültige Liste an. Der folgende Hinweis besagt, dass temp
nur dann neu initialisiert wird, wenn es das nächste VLAN findet.
Beachte, wie strip()
verwendet wird. Du kannst strip()
verwenden, um nicht nur Leerzeichen zu entfernen, sondern auch bestimmte Teilstrings innerhalb eines String-Objekts. Außerdem haben wir mehrere Methoden miteinander in einer einzigen Python-Anweisung verknüpft.
Wenn zum Beispiel der Wert ' name WEB'
verwendet wird, gibt strip()
zuerst 'name WEB'
zurück. Dann haben wir strip('name')
verwendet, was ' WEB'
ergibt, und schließlich strip()
, um alle Leerzeichen zu entfernen, die noch übrig sind, um den endgültigen Namen 'WEB'
zu erhalten.
Hinweis
Das vorherige Beispiel ist nicht die einzige Möglichkeit, eine Operation zum Einlesen von VLANs durchzuführen. Das Beispiel ging davon aus, dass es für jedes VLAN eine VLAN-ID und einen Namen gibt, was normalerweise nicht der Fall ist, aber so gemacht wird, um bestimmte Konzepte zu vermitteln. temp
wurde nur initialisiert, wenn "VLAN" gefunden wurde, und temp
wurde erst angehängt, nachdem der "Name" hinzugefügt wurde (dies würde nicht funktionieren, wenn nicht für jedes VLAN ein Name existiert, und ist ein guter Anwendungsfall für die Fehlerbehandlung in Python mit try/except
Anweisungen - was in diesem Buch nicht behandelt werden kann).
Schreiben in eine Datei
Das nächste Beispiel zeigt, wie man Daten in eine Datei schreibt.
Das vlans
Objekt, das im vorherigen Beispiel erstellt wurde, wird auch hier verwendet.
>>>
vlans
[{
'id'
:
'10'
,
'name'
:
'USERS'
},
{
'id'
:
'20'
,
'name'
:
'VOICE'
},
{
'id'
:
'30'
,
'name'
:
'WLAN'
},
{
'id'
:
'40'
,
'name'
:
'APP'
},
{
'id'
:
'50'
,
'name'
:
'WEB'
},
{
'id'
:
'60'
,
'name'
:
'DB'
}]
Es werden noch ein paar VLANs erstellt, bevor wir versuchen, die VLANs in eine neue Datei zu schreiben.
>>>
add_vlan
=
{
'id'
:
'70'
,
'name'
:
'MISC'
}
>>>
vlans
.
append
(
add_vlan
)
>>>
>>>
add_vlan
=
{
'id'
:
'80'
,
'name'
:
'HQ'
}
>>>
vlans
.
append
(
add_vlan
)
>>>
>>>
(
vlans
)
[{
'id'
:
'10'
,
'name'
:
'USERS'
},
{
'id'
:
'20'
,
'name'
:
'VOICE'
},
{
'id'
:
'30'
,
'name'
:
'WLAN'
},
{
'id'
:
'40'
,
'name'
:
'APP'
},
{
'id'
:
'50'
,
'name'
:
'WEB'
},
{
'id'
:
'60'
,
'name'
:
'DB'
},
{
'id'
:
'70'
,
'name'
:
'MISC'
},
{
'id'
:
'80'
,
'name'
:
'HQ'
}]
>>>
Es gibt jetzt acht VLANS in der Liste vlans
. Wir schreiben sie in eine neue Datei, behalten aber die Formatierung bei, wie sie sein sollte, mit den richtigen Abständen.
Der erste Schritt besteht darin, die neue Datei zu öffnen. Wenn die Datei nicht existiert, was in unserem Fall nicht der Fall ist, wird sie erstellt. Das kannst du in der ersten Codezeile sehen, die folgt.
Sobald sie geöffnet ist, verwenden wir erneut die Methode get()
, um die erforderlichen VLAN-Werte aus jedem Wörterbuch zu extrahieren, und verwenden dann die Dateimethode write()
, um die Daten in die Datei zu schreiben. Zum Schluss wird die Datei geschlossen.
>>>
write_file
=
open
(
'vlans_new.cfg'
,
'w'
)
>>>
>>>
for
vlan
in
vlans
:
...
id
=
vlan
.
get
(
'id'
)
...
name
=
vlan
.
get
(
'name'
)
...
write_file
.
write
(
'vlan '
+
id
+
'
\n
'
)
...
write_file
.
write
(
' name '
+
name
+
'
\n
'
)
...
>>>
>>>
write_file
.
close
()
>>>
Der vorherige Code hat die Datei vlans_new.cfg erstellt und den folgenden Inhalt in der Datei erzeugt:
$ cat vlans_new.cfg vlan 10 name USERS vlan 20 name VOICE vlan 30 name WLAN vlan 40 name APP vlan 50 name WEB vlan 60 name DB vlan 70 name MISC vlan 80 name HQ
Wenn du anfängst, mehr mit Dateiobjekten zu arbeiten, kannst du einige interessante Dinge beobachten. Du vergisst zum Beispiel, eine Datei zu schließen, und wunderst dich, warum keine Daten in der Datei sind, von der du weißt, dass sie Daten enthalten sollte!
Hinweis
Standardmäßig wird das, was du mit der Methode write()
schreibst, in einem Puffer gespeichert und erst in die Datei geschrieben, wenn die Datei geschlossen wird. Diese Einstellung ist konfigurierbar.
Es ist auch möglich, die with
Anweisung, einen Kontextmanager, zu verwenden, um diesen Prozess zu verwalten.
Hier ist ein kurzes Beispiel mit with
. Einer der Vorteile von with
ist, dass die Datei automatisch geschlossen wird.
>>>
with
open
(
'vlans_new.cfg'
,
'w'
)
as
write_file
:
...
write_file
.
write
(
'vlan 10
\n
'
)
...
write_file
.
write
(
' name TEST_VLAN
\n
'
)
...
>>>
Hinweis
Wenn du eine Datei mit open()
wie mit open('vlans.cfg', 'r')
öffnest, kannst du sehen, dass zwei Parameter gesendet werden. Der erste ist der Name der Datei einschließlich des relativen oder absoluten Pfads der Datei. Der zweite ist der Modus, der ein optionales Argument ist, aber wenn er nicht angegeben wird, entspricht er dem Nur-Lese-Modus, dem Modus von r
. Andere Modi sind w
, der eine Datei nur zum Schreiben öffnet (wenn du den Namen einer bereits existierenden Datei verwendest, wird der Inhalt gelöscht), a
öffnet eine Datei zum Anhängen und r+
öffnet eine Datei zum Lesen und Schreiben.
In diesem Kapitel haben wir bisher nur den dynamischen Python-Interpreter verwendet. Das hat gezeigt, wie leistungsfähig der Interpreter ist, um neue Methoden, Funktionen oder bestimmte Abschnitte deines Codes zu schreiben und zu testen. Aber egal, wie gut der Interpreter ist, wir müssen auch in der Lage sein, Programme und Skripte zu schreiben, die als eigenständige Einheiten laufen können. Genau das werden wir als Nächstes behandeln.
Python-Programme erstellen
Unter erfährst du, wie du auf der Python-Shell aufbauen und ein eigenständiges Python-Skript oder -Programm erstellen und ausführen kannst. In diesem Abschnitt wird gezeigt, wie du mit dem bisher Gelernten innerhalb weniger Minuten ein Skript erstellen kannst.
Hinweis
Wenn du mitmachst, kannst du jeden Texteditor verwenden, mit dem du gut zurechtkommst, z. B. vi, vim, Sublime Text, Notepad++ oder sogar eine vollwertige integrierte Entwicklungsumgebung (IDE) wie PyCharm.
Schauen wir uns ein paar Beispiele an.
Ein grundlegendes Python-Skript erstellen
Der erste Schritt besteht darin, eine neue Python-Datei zu erstellen, die mit der Endung .py endet. Im Linux-Terminal erstellst du eine neue Datei durch Eingabe von touch net_script.py
und öffne sie in deinem Texteditor. Wie erwartet, ist die Datei völlig leer.
Das erste Skript, das wir schreiben werden, gibt einfach Text auf dem Terminal aus. Füge die folgenden fünf Textzeilen in net_script.py ein, um ein grundlegendes Python-Skript zu erstellen.
#!/usr/bin/env python
if
__name__
==
"__main__"
:
(
'^'
*
30
)
(
'HELLO NETWORK AUTOMATION!!!!!'
)
(
'^'
*
30
)
Jetzt, wo das Skript erstellt ist, lass es uns ausführen.
Um ein Python-Skript über das Linux-Terminal auszuführen, verwendest du den Befehl python
. Alles, was du tun musst, ist, den Skriptnamen an den Befehl anzuhängen, wie hier gezeigt.
$
python
net_script
.
py
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
HELLO
NETWORK
AUTOMATION
!!!!!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Und das war's! Wenn du mitverfolgt hast, hast du gerade ein Python-Skript erstellt. Du hast vielleicht bemerkt, dass alles unter der Anweisung if __name__ == "__main__":
dasselbe ist, als ob du den Python-Interpreter benutzt hättest.
Jetzt werfen wir einen Blick auf die beiden einzigartigen Anweisungen, die optional sind, aber empfohlen werden, wenn du Python-Skripte schreibst. Die erste wird Shebang genannt. Vielleicht erinnerst du dich noch daran, dass wir den Shebang in Kapitel 3 eingeführt haben.
Das ganze Paket verstehen
Der shebang ist die erste Zeile im Skript: #!/usr/bin/env python
. Dies ist eine besondere und einzigartige Zeile für Python-Programme.
Es ist die einzige Codezeile, die das #
als erstes Zeichen verwendet, abgesehen von Kommentaren. Wir werden uns später im Kapitel mit Kommentaren befassen, aber beachte jetzt schon, dass #
in Python häufig für Kommentare verwendet wird. Der Shebang ist die Ausnahme und muss auch die erste Zeile in einem Python-Programm sein, wenn er verwendet wird.
Hinweis
Python-Linters, die zur Überprüfung des Codes verwendet werden, können auch auf den Text reagieren, der nach den Kommentaren kommt und mit #
beginnt.
Der Shebang weist das System an, welchen Python-Interpreter es zur Ausführung des Programms verwenden soll. Das setzt natürlich auch voraus, dass die Dateiberechtigungen für deine Programmdatei korrekt sind (d.h. dass die Datei ausführbar ist). Wenn der Shebang nicht enthalten ist, musst du das Schlüsselwort python
verwenden, um das Skript auszuführen, was wir in allen unseren Beispielen ohnehin tun.
Wenn wir zum Beispiel das folgende Skript hätten:
if
__name__
==
"__main__"
:
(
'Hello Network Automation!'
)
könnten wir mit der Anweisung $ python hello.py
ausführen, vorausgesetzt, die Datei wurde als hello.py gespeichert. Mit der Anweisung $ ./hello.py
könnten wir sie jedoch nicht ausführen. Damit die Anweisung $ ./hello.py
ausgeführt werden kann, müssen wir das Shebang zur Programmdatei hinzufügen, denn so weiß das System, wie das Skript ausgeführt werden soll.
Der Shebang, wie wir ihn haben, /usr/bin/env python
, verwendet standardmäßig Python 2.7 auf dem System, das wir zum Schreiben dieses Buches benutzen. Wenn du aber mehrere Python-Versionen installiert hast, kannst du den Shebang auch so ändern, dass er eine andere Version verwendet, z. B. /usr/bin/env python3
, um Python 3 zu verwenden.
Hinweis
Erwähnenswert ist auch, dass du mit shebang /usr/bin/env python
die Systemumgebung ändern kannst, damit du nicht jedes einzelne Skript ändern musst, falls du mit einer anderen Python-Version testen möchtest. Du kannst den Befehl which python
verwenden, um zu sehen, welche Version auf deinem System verwendet wird.
Unser System ist zum Beispiel standardmäßig auf Python 2.7.6 eingestellt:
$ which python /usr/bin/python $ $ /usr/bin/python Python 2.7.6 (default, Jun 22 2015, 17:58:13) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
Als Nächstes werden wir uns die Anweisung if __name__ == "__main__":
genauer ansehen. Anhand der Anführungszeichen kannst du erkennen, dass __name__
eine Variable und "__main__"
ein String ist. Wenn eine Python-Datei als eigenständiges Skript ausgeführt wird, wird der Variablenname __name__
automatisch auf "__main__"
gesetzt. Wenn du also die Anweisung python <script>.py
aufrufst, wird alles unterhalb der Anweisung if __name__ == "__main__"
ausgeführt.
An dieser Stelle fragst du dich wahrscheinlich, wann __name__
nicht gleichbedeutend mit "__main__"
sein sollte. Das wird in "Arbeiten mit Python-Modulen" besprochen, aber die kurze Antwort lautet: Wenn du bestimmte Objekte aus Python-Dateien importierst, aber diese Dateien nicht unbedingt als eigenständiges Programm verwendest.
Jetzt, wo du den Shebang und die Anweisung if __name__ == "__main__":
verstehst, können wir uns weiter mit eigenständigen Python-Skripten beschäftigen.
Code aus dem Python-Interpreter in ein Python-Skript migrieren
Dieses nächste Beispiel ist dasselbe wie das aus dem Abschnitt über Funktionen. Der Grund dafür ist, dir aus erster Hand zu zeigen, wie einfach es ist, von der Verwendung des Python-Interpreters auf das Schreiben eines eigenständigen Python-Skripts umzusteigen.
Das nächste Skript heißt push.py.
#!/usr/bin/env python
def
get_commands
(
vlan
,
name
):
commands
=
[]
commands
.
append
(
'vlan '
+
vlan
)
commands
.
append
(
'name '
+
name
)
return
commands
def
push_commands
(
device
,
commands
):
(
'Connecting to device: '
+
device
)
for
cmd
in
commands
:
(
'Sending command: '
+
cmd
)
if
__name__
==
"__main__"
:
devices
=
[
'switch1'
,
'switch2'
,
'switch3'
]
vlans
=
[{
'id'
:
'10'
,
'name'
:
'USERS'
},
{
'id'
:
'20'
,
'name'
:
'VOICE'
},
{
'id'
:
'30'
,
'name'
:
'WLAN'
}]
for
vlan
in
vlans
:
vid
=
vlan
.
get
(
'id'
)
name
=
vlan
.
get
(
'name'
)
(
'
\n
'
)
(
'CONFIGURING VLAN:'
+
vid
)
commands
=
get_commands
(
vid
,
name
)
for
device
in
devices
:
push_commands
(
device
,
commands
)
(
'
\n
'
)
Das Skript wird mit dem Befehl python push.py
ausgeführt.
Die Ausgabe, die du siehst, ist genau die gleiche, die du gesehen hast, als sie auf dem Python-Interpreter ausgeführt wurde.
Wenn du mehrere Skripte erstellst, die verschiedene Konfigurationsänderungen im Netzwerk vornehmen, können wir vernünftigerweise davon ausgehen, dass die Funktion namens push_commands()
in fast allen Skripten benötigt wird. Eine Möglichkeit wäre, die Funktion zu kopieren und in alle Skripte einzufügen. Das wäre natürlich nicht optimal, denn wenn du einen Fehler in dieser Funktion beheben müsstest, müsstest du diese Änderung in allen Skripten vornehmen.
Genauso wie Funktionen es uns ermöglichen, Code innerhalb eines einzelnen Skripts wiederzuverwenden, gibt es auch eine Möglichkeit, Code zwischen Skripten/Programmen wiederzuverwenden und auszutauschen. Das tun wir, indem wir ein Python-Modul erstellen, das wir als Nächstes behandeln werden, während wir auf dem vorherigen Beispiel aufbauen.
Arbeiten mit Python-Modulen
Wir werden die Datei push.py, die wir im vorigen Abschnitt erstellt haben, weiter nutzen, um besser zu verstehen, wie man mit einem Python-Modul arbeitet. Ein Modul ist eine Art Python-Datei, die Informationen (z. B. Python-Objekte) enthält, die von anderen Python-Programmen genutzt werden können, aber kein eigenständiges Skript oder Programm ist.
Für dieses Beispiel kehren wir in den Python-Interpreter zurück, während wir uns im selben Verzeichnis befinden, in dem die Datei push.py existiert.
Nehmen wir an, du musst eine neue Liste von Befehlen erstellen, die du an eine neue Liste von Geräten senden möchtest. Du erinnerst dich, dass du diese Funktion namens push_commands()
in einer anderen Datei hast, die bereits die Logik enthält, um eine Liste von Befehlen an ein bestimmtes Gerät zu senden. Anstatt die gleiche Funktion in deinem neuen Programm (oder im Interpreter) neu zu erstellen, verwendest du die Funktion push_commands()
in push.py wieder. Schauen wir uns an, wie das geht.
In der Python-Shell geben wir import push
ein und drücken Enter. Dadurch werden alle Objekte in der Datei push.py importiert.
>>>
import
push
>>>
Sieh dir die importierten Objekte mit dir(push)
an.
>>>
dir
(
push
)
[
'__builtins__'
,
'__doc__'
,
'__file__'
,
'__name__'
,
'__package__'
,
'get_commands'
,
'push_commands'
]
>>>
Genau wie wir es bei den Standard-Python-Datentypen gesehen haben, hat auch push
Methoden, die mit Unterstrichen beginnen und enden, aber du solltest auch die beiden Objekte namens get_commands
und push_commands
beachten, die die Funktionen aus der Datei push.py sind!
Wenn du dich erinnerst, benötigt push_commands()
zwei Parameter. Der erste ist ein Gerät und der zweite ist eine Liste von Befehlen. Verwenden wir nun push_commands()
vom Interpreter aus.
>>>
device
=
'router1'
>>>
commands
=
[
'interface Eth1/1'
,
'shutdown'
]
>>>
>>>
push
.
push_commands
(
device
,
commands
)
Connecting
to
device
:
router1
Sending
command
:
interface
Eth1
/
1
Sending
command
:
shutdown
>>>
Wie du siehst, haben wir als erstes zwei neue Variablen (device
und commands
) erstellt, die als Parameter an push_commands()
gesendet werden.
push_commands()
wird dann als ein Objekt von push
mit den Parametern device
und commands
aufgerufen.
Wenn du mehrere Module importierst und es zu Überschneidungen zwischen den Funktionsnamen kommen kann, ist die Methode von import push
eine gute Option. Sie macht es auch sehr einfach zu wissen, wo (in welchem Modul) die Funktion existiert. Andererseits gibt es auch andere Möglichkeiten, Objekte zu importieren.
Eine andere Möglichkeit ist, from import
zu verwenden. Für unser Beispiel würde es so aussehen: from push import push_commands
. Beachte im folgenden Code, dass du push_commands()
direkt verwenden kannst, ohne auf push
zu verweisen.
>>>
from
push
import
push_commands
>>>
>>>
device
=
'router1'
>>>
commands
=
[
'interface Eth1/1'
,
'shutdown'
]
>>>
>>>
push_commands
(
device
,
commands
)
Connecting
to
device
:
router1
Sending
command
:
interface
Eth1
/
1
Sending
command
:
shutdown
>>>
Tipp
Es wird empfohlen, die import
Anweisungen so spezifisch wie möglich zu gestalten und nur das zu importieren, was in deinem Code verwendet wird. Du solltest keine Wildcard-Importe verwenden, wie z. B. from push import *
. Mit solchen Anweisungen werden alle Objekte aus dem Modul geladen, was zu Überlastungen und Konflikten mit den von dir definierten Objekten in den Namensräumen führen kann. Außerdem wird dadurch die Fehlersuche erschwert, da es schwierig ist, herauszufinden, wo ein Objekt definiert wurde oder herkommt.
Eine andere Möglichkeit ist, das Objekt während des Imports umzubenennen, indem du from import as
verwendest. Wenn dir der Name des Objekts nicht gefällt oder er dir zu lang ist, kannst du ihn beim Import umbenennen. In unserem Beispiel sieht das so aus:
>>>
from
push
import
push_commands
as
pc
>>>
Beachte, wie einfach es ist, das Objekt umzubenennen und ihm einen kürzeren oder intuitiveren Namen zu geben.
Lass uns das in einem Beispiel verwenden.
>>>
from
push
import
push_commands
as
pc
>>>
>>>
device
=
'router1'
>>>
commands
=
[
'interface Eth1/1'
,
'shutdown'
]
>>>
>>>
pc
(
device
,
commands
)
Connecting
to
device
:
router1
Sending
command
:
interface
Eth1
/
1
Sending
command
:
shutdown
>>>
Tipp
In unseren Beispielen haben wir den Python Dynamic Interpreter aus demselben Verzeichnis aufgerufen, in dem das Modul push.py gespeichert wurde. Um dieses Modul oder jedes andere neue Modul von überall auf deinem System aus nutzen zu können, musst du dein Modul in einem Verzeichnis ablegen, das in deinem PYTHONPATH
definiert ist. Dabei handelt es sich um eine Linux-Umgebungsvariable, die alle Verzeichnisse definiert, in denen dein System nach Python-Modulen und -Programmen suchen soll.
Inzwischen solltest du nicht nur wissen, wie du ein Skript erstellst, sondern auch, wie du ein Python-Modul mit Funktionen (und anderen Objekten) erstellst und wie du diese wiederverwendbaren Objekte in anderen Skripten und Programmen nutzen kannst.
Übergabe von Argumenten an ein Python-Skript
In haben wir uns in den letzten beiden Abschnitten mit dem Schreiben von Python-Skripten und der Verwendung von Python-Modulen beschäftigt. Jetzt schauen wir uns ein Modul an, das Teil der Python-Standardbibliothek ist (d.h. standardmäßig mit Python geliefert wird) und mit dem wir ganz einfach Argumente von der Kommandozeile in ein Python-Skript einfügen können. Das Modul heißt sys
, und wir werden ein Attribut (oder eine Variable) innerhalb des Moduls namens argv
verwenden.
Schauen wir uns ein einfaches Skript namens send_command.py an, das nur eine einzige print
Anweisung enthält.
#!/usr/bin/env python
import
sys
if
__name__
==
"__main__"
:
(
sys
.
argv
)
Jetzt führen wir das Skript aus und geben ein paar Argumente ein, die die Daten simulieren, die wir brauchen, um uns bei einem Gerät anzumelden und einen show
Befehl zu geben.
ntc@ntc:~$
python send-command.py username password 10.1.10.10"show version"
[
'send-command.py'
,'username'
,'password'
,'10.1.10.10'
,'show version'
]
ntc@ntc:~$
Du solltest sehen, dass sys.argv
eine Liste ist. Genau genommen ist es einfach eine Liste von Strings, die wir über die Linux-Befehlszeile übergeben haben. Es handelt sich um eine Standard-Python-Liste, deren Elemente den übergebenen Argumenten entsprechen. Du kannst auch ableiten, was wirklich passiert ist: Python hat einen Split (str.split(" ")
) auf send-command.py username password 10.1.10.10 "show version"
durchgeführt, wodurch die Liste mit fünf Elementen entstanden ist!
Beachte, dass bei der Verwendung von sys.argv
das erste Element immer der Skriptname ist.
Wenn du möchtest, kannst du den Wert von sys.argv
einer beliebigen Variablen zuweisen, um die Arbeit mit den übergebenen Parametern zu vereinfachen. In jedem Fall kannst du die Werte mit den entsprechenden Indexwerten extrahieren, wie gezeigt:
#!/usr/bin/env python
import
sys
if
__name__
==
"__main__"
:
args
=
sys
.
argv
(
"Username: "
+
args
[
0
])
(
"Password: "
+
args
[
1
])
(
"Device IP: "
+
args
[
2
])
(
"Command: "
+
args
[
3
])
Und wenn wir dieses Skript ausführen:
ntc@ntc:~$
python send-command.py username password 10.1.10.10"show version"
Username: send-command.py Password: username Device IP: password Command: 10.1.10.10 ntc@ntc:~$
Wenn du dieses Buch weiter liest, kannst du darauf aufbauen, um sinnvollere Netzwerkaufgaben durchzuführen. Nach der Lektüre von Kapitel 7 wirst du zum Beispiel in der Lage sein, solche Parameter an ein Skript zu übergeben, das eine Verbindung zu einem Gerät herstellt, indem es eine API verwendet, die einen show
-Befehl (oder einen ähnlichen) ausgibt.
Hinweis
Wenn du sys.argv
verwendest, musst du trotzdem die Fehlerbehandlung berücksichtigen (überprüfe zumindest die Länge der Liste). Außerdem muss der Benutzer des Skripts die genaue Reihenfolge der Elemente kennen, die übergeben werden müssen. Für eine fortgeschrittene Argumentbehandlung solltest du dir das Python-Modul argparse
ansehen, das eine sehr benutzerfreundliche Methode zur Übergabe von Argumenten mit "Flags" und einem integrierten Hilfemenü bietet. Dies würde den Rahmen dieses Buches sprengen.
Pip verwenden und Python-Pakete installieren
Wenn du mit Python anfängst, wirst du wahrscheinlich auch andere in Python geschriebene Software installieren müssen. Vielleicht möchtest du z. B. die Automatisierung von Netzwerkgeräten mit netmiko
testen, einem beliebten SSH-Client für Python, den wir in Kapitel 7 behandeln. Es ist üblich, Python-Software, einschließlich netmiko
, über den Python Package Index (PyPI) zu verbreiten, ausgesprochen "pie-pie". Du kannst das PyPI-Repository auch direkt unter https://pypi.python.org/pypi durchstöbern und durchsuchen .
Für jede Python-Software, die auf PyPI gehostet wird, wie z. B. netmiko
, kannst du das Programm pip
verwenden, um sie direkt von PyPI auf deinem Rechner zu installieren. pip
ist ein Installationsprogramm, das standardmäßig zu PyPI geht, die Software herunterlädt und auf deinem Rechner installiert.
Mit pip
kannst du netmiko
mit einer einzigen Zeile auf deinem Linux-Rechner installieren:
ntc@ntc:~$
sudo pip install netmiko# output omitted
Dadurch wird netmiko
in einem Systempfad installiert (dieser hängt vom verwendeten Betriebssystem ab).
Standardmäßig wird dabei die neueste und beste (stabile) Version eines bestimmten Python-Pakets installiert. Du kannst aber auch sicherstellen, dass du eine bestimmte Version installierst - das ist hilfreich, um sicherzustellen, dass du nicht automatisch die nächste Version installierst, ohne sie zu testen. Dies wird als Pinning bezeichnet. Du kannst deine Installation an eine bestimmte Version binden. Im nächsten Beispiel zeigen wir dir, wie du die Installation von netmiko
an die Version 1.4.2 festmachen kannst.
ntc@ntc:~$
sudo pip installnetmiko
==
1.4.2# output omitted
Du kannst pip
auch zum Upgrade von Softwareversionen verwenden. Wenn du zum Beispiel die Version 1.4.2 von netmiko
installiert hast, kannst du mit dem --upgrade
oder -U
Flag auf der Kommandozeile auf die neueste Version upgraden, wenn du dazu bereit bist.
ntc@ntc:~$
sudo pip install netmiko --upgrade# output omitted
Es kommt auch häufig vor, dass du Python-Pakete aus dem Quellcode installieren musst. Das bedeutet einfach, dass du dir die neueste Version besorgst, zum Beispiel von GitHub, einem System zur Versionskontrolle, das wir in Kapitel 8 behandeln. Vielleicht enthält das Softwarepaket auf GitHub eine Fehlerbehebung, die du brauchst und die noch nicht auf PyPI veröffentlicht wurde. In diesem Fall ist es üblich, eine git clone
durchzuführen - wie das geht, zeigen wir dir ebenfalls in Kapitel 8.
Wenn du ein Python-Projekt von GitHub klonst, ist die Wahrscheinlichkeit groß, dass du zwei Dateien im Stammverzeichnis des Projekts findest: requirements.txt und setup.py. Mit diesen Dateien kannst du das Python-Paket aus dem Quellcode installieren. Die Anforderungsdatei listet die Anforderungen auf, die für die Anwendung erforderlich sind. Hier ist zum Beispiel die aktuelle requirements.txt für netmiko
:
paramiko>=
1.13.0 scp>=
0.10.0 pyyaml
Du kannst sehen, dass netmiko
drei Abhängigkeiten hat, die allgemein als Deps bezeichnet werden. Du kannst diese Deps auch direkt von PyPI mit einer einzigen Anweisung installieren, wiederum mit dem pip
Installationsprogramm.
ntc@ntc:~$
sudo pip install -r requirements.text# output omitted
Um netmiko
komplett (aus dem Quellcode) einschließlich der Anforderungen zu installieren, kannst du auch die Datei setup.py ausführen, die du nach dem Git-Clone im selben Verzeichnis findest.
ntc@ntc:~$
sudo python setup.py install# output omitted
Wenn du die Software mit setup.py installierst, wird sie standardmäßig auch direkt in einen Systempfad installiert. Wenn du zu einem bestimmten Projekt auf GitHub beitragen und aktiv an dem Projekt entwickeln möchtest, kannst du die Anwendung auch direkt von dort installieren, wo die Dateien existieren (Verzeichnis, in das du das Projekt geklont hast).
ntc@ntc:~$
sudo python setup.py develop# output omitted
Dadurch wird sichergestellt, dass die Dateien in deinem lokalen Verzeichnis die sind, die in unserem Beispiel netmiko
ausführen. Wenn du beim Ausführen von setup.py die Option install
verwendest, musst du die Dateien in deinem Systempfad ändern, um die lokale Installation netmiko
durchzuführen (z. B. zur Fehlerbehebung).
Zusätzliche Tipps, Tricks und allgemeine Informationen zur Verwendung von Python lernen
Wir schließen dieses Kapitel mit so genannten Python-Tipps, -Tricks und allgemeinen Informationen ab. Es handelt sich um nützliche Informationen für die Arbeit mit Python - einiges davon ist einführend, anderes ist fortgeschrittener, aber wir wollen dich wirklich darauf vorbereiten, nach diesem Kapitel weiter in Python einzutauchen.
Diese Tipps, Tricks und allgemeinen Informationen werden hier in keiner bestimmten Reihenfolge angegeben:
-
Du musst vielleicht auf bestimmte Teile einer Zeichenkette oder Elemente in einer Liste zugreifen. Vielleicht brauchst du nur das erste Zeichen oder Element. Du kannst den Index von
0
für Zeichenketten (noch nicht behandelt), aber auch für Listen verwenden. Wenn es eine Variable namensrouter
gibt, der der Wert'DEVICE'
zugewiesen ist, gibtrouter[0]
'D'
zurück. Das Gleiche gilt für Listen, die wir bereits behandelt haben. Aber was ist mit dem Zugriff auf das letzte Element in einer Zeichenkette oder Liste? Erinnere dich daran, dass wir gelernt haben, dass wir dafür den Index-1
verwenden können.router[-1]
gibt'E'
zurück und das Gleiche gilt auch für eine Liste. -
Aufbauend auf dem vorherigen Beispiel wird diese Notation erweitert, um die ersten oder letzten Zeichen zu erhalten (wie bei einer Liste):
>>>
hostname
=
'DEVICE_12345'
>>>
>>>
hostname
[
4
:]
'CE_12345'
>>>
>>>
hostname
[:
-
2
]
'DEVICE_123'
>>>
Das kann ziemlich mächtig werden, wenn du verschiedene Arten von Objekten analysieren musst.
-
Mit
str(10)
kannst du eine ganze Zahl in eine Zeichenkette umwandeln (oder casten). Du kannst auch das Gegenteil tun, indem du eine Zeichenkette in eine ganze Zahl umwandelst, indem duint('10')
verwendest. -
Wir haben
dir()
ziemlich oft benutzt, als wir die eingebauten Methoden kennengelernt haben, und auch die Funktionentype()
undhelp()
erwähnt. Ein hilfreicher Arbeitsablauf für die Verwendung aller drei Funktionen ist folgender:-
Überprüfe deinen Datentyp mit
type()
-
Überprüfe die verfügbaren Methoden für dein Objekt mit
dir()
-
Wenn du weißt, welche Methode du verwenden willst, lernst du, wie du sie anwenden kannst.
help()
Hier ist ein Beispiel für diesen Arbeitsablauf:
>>>
hostname
=
'router1'
>>>
>>>
type
(
hostname
)
<
type
'str'
>
>>>
>>>
dir
(
hostname
)
>>>
# output omitted; it would show all methods including "upper"
>>>
>>>
help
(
hostname
.
upper
)
# output omitted
>>>
-
-
Wenn du einen Variablentyp innerhalb von Python überprüfen musst, kann die Fehlerbehandlung (
try/except
) verwendet werden. Wenn du aber explizit wissen musst, welcher Typ eines Objekts etwas ist, istisinstance()
eine großartige Funktion, die du kennen solltest. Sie gibtTrue
zurück, wenn die übergebene Variable vom Typ des Objekts ist, das auch übergeben wird.>>>
hostname
=
''
>>>
devices
=
[]
>>>
if
isinstance
(
devices
,
list
):
...
print
(
'devices is a list'
)
...
devices
is
a
list
>>>
>>>
if
isinstance
(
hostname
,
str
):
...
print
(
'hostname is a string'
)
...
hostname
is
a
string
>>>
-
Wir haben gelernt, wie man den Python-Interpreter benutzt und Python-Skripte erstellt. Python bietet das
-i
Flag, das beim Ausführen eines Skripts verwendet werden kann. Statt das Skript zu beenden, wird der Interpreter aufgerufen, der dir Zugriff auf alle Objekte des Skripts gibt - das ist ideal zum Testen.Hier ist eine Beispieldatei namens test.py:
if __name__ == "__main__": devices = ['r1', 'r2', 'r3'] hostname = 'router5'
Schauen wir uns an, was passiert, wenn wir das Skript mit gesetztem
-i
Flag ausführen.$
python
-
i
test
.
py
>>>
>>>
print
(
devices
)
[
'r1'
,
'r2'
,
'r3'
]
>>>
>>>
print
(
hostname
)
router5
>>>
Beachte, wie es ausgeführt wurde, aber dann hat es dich direkt in die Python-Shell fallen lassen und du hast Zugriff auf diese Objekte. Ziemlich cool, oder?
-
Objekte sind
True
, wenn sie nicht null sind, undFalse
, wenn sie null sind. Hier sind ein paar Beispiele:>>>
devices
=
[]
>>>
if
not
devices
:
...
print
(
'devices is empty'
)
...
devices
is
empty
>>>
>>>
hostname
=
'something'
>>>
>>>
if
hostname
:
...
print
(
'hostname is not null'
)
...
hostname
is
not
null
>>>
-
In, dem Abschnitt über Zeichenketten, haben wir uns die Verkettung von Zeichenketten mit dem Pluszeichen angesehen (
+
), aber auch gelernt, wie man die Methodeformat()
verwendet, die viel sauberer ist. Es gibt noch eine weitere Möglichkeit, das Gleiche mit%
zu tun. Ein Beispiel für das Einfügen von Zeichenketten (s
) findest du hier:>>>
hostname
=
'r5'
>>>
>>>
interface
=
'Eth1/1'
>>>
>>>
test
=
'Device
%s
has one interface:
%s
'
%
(
hostname
,
interface
)
>>>
>>>
print
(
test
)
Device
r5
has
one
interface
:
Eth1
/
1
>>>
-
Wir haben uns noch nicht mit Kommentaren beschäftigt, aber wir haben erwähnt, dass das # (auch bekannt als Hashtag, Nummernzeichen oder Pfundzeichen) für Inline-Kommentare verwendet wird.
def
get_commands
(
vlan
,
name
):
commands
=
[]
# building list of commands to configure a vlan
commands
.
append
(
'vlan '
+
vlan
)
commands
.
append
(
'name '
+
name
)
# appending name
return
commands
-
Ein Docstring wird normalerweise zu Funktionen, Methoden und Klassen hinzugefügt, um zu beschreiben, was das Objekt tut. Er sollte in dreifachen Anführungszeichen (
"""
) stehen und ist normalerweise auf eine Zeile begrenzt.def
get_commands
(
vlan
,
name
):
"""Get commands to configure a VLAN.
"""
commands
=
[]
commands
.
append
(
'vlan '
+
vlan
)
commands
.
append
(
'name '
+
name
)
return
commands
Du hast gelernt, wie man ein Modul importiert, nämlich push.py. Importieren wir es jetzt noch einmal, um zu sehen, was passiert, wenn wir die Hilfe auf
get_commands()
verwenden, da wir jetzt einen Docstring konfiguriert haben.>>>
import
push
>>>
>>>
help
(
push
.
get_commands
)
Help
on
function
get_commands
in
module
push
:
get_commands
(
vlan
,
name
)
Get
commands
to
configure
a
VLAN
.
(
END
)
>>>
Du siehst alle Docstrings, wenn du die Hilfe benutzt. Außerdem siehst du Informationen über die Parameter und die Daten, die zurückgegeben werden, wenn sie richtig dokumentiert sind.
Wir haben jetzt die Werte für Args und Returns zum Docstring hinzugefügt.
def
get_commands
(
vlan
,
name
):
"""Get commands to configure a VLAN.
Args:
vlan (int): vlan id
name (str): name of the vlan
Returns:
List of commands is returned.
"""
commands
=
[]
commands
.
append
(
'vlan '
+
vlan
)
commands
.
append
(
'name '
+
name
)
return
commands
Diese werden jetzt angezeigt, wenn die Funktion
help()
verwendet wird, um den Nutzern dieser Funktion mehr Informationen zur Verfügung zu stellen, wie sie sie verwenden können.>>>
import
push
>>>
>>>
help
(
push
.
get_commands
)
Help
on
function
get_commands
in
module
push
:
get_commands
(
vlan
,
name
)
Get
commands
to
configure
a
VLAN
.
Args
:
vlan
(
int
):
vlan
id
name
(
str
):
name
of
the
vlan
Returns
:
List
of
commands
is
returned
.
(
END
)
-
Das Schreiben eigener Klassen wurde in diesem Kapitel nicht behandelt, weil Klassen ein fortgeschrittenes Thema sind, aber eine sehr grundlegende Einführung in ihre Verwendung wird hier gezeigt, weil sie in den folgenden Kapiteln verwendet werden.
Wir werden ein Beispiel durchgehen, in dem wir nicht nur eine Klasse verwenden, sondern auch die Klasse importieren, die Teil eines Python-Pakets ist (ein weiteres neues Konzept). Bitte beachte, dass es sich hierbei um ein Modell und ein allgemeines Beispiel handelt. Es handelt sich nicht um ein echtes Python-Paket.
>>>
from
vendors.cisco.device
import
Device
>>>
>>>
switch
=
Device
(
ip
=
'10.1.1.1'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
>>>
switch
.
show
(
'show version'
)
# output omitted
In diesem Beispiel importieren wir die Klasse
Device
aus dem Modul device.py, das Teil des Python-Pakets vendors ist (das nur ein Verzeichnis ist). Das war vielleicht etwas langatmig, aber unterm Strich sollte der Import so aussehen, wie du es in "Arbeiten mit Python-Modulen" gesehen hast , und ein Python-Paket ist einfach eine Sammlung von Modulen, die in verschiedenen Verzeichnissen gespeichert sind.Wenn du dir den Beispielcode ansiehst und ihn mit dem Import der Funktion
push_commands()
aus "Arbeiten mit Python-Modulen" vergleichst , wirst du einen Unterschied feststellen. Die Funktion wird sofort verwendet, aber die Klasse muss erst initialisiert werden.Mit dieser Anweisung wird die Klasse initialisiert:
>>>
switch
=
Device
(
ip
=
'10.1.1.1'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
Die übergebenen Argumente werden verwendet, um eine Instanz von
Device
zu erstellen. Hättest du mehrere Geräte, würdest du zu diesem Zeitpunkt etwa so aussehen:>>>
switch1
=
Device
(
ip
=
'10.1.1.1'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
switch2
=
Device
(
ip
=
'10.1.1.2'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
switch3
=
Device
(
ip
=
'10.1.1.3'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
In diesem Fall ist jede Variable eine eigene Instanz von
Device
.Hinweis
Parameter werden nicht immer verwendet, wenn eine Klasse initialisiert wird. Jede Klasse ist anders, aber wenn Parameter verwendet werden, werden sie an den sogenannten Konstruktor der Klasse übergeben; in Python ist dies die Methode
__init__
. Eine Klasse ohne Konstruktor würde wie folgt initialisiert werden:demo = FakeClass()
. Dann würdest du ihre Methoden wie folgt verwenden:demo.method()
.Sobald das Klassenobjekt initialisiert und erstellt ist, kannst du seine Methoden verwenden. Das ist genauso wie die Verwendung der eingebauten Methoden für die Datentypen, die wir weiter oben im Kapitel kennengelernt haben. Die Syntax lautet
class_object.method
.In diesem Beispiel heißt die verwendete Methode
show()
. Und sie liefert in Echtzeit Daten von einem Netzwerkgerät.Hinweis
Zur Erinnerung: Die Verwendung von Methodenobjekten einer Klasse ist genauso wie die Verwendung von Methoden der verschiedenen Datentypen wie Strings, Listen und Wörterbüchern. Obwohl das Erstellen von Klassen ein fortgeschrittenes Thema ist, solltest du wissen, wie man sie benutzt.
Wenn wir
show
fürswitch2
undswitch3
ausführen würden, bekämen wir wie erwartet die richtigen Rückgabedaten zurück, da jedes Objekt eine andere Instanz vonDevice
ist.Hier ist ein kurzes Beispiel, das die Erstellung von zwei
Device
Objekten zeigt und dann diese Objekte verwendet, um die Ausgabe vonshow hostname
auf jedem Gerät zu erhalten. Die verwendete Bibliothek gibt standardmäßig XML-Daten zurück, aber das kann bei Bedarf leicht in JSON geändert werden.>>>
switch1
=
Device
(
ip
=
'nycsw01'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
switch2
=
Device
(
ip
=
'nycsw02'
,
username
=
'cisco'
,
password
=
'cisco'
)
>>>
>>>
switches
=
[
switch1
,
switch2
]
>>>
for
switch
in
switches
:
...
response
=
switch
.
show
(
'show hostname'
)[
1
]
...
print
(
response
)
...
# output omitted
>>>
Zusammenfassung
Dieses Kapitel bot eine grundlegende Einführung in Python für Netzwerktechniker/innen. Wir haben grundlegende Konzepte wie die Arbeit mit Datentypen, Bedingungen, Schleifen, Funktionen und Dateien behandelt und sogar gezeigt, wie du ein Python-Modul erstellst, mit dem du denselben Code in verschiedenen Python-Programmen/Skripten wiederverwenden kannst. Zum Abschluss des Kapitels haben wir dir einige Tipps und Tricks sowie weitere allgemeine Informationen gegeben, die du als Nachschlagewerk nutzen solltest, wenn du deine Reise mit Python und Netzwerkautomatisierung fortsetzt.
In Kapitel 5 stellen wir dir verschiedene Datenformate wie YAML, JSON und XML vor und bauen dabei auch auf dem auf, was in diesem Kapitel behandelt wurde. Du wirst zum Beispiel lernen, Python-Module zu verwenden, um die Arbeit mit diesen Datentypen zu vereinfachen, und du wirst den direkten Zusammenhang zwischen YAML, JSON und Python-Wörterbüchern erkennen.
Get Netzwerk-Programmierbarkeit und Automatisierung now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.