Blockbereich in Python

90

Wenn Sie in anderen Sprachen codieren, erstellen Sie manchmal einen Blockbereich wie folgt:

statement
...
statement
{
    statement
    ...
    statement
}
statement
...
statement

Ein (von vielen) Zweck besteht darin, die Lesbarkeit des Codes zu verbessern: zu zeigen, dass bestimmte Anweisungen eine logische Einheit bilden oder dass bestimmte lokale Variablen nur in diesem Block verwendet werden.

Gibt es eine idiomatische Möglichkeit, dasselbe in Python zu tun?

Johan Råde
quelle
2
One purpose (of many) is to improve code readability- Richtig geschriebener Python-Code (dh nach dem Zen von Python ) würde eine solche Verzierung nicht benötigen, um lesbar zu sein. Tatsächlich ist es eines der (vielen) Dinge, die ich an Python mag.
Burhan Khalid
Ich habe versucht, mit __exit__und withAussage zu spielen , das zu ändern, globals()aber ich habe versagt.
Ruggero Turra
1
Es wäre sehr nützlich, eine variable Lebensdauer zu definieren, die mit der Ressourcenerfassung verbunden ist
Ruggero Turra
23
@ BurhanKhalid: Das ist nicht wahr. Der Zen von Python hindert Sie nicht daran, hier und da einen lokalen Bereich mit einer temporären Variablen zu verschmutzen. Wenn Sie jede Verwendung einer einzelnen temporären Variablen in die Definition einer verschachtelten Funktion umwandeln, die sofort aufgerufen wird, ist auch der Zen von Python nicht zufrieden. Das explizite Einschränken des Bereichs einer Variablen ist ein Werkzeug zur Verbesserung der Lesbarkeit, da es direkt antwortet: "Werden diese Bezeichner unten verwendet?" - Eine Frage, die sich beim Lesen des elegantesten Python-Codes stellen kann.
bluenote10
15
@ BurhanKhalid Es ist in Ordnung, keine Funktion zu haben. Aber das "Zen" zu nennen ist einfach ekelhaft.
Phil

Antworten:

79

Nein, es gibt keine Sprachunterstützung zum Erstellen eines Blockbereichs.

Die folgenden Konstrukte erstellen einen Bereich:

  • Modul
  • Klasse
  • Funktion (inkl. Lambda)
  • Generatorausdruck
  • Verständnis (diktieren, setzen, auflisten (in Python 3.x))
ThomasH
quelle
36

Die idiomatische Methode in Python besteht darin, Ihre Funktionen kurz zu halten. Wenn Sie glauben, dass Sie dies benötigen, überarbeiten Sie Ihren Code! :) :)

Python erstellt einen neuen Bereich für jedes Modul, jede Klasse, Funktion, jeden Generatorausdruck, jedes Diktatverständnis, jedes Mengenverständnis und in Python 3.x auch für jedes Listenverständnis. Abgesehen von diesen gibt es innerhalb von Funktionen keine verschachtelten Bereiche.

Sven Marnach
quelle
11
"Das Wichtigste bei der Programmierung ist die Fähigkeit, etwas zu benennen. Das zweitwichtigste ist, dass man nicht verpflichtet ist, etwas zu benennen." Zum größten Teil verlangt Python, dass Bereiche (für Variablen usw.) benannt werden. In dieser Hinsicht ist Python der zweitwichtigste Test.
Krazy Glew
18
Das Wichtigste bei der Programmierung ist die Fähigkeit, die Abhängigkeiten Ihrer Anwendung zu verwalten und Bereiche von Codeblöcken zu verwalten. Mit anonymen Blöcken können Sie die Lebensdauer von Rückrufen begrenzen, während Ihre Rückrufe ansonsten nur einmal verwendet werden, aber für die Dauer des Programms gültig sind. Dies führt zu globaler Unordnung im Bereich und beeinträchtigt die Lesbarkeit des Codes.
Dmitry
Mir ist gerade aufgefallen, dass Variablen auch lokal sind, um das Verständnis zu diktieren / festzulegen. Ich habe Python 2.7 und 3.3 ausprobiert, bin mir aber nicht sicher, ob es versionabhängig ist.
Wjandrea
1
@wjandrea Sie haben Recht - zur Liste hinzugefügt. Es sollte für diese keinen Unterschied zwischen Python-Versionen geben.
Sven Marnach
3
Ich würde den letzten Satz umformulieren, da Sie sehr gut Funktionen innerhalb von Funktionen erstellen können. Es gibt also verschachtelte Bereiche innerhalb von Funktionen.
ThomasH
17

Sie können etwas Ähnliches wie einen C ++ - Blockbereich in Python tun, indem Sie eine Funktion in Ihrer Funktion deklarieren und sie dann sofort aufrufen. Beispielsweise:

def my_func():
    shared_variable = calculate_thing()

    def do_first_thing():
        ... = shared_variable
    do_first_thing()

    def do_second_thing():
        foo(shared_variable)
        ...
    do_second_thing()

Wenn Sie sich nicht sicher sind, warum Sie dies tun möchten, kann Sie dieses Video überzeugen.

Das Grundprinzip besteht darin, alles so eng wie möglich do_first_thing()zu erfassen, ohne dass "Müll" (zusätzliche Typen / Funktionen) in einen größeren Bereich als unbedingt erforderlich eingeführt wird. Nichts anderes möchte die Methode beispielsweise verwenden, damit sie nicht außerhalb des Bereichs liegt Funktion aufrufen.

Ben
quelle
Es ist auch die Art und Weise, wie es von Google-Entwicklern in TensorFlow-Tutorials verwendet wird, wie zum Beispiel hier
Nino Filiu
13

Ich bin damit einverstanden, dass es keinen Blockumfang gibt. Aber eine Stelle in Python 3 lässt es so aussehen, als hätte es einen Blockbereich.

Was ist passiert, was diesen Blick gab? Dies funktionierte in Python 2 ordnungsgemäß, aber um die variable Leckage in Python 3 zu stoppen, haben sie diesen Trick ausgeführt, und diese Änderung lässt es so aussehen, als hätte es hier einen Blockbereich.

Lassen Sie mich erklären.


Wenn wir Variablen mit demselben Namen in denselben Bereich einführen, sollte gemäß der Idee des Bereichs der Wert geändert werden.

Dies ist, was in Python 2 passiert

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'W'

Obwohl in Python 3 die gleichnamige Variable eingeführt wird, wird sie nicht überschrieben. Das Listenverständnis verhält sich aus irgendeinem Grund wie eine Sandbox und scheint darin einen neuen Bereich zu erstellen.

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'OLD'

und diese Antwort widerspricht der Aussage von answerer @ Thomas. Das einzige Mittel zum Erstellen eines Bereichs sind Funktionen, Klassen oder Module, da dies wie ein anderer Ort zum Erstellen eines neuen Bereichs aussieht.

Harish Kayarohanam
quelle
0

Module (und Pakete) sind eine großartige pythonische Möglichkeit, Ihr Programm in separate Namespaces zu unterteilen, was ein implizites Ziel dieser Frage zu sein scheint. Als ich die Grundlagen von Python lernte, war ich frustriert über das Fehlen einer Block-Scope-Funktion. Sobald ich jedoch die Python-Module verstanden hatte, konnte ich meine vorherigen Ziele eleganter verwirklichen, ohne dass ein Blockumfang erforderlich war.

Als Motivation und um die Menschen in die richtige Richtung zu weisen, halte ich es für nützlich, explizite Beispiele für einige der Scoping-Konstrukte von Python zu geben. Zuerst erkläre ich meinen fehlgeschlagenen Versuch, Python-Klassen zum Implementieren des Blockbereichs zu verwenden. Als nächstes erkläre ich, wie ich mit Python-Modulen etwas Nützlicheres erreicht habe. Am Ende skizziere ich eine praktische Anwendung von Paketen zum Laden und Filtern von Daten.

Blockbereich mit Klassen versuchen

Für einige Momente dachte ich, ich hätte den Blockumfang erreicht, indem ich Code in eine Klassendeklaration gesteckt habe:

x = 5
class BlockScopeAttempt:
    x = 10
    print(x) # Output: 10
print(x) # Output: 5

Leider bricht dies zusammen, wenn eine Funktion definiert wird:

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(x) 
    printx2() # Output: 5!!!

Dies liegt daran, dass innerhalb einer Klasse definierte Funktionen einen globalen Bereich verwenden. Der einfachste (wenn auch nicht der einzige) Weg, dies zu beheben, besteht darin, die Klasse explizit anzugeben:

x = 5 
class BlockScopeAttempt: 
    x = 10
    print(x) # Output: 10
    def printx2(): 
        print(BlockScopeAttempt.x)  # Added class name
    printx2() # Output: 10

Dies ist nicht so elegant, da man Funktionen unterschiedlich schreiben muss, je nachdem, ob sie in einer Klasse enthalten sind oder nicht.

Bessere Ergebnisse mit Python-Modulen

Module sind statischen Klassen sehr ähnlich, aber Module sind meiner Erfahrung nach viel sauberer. Um dasselbe mit Modulen zu tun, erstelle ich eine my_module.pyim aktuellen Arbeitsverzeichnis aufgerufene Datei mit folgendem Inhalt:

x = 10
print(x) # (A)

def printx():
    global x
    print(x) # (B)

Dann mache ich es in meiner Hauptdatei oder in einer interaktiven Sitzung (z. B. Jupyter)

x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5

Zur Erklärung definiert jede Python-Datei ein Modul mit einem eigenen globalen Namespace. Durch das Importieren eines Moduls können Sie mit der .Syntax auf die Variablen in diesem Namespace zugreifen .

Wenn Sie in einer interaktiven Sitzung mit Modulen arbeiten, können Sie diese beiden Zeilen zu Beginn ausführen

%load_ext autoreload
%autoreload 2

und Module werden automatisch neu geladen, wenn die entsprechenden Dateien geändert werden.

Pakete zum Laden und Filtern von Daten

Die Idee der Pakete ist eine leichte Erweiterung des Modulkonzepts. Ein Paket ist ein Verzeichnis mit einer (möglicherweise leeren) __init__.pyDatei, die beim Import ausgeführt wird. Auf Module / Pakete in diesem Verzeichnis kann mit der .Syntax zugegriffen werden .

Für die Datenanalyse muss ich häufig eine große Datendatei lesen und dann interaktiv verschiedene Filter anwenden. Das Lesen einer Datei dauert einige Minuten, daher möchte ich es nur einmal tun. Basierend auf dem, was ich in der Schule über objektorientiertes Programmieren gelernt habe, war ich der Meinung, dass man den Code zum Filtern und Laden als Methoden in einer Klasse schreiben sollte. Ein Hauptnachteil dieses Ansatzes besteht darin, dass sich die Definition meiner Klasse ändert, wenn ich meine Filter neu definiere, sodass ich die gesamte Klasse einschließlich der Daten neu laden muss.

Heutzutage definiere ich mit Python ein Paket namens, my_datadas Submodule mit dem Namen loadund enthält filter. Innerhalb von filter.pykann ich einen relativen Import durchführen:

from .load import raw_data

Wenn ich ändere filter.py, autoreloadwerden die Änderungen erkannt. Es wird nicht neu geladen load.py, daher muss ich meine Daten nicht neu laden. Auf diese Weise kann ich meinen Filtercode in einem Jupyter-Notizbuch prototypisieren, als Funktion umschließen und dann direkt aus meinem Notizbuch ausschneiden und einfügen filter.py. Das herauszufinden hat meinen Workflow revolutioniert und mich von einem Skeptiker zu einem Gläubigen des „Zen of Python“ gemacht.

Ben Mares
quelle