Maison >développement back-end >Tutoriel Python >Valeurs mutables de mise en cache Python

Valeurs mutables de mise en cache Python

Barbara Streisand
Barbara Streisandoriginal
2025-01-26 16:13:10896parcourir

Python Caching mutable values

La mise en cache accélère considérablement le traitement, des opérations au niveau du processeur aux interfaces de base de données. Invalidation du cache - déterminant quand supprimer les données mises en cache - est un défi complexe. Ce message aborde un problème de mise en cache plus simple, mais insidieux.

Ce problème, qui se cache pendant 18 mois, n'a fait surface que lorsque les utilisateurs se sont déviés du modèle d'utilisation recommandé. Le problème provenait d'un cadre d'apprentissage automatique personnalisé (ML) (construit sur Scikit-Learn) au sein de mon organisation. Ce cadre accède fréquemment à plusieurs sources de données, nécessitant une couche de mise en cache pour les performances et l'optimisation des coûts (réduction des coûts de sortie de BigQuery).

Initialement,

a été utilisé, mais un cache persistant était nécessaire pour les données statiques fréquemment accessibles pendant le développement. lru_cache, une bibliothèque Python utilisant SQLite, a été choisie pour sa simplicité et sa compatibilité avec notre environnement 32-processus et les Pandas DataFrames (jusqu'à 500 Mo). Une couche DiskCache a été ajoutée en haut pour un accès en mémoire. lru_cache

Le problème est apparu alors que de plus en plus d'utilisateurs ont expérimenté le cadre. Des résultats incorrects au hasard ont été rapportés, difficiles à reproduire de manière cohérente. La cause profonde: modification en place des pandas cached Pandas.

Notre norme de codage a dicté la création de nouveaux dataframes après tout traitement. Cependant, certains utilisateurs, par habitude, ont utilisé

, modifiant directement l'objet mis en cache. Cela a non seulement modifié leurs résultats immédiats, mais a également corrompu les données mises en cache, affectant les demandes ultérieures. inplace=True

pour illustrer, considérez cet exemple simplifié en utilisant des dictionnaires:

<code class="language-python">from functools import lru_cache
import time
import typing as t
from copy import deepcopy

@lru_cache
def expensive_func(keys: str, vals: t.Any) -> dict:
    time.sleep(3)
    return dict(zip(keys, vals))


def main():
    e1 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e1)

    e2 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e2)

    e2['d'] = "amazing"

    print(e2)

    e3 = expensive_func(('a', 'b', 'c'), (1, 2, 3))
    print(e3)


if __name__ == "__main__":
    main()</code>

fournit une référence, pas une copie. La modification lru_cache modifie les données en cache. e2

Solution:

La solution consiste à renvoyer une copie profonde de l'objet mis en cache:

<code class="language-python">from functools import lru_cache, wraps
from copy import deepcopy

def custom_cache(func):
    cached_func = lru_cache(func)

    @wraps(func)
    def _wrapper(*args, **kwargs):
        return deepcopy(cached_func(*args, **kwargs))

    return _wrapper</code>
Cela ajoute une petite surcharge (duplication de données), mais empêche la corruption des données.

Prise des clés:

    Une compréhension plus profonde du comportement de référence de
  • . lru_cache
  • adhérer aux normes de codage minimise les bogues.
  • Comptez les écarts des utilisateurs par rapport aux meilleures pratiques dans la mise en œuvre. La robustesse l'emporte souvent sur l'élégance.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn