Постоянная мемоизация в Python

У меня есть дорогая функция, которая принимает и возвращает небольшое количество данных (несколько целых чисел и чисел с плавающей запятой). Я уже запомнил эту функцию, но я хотел бы сделать записку постоянным.Уже существует пара потоков, связанных с этим, но я не уверен в потенциальных проблемах с некоторыми из предлагаемых подходов, и у меня есть некоторые довольно специфические требования:

  • Я обязательно буду использовать функцию из нескольких потоков и процессов одновременно ( как с использованием многопроцессорной обработки , так и из отдельных скриптов Python)
  • Мне не потребуется доступ для чтения или записи к заметке извне этой функции Python
  • Меня не беспокоит, что заметка может быть повреждена в редких случаях (например, отключение от сети или случайная запись в файл без его блокировки), поскольку восстановление , которое не требует больших затрат (обычно 10-20 минут), но я бы предпочел, чтобы он не был поврежден из-за исключений или завершение процесса python вручную (я не знаю, насколько это реалистично)
  • Я бы настоятельно предпочел решения, не требующие больших внешних библиотек, поскольку у меня очень ограниченный объем места на жестком диске на одной машине. бегать по треске e в
  • Я слабо предпочитаю кроссплатформенный код, но я, скорее всего, буду использовать его только в Linux

В этом потоке обсуждается модуль полка , который, по-видимому, не является процессным. безопасно. В двух ответах предлагается использовать fcntl.flock для блокировки файла полки. Некоторые ответы в этой ветке , однако, предполагают, что это чревато проблемами, но я не совсем уверен, что это такое. Похоже, что это ограничено Unix (хотя, очевидно, в Windows есть эквивалент под названием msvcrt.блокировка ), а блокировка носит «рекомендательный» характер - то есть она не помешает мне случайно записать в файл, не проверив, что он заблокирован. Есть ли другие потенциальные проблемы? Может ли запись в копию файла и замена главной копии в качестве последнего шага снизить риск повреждения?

Не похоже, что модуль dbm будет работать лучше, чем полка . Я бегло просмотрел sqlite3 , но для этой цели он кажется излишним. В этой ветке и в этой упоминается несколько сторонних библиотек, в том числе ZODB , но есть много вариантов, и все они кажутся слишком большими и сложными для этого задача.

Есть ли у кого-нибудь совет?

ОБНОВЛЕНИЕ : kindall упомянул IncPy ниже, что выглядит очень интересно. К сожалению, я не хотел бы возвращаться к Python 2.6 (на самом деле я использую 3.2), и похоже, что его немного неудобно использовать с библиотеками C (среди прочего я интенсивно использую numpy и scipy). Другая идея

kindall поучительна, но я думаю, что адаптировать ее к нескольким процессам будет немного сложно - я полагаю, что проще всего было бы заменить очередь блокировкой файлов или базой данных.

Если снова взглянуть на ZODB, он действительно выглядит идеально для задачи, но я действительно хочу избежать использования каких-либо дополнительных библиотек.Я до сих пор не совсем уверен, в чем заключаются все проблемы с простым использованием flock - я полагаю, одна большая проблема заключается в том, если процесс завершается во время записи в файл или до снятия блокировки?

Итак Я последовал совету синтезатора и перешел на sqlite3. Если кому-то интересно, я решил сделать замену для dict , которая хранит свои записи в виде солений в базе данных (я не беспокоюсь о том, чтобы хранить их в памяти, поскольку доступ к базе данных и травление достаточно быстрые по сравнению с ко всему остальному, что я делаю). Я уверен, что есть более эффективные способы сделать это (и я не знаю, могут ли у меня все еще проблемы с параллелизмом), но вот код:

from collections import MutableMapping
import sqlite3
import pickle


class PersistentDict(MutableMapping):
    def __init__(self, dbpath, iterable=None, **kwargs):
        self.dbpath = dbpath
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'create table if not exists memo '
                '(key blob primary key not null, value blob not null)'
            )
        if iterable is not None:
            self.update(iterable)
        self.update(kwargs)

    def encode(self, obj):
        return pickle.dumps(obj)

    def decode(self, blob):
        return pickle.loads(blob)

    def get_connection(self):
        return sqlite3.connect(self.dbpath)

    def  __getitem__(self, key):
        key = self.encode(key)
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'select value from memo where key=?',
                (key,)
            )
            value = cursor.fetchone()
        if value is None:
            raise KeyError(key)
        return self.decode(value[0])

    def __setitem__(self, key, value):
        key = self.encode(key)
        value = self.encode(value)
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'insert or replace into memo values (?, ?)',
                (key, value)
            )

    def __delitem__(self, key):
        key = self.encode(key)
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'select count(*) from memo where key=?',
                (key,)
            )
            if cursor.fetchone()[0] == 0:
                raise KeyError(key)
            cursor.execute(
                'delete from memo where key=?',
                (key,)
            )

    def __iter__(self):
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'select key from memo'
            )
            records = cursor.fetchall()
        for r in records:
            yield self.decode(r[0])

    def __len__(self):
        with self.get_connection() as connection:
            cursor = connection.cursor()
            cursor.execute(
                'select count(*) from memo'
            )
            return cursor.fetchone()[0]

10
задан Community 23 May 2017 в 10:27
поделиться