Вопрос или проблема
У меня есть собственный модуль Python, назовем его mymodule
, с функцией, которая загружает некоторые данные из json-файла по ключу:
import json
def fetch_data(k):
fname = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"data.json"
)
with open(fname, "r") as f:
data = json.load(f)
return data[k]
...
Затем я использую его в другом скрипте, например:
from mymodule import fetch_data
for k in "ABCD":
print(fetch_data(k))
Как реализовано, проблема в том, что функция открывает файл каждый раз, а если json большой, это может значительно замедлить процесс. Я подумал о том, чтобы загрузить json вне функции в глобальную переменную и затем обращаться к ней в функции, но так как модуль делает много других вещей, которые не требуют этих данных, кажется глупым открывать json каждый раз, когда модуль вызывается, особенно если json большой).
Какова лучшая практика в Python, чтобы
- Загрузить внешние данные только по мере необходимости
- Держать данные в памяти после того, как они были загружены один раз, если они нужны позже (как в приведенном выше примере)
Я думал о классе, который инициализируется только при первом вызове с функцией-геттером для ключа, но я не совсем уверен, как это правильно реализовать.
Вы не загружаете файл в память и не используете его повторно, на каждом шаге цикла вы заново загружаете файл.
Я бы предложил изменить ваш метод fetch_data()
, чтобы он возвращал весь json-словарь.
import json
def fetch_data():
fname = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"data.json"
)
with open(fname, "r") as f:
data = json.load(f)
return data
Тогда вы можете вызвать ваш цикл так:
data = fetch_data()
for k in "ABCD":
print(data.get(k))
Это загружает файл в переменную выше цикла, и вы используете отформатированный словарь для поиска ключей на каждой итерации, что исключает многократные чтения файла.
Это менее вопрос “лучшей практики”, а скорее вопрос о том, что работает лучше, учитывая ваши ограничения по памяти и производительности.
Учитывая вашу текущую модель доступа – только перебор ключей верхнего уровня очень большого словаря и отсутствие необходимости иметь их все в памяти одновременно – вам может быть полезно рассмотреть возможность использования другой библиотеки для разбора JSON, такой как ijson
. Если ваша примерная модель доступа отражает то, что вы на самом деле делаете, то это может быть более эффективно – она будет занимать ровно столько памяти, сколько вам действительно нужно в любой момент времени.
Ответ или решение
Для решения вашей задачи по эффективной загрузке данных из JSON-файла в Python, предлагаю использовать класс для управления загрузкой данных. Это позволит вам загружать данные только один раз и эффективно к ним обращаться позже. Ниже представлен пример кода, который демонстрирует этот подход.
import json
import os
class DataLoader:
_instance = None
_data = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(DataLoader, cls).__new__(cls)
# Загрузка данных при создании экземпляра класса.
cls.load_data()
return cls._instance
@classmethod
def load_data(cls):
if cls._data is None:
# Указываем путь к файлу данных
fname = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"data.json"
)
with open(fname, "r") as f:
cls._data = json.load(f)
def fetch_data(self, k):
# Получаем данные по ключу
return self._data.get(k)
# Пример использования
if __name__ == "__main__":
loader = DataLoader()
for k in "ABCD":
print(loader.fetch_data(k))
Пояснение кода:
-
Класс
DataLoader
: Использование паттерна "одиночка" (Singleton) позволяет убедиться, что данные загружаются только один раз, при первом доступе к классу. -
Метод
__new__
: Этот метод отвечает за создание нового экземпляра. Он проверяет, существует ли уже экземпляр, и если нет, вызываетload_data
. -
Метод
load_data
: Загружает данные из JSON-файла и сохраняет их в атрибуте_data
. Данные загружаются только один раз при первом создании экземпляра класса. -
Метод
fetch_data(k)
: Позволяет получать данные по заданному ключуk
. Если данных нет, возвращаетсяNone
.
Почему это хорошая практика:
-
Эффективность: Данные загружаются только один раз, что сокращает время доступа к данным и уменьшает нагрузку на систему ввода-вывода.
-
Чистота кода: Используя класс, вы изолируете логику загрузки и доступа к данным от остального кода, что улучшает поддерживаемость и читаемость кода.
-
Легкость в расширении: Этот подход легко адаптировать лицом к изменениям, например, к поддержке нескольких форматов данных или методов их загрузки.
Альтернативы:
Если ваш JSON-файл очень большой и вы часто ищете только по части ключей, вы также можете рассмотреть использование библиотеки ijson
для потокового чтения JSON. Это позволит вам избежать загрузки всего файла в память, однако в вашем случае, когда данные могут быть не очень большими, предложенный выше подход выглядит оптимальным.
Следуя этим рекомендациям, вы сможете создать эффективный и хорошо организованный код для работы с JSON-данными в вашем проекте.