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.
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.
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()
configure
verwendet werden können, ist der Freiheitsgrad begrenzt.auf diese Weise,
Nennen wir das Muster ** Glokale Variable **.
Es wird in einigen Python-Bibliotheken verwendet.
Bei Racket gibt es eine Syntax namens parametrize
, die allgemeine Funktionen für glokale Variablen bietet.
Der Anfangswert kann auch im Voraus festgelegt werden. In mxnet ist die Berechnung auf der CPU der Standardwert.
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.
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
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