Es war einmal in meiner Studienzeit, als ich von einer Python gebissen wurde, obwohl sich die Inkubationszeit verzögerte und sich herausstellte, dass ich ein Perlenprogrammierer wurde.
Irgendwann erschöpfte sich die Perle jedoch und ich entschied mich für Python. Zuerst tat ich einfach etwas und fand heraus, was für diese Aufgabe benötigt wurde. Dann wurde mir klar, dass ich systematisches Wissen brauchte und mehrere Bücher las:
- Bill Lyubanovich „Einfache Python. Moderner Programmierstil “
- Dan Bader "Reine Python. Die Feinheiten der Programmierung für Profis »
- Brett Slatkin „Python-Geheimnisse: 59 Tipps zum Schreiben von effektivem Code“
Das schien mir durchaus geeignet zu sein, um die grundlegenden Feinheiten der Sprache zu verstehen, obwohl ich mich nicht daran erinnere, Slots in ihnen erwähnt zu haben, aber ich bin mir nicht sicher, ob dies eine wirklich notwendige Funktion ist - wenn ich sie aus dem Speicher gedrückt habe, wird diese Methode höchstwahrscheinlich nicht ausreichen, aber natürlich es hängt alles von der Situation ab.
Infolgedessen habe ich einige Notizen zu den Funktionen von Python gesammelt, die meines Erachtens für jemanden nützlich sein können, der aus anderen Sprachen darauf migrieren möchte.
Mir ist aufgefallen, dass sie bei Python-Interviews häufig Fragen zu Dingen stellen, die nicht mit der tatsächlichen Entwicklung zusammenhängen, z. B. was der Schlüssel des Wörterbuchs sein könnte (oder was x = yield y
bedeutet), na ja, Leute, im wirklichen Leben kann der Schlüssel sein Nur eine Zahl oder eine Zeichenfolge. In diesen einzigartigen Fällen, in denen dies nicht der Fall ist, können Sie die Dokumentation lesen und herausfinden, warum Sie dies fragen. Um herauszufinden, was der Befragte nicht weiß? Am Ende wird sich jeder an die Antwort auf diese spezielle Frage erinnern und sie wird nicht mehr funktionieren.
Ich halte Python-Versionen über 3.5 für relevant ( es ist Zeit , die zweite Python für eine lange Zeit zu vergessen ) Dies ist die Version in Stable Debian, was bedeutet, dass es an allen anderen Stellen neuere Versionen gibt.
Da ich überhaupt kein Python-Guru bin, hoffe ich, dass sie mich in den Kommentaren korrigieren, wenn ich plötzlich eine Art Dummheit einfriere.
Tippen
Python ist eine dynamisch typisierte Sprache, d.h. Zur Laufzeit wird nach Typübereinstimmungen gesucht, zum Beispiel:
cat type.py a=5 b='5' print(a+b)
durchführen:
python3 type.py ... TypeError: unsupported operand type(s) for +: 'int' and 'str'
Wenn Ihr Projekt jedoch auf die Notwendigkeit einer statischen Typisierung ausgereift ist, bietet Python auch eine solche Möglichkeit, indem Sie den statischen Analysator mypy
verwenden:
mypy type.py type.py:3: error: Unsupported operand types for + ("int" and "str")
Es stimmt, nicht alle Fehler werden auf folgende Weise abgefangen:
cat type2.py def greeting(name): return 'Hello ' + name greeting(5)
mypy wird hier nicht schwören, aber während der Ausführung tritt ein Fehler auf, sodass die aktuellen Versionen von Python eine spezielle Syntax zum Angeben von Arten von Funktionsargumenten unterstützen:
cat type3.py def greeting(name: str) -> str: return 'Hello ' + name greeting(5)
und jetzt:
mypy type3.py type3.py:4: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"
Variablen und Daten
Variablen in Python speichern keine Daten, sondern verweisen nur auf diese. Die Daten können veränderlich (veränderlich) und unveränderlich (unveränderlich) sein.
Dies führt je nach Datentyp in nahezu identischen Situationen zu unterschiedlichem Verhalten, z. B. einem solchen Code:
x = 1 y = x x = 2 print(y)
führt dazu, dass sich die Variablen x
und y
auf unterschiedliche Daten beziehen, und dies:
x = [1, 2, 3] y = x x[0] = 7 print(y)
Nein, x
und y
bleiben Links zu derselben Liste (obwohl das Beispiel , wie in den Kommentaren erwähnt, nicht sehr erfolgreich ist, aber ich habe es noch nicht besser gefunden), die Sie übrigens in Python mit dem Operator is überprüfen können (ich bin sicher, dass der Ersteller von Java für immer guten Schlaf verloren hat aus Scham, als ich in Python von diesem Operator erfuhr).
Obwohl Zeilen wie eine Liste aussehen, handelt es sich um einen unveränderlichen Datentyp. Dies bedeutet, dass die Zeichenfolge selbst nicht geändert werden kann. Sie können nur eine neue generieren, der Variablen jedoch einen anderen Wert zuweisen, obwohl sich die ursprünglichen Daten nicht ändern:
>>> mystr = 'sss' >>> newstr = mystr # >>> mystr[0] = 'a' ... TypeError: 'str' object does not support item assignment >>> mystr = 'ssa' # >>> newstr # 'sss'
Apropos Zeichenfolgen: Aufgrund ihrer Immunität ist das Verketten einer sehr großen Liste von Zeichenfolgen durch Hinzufügen oder Anhängen in einer Schleife möglicherweise nicht sehr effektiv (abhängig von der Implementierung in einem bestimmten Compiler / einer bestimmten Version). In solchen Fällen wird normalerweise empfohlen, die Join- Methode zu verwenden, die sich verhält ein bisschen unerwartet:
>>> str_list = ['ss', 'dd', 'gg'] >>> 'XXX'.join(str_list) 'ssXXXddXXXgg' >>> str = 'hello' >>> 'XXX'.join(str) 'hXXXeXXXlXXXlXXXo'
Erstens wird die Zeile, in der die Methode aufgerufen wird, zu einem Trennzeichen und nicht zum Anfang einer neuen Zeile, wie man meinen könnte, und zweitens müssen Sie eine Liste (ein iterierbares Objekt) und keine separate Zeile übergeben, da es sich auch um ein iterierbares Objekt handelt und symbolisiert wird .
Da Variablen Verknüpfungen sind, ist es ganz normal, eine Kopie eines Objekts erstellen zu wollen, um das ursprüngliche Objekt nicht zu beschädigen. Es gibt jedoch eine Gefahr: Die Kopierfunktion kopiert nur eine Ebene, was eindeutig nicht von einer Funktion mit diesem Namen erwartet wird. Verwenden Sie daher deepcopy
.
Ein ähnliches Problem beim Kopieren kann auftreten, wenn eine Sammlung mit einem Skalar multipliziert wird, wie hier erläutert.
Geltungsbereich
Das Scope-Thema verdient wahrscheinlich einen separaten Artikel, aber es gibt eine gute Antwort auf SO .
Kurz gesagt, der Bereich ist lexikalisch und es gibt sechs Sichtbarkeitsbereiche - Variablen im Hauptteil der Funktion, im Abschluss, im Modul, im Klassenkörper, integrierte Python-Funktionen und Variablen in der Liste und andere Einschlüsse.
Es gibt eine Subtilität: Die Standardvariable kann in lexikalisch verschachtelten Namespaces gelesen werden. Für die Änderung müssen jedoch spezielle Schlüsselwörter verwendet werden, die nicht nonlocal
und global
, um die Variablen eine Ebene höher bzw. die globale Sichtbarkeit zu ändern.
Zum Beispiel ein Code wie dieser:
x = 7 print(id(x)) def func(): print(id(x)) return x print(func())
Es funktioniert mit einer globalen Variablen und dieser:
x = 7 print(id(x)) def func(): x = 1 print(id(x)) return x print(func()) print(x)
bringt bereits einen lokalen hervor.
Aus meiner Sicht ist dies im Prinzip nicht sehr gut. Jede Verwendung nicht lokaler Variablen in einer Funktion ist Teil der öffentlichen Schnittstelle der Funktion, ihrer Signatur, was bedeutet, dass sie zu Beginn der Funktion explizit und sichtbar deklariert werden sollte. Außerdem sind die Schlüsselwörter nicht sehr informativ - global
klingt wie eine Definition einer globalen Funktion, bedeutet aber tatsächlich, use global
.
In Python gibt es keinen obligatorischen Einstiegspunkt, von dem aus das Programm gestartet wird, wie dies in vielen Sprachen der Fall ist. Nur alles, was auf Modulebene geschrieben wird, wird nacheinander ausgeführt. Da Variablen auf Modulebene jedoch globale Variablen sind, sollte dies aus meiner Sicht eine gute Praxis sein Cramming des Hauptcodes in die main()
Funktion, gefolgt von seinem Aufruf am Ende der Datei:
if __name__ == '__main__': main()
Diese Bedingung funktioniert, wenn die Datei als Skript aufgerufen und nicht als Modul importiert wird.
Funktionsargumente
Python bietet einfach schicke Möglichkeiten zum Definieren von Funktionsargumenten - positionelle, benannte Argumente und deren Kombinationen.
Aber Sie müssen verstehen, wie Argumente übergeben werden - weil In Python sind alle Variablen Links zu Daten. Dann können Sie davon ausgehen, dass die Übertragung als Referenz erfolgt. Es gibt jedoch eine Besonderheit: Der Link selbst wird als Wert übergeben, d. h. Sie können den veränderlichen Wert durch Referenz ändern:
def add_element(mylist): mylist.append(3) mylist = [1,2] add_element(mylist) print(mylist)
durchführen:
python3 arg_modify.py [1, 2, 3]
Sie können den ursprünglichen Link in einer Funktion jedoch nicht überschreiben:
def try_del(mylist): mylist = [] return mylist mylist = [1,2] try_del(mylist) print(mylist)
Der Quelllink ist aktiv und funktioniert:
python3 arg_kill.py [1, 2]
Sie können auch Standardwerte für die Argumente festlegen, aber eines ist nicht offensichtlich: Die Standardwerte werden beim Definieren der Funktion einmal berechnet. Dies verursacht keine Probleme, wenn Sie unveränderte Daten als Standardwert übergeben und wenn Sie übergeben Bei variablen Daten oder dynamischen Werten ist das Ergebnis etwas unerwartet:
veränderbare Daten:
cat arg_list.py def func(arg = []): arg.append('x') return arg print(func()) print(func()) print(func())
Ergebnis:
python3 arg_list.py ['x'] ['x', 'x'] ['x', 'x', 'x']
dynamischer Wert:
cat arg_now.py from datetime import datetime def func(arg = datetime.now()): return arg print(func()) print(func()) print(func())
wir bekommen:
python3 arg_now.py 2018-09-28 10:28:40.771879 2018-09-28 10:28:40.771879 2018-09-28 10:28:40.771879
OOP
OOP in Python wurde sehr interessant gemacht (einige Eigenschaften sind es wert) und dies ist ein großes Thema, aber Sapiens, die mit OOP vertraut sind, können alles googeln (oder es auf dem Hub finden ), was er will, so dass es keinen Sinn macht, es zu wiederholen, obwohl es sich lohnt, festzulegen, dass Python ein wenig sein sollte eine andere Philosophie - ist , dass ein Programmierer intelligenter Maschinen und keine Bedrohung (UPD: mehr ), so dass die python - Standard nicht üblich für andere Sprachen Zugriffsmodifikatoren ist: durch Hinzufügen eines doppelten Unterstrich private Methoden implementiert (die die Laufzeit des Methodennamen ändert , ist nicht LRV Thread Chance, es zu benutzen) und geschützter einen Unterstrich (die nichts tun, es ist nur eine Namenskonvention).
Diejenigen, die die übliche Funktionalität vermissen, können nach Versuchen suchen, solche Möglichkeiten für Python bereitzustellen. Einige Optionen ( lang , python-access ) wurden von mir gegoogelt, aber ich habe sie nicht getestet oder studiert.
Das einzige Minus der Standardklassen ist der Boilerplate-Code in allen Dunder-Methoden . Ich persönlich mag die attrs- Bibliothek, sie ist viel pythonischer.
Es ist erwähnenswert, dass in Python alle Objekte, einschließlich Funktionen und Klassen, Klassen dynamisch (ohne Verwendung von eval
) durch die Typfunktion erstellt werden können.
Es lohnt sich auch, über Metaklassen ( auf dem Habr ) und Deskriptoren ( Habr ) zu lesen.
Eine Besonderheit, an die man sich erinnern sollte, ist, dass die Attribute einer Klasse und eines Objekts nicht dasselbe sind. Bei unveränderlichen Attributen verursacht dies keine Probleme, da die Attribute "Shadowing" sind - Attribute des Objekts mit demselben Namen werden automatisch erstellt, bei veränderlichen Attributen jedoch bekomme nicht ganz was erwartet wurde:
cat class_attr.py class MyClass: storage = [7,] def __init__(self, number): self.number = number obj = MyClass(1) obj2 = MyClass(2) obj.number = 5 obj.storage.append(8) print(obj2.storage, obj2.number)
wir bekommen:
python3 class_attr.py [7, 8] 2
Wie Sie sehen können, haben sie obj
geändert, und der storage
sich auch in obj2
geändert. Dieses Attribut gehört (im Gegensatz zu number
) nicht zur Instanz, sondern zur Klasse.
Konstanten
Da im Fall von Zugriffsmodifikatoren Python nicht versucht, den Entwickler einzuschränken, ist es unmöglich, eine vor Änderungen geschützte skalare Variable auf standardmäßige Weise zu definieren. Es besteht lediglich eine Vereinbarung, dass Variablen mit einem Namen in Großbuchstaben als Konstanten betrachtet werden sollten.
Python hingegen verfügt über unveränderliche Datenstrukturen wie Tupel. Wenn Sie also eine globale Struktur wie eine Konfiguration unveränderlich machen und keine zusätzlichen Abhängigkeiten wünschen, ist namedtuple eine gute Wahl, obwohl die Beschreibung der Typen etwas aufwändiger sein muss Ich mag die alternative Implementierung der unveränderlichen Struktur mit Punktnotation - Box (siehe gefrorener_Box-Parameter).
Wenn Sie skalare Konstanten wünschen, können Sie die Zugriffssteuerung in der Phase der "Kompilierung" implementieren, d. H. prüft durch mypy, Beispiel und Details .
.sort () vs sortiert ()
Es gibt zwei Möglichkeiten, eine Liste in Python zu sortieren. Die erste ist die .sort()
-Methode, die die ursprüngliche Liste ändert und nichts (keine) zurückgibt, d. H. kann das nicht:
my_list = my_list.sort()
Die zweite ist die sorted()
Funktion, die eine neue Liste erzeugt und mit allen iterierbaren Objekten arbeiten kann. Wer mehr Infos will, sollte mit SO beginnen .
Standardbibliothek
Normalerweise enthält die Standard-Python-Bibliothek hervorragende Lösungen für häufig auftretende Probleme. Es lohnt sich jedoch, kritisch zu sein, da es genügend Kuriositäten gibt. Es kommt zwar auch vor, dass sich das, was auf den ersten Blick seltsam erscheint, als die beste Lösung herausstellt. Sie müssen nur alle Bedingungen kennen (Reichweite siehe unten), aber es gibt immer noch Kuriositäten.
Zum Beispiel hat das unittest Unit-Modul, das mit dem Kit geliefert wird, nichts mit Python und einem Hauch von Java zu tun, wie der Autor des Pythons sagt : "Jeder benutzt py.test ...". Obwohl sehr interessant, wenn auch nicht immer geeignet, ist das Doctest- Modul Standard.
Das mitgelieferte urllib- Modul verfügt nicht über eine so schöne Schnittstelle wie das Anforderungsmodul eines Drittanbieters.
Die gleiche Geschichte mit dem Modul zum Parsen von Befehlszeilenparametern - das mitgelieferte Argparse ist eine Demonstration der OOP des Gehirns, und das docopt- Modul scheint nur eine intelligente Lösung zu sein - ultimative Selbstdokumentation ! Obwohl Gerüchten zufolge trotz docopt und for click eine Nische bleibt.
Auch mit dem Debugger - so wie ich es verstehe, verwenden nur wenige Leute die im Paket enthaltene pdb , es gibt viele Alternativen, aber es scheint, dass die Mehrheit der Entwickler ipdb verwendet , was aus meiner Sicht am bequemsten über das Debug- Wrapper-Modul zu verwenden ist.
Anstatt import ipdb;ipdb.set_trace()
einfach ein import debug
schreiben, und es wird ein see- Modul zur einfachen Überprüfung von Objekten import ipdb;ipdb.set_trace()
.
Um das Standard-Serialisierungsmodul zu ersetzen, wird Pickle übrigens aus Dill hergestellt. Beachten Sie, dass diese Module seitdem nicht für den Datenaustausch in externen Systemen geeignet sind Das Wiederherstellen von beliebigen Objekten, die von einer unkontrollierten Quelle empfangen wurden, ist unsicher. In solchen Fällen gibt es json (für REST) und gRPC (für RPC).
Um das Standardmodul für die Verarbeitung regulärer Ausdrücke zu ersetzen, erstellt re das Regex- Modul mit allen möglichen zusätzlichen Extras, z. B. den Zeichenklassen ala \p{Cyrillic}
.
Übrigens ist für Python kein lustiger Debugger für Regexes ähnlich wie Perlgerste aufgetaucht.
Hier ist ein weiteres Beispiel: Eine Person hat ihr In-Place- Modul erstellt, um die Krümmung und Unvollständigkeit der API des Standard- Dateieingabemoduls im vorhandenen Teil der Dateibearbeitung zu beheben.
Nun, ich denke oft an solche Fälle, da ich sogar auf mehr als einen gestoßen bin. Seien Sie also vorsichtig und vergessen Sie nicht, alle möglichen nützlichen Listen anzusehen. Ich denke, ein guter Ernährungsberater hat eine Nase für das Maß der Rationalität der Lösung. Dies ist übrigens ein Thema für eine andere Diskussion. Nach meinen Gefühlen (natürlich gibt es keine Statistiken zu diesem Thema und kann es anscheinend nicht sein) ist das Niveau der Spezialisten in der Python-Welt überdurchschnittlich, da sich oft herausstellt, dass gute Software in Python geschrieben ist. Schreiben Sie in die Kommentare, was Sie darüber denken.
Parallelität und Wettbewerb
Python bietet zahlreiche Möglichkeiten für parallele und wettbewerbsfähige Programmierung, jedoch nicht ohne Funktionen.
Wenn Sie Parallelität benötigen und dies geschieht, wenn Ihre Aufgaben eine Berechnung erfordern, sollten Sie auf das Multiprozessor- Modul achten.
Und wenn Ihre Aufgaben viele E / A- Erwartungen haben, bietet Python eine Vielzahl von Optionen zur Auswahl, von Threads und Gevent bis hin zu Asyncio .
Alle diese Optionen scheinen für die Verwendung gut geeignet zu sein (obwohl Threads viel mehr Ressourcen erfordern), aber es besteht das Gefühl, dass Asyncio den Rest langsam herausdrückt, auch dank aller Arten von Extras wie uvloop .
Wenn jemand es nicht bemerkt hat - in Python geht es in Threads nicht um Parallelität, ich bin nicht kompetent genug, um gut über GIL zu sprechen, aber es gibt genug Material zu diesem Thema, daher gibt es keine solche Notwendigkeit. Die Hauptsache ist, dass die Threads in Python (genauer gesagt) in CPython) verhalten sie sich anders als andere Programmiersprachen - sie werden nur auf einem Kern ausgeführt, was bedeutet, dass sie nicht für Fälle geeignet sind, in denen echte Parallelität erforderlich ist. Die Thread-Ausführung wird jedoch angehalten, wenn auf Eingabe / Ausgabe gewartet wird, sodass sie verwendet werden können zu konkurrieren.
Andere Kuriositäten
In Python ist a = a + b
nicht immer äquivalent zu a += b
:
a = [1] a = a + (2,3) TypeError: can only concatenate list (not "tuple") to list a += (2,3) a [1, 2, 3]
Ich sende es für Details an SO , bis ich die Zeit gefunden habe, herauszufinden, warum es so ist, in dem Sinne, aus welchem Grund sie es getan haben, so geht es wieder um Veränderlichkeit.
Seltsamkeiten, die keine Seltsamkeiten sind
Auf den ersten Blick kam es mir seltsam vor, dass der Bereichstyp nicht den rechten Rand enthält, aber dann sagte mir eine freundliche Person zu ignoramus, wo ich lernen muss, und es stellte sich heraus, dass alles ziemlich logisch ist.
Ein separates großes Thema ist das Runden (obwohl dieses Problem für fast alle Programmiersprachen gleich ist), zusätzlich zur Verwendung der Rundung, wie Sie möchten, mit der Ausnahme, dass jeder im Mathematikkurs Mathematik studiert hat, da die Probleme der Darstellung von Gleitkommazahlen immer noch überlagert sind ausführlicher Artikel .
Grob gesagt wird anstelle des für die Schulmathematik üblichen Halbrundenalgorithmus der Halbrundalgorithmus verwendet, der die Wahrscheinlichkeit von Verzerrungen bei der statistischen Analyse verringert und daher vom IEEE 754-Standard empfohlen wird.
Ich konnte auch nicht verstehen, warum -22//10=-3
, und dann wies eine andere freundliche Person darauf hin, dass dies unweigerlich aus der mathematischen Definition selbst folgt, wonach der Rest nicht negativ sein kann, was zu solch ungewöhnlichem Verhalten für führt negative Zahlen.
ACHTUNG! Das ist wieder eine seltsame Sache und ich verstehe nichts, siehe diesen Thread .
Debugging für reguläre Ausdrücke
Und hier stellte sich heraus, dass es in der Python-Welt kein Tool zum interaktiven Debuggen regulärer Ausdrücke gibt, das dem hervorragenden Perlenmodul Regexp :: Debugger ( Videopräsentation ) ähnelt. Natürlich gibt es eine Reihe von Online-Tools, es gibt eine Art von Windows-eigenen Lösungen, aber für mich ist das nicht so. Es kann sich lohnen, ein Perlenbar-Werkzeug zu verwenden, da sich Python-Rexes nicht sehr von Perlenbarren unterscheiden. Ich schreibe eine Anleitung für diejenigen, die keine Perlenbarren besitzen:
sudo apt install cpanminus cpanm Regexp::Debugger perl -I ~/perl5/lib/perl5/ -E "use Regexp::Debugger; 'ababc' =~ /(a|b) b+ c/x"
Ich denke, selbst eine Person, die mit der Perle nicht vertraut ist, wird verstehen, wo die Zeile eingegeben werden muss und wo der reguläre Ausdruck steht. x
ist eine Flagge, die der Python re.VERBOSE ähnlich ist.
Drücken Sie s
und gehen Sie den regulären Ausdruck durch, eine detaillierte Beschreibung der verfügbaren Befehle in der Dokumentation .
Die Dokumentation
In Python gibt es eine Hilfefunktion, mit der Sie Hilfe zu jeder geladenen Funktion (aus der Dokumentzeichenfolge) erhalten können. Der Name der Funktion wird als Parameter übergeben:
$ python3 >>> help(help)
Dies ist jedoch nicht immer ein bequemer Weg und es ist oft bequemer, das Dienstprogramm pydoc zu verwenden:
pydoc3 urllib.parse.urlparse
Mit dem Dienstprogramm können Sie nach Schlüsselwörtern suchen und sogar einen lokalen Server mit HTML-Dokumentation starten, letzteres habe ich jedoch nicht getestet.