Globals vs Parameter für Funktionen mit den gleichen Parametern

Ich lese durch "Making Games mit Python & Pygame" und ich habe das bemerkt, weil der Autor viele Funktionen mit dem Zeichnen verwendet, um seinen Code zu brechen, verwendet er viele Globals wie GRIDSIZE oder GRIDSIZE . Mir wurde immer gesagt, dass die Globalen allgemein schlecht waren, aber ohne sie würde jede Zeichnungsfunktion zehn weitere haben, Parameter wiederholen – und mir wurde auch gesagt, dass die Wiederholung schlecht ist. Ich habe mich aber gefragt, dass der Autor bei der Verwendung von Globals für Parameter, die in den meisten (Zeichnungs-) Funktionen erscheinen, korrekt ist oder einfach nur mehr wiederholte Parameter verwendet hätte.

5 Solutions collect form web for “Globals vs Parameter für Funktionen mit den gleichen Parametern”

Globals sind in der Regel schlecht, weil es schwierig ist, die Werte in einem riesigen Programm zu verfolgen. Das ist ein bisschen weniger schlecht in Python weil "globale" Variablen Modul-Ebene sind, aber auch in diesem Fall sind Sie gut beraten, sie zu vermeiden: Ihre Module können riesig werden (machen globale Variablen schwer zu verfolgen), Module-Level-Variablen sind Anfällig für andere Module etc.

Ich meine nicht, dass du sie nicht benutzen solltest – du solltest sie benutzen, wenn sie gebraucht werden . Zwei häufige Fälle, in denen es benötigt wird, teilen sich Staat und Konstanten .

Modul-Level-Variablen für den Sharing-Zustand

Zum Beispiel sind Module-Level-Variablen die beste Möglichkeit, einen Status zu speichern, der über das gesamte Modul geteilt wird, oder sogar über das gesamte Programm. Nehmen wir an, wir werden den Renderer eines Spiels implementieren. Es sollte eine Karte aller Bilder speichern, die von allen Programmen geteilt wird. So können wir bei unserem Modul so etwas machen:

 # map_renderer.py _map = [ [None, None, None, None], [None, None, None, None], [None, None, None, None], [None, None, None, None] ] def put_sprint(sprint, x, y): _map[x][y] = sprint def get_sprint(x, y): return _map[x][y] 

Jetzt können Sie sogar ein Modul wie folgt schreiben:

 # background.py import map_renderer as mr def draw_background(background_images): for i, row in enumerate(background_images): for j, image in enumerate(row): put_sprint(image, i, j) 

… und in einem anderen Modul so etwas machen

 # npc.py import map_renderer as mr def draw_npc(npc, x, y): image = npc.image put_sprint(image, x, y) 

Da beide Module die gleiche Karte aktualisieren sollten, können Sie Modul-Level-Variablen verwenden, um sie zu erreichen.

HINWEIS : Für diejenigen, die mehr Design-Muster-orientiert, können wir sagen, dass Module-Level-Variablen sind eine sehr effektive und einfache Art und Weise der Umsetzung Singletons.

Modul-Level-Variablen als Konstanten

Der Code in Ihrem Buch sind jedoch Beispiele für eine weitere gute Möglichkeit, Modul-Level-Variablen zu verwenden: Konstanten. (Ich vermute, diese globalen Variablen sind eigentlich Konstanten, weil sie dem Stil für Konstanten von PEP 8 folgen.)

Betrachten wir z. B. die Variable BACKGROUNDCOLOR . Ich nehme an, es ist ein Wert, der das gleiche durch das ganze Modul sein wird, vielleicht durch das ganze Programm. Angenommen, dieser Wert ist 0xFA2BEE . Dann haben Sie einige Optionen:

  • Sie können (in der Theorie) den Wert überall schreiben, wo er verwendet wird:

     def paint_frame(image, x, y, h, w, fx, fy): paint_background(0xFA2BEE, fx, fy) spring = slice_image(image, x, y, x+w, y+h) paint(image, px, py) def clear_frame(fx, fy): paint_background(0xFA2BEE, fx, fy) 

    Das ist aber eindeutig eine schlechte Idee. Zuerst, was zum frick ist 0xFA2BEE ? Jeder, der Ihren Code liest – auch Sie in der Zukunft – wird von ihm verwirrt. Auch was, wenn der Hintergrund geändert werden sollte, sagen wir, 0xB2BAA3 ? Jetzt müssen Sie alle 0xFA2BEE in Ihrem Code zu finden, stellen Sie sicher, dass es der Wert des Hintergrunds statt der gleichen anderen Wert und dann ersetzen Sie es. Nicht nett, richtig

  • Wenn das schlimm ist, kann man sich gut vorstellen, denn ich sollte "globale" Variablen vermeiden, aber die Rohwerte sind nicht gut zu bedienen, ich kann sie als Parameter übergeben!

     def paint_frame(image, x, y, h, w, fx, fy, background): paint_background(background, fx, fy) spring = slice_image(image, x, y, x+w, y+h) paint(image, px, py) def clear_frame(fx, fy, background): paint_background(background, fx, fy) 

    Nun, zumindest jetzt bist du 0xFA2BEE als letztes Argument von paint_frame / clear_frame ist der Wert des Hintergrundes. Allerdings setzen Sie ein anderes Argument in einige bereits komplizierte Funktionen. Auch wenn du den Hintergrund paint_frame , musst du schon alle Anrufe in paint_frame / clear_frame in deiner gesamten Codebasis finden . Nicht cool, bro.

    Stattdessen könnten Sie …

  • … schaffen Konstanten! In der Tat ist es, was der Autor tut:

     BACKGROUND = 0xFA2BEE def paint_frame(image, x, y, h, w, fx, fy): paint_background(BACKGROUND, fx, fy) spring = slice_image(image, x, y, x+w, y+h) paint(image, px, py) def clear_frame(fx, fy): paint_background(BACKGROUND, fx, fy) 

    Jetzt musst du aber nicht erraten wenn 0xFA2BEE ein Hintergrundwert oder etwas anderes ist, 2) deine Funktionen einfacher gehalten werden und 3) man kann den Hintergrundwert einfach nur an einer Stelle ändern. Wie Sie sehen können, hat die Verwendung von Modul-Level-Variablen hier gut bezahlt. Auch wenn Sie den gleichen Hintergrundwert in den meisten aber nicht allen Orten verwenden müssen, ist die Konstante hilfreich: machen Sie einfach den Standardwert des background :

     BACKGROUND = 0xFA2BEE def paint_frame(image, x, y, h, w, fx, fy, background=BACKGROUND): paint_background(background, fx, fy) spring = slice_image(image, x, y, x+w, y+h) paint(image, px, py) def clear_frame(fx, fy, background=BACKGROUND): paint_background(background, fx, fy) 

    Jetzt rufst du an, lass uns sagen, paint_frame(img, i, j, FRAME_SIZE, FRAME_SIZE, k*FRAME_SIZE, l*FRAME_SIZE) meiste Zeit und gibt nur ein Hintergrund-Argument, wenn nötig

Konstanten sind geeignet, globale Variablen / Modul-Ebene Variablen auch weil sie sich nicht ändern . Globale Variablen sind besonders böse, weil man ihre Werte verfolgen sollte. Wenn sich die Werte nicht ändern, wird das aber kein Problem sein, oder?

HINWEIS : Diese Variablen sind Konstanten nach Konvention und können aktualisiert werden. Python hat keine Art von Read-Only-Variable, aber Sie können Probleme vermeiden, indem Sie den Code-Standard.)

Die einzige universelle Regel ist "es kommt"

Sie finden eine Menge Leute sagen "nicht verwenden X", "nicht tun Y", "immer verwenden Z" etc. Nun, die meiste Zeit diese Vorschläge sind richtig, aber Sie werden wahrscheinlich immer finden Kontexte wo Sie gelten nicht. Dies ist der Fall mit der Regel "Globals" nicht verwenden. Es gibt Situationen, in denen der Einsatz von Global die beste Wahl ist. Sogar in C gibt es solche Fälle, und Python haben viele andere Situationen, in denen du es tun kannst, da Module-Level-Variablen noch sicherer sind als globals.

Der Vorschlag, globale (oder Modul-Level-Variablen) zu vermeiden, ist ein guter, speziell für einen Anfänger. Jedoch, wie Sie sehen können, gibt es Fälle, in denen es besser ist, ihm nicht zu folgen.

In Python hast du kein so großes Problem mit globals, denn Python-Globals sind auf Modul-Ebene. (So global ist ein bisschen ein Irrtum sowieso.) Es ist gut, Module-Level-Globals unter vielen Umständen zu verwenden, wo wahre Globale unangemessen wäre.

Beispielcode in Büchern ist nicht der gleiche wie Produktionscode

Code in Büchern und Artikeln erklären Dinge Dinge tun, um den Punkt zu halten und nicht überschwemmen den Leser mit dem, was Lärm oder Tangenten zum spezifischen Thema sein könnte.

Der Autor nimmt Abkürzungen, um den Code als spezifisch für das Problem zu halten, das sie wie möglich erklären.

Das heißt, es gibt bessere Möglichkeiten zu tun, was sie tun, als mit Modul Ebene Variablen. Auch in Büchern und Tutorials.

Riesige Staatsmaschinen sind schlecht

Variablen, die zu viel Umfang umfassen, egal auf welcher Ebene sie sind, neigen dazu, die Nebenwirkung der Schaffung von unsichtbaren Nebenwirkungen zu haben, die sich von der riesigen Staatsmaschine manifestieren, die die Anwendung wird.

Übermäßige Parameter für Funktionen ist ein Code-Geruch

Es sollte Ihnen sagen, dass Sie wahrscheinlich Ihre Daten besser verkapseln müssen. Mehrere verwandte Parameter sollten in eine einzige Datenstruktur gruppiert werden und einfach diese einzelne Datenstruktur als Parameter übergeben.

Konstante Globals können in Ordnung sein, trotz aller allgemeinen Warnungen, die Sie vielleicht gehört haben.

In Python bedeutet "Konstante" in Großbuchstaben als Hinweis, dass niemand sie modifizieren sollte, und "globals" bedeutet "Modul-Level-Variablen", aber das Prinzip gilt immer noch. Also hoffentlich GRIDSIZE und GRIDSIZE sind für die Dauer des Programms konstant, aber es ist möglich, dass sie nicht sind: Ich habe den Code nicht gesehen.

Es gibt viele Beispiele für Modul-Level-Konstanten in den Standard-Python-Bibliotheken. Zum Beispiel errno enthält E2BIG , EACCESS , etc. math enthält math.pi

Allerdings sind diese Beispiele noch konstanter als GRIDSIZE . Vermutlich könnte es zwischen verschiedenen Läufen des gleichen Programms math.pi , was math.pi nicht. So müssen Sie die Konsequenzen dieser besonderen globalen beurteilen und entscheiden, ob sie es benutzen oder nicht.

Mir wurde immer gesagt, dass Globals allgemein schlecht waren

Schlechte Dinge sind aus Gründen schlecht, nicht "allgemein schlecht". Um zu entscheiden, was zu tun ist, müssen Sie die Gründe verstehen. Mit globals (oder, in Python, Module-scope-Objekte) verursacht bestimmte Probleme:

  • Abhängigkeiten von zuvor ausgeführten Code: das ist das schlimmste Problem, aber es gilt nur für veränderliche globals.

Wenn Sie eine globale verwenden, die an anderer Stelle geändert werden kann, dann für jemanden, der den Code liest, um über seinen aktuellen Wert zu sprechen, müssen sie über das gesamte Programm wissen, weil irgendein Teil des Programms auf die globale zugreifen könnte. Wenn Sie Ihre Mutables in einem Objekt halten, dass nur Teile Ihres Codes jemals zu sehen bekommen, dann müssen Sie nur über die Teile Ihres Codes, die mit diesem Objekt interagieren zu begründen.

Das klingt vielleicht nicht wie ein großer Unterschied, wenn dein Programm eine Datei ist, die 100 Zeilen lang ist, aber es wird eins schließlich. Machen Sie Ihren Code einfach zu arbeiten ist fast alles über die es einfach für einen Leser zu Grund über das, was der Code tut. Was sonst noch beeinflussen kann, ist ein großer Teil davon, also ist das wichtig. Und "ein Leser" bist du, das nächste Mal, wenn du etwas ändern willst, also hilft dir "Leser" deines Codes reines Eigeninteresse.

  • Abhängigkeiten bei gleichzeitiger Ausführung von Code: mutable globals + threads = braucht locks = effort.

  • Versteckte Abhängigkeiten: das gilt auch für Konstanten.

Sie müssen entscheiden, ob es für dieses Bit von Code ok ist, einige Abhängigkeiten zu haben, die nicht als Parameter ("injiziert") vom Anrufer dieses Codes geliefert werden.

Fast immer ist die Antwort auf diese Frage "ja". Wenn es "absolut nicht" ist, dann sind Sie in einer engen Stelle, wenn Sie viele Parameter nicht mögen, weil Sie nicht einmal Python builtins mit Namen verwenden würden, geschweige denn Namen von Modulen, die der Code importiert. Nachdem ich beschlossen habe, dass es okay ist, versteckte Abhängigkeiten zu haben, musst du an die Konsequenzen von GRIDSIZE , die einer von ihnen sind, auf dem Weg, den du benutzt und besonders den Code GRIDSIZE . Vorausgesetzt, dass Sie nicht auf unterschiedlich große Gitter im selben Programm zeichnen möchten, sind Sie grundsätzlich gut, bis Sie Tests schreiben möchten, die viele verschiedene Werte von GRIDSIZE . Du würdest das tun, um dir selbst Vertrauen zu geben, wenn du GRIDSIZE später GRIDSIZE nichts brechen.

An diesem Punkt möchten Sie wahrscheinlich in der Lage sein, es als Parameter zu liefern. Aus diesem Grund können Sie es vielleicht nützlicher finden, Parameter mit Standardwerten und nicht globalen Werten zu haben. Um lange repetitive Parameterlisten zu vermeiden, können Sie ein Objekt passieren, das alle diese Einstellungen und nicht separat kombiniert. Achten Sie nur darauf, ein Objekt mit mehreren unabhängigen Zwecken zu schaffen, denn die anderen sind ablenkend, wenn Sie nur über eine Frage reden wollen.

  • Namespace Verschmutzung: relevant in C, wenn Sie nicht verwenden static dann zwei völlig nicht verwandte Dateien können nicht verschiedene Funktionen des gleichen Namens. Nicht so relevant in Python wo alles in Namespaces ist.

Es hängt von der Situation ab.

Globals sind nicht allgemein schlecht, aber sollten vermieden werden, wenn nicht unbedingt notwendig

Zum Beispiel sind Konstanten vollkommen ok als globals, während Konfigurationsdaten so weit wie möglich gebündelt (eingekapselt) werden sollen.

Wenn du alle diese Daten auf ein (oder wenige) Objekte konzentrierst, möchtest du entweder die benannten Funktionen als Methoden der Objektklasse (n) haben oder aus diesen Funktionen auf diese Objekte zugreifen.

Am Ende ist es Geschmackssache. Wenn du denkst, dass die Dinge in einer bequemen Weise handhabbar sind, lass es so sein wie es ist. Wenn es aussieht wie Verwirrung, besser ändern Sie es.

  • Lesen eines .JPG-Bildes und Speichern ohne Dateigrößenänderung
  • Verblassen zwischen zwei Musik-Tracks in-progress in Pygame
  • Wie installiere ich PyGame auf Python 3.4?
  • Weird Shifting bei der Verwendung von pygame.transform.rotate ()
  • Pygame-Ereigniswarteschlange
  • Fehler im Pygame "Hello World" Programm Mac OS 10.8
  • Python select.select, select.poll: Beschädigte Doppelverknüpfte Liste
  • "Keine passende Architektur im Universal-Wrapper" beim Import von Pygame
  • AttributeError: KeinType-Objekt hat kein Attribut 'Gesundheit'
  • Pygame Anzeigeposition
  • Wie integriere ich Pygame und PyQt4?
  • Python ist die beste Programmiersprache der Welt.