[PYTHON] Oreore-Entwurfsmuster: Glokale Variable

Überblick

Ich habe das Entwurfsmuster "globale Variablen, auf die nur mit zugegriffen werden kann" genannt, die ich manchmal in der Python-Bibliothek sehe.

Es tut mir leid, wenn es bereits einen Namen hat.

Beispiel

Schreiben Sie ein Programm, das ein Experiment (Funktion Experiment) basierend auf einer bestimmten Einstellung ausführt.

Das Experiment ist in mehrere Funktionen unterteilt ("erste" zweite "), und beide führen die experimentelle Operation unter Bezugnahme auf die Einstellungen durch.

Ich möchte zwei Experimente basierend auf config0 und config1 durchführen.

Schreiben Sie es so.

#Es gibt tatsächlich mehr Einstellungen
config0 = {
    "id": 0
}


config1 = {
    "id": 1
}


def first(conf):
    #Etwas tun
    print(f"{conf['id']}: first")


def second(conf):
    #Etwas tun
    print(f"{conf['id']}: second")


def experiment(conf):
    first(conf)
    second(conf)


def main():
    experiment(config0)
    experiment(config1)


main()

Bei dieser Schreibmethode ist es jedoch etwas mühsam, die Einstellungen in einem Bucket weiterzuleiten, wenn das Programm kompliziert wird. Kannst du nicht darauf verzichten?

Eine Lösung besteht darin, globale Variablen zu verwenden ...

conf = config0


def first():
    print(f"{conf['id']}: first")


def second():
    print(f"{conf['id']}: second")


def experiment():
    first()
    second()


def main():
    global conf
    experiment()
    conf = config1
    experiment()


main()

Offensichtlich ist das verrückt.

Einführung von Mustern

Da wir das Bucket Relay vermeiden und die Einführung globaler Variablen vermeiden möchten, werden wir das Muster der glokalen Variablen als Zwischenschreibweise einführen.

config.py


from contextlib import contextmanager


_config = None
_initialized = False


@contextmanager
def configure(data):
    global _config
    global _initialized
    before = _config
    before_initialized = _initialized
    _config = data
    _initialized = True
    try:
        yield
    finally:
        _config = before
        _initialized = before_initialized


def get_config():
    if _initialized:
        return _config
    else:
        #Eigentlich sollte ich eine Ausnahme etwas ernster werfen
        raise RuntimeError
from config import get_config, configure


def first():
    print(f"{get_config()['id']}: first")

          
def second():
    print(f"{get_config()['id']}: second")

          
def experiment():
    first()
    second()

          
def main():
    with configure(config0):
          experiment()
    with configure(config1):
          experiment()


main()

auf diese Weise,

Nennen wir das Muster ** Glokale Variable **.

Wann benutzt du es?

Illustration

Es wird in einigen Python-Bibliotheken verwendet.

Bei Racket gibt es eine Syntax namens parametrize, die allgemeine Funktionen für glokale Variablen bietet.

Variation

Standardwert

Der Anfangswert kann auch im Voraus festgelegt werden. In mxnet ist die Berechnung auf der CPU der Standardwert.

Veränderung

Wenn Sie einen Setter vorbereiten, können Sie die Glokalvariable mit ändern.

param.py


_param = None
_initialized = False


@contextmanager
def parametrize(data):
    global _param
    global _initialized
    before = _param
    before_initialized = _initialized
    _param = data
    _initialized = True
    try:
        yield
    finally:
        _param = before
        _initialized = before_initialized


def set_param(data):
    if _initialized:
        global _param
        _param = data
    else:
        raise RuntimeError
    
    
def get_param():
    if _initialized:
        return _param
    else:
        raise RuntimeError
from param import parametrize, set_param, get_param

with parametrize(3):
    with parametrize(4):
        print(get_param())
        set_param(2)
        print(get_param())
    print(get_param())
get_param()

# 4
# 2
# 3
# RuntimeError

Im Vergleich zu dem Fall, in dem nur das Lesen möglich ist, ist das Risiko aufgrund des zur Verfolgung des Zustands erforderlichen Aufwands höher.

Die Auswirkung von Statusänderungen kann jedoch auf innerhalb von mit beschränkt werden, sodass sie sicherer als globale Variablen sind.

Beim Schreiben eines Parsers usw. kann es zweckmäßig sein, "noch nicht gelesene Sätze" als Glocal-Variable zu schreiben und von Anfang an schrittweise ohne Bucket-Relay zu verbrauchen.

Hinweis

Beachten Sie, dass der Wert der Glokalvariablen bestimmt wird, wenn der Getter ausgeführt wird, nicht dort, wo er beschrieben ist.

def get_print_config():
    #Nicht das 2
    with configure(2):
        def print_config():
            print(get_config())
        return print_config


print_config = get_print_config()
#Auf diese 3 wird verwiesen
with configure(3):
    print_config()
# 3

Bemerkungen

Ursprünglich dachte ich, dass sogar Python wie die Do-Notation der Staatsmonade geschrieben werden könnte, also erinnerte ich mich an flask und mxnet und erkannte, dass es ein solches Muster gab.

Recommended Posts

Oreore-Entwurfsmuster: Glokale Variable
Entwurfsmuster #Builder
Entwurfsmuster #Adapter
Entwurfsmuster #Observer
Entwurfsmuster #Facade
Entwurfsmuster #Strategie
Entwurfsmuster #Singleton
Entwurfsmuster #Proxy
Entwurfsmuster #Factory-Methode
Entwurfsmuster # Template-Methode
Python Design Pattern - Template-Methode
[Viererbande] Designmuster lernen
Grobe Zusammenfassung des GoF-Java-Entwurfsmusters