J'ai nommé le modèle de conception "variables globales accessibles uniquement avec" que je vois parfois dans la bibliothèque Python.
Je suis désolé s'il a déjà un nom.
Écrivez un programme qui effectue une expérience (fonction «expérience») basée sur un certain paramètre.
L'expérience est divisée en plusieurs fonctions («première» seconde »), et les deux effectuent l'opération expérimentale en référence aux paramètres.
Je souhaite effectuer deux expériences basées sur «config0» et «config1».
Écrivez-le comme ça.
#Il y a en fait plus de paramètres
config0 = {
"id": 0
}
config1 = {
"id": 1
}
def first(conf):
#Faire quelque chose
print(f"{conf['id']}: first")
def second(conf):
#Faire quelque chose
print(f"{conf['id']}: second")
def experiment(conf):
first(conf)
second(conf)
def main():
experiment(config0)
experiment(config1)
main()
Cependant, avec cette méthode d'écriture, il est un peu gênant de relayer les paramètres dans un bucket lorsque le programme devient compliqué. Tu ne peux pas t'en passer?
Une solution consiste à utiliser des variables globales ...
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()
C'est évidemment fou.
conf
est config1
après avoir appelé main
et exécutez main
à nouveau avec l'intention de config0
, c'est dangereux.Puisque nous voulons éviter le relais de seau et éviter d'introduire des variables globales, nous allons introduire le modèle de variable glocale comme moyen d'écriture intermédiaire.
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:
#En fait, je devrais jeter une exception un peu plus sérieusement
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
, il y a une limite au degré de liberté.get_config
autrement que" scope "(avec), l'erreur ne peut pas être détectée par une analyse statique.de cette façon,
Appelons le modèle ** Glocal Variable **.
Il est utilisé dans certaines bibliothèques Python.
Avec Racket, il existe une syntaxe appelée parametrize
, qui fournit une fonctionnalité Glocal Variable à usage général.
La valeur initiale peut également être déterminée à l'avance. Dans mxnet mentionné précédemment, le calcul sur le CPU est la valeur par défaut.
Si vous préparez un setter, vous pouvez modifier la variable glocale avec.
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
Par rapport au cas où seule la lecture est possible, le risque est plus élevé en raison de l'effort requis pour suivre l'état.
Cependant, l'effet des changements d'état peut être limité à l'intérieur avec, donc il est plus sûr que les variables globales.
Lors de l'écriture d'un analyseur, etc., il peut être pratique d'écrire des «phrases qui n'ont pas encore été lues» sous forme de variable glocale et de les consommer progressivement depuis le début sans relais de seau.
Notez que la valeur de la variable glocale est déterminée lorsque le getter est ** exécuté, pas là où il est ** décrit **.
def get_print_config():
#Pas ça 2
with configure(2):
def print_config():
print(get_config())
return print_config
print_config = get_print_config()
#Ce 3 est référencé
with configure(3):
print_config()
# 3
À l'origine, je pensais que même Python pouvait être écrit comme la notation do de la monade d'état, alors je me suis souvenu de flask et de mxnet et j'ai réalisé qu'il y avait un tel modèle.
Recommended Posts