Élément de note Python efficace 20 Utilisez Aucun et la chaîne de documentation lors de la spécification d'arguments par défaut dynamiques

Il s'agit d'un mémo écrit du livre efficace python d'O'Reilly Japon. https://www.oreilly.co.jp/books/9784873117560/ P47~50

Soyez prudent lorsque vous mettez des arguments dynamiques dans des arguments de fonction

Par exemple, lors de la définition d'une fonction qui génère l'heure de connexion et le message de journalisation en même temps

from datetime import datetime
from time import sleep

def log(massage, when=datetime.now()):
    print('%s: %s' % (when, massage))

Maintenant, si vous donnez un message à la fonction log () comme argument, cette heure sera sortie en même temps que le message ... Quand j'essaye réellement

log('Hi there!')
sleep(1)
log('Hi there!')

>>>
2016-06-14 00:22:04.668549: Hi there!
2016-06-14 00:22:04.668549: Hi there!

L'heure est la même même si j'ai mis sleep () pendant 1s. De plus, cette heure n'est pas l'heure à laquelle le message est entré, mais l'heure à laquelle la fonction log () est définie. Pourquoi cela arrive-t-il?

L'argument par défaut n'est évalué qu'une seule fois lorsque le module est appelé

Autrement dit, datetime.now () n'est évalué que pour le moment où la fonction log () est définie, et toutes les valeurs suivantes sont identiques. ** Pour résoudre ce problème, définissez l'argument par défaut sur Aucun **

def log2(massage, when=None):
    """Lof a massage with a timestamp.
    
    Args:
        massage: Massage to print.
        when: datetime of when the massage occurred.
            Dafaults to the present time.
    """
    when = datetime.now() if when is None else when
    print('%s: %s' % (when, massage))

L'attribut None est donné à l'argument lorsqu'il est à l'avance, et quand est défini et appelé à chaque fois dans la fonction. À ce stade, il est facile de comprendre si vous écrivez le comportement dans le document

log2('Hi there!')
sleep(1)
log2('Hi there!')

>>>
2016-06-14 00:34:21.793073: Hi there!
2016-06-14 00:34:22.794493: Hi there!

L'horodatage est maintenant correct.

Un autre exemple! Pour les fonctions qui chargent des données JSON

def decode(data, default={}):
    try:
        return json.loads(data)
    except ValueError:
        return default

Chaque fois que j'essaye d'appeler les données décodées en tant que données JSON à l'aide de la fonction decode ()

foo = decode('bad data')
foo ['sruff'] = 5
bar = decode('also bad')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)

>>>
Foo: {'sruff': 5, 'meep': 1}
Bar: {'sruff': 5, 'meep': 1}

Dans ce cas, on suppose que chacun de Foo et Bar sera un dictionnaire d'une clé et d'une valeur, mais comme avec les arguments dynamiques ci-dessus, le dictionnaire par défaut sera fixé lorsque decode () est défini. Par conséquent, même si vous appelez plusieurs fois decode (), le dictionnaire à l'intérieur en héritera toujours.

Ceci est également résolu en mettant None dans la valeur par défaut (le comportement est écrit dans le document)

def decode2(data, default=None):
    """Load JSON data from a string.
    Args:
        data: JSON data to decode
        default: Value to return if decoding fails.
            Dafaults to an empty dictionary.
    """
    if default is None:
        default = {}
    try:
        return json.loads(data)
    except ValueError:
        return default

foo = decode2('bad data')
foo ['sruff'] = 5
bar = decode2('also bad')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)

>>>
Foo: {'sruff': 5}
Bar: {'meep': 1}

** Résumé **

Recommended Posts

Élément de note Python efficace 20 Utilisez Aucun et la chaîne de documentation lors de la spécification d'arguments par défaut dynamiques
Point 17 de la note Python efficace Respect de la certitude lors de l'utilisation d'itérateurs pour les arguments
Élément de mémo Python efficace 18 Utilisez des arguments de position de longueur variable pour rendre l'apparence plus propre
Soyez prudent lorsque vous spécifiez la valeur d'argument par défaut dans la série Python 3
Élément de mémoire Python efficace 7 Utilisez la notation d'inclusion de liste au lieu de la carte et du filtre
[Python] Utiliser et et ou lors de la création de variables
Utilisez python sur Raspberry Pi 3 pour éclairer la LED quand il fait noir!
Quand les fonctions python affectent-elles les arguments de l'appelant?
Spécification de la plage des tableaux ruby et python
Remarque Python: lors de l'attribution d'une valeur à une chaîne
json.dumping None en python renvoie la chaîne null
Le moment auquel la valeur de l'argument par défaut est évaluée diffère entre Ruby et Python.
Quand les arguments par défaut seront-ils liés en python? Lorsque les variables sont liées dans l'évaluation différée de la fermeture.