Modèle de conception Oreore: variable glocale

Aperçu

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.

exemple

É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.

Introduction de modèles

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()

de cette façon,

Appelons le modèle ** Glocal Variable **.

Quand l'utilisez vous?

Illustration

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.

variation

Valeur par défaut

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.

Changement

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.

Mise en garde

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

Remarques

À 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

Modèle de conception Oreore: variable glocale
Modèle de conception #Builder
Modèle de conception #Adapter
Modèle de conception #Observer
Modèle de conception #Facade
Modèle de conception #Strategy
Modèle de conception #Singleton
Modèle de conception #Proxy
Design Pattern #Factory, méthode
Design Pattern #Template, méthode
Python Design Pattern - Méthode de modèle
[Gang of Four] Apprentissage des modèles de conception
Résumé du modèle de conception Java GoF