Effektiv Python war effektiv, wie der Name schon sagt

Zusammenfassung

Ich habe mir Effective Python kurz angesehen. Ich denke, es ist ein sehr gutes Buch. Ich denke, ich bin ein Anfänger in Python. Ich denke, dass es eine Rolle bei der Überbrückung von Anfängern und Fortgeschrittenen spielt, mehr als genug, weil es Dinge enthält, über die ich bis zu einem gewissen Grad nachdenke und die mir in den Sinn kommen. Ich bin ein großer Google-Experte. Als Entwickler gibt es einige Erklärungen, die für mich nicht intuitiv nützlich sind, da ich viel kleinen Code schreibe, aber es ist nicht Brett Slatkins Schuld, weil ich nicht allein bin.

Es ist leicht zu vergessen, wenn ich es nur lese. Ich möchte einige der Punkte zusammenfassen, die mich beeindruckt haben. Der hier verwendete Code ist Github [ ^ 1].

Abdruckpunktzusammenfassung (Promotion)

Da einige Auszüge veröffentlicht werden, kann es schwierig sein, den Kontext zu erfassen, aber bitte kaufen Sie ihn und lesen Sie ihn.

Verwenden Sie die Generatorformel!

Sie können Generatorausdrücke verwenden, um eine große Anzahl von Eingaben nacheinander zu verarbeiten:

import random

with open('my_file.txt', 'w') as f:
    for _ in range(10):
        f.write('a' * random.randint(0, 100))
        f.write('\n')

it = (len(x) for x in open('my_file.txt'))
print(next(it))
print(next(it))

Dies alles auf einmal mit einer Liste oder einem Taple zu tun, verbraucht viel Speicher. Ich denke, es ist eine ziemlich bekannte Methode, aber Expert Python Programming /books/books/detail/978-4-04-868629-7.shtml) war auch beeindruckend, dass die Generatorformel dringend empfohlen wurde.

Verwenden Sie nicht "für ... sonst, während ... sonst"

Es ist ein "für ... sonst", das nicht nützlich zu sein scheint, aber es scheint die beste Vorgehensweise zu sein, es doch nicht zu verwenden. Der Grund dafür ist, dass sich der Kontext vom allgemeinen "sonst" unterscheidet. Wenn der vorherige Block fehlschlägt Es ist normal, zu elsezu springen, aberfor ... else führt einen else-Block aus, wenn die for-Anweisung erfolgreich abgeschlossen wurde (ohne break`). Dies beeinträchtigt die Lesbarkeit. Das ist.

Als ich es zum ersten Mal las, dachte ich: "Was ist das? ...", aber ** "Effektiv ..." betont konsequent "generisch sein". ** Programmierung Die Behauptung, dass eine Verwendung, die die Bedeutung von "sonst" in der allgemeinen Sprache untergräbt, vermieden werden sollte, scheint jetzt, da ich sie gelesen habe, natürlich zu sein. Ich denke, es macht Sinn zu lesen.

Was ist eine Schließung?

Eine Schließung ist ein staatlicher Mechanismus, aber ich bin mir nicht sicher, zum Beispiel:

def closure():
    flag = False

    def getter():
        print(flag)
    return getter


func = closure()
func() # False

func () enthält eine Variable namens flag. Es ist praktisch, diese Technik zu verwenden, um globale Variablen zu reduzieren.

Eine Sache, die Sie beachten sollten, ist, dass traurige Dinge passieren, wenn es um Aufgaben geht:

def closure():
    flag = False

    def setter():
        flag = True
    setter()
    return flag


print(closure()) # False

Ich versuche "flag = True" mit "setter ()", aber der Rückgabewert von "schloss ()" ist "False", da sich im Bereich "setter ()" kein "flag" im Bereich befindet. Zuweisungen wurden in als Variablendefinitionen behandelt. Wenn Sie Schließungen nutzen möchten, müssen Sie mit Zuweisungen sehr vorsichtig sein. Um dieses Problem zu lösen, verwenden Sie "nicht lokal":

def closure():
    flag = False

    def setter():
        nonlocal flag
        flag = True
    setter()
    return flag


print(closure()) # True

Dies bezieht sich auf den nächsthöheren Bereich. Die Wurzel dieses Problems liegt darin, dass Variablendefinitionen und -zuweisungen in Python dieselbe Syntax haben. Um dies zu lösen, ähneln Variablendefinitionen Javascript. Es scheint, dass vorgeschlagen wurde, "var flag = False" auf "var flag = False" zu setzen, aber es scheint, dass "nonlocal" unter Berücksichtigung der Kompatibilität übernommen wurde.

Verschlüsse sind praktisch, aber die Verwendung von "nicht lokal" in großen Funktionen erschwert das Erfassen. Daher scheint es in solchen Fällen besser zu sein, Klassen zu verwenden.

Achten Sie auf die Position des Keyword-Arguments

Im Detail müssen Schlüsselwortargumente nach Positionsargumenten angegeben werden:

def remainder(number, divisor):
    return number % divisor


remainder(20, 7)                # OK
remainder(20, divisor=7)        # OK
remainder(number=20, divisor=7) # OK
remainder(divisor=7, number=20) # OK
remainder(number=20, 7)         # NG

Verschachteln Sie keine Wörterbücher! Klasse verwenden

Wörterbücher eignen sich sehr gut als Container, aber es kann sehr schwierig sein, sie so zu verschachteln, dass sie sich in den Wörterbüchern befinden. Teilen Sie sie vorher in Klassen auf:

import collections
Grade = collections.namedtuple('Grade', ('score', 'weight'))


class Subject(object):
    def __init__(self):
        self._grades = []

    def report_grade(self, score, weight):
        self._grades.append(Grade(score, weight))

    def average_grade(self):
        total, total_weight = 0, 0
        for grade in self._grades:
            total += grade.score * grade.weight
            total_weight += grade.weight
        return total / total_weight


class Student(object):
    def __init__(self):
        self._subjects = {}

    def subject(self, name):
        if name not in self._subjects:
            self._subjects[name] = Subject()
        return self._subjects[name]

    def average_grade(self):
        total, count = 0, 0
        for subject in self._subjects.values():
            total += subject.average_grade()
            count += 1
        return total / count


class Gradebook(object):
    def __init__(self):
        self._students = {}

    def student(self, name):
        if name not in self._students:
            self._students[name] = Student()
        return self._students[name]


book = Gradebook()
albert = book.student('Albert Einstein')
math = albert.subject('Math')
math.report_grade(80, 0.10)
math.report_grade(80, 0.10)
math.report_grade(70, 0.80)
gym = albert.subject('Gym')
gym.report_grade(100, 0.40)
gym.report_grade(85, 0.60)
print(albert.average_grade())

Es ist ein bisschen lang, aber lasst es uns gut lesen. ** Es scheint eine Antwort auf die Frage zu sein: "Was ist ein Klassendesign, das einfach zu warten und zu erweitern ist?" **

Die Schichtstruktur von "1. Notenbuch → 2. Schüler → 3. Fächer → 4. Noten" wird durch die Instanzvariablen (Wörterbücher) jedes Objekts realisiert, das die Objekte in der unteren Hierarchie enthält. Es gibt eine Methode zum Festlegen einer Instanz einer niedrigeren Klasse auf eine Instanzvariable und eine Methode zum Berechnen der durchschnittlichen Punktzahl. Wenn Sie genauer hinschauen, werden Sie feststellen, dass dieser Code in hohem Maße erweiterbar ist. Es ist gefährlich zu versuchen, es mit einem Wörterbuch zu realisieren.

Ich bin mir nicht sicher, ob der obige Code intuitiv und unkompliziert ist. Ich habe ihn nicht sofort verstanden. Aber als ich ihn wusste, war ich überzeugt, dass es eine sehr einfache Idee war. Andererseits konnte ich ihn verstehen. Mancher Code fühlt sich immer noch wie "Warum haben Sie eine so chaotische Implementierung?" An. ** Leicht verständlicher Code ist nicht immer guter Code. Leicht zu erklärender Code ist (wahrscheinlich) guter Code. ** Zen of Python sagt auch, dass:

Although that way may not be obvious at first unless you're Dutch. Die Methode ist auf den ersten Blick möglicherweise schwer zu verstehen. Es kann nur für Niederländer leicht zu verstehen sein.

If the implementation is hard to explain, it's a bad idea. Wenn es schwierig ist zu erklären, was der Code ist, ist es eine schlechte Implementierung.

If the implementation is easy to explain, it may be a good idea. Wenn Sie den Inhalt Ihres Codes leicht erklären können, ist dies wahrscheinlich eine gute Implementierung.

aus Das Zen von Python

Wo benutzt du @ classmethod?

Es scheint gut zu sein, es für die Methode zu verwenden, die ein eigenes Objekt erstellt. Betrachten Sie die folgende übergeordnete Klasse / untergeordnete Klasse:

from tempfile import TemporaryDirectory

class GenericInputData(object):
    def read(self):
        raise NotImplementedError

    @classmethod
    def generate_inputs(cls, config):
        raise NotImplementedError


class PathInputData(GenericInputData):
    def __init__(self, path):
        super().__init__()
        self.path = path

    def read(self):
        return open(self.path).read()

    @classmethod
    def generate_inputs(cls, config):
        data_dir = config['data_dir']
        for name in os.listdir(data_dir):
            yield cls(os.path.join(data_dir, name))


class GenericWorker(object):
    def __init__(self, input_data):
        self.input_data = input_data
        self.result = None

    def map(self):
        raise NotImplementedError

    def reduce(self, other):
        raise NotImplementedError

    @classmethod
    def create_workers(cls, input_class, config):
        workers = []
        for input_data in input_class.generate_inputs(config): #Erstellen Sie eine Instanz der Klasse, die als Argument verwendet wird
            workers.append(cls(input_data)) #Erstellen Sie Ihre eigene Instanz
        return workers #Gibt eine eigene Instanz zurück


class LineCountWorker(GenericWorker):
    def map(self):
        data = self.input_data.read()
        self.result = data.count('\n')

    def reduce(self, other):
        self.result += other.result  


with TemporaryDirectory() as tmpdir:
    config = {'data_dir': tmpdir}
    workers = LineCountWorker.create_workers(PathInputData, config)

Das ist auch lang, aber gib dein Bestes! Es gibt zwei wichtige Punkte.

Das erste ist, dass die Klassenmethode der Klasse selbst für das Erstellen der Instanz verantwortlich ist. ** Wenn neue untergeordnete Klassen hinzugefügt werden, ist es die Verwaltung von "wo und wer die Instanz der Klasse erstellt". Im obigen Beispiel erstellt "LineCountWorker.create_workers ()" eine eigene Instanz unter "with" und erstellt darin eine Instanz von "PathInputData" [^ 2]. Mit anderen Worten, "wo" und "wer macht es" sind klar.

Die zweite ist eng mit der ersten verwandt. Dies bedeutet, dass " cls durch die aufgerufene Klasse ersetzt wird ". Nun, es klingt offensichtlich, ist aber sehr wichtig. Im obigen Code * * cls (input_data) definiert inGenericWorker.create_workers ()wird beim Aufruf über die untergeordnete Klasse LineCountWorker alsLineCountWorker (input_data)verstümmelt. ** Dank dessen` Selbst wenn Sie eine neue abgeleitete Klasse "HogeCountWorker" erstellen, deren übergeordnete Klasse "GenericWorker" ist, erstellt "HogeCountWorker.create_workers ()" immer noch eine Instanz davon, und in "HogeCountWorker" wird eine neue Methode "create_workers" erstellt. Es ist nicht erforderlich. Obwohl es sich um eine Methode handelt, die von der übergeordneten Klasse geerbt wurde, wechselt sie beim Aufruf von der abgeleiteten Klasse schnell zu einer Methode, die diesem untergeordneten Element gewidmet ist! Dies ist das "Generikum", über das in "Effektiv ..." oft gesprochen wird.

Wieder Beförderung.

Es gibt viele andere nützliche Dinge, wie Tipps zu Iteratorgeneratoren und zur Verwendung von "@ property". Bitte kaufen Sie es und lesen Sie es [^ 3].

Nicht so gut

Effective Python ist ein großartiges Buch, aber es gibt einige Dinge, auf die ich neugierig bin. ** Qualität der Übersetzung. ** Zum Beispiel ...

Beispiel 1
(Original) You'd want a similar abstract interface for the MapReduce worker that consumes the input data in a standard way.
(Übersetzung) Angenommen, Sie möchten eine ähnliche abstrakte Schnittstelle für MapReduce Worker, die standardmäßig Eingabedaten verwenden.
Beispiel 2
(Original) Here, I extend the InputData class with a generic class method that's responsible for creating new InputData instances using a common interface.
(Übersetzung) Erweitern Sie die InputData-Klasse und fügen Sie eine generische Klassenmethode hinzu, die für die Erstellung neuer InputData-Instanzen mit einer gemeinsamen Schnittstelle verantwortlich ist.

Hmm ... es ist subtil. Ich hatte viele Zweifel, ob ich den Inhalt wirklich verstanden und geschrieben habe. Insbesondere "Kapitel 3: Klasse und Vererbung" war schmerzhaft. Ich machte mein Unverständnis für die Übersetzung verantwortlich. Es war so sehr, dass ich wollte ... Nein, es tut mir leid, dass ich nicht klug genug bin.

Der Inhalt selbst ist jedoch immer noch wunderbar. Wer gut Englisch kann, sollte den Originaltext lesen.

abschließend

Ich denke, dass das Wichtigste in Lernbüchern, nicht nur das Programmieren, ein "ausgezeichnetes Einführungsbuch" ist. Es gibt eine hohe Barriere zwischen Anfängern und Anfängern, um Schüler zu sehen, die Mathematik aufgeben, und Studenten, die in C-Sprache abbrechen. Es ist offensichtlich.

Zum Glück ist Python voll von Einführungsbüchern. Wenn Sie eine große Anzahl von Büchern haben, gibt es ausgezeichnete Bücher unter ihnen. Was ist dagegen mit japanischen Büchern für Anfänger und Fortgeschrittene? Es gibt viele Python-Bücher von O'Reilly, aber die einzigen Anfängerbücher, die eine breite Palette von Menschen abdecken, sind "Effective Python" und "Practical Python 3", und mit Ausnahme von O'Reilly ist "Expert Python Programming" wahrscheinlich das einzige [^ 4]. Ich bin wirklich froh, dass einer von ihnen, "Effective Python", mit dieser Perfektion veröffentlicht wurde.

Nachschlagewerke

Recommended Posts

Effektiv Python war effektiv, wie der Name schon sagt
Holen Sie sich den Hostnamen in Python
[Python] Ruft den Variablennamen mit str ab
Legen Sie den Prozessnamen des Python-Programms fest
Der Dateiname war in Python schlecht und ich war süchtig nach Import
[Python] Ich habe versucht, den Funktionsnamen durch den Funktionsnamen zu ersetzen
Was! Der entpackte Dateiname war SJIS! Befehl?
[Python] Ich habe versucht, den Typnamen als Zeichenfolge aus der Typfunktion abzurufen
So erhalten Sie den Variablennamen selbst in Python
Geben Sie die Zeit ab dem Start des Programms in Python aus
Python-Programm, das nach demselben Dateinamen sucht
Geben Sie MinGW als den in Python verwendeten Compiler an
Finden Sie das maximale Python
[Python] Teilen Sie das Datum
Namensidentifikation mit Python
Effektives Python-Memo Punkt 3
Ich war 2 Minuten lang süchtig nach Python-Debugger-PDF
[Python] Ändern Sie den Namen der Bilddatei in eine Seriennummer
Holen Sie sich nur die Python-Version (z. B. 2.7.5) in die CentOS 7-Shell
In Python werden die Elemente in der Liste sortiert und als Elemente und Vielfache ausgegeben.
'De' == 'De' ist falsch! Eigentlich war das zweite "de" zwei Buchstaben. [Python]
Versuchen Sie, die verstümmelten Zeichen im angehängten Dateinamen mit Python zu entschlüsseln