[PYTHON] Résumé de la façon de partager l'état avec plusieurs fonctions

Lors de la programmation, je souhaite souvent "partager l'état avec plusieurs fonctions".

Pour le moment, il est préférable de choisir le meilleur parmi différentes solutions que d'utiliser des variables globales ou un seau relayer le pointeur sans réfléchir.

Par conséquent, je vais résumer la méthode de partage de l'état avec de multiples fonctions, ainsi que les avantages et les inconvénients, autant que je puisse penser.

Utilisez Python comme exemple de code et utilisez Racket (un type de Scheme) lorsque vous ne pouvez pas écrire en Python. parce que j'aime.

Variables globales

state = "initial state"

def fuga0():
    #Changer après avoir fait référence à l'état
    global state
    print(f"fuga0: {state}")
    state = "another state"
    return "result of fuga0"

def fuga1():
    #Se référer à l'état
    print(f"fuga1: {state}")

def hoge():
    print(fuga0())
    fuga1()
    print(f"last: {state}")

hoge()

Résultat (ce qui suit est omis)


fuga0: initial state
result of fuga0
fuga1: another state
last: another state

Pas un style d'écriture très recommandé.

Pointeur de relais de godet

from dataclasses import dataclass
from typing import Any

#Un pointeur auquel ce mec est relayé
@dataclass
class Box:
    value: Any

def fuga0(box):
    print(f"fuga0: {box.value}")
    box.value = "another state"
    return "result of fuga0"

def fuga1(box):
    print(f"fuga1: {box.value}")

def hoge(box):
    print(fuga0(box))
    fuga1(box)
    print(f"last: {box.value}")

b = Box("initial state")

hoge(b)

Dans le cas d'une constante, la valeur doit être relayée par compartiment au lieu du pointeur.

Reçoit l'état actuel en tant qu'argument et renvoie le nouvel état en tant que valeur de retour

C'est un style d'écriture dit fonctionnel, et l'état ne change pas dans le code.

def fuga0(state):
    print(f"fuga0: {state}")
    return "result of fuga0", "another state"

def fuga1(state):
    print(f"fuga1: {state}")

def hoge(state):
    result, new_state = fuga0(state)
    print(result)
    fuga1(new_state)
    print(f"last: {new_state}")

hoge("initial")

Orientation objet

Une méthode souvent utilisée dans les langages orientés objet. Définissez l'état dans lequel vous souhaitez partager une variable membre.

class Hoge:
    def __init__(self):
        self._state = "initial state"
    
    def _fuga0(self):
        print(f"fuga0: {self._state}")
        self._state = "another state"
        return "result of fuga0"
    
    def _fuga1(self):
        print(f"fuga0: {self._state}")
    
    def __call__(self):
        print(self._fuga0())
        self._fuga1()
        print(f"last: {self._state}")

hoge = Hoge()
hoge()

fermeture

Proche de l'orientation objet.

def create_hoge():
    state = "initial"
    
    def fuga0():
        nonlocal state
        print(f"fuga0: {state}")
        state = "another state"
        return "result of fuga0"
    
    def fuga1():
        print(f"fuga1: {state}")
    
    def hoge():
        print(fuga0())
        fuga1()
        print(f"last: {state}")
    
    return hoge

create_hoge()()

Glocal Variable

Motif de mes pensées. Créez une variable qui ne peut être utilisée que dans avec. Voir le lien pour plus de détails.

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 get_param, set_param, parametrize

def fuga0():
    print(f"fuga0: {get_param()}")
    set_param("another state")
    return "result of fuga0"

def fuga1():
    print(f"fuga0: {get_param()}")

def hoge():
    print(fuga0())
    fuga1()
    print(f"last: {get_param()}")

with parametrize("initial state"):
    hoge()

Monade d'État

Qu'est-ce que Monad? Qu'est-ce que State Monad? S'il vous plaît google pour quelque chose comme ça.

Écrivons avec Racket. Je ne suis pas sûr de l'implémentation de l'applicatif

;Définition de la monade d'État à partir d'ici
(require (prefix-in base: racket/base))
(require data/functor)
(require data/applicative)
(require data/monad)

(struct result (value state) #:transparent)

(struct state (f)
  #:methods gen:functor
  [(define (map g x)
     (state (λ (s)
              (match-define (result v ss) (run x s))
              (result (g v) ss))))]
  #:methods gen:applicative
  [(define (pure _ x)
     (state (λ (s) (result x s))))
   (define (apply f xs)
     (define (get-args xs s)
       (match xs
         [(cons x rest)
          (match-define (result xv xs) (run x s))
          (match-define (result args argss) (get-args rest xs))
          (result (cons xv args) argss)]
         [_ (result `() s)]))
     (state (λ (s)
              (match-define (result fv fs) (run f s))
              (match-define (result args argss) (get-args xs fs))
              (result (base:apply fv args) argss))))]
  #:methods gen:monad
  [(define (chain f x)
     (state (λ (s)
              (match-define (result xv xs) (run x s))
              (match-define (result fv fs) (run (f xv) xs))
              (result fv fs))))])

(define (run m s) ((state-f m) s))

(define get
  (state (λ (s) (result s s))))

(define (set ns)
  (state (λ (s) (result s ns))))
;Définition jusqu'à ici
  
(define fuga0
  (do [x <- get]
      (pure (printf "fuga0: ~a\n" x))
      (set "another state")
      (pure "result of fuga0")))

(define fuga1
  (do [x <- get]
      (pure (printf "fuga1: ~a\n" x))))

(define hoge
  (do [x <- fuga0]
      (pure (displayln x))
      fuga1
      [y <- get]
      (pure (printf "last: ~a\n" y))))

(run hoge "initial state")

Pour les constantes, utilisez la monade Reader.

C'est assez pénible en Python, mais si vous faites de votre mieux, cela ressemble à ceci.

#Définition de la monade d'État à partir d'ici
from typing import Callable, TypeVar, Generic, Tuple


S = TypeVar("S") #Type d'état
R = TypeVar("R") #Type de retour
A = TypeVar("A") #Nouveau type de retour

class State(Generic[S, R]):
    def __init__(self, f: Callable[[S], Tuple[S, R]]):
        self._f = f
    
    def run(self, state: S) -> Tuple[S, R]:
        return self._f(state)
    
    @staticmethod
    def of(value: R):
        return State(lambda s: (value, s))
    
    def flatmap(self, g: Callable[[R], State[S, A]]) -> State[S, A]:
        def _new(state):
            f_ret, f_state = self.run(state)
            return g(f_ret).run(f_state)
        return State(_new)
    
    def map(self, g: Callable[[R], A]) -> State[S, A]:
        return self.flatmap(lambda x: State.of(g(x)))
    
    def then(self, m: State[S, A]) -> State[S, A]:
        return self.flatmap(lambda _: m)
    
    def value(self, v: A) -> State[S, A]:
        return self.map(lambda _: v)

get_m = State(lambda s: (s, s))

def set_m(new_state):
    return State(lambda s: (s, new_state))
#Définition de la monade d'État jusqu'à présent

fuga0 = (get_m
    .map(lambda v: print(f"fuga0: {v}"))
    .then(set_m("another_state"))
    .value("result of fuga0"))

fuga1 = (get_m
    .map(lambda v: print(f"fuga1: {v}")))

hoge = (fuga0
    .map(print)
    .then(fuga1)
    .then(get_m)
    .map(lambda v: print(f"last: {v}")))

hoge.run("initial state")

Continuation limitée

Qu'est-ce que la continuation limitée? Alors, pourquoi pouvez-vous gérer les changements d'état? Pour ceux qui sont intéressés, voir le tutoriel d'Asai-sensei.

(require racket/control)

(define (get)
  (shift k
         (λ (x)
           ((k x) x))))

(define (set s)
  (shift k
         (λ (x)
           ((k x) s))))

(define (run m s)
  ((reset
    (let ([ret (m)])
      (λ (_) ret))) s))


(define (fuga0)
  (printf "fuga0: ~a\n" (get))
  (set "another state")
  "result of fuga0")

(define (fuga1)
  (printf "fuga1: ~a\n" (get)))

(define (hoge)
  (displayln (fuga0))
  (fuga1)
  (printf "last: ~a\n" (get)))

(run hoge "initial state")

Résumé

Tout le monde dit tout le monde différemment.

Au lieu de vous en tenir à ce que vous savez en tant que programmeur, augmentez le nombre d'outils et utilisez-les correctement.

De plus, il y a contextvars dans la bibliothèque Python standard. Je ne l'ai pas mentionné cette fois car il semble avoir été conçu dans un esprit asynchrone.

Recommended Posts

Résumé de la façon de partager l'état avec plusieurs fonctions
Résumé de l'utilisation de pandas.DataFrame.loc
Résumé de l'utilisation de pyenv-virtualenv
Résumé de l'utilisation de csvkit
[Python] Résumé de l'utilisation des fonctions de fractionnement et de jointure
[Python] Résumé des fonctions eval / exec + Comment écrire une chaîne de caractères avec des sauts de ligne
[Python] Résumé de l'utilisation des pandas
Comment titrer plusieurs figures avec matplotlib
J'ai essayé de résumer brièvement la procédure de démarrage du développement de Django
[Python2.7] Résumé de l'utilisation d'unittest
Résumé de l'utilisation de la liste Python
[Python2.7] Résumé de l'utilisation du sous-processus
Résumé de l'écriture d'AWS Lambda
Résumé de la création d'un environnement LAMP + Wordpress avec Sakura VPS
Résumé de la façon d'importer des fichiers dans Python 3
[Python] Comment dessiner plusieurs graphiques avec Matplotlib
Résumé de l'utilisation de MNIST avec Python
Comment spécifier des attributs avec Mock of Python
Comment implémenter "named_scope" de RubyOnRails avec Django
[Java] Comment basculer entre plusieurs versions de Java
Comment renvoyer plusieurs index avec la méthode d'index
Résumé de la lecture des données numériques avec python [CSV, NetCDF, Fortran binary]
[Blender] Résumé de la procédure d'installation / de mise à jour / de désinstallation des modules complémentaires
Comment partager des dossiers avec Docker et Windows avec tensorflow
Comment afficher plusieurs images d'une galaxie en tuiles
Comment générer un CSV d'en-tête multiligne avec des pandas
Comment déduire l'estimation MAP de HMM avec PyStruct
[Python] Résumé de la façon de spécifier la couleur de la figure
Comment déduire une estimation MAP de HMM avec OpenGM
Comment apprendre le SVM structuré de ChainCRF avec PyStruct
[IPython] Comment partager un bloc-notes IPython
Comment mettre à jour avec SQLAlchemy?
Comment lancer avec Theano
Comment séparer les chaînes avec ','
Comment faire RDP sur Fedora31
Comment supprimer avec SQLAlchemy?
[python] Résumé de la récupération des listes et des éléments du dictionnaire
Comment activer la lecture / écriture de net.Conn avec Golang pour annuler avec le contexte
[Linux] [C / C ++] Résumé de la façon d'obtenir pid, ppid, tid
Comment afficher une liste des versions installables avec pyenv
Comparaison de l'utilisation des fonctions d'ordre supérieur dans Python 2 et 3
Résumé de l'écriture des fichiers .proto utilisés dans gRPC
Comment annuler RT avec Tweepy
Comment extraire des fonctionnalités de données de séries chronologiques avec les bases de PySpark
Python: comment utiliser async avec
Comment gérer "Vous avez plusieurs backends d'authentification configurés ..." (Django)
Connectez-vous à plusieurs bases de données avec SQL Alchemy
Comment obtenir l'ID de Type2Tag NXP NTAG213 avec nfcpy
Pour utiliser virtualenv avec PowerShell
Comment installer python-pip avec ubuntu20.04LTS
Comment gérer les données déséquilibrées
Grammaire de base du système Python3 (comment utiliser les fonctions, la fermeture, la fonction lambda)
Comment démarrer avec Python
Comment se connecter à Cloud Firestore à partir de Google Cloud Functions avec du code Python
Comment gérer l'erreur DistributionNotFound
Comment démarrer avec Django
10 fonctions du "langage avec batterie" python
Comment surveiller l'état d'exécution de sqlldr avec la commande pv