Python: устойчивые способы записи данных в файлы и их чтение
Основные подходы к хранению данных в файлах
JSON: универсальное текстовое хранение
Для большинства задач долговременного хранения структурированных данных (словари, списки, числа, строки) оптимальным решением является формат JSON. Он читается человеком, поддерживается многими языками, а модуль json входит в стандартную библиотеку Python.
import json
data = {
'user': 'alice',
'scores': [95, 88, 72],
'active': True,
'profile': {'age': 30, 'city': 'Moscow'}
}
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open('data.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
print(loaded)ввод программ на python (ввод данных в программе python)
{'user': 'alice', 'scores': [95, 88, 72], 'active': True, 'profile': {'age': 30, 'city': 'Moscow'}}Python file io (ввод-вывод файлов в python)
Пошаговое пояснение:
- json.dump(data, f, ...) – сериализует объект Python в файл.
- Параметр ensure_ascii=False разрешает кириллицу.
- indent=2 делает вывод форматированным.
- При загрузке json.load(f) восстанавливает объект.
Типичные проблемы и ошибки:
- Файла не существует – возникает FileNotFoundError. Решение: проверять существование через os.path.exists или обрабатывать исключение.
- Содержимое файла не является валидным JSON – json.JSONDecodeError. Причина: ручное редактирование, повреждение данных. Рекомендуется использовать try/except.
- JSON не умеет сериализовать произвольные объекты (например, datetime). Можно написать свой кодировщик или преобразовать в строку.
- Проблема с кодировкой – если файл открыт без encoding='utf-8', возможны ошибки на Windows.
Цели и случаи использования:
- Хранение настроек приложения, небольших баз данных (список пользователей, игровая статистика).
- Обмен данными между разными программами через текстовый файл.
- Лёгкое редактирование вручную.
Как сохранить произвольные Python объекты без преобразования?
Модуль pickle позволяет сериализовать практически любой объект Python (классы, функции, исключения). Формат бинарный, нечитаемый человеком.
import pickle
class Account:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
acc = Account('Alice', 1000.50)
with open('account.pkl', 'wb') as f:
pickle.dump(acc, f)
with open('account.pkl', 'rb') as f:
acc_loaded = pickle.load(f)
print(acc_loaded.owner, acc_loaded.balance)Python temp files (временные файлы в python)
Alice 1000.5
Python index files (индексация файлов в python)
Ошибки и риски:
- pickle.UnpicklingError – если файл повреждён или создан другой версией Python.
- Критическая уязвимость: загрузка pickle‑файла из ненадёжного источника может выполнить произвольный код. Использовать только для доверенных данных.
- Несовместимость версий Python: pickle может не загружаться после обновления интерпретатора.
Использование: временное сохранение состояния, кеширование объектов, передача через сокет внутри защищённой сети.
Как хранить табличные данные в текстовом виде?
Формат CSV (Comma‑Separated Values) удобен для строк с одинаковыми полями. Модуль csv встроен в Python.
import csv
rows = [
{'name': 'Alice', 'age': 30, 'score': 95},
{'name': 'Bob', 'age': 25, 'score': 88}
]
with open('users.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['name', 'age', 'score'])
writer.writeheader()
writer.writerows(rows)
with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row)File python class (класс для работы с файлами в python)
{'name': 'Alice', 'age': '30', 'score': '95'}
{'name': 'Bob', 'age': '25', 'score': '88'}Python file utf 8 (кодировка utf-8 для файлов в python)
Типичные проблемы:
- Разделитель по умолчанию запятая – если данные содержат запятые, следует использовать delimiter=';' или экранирование.
- Числа читаются как строки – требуется приведение типов вручную.
- Разные кодировки – указывать encoding.
Применение: экспорт/импорт из Excel, простые логи, списки контактов.
Как организовать конфигурацию приложения?
Для INI‑файлов используется configparser, для YAML – сторонняя библиотека pyyaml.
ConfigParser (INI):
import configparser
config = configparser.ConfigParser()
config['DEFAULT'] = {'debug': 'false', 'port': '8080'}
config['DATABASE'] = {'host': 'localhost', 'user': 'admin'}
with open('config.ini', 'w') as f:
config.write(f)
config.read('config.ini')
print(config['DATABASE']['host'])Python config files (конфигурационные файлы в python)
localhost
Python copy file (копирование файла в python)
Ошибки:
- Отсутствие секции – KeyError. Проверять через config.has_section.
- Параметры читаются как строки – нужны явные преобразования через getint(), getboolean().
YAML (с pyyaml):
import yaml
config = {
'server': {'host': '0.0.0.0', 'port': 5000},
'database': {'url': 'sqlite:///app.db'}
}
with open('config.yaml', 'w') as f:
yaml.dump(config, f, default_flow_style=False)
with open('config.yaml') as f:
loaded = yaml.safe_load(f)
print(loaded)Python log file (логирование в файл в python)
{'server': {'host': '0.0.0.0', 'port': 5000}, 'database': {'url': 'sqlite:///app.db'}}Python file methods (методы работы с файлами в python)
Проблемы YAML:
- Использование yaml.load без аргумента Loader может быть опасным. Всегда применять yaml.safe_load.
- Ошибки синтаксиса (неправильные отступы) – yaml.scanner.ScannerError.
Использование: конфигурационные файлы приложений, DevOps‑контексты.
Как хранить данные в формате ключ‑значение без базы данных?
Модуль shelve предоставляет постоянное хранилище, подобное словарю. Данные сериализуются через pickle.
import shelve
with shelve.open('mydata') as db:
db['user'] = {'name': 'Alice', 'age': 30}
db['scores'] = [100, 90]
with shelve.open('mydata') as db:
print(db['user'])File models in python (модели файлов в python)
{'name': 'Alice', 'age': 30}Ошибки:
- Файл shelve блокируется – не рекомендуется при конкурентном доступе.
- Создаются вспомогательные файлы (.dat, .dir, .bak), их нельзя удалять отдельно.
- Зависимость от pickle – те же риски безопасности.
Применение: кеш, простые БД с малым объёмом данных, локальное состояние приложения.
Расширенные примеры хранения данных
import json, os, datetime
from pathlib import Path
# Пример: сохранение нескольких записей с метками времени
data = [
{'id': 1, 'name': 'Alice', 'timestamp': datetime.datetime.now().isoformat()},
{'id': 2, 'name': 'Bob', 'timestamp': '2024-01-15T10:30:00'}
]
def save_json(filename, data, mode='w'):
try:
# Преобразование всех datetime в строки
serializable = []
for item in data:
item_copy = item.copy()
for k, v in item_copy.items():
if isinstance(v, datetime.datetime):
item_copy[k] = v.isoformat()
serializable.append(item_copy)
with open(filename, mode, encoding='utf-8') as f:
json.dump(serializable, f, ensure_ascii=False, indent=2)
return True
except (IOError, TypeError) as e:
print(f'Ошибка записи: {e}')
return False
def load_json(filename):
if not Path(filename).exists():
return []
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
except json.JSONDecodeError as e:
print(f'Ошибка разбора JSON: {e}')
return []
# Сохраняем
if save_json('records.json', data):
print('Данные сохранены')
# Добавляем новую запись
existing = load_json('records.json')
existing.append({'id': 3, 'name': 'Charlie', 'timestamp': '2024-02-20T12:00:00'})
save_json('records.json', existing) # перезапись
# Читаем и выводим первое значение
records = load_json('records.json')
print(records[0]['name'])Данные сохранены Alice
# Пример: работа с большим файлом – чтение построчно без загрузки всего файла
# Потенциально для JSON Lines (.jsonl) – каждая строка – отдельный JSON
def append_json_line(filename, obj):
with open(filename, 'a', encoding='utf-8') as f:
json.dump(obj, f, ensure_ascii=False)
f.write('\n')
def read_json_lines(filename):
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
if line.strip():
yield json.loads(line)
# Запишем три строки
append_json_line('big_data.jsonl', {'a': 1})
append_json_line('big_data.jsonl', {'b': 2})
append_json_line('big_data.jsonl', {'c': 3})
# Прочитаем построчно
for record in read_json_lines('big_data.jsonl'):
print(record){'a': 1}
{'b': 2}
{'c': 3}# Пример: сериализация сложных структур с помощью custom encoder (JSON)
from json import JSONEncoder
class Person:
def __init__(self, name, birth):
self.name = name
self.birth = birth # datetime.date
def to_json(self):
return {'__person__': True, 'name': self.name, 'birth': self.birth.isoformat()}
class PersonEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Person):
return obj.to_json()
return super().default(obj)
p = Person('Alice', datetime.date(1995, 5, 10))
json_str = json.dumps(p, cls=PersonEncoder, indent=2)
print(json_str)
# Загрузка с проверкой
def decode_person(dct):
if '__person__' in dct:
return Person(dct['name'], datetime.date.fromisoformat(dct['birth']))
return dct
loaded = json.loads(json_str, object_hook=decode_person)
print(loaded.name, loaded.birth){
"__person__": true,
"name": "Alice",
"birth": "1995-05-10"
}
Alice 1995-05-10# Пример: использование pickle для передачи состояния между запусками (с проверкой версии)
import pickle, sys
class GameState:
def __init__(self, level, lives):
self.__version__ = 2 # версия для совместимости
self.level = level
self.lives = lives
def upgrade(self):
# при необходимости обновление старых объектов
pass
state = GameState(5, 3)
with open('savegame.pkl', 'wb') as f:
pickle.dump(state, f)
# Загрузка с проверкой версии
with open('savegame.pkl', 'rb') as f:
loaded = pickle.load(f)
if loaded.__version__ != 2:
loaded.upgrade()
print(loaded.level, loaded.lives)5 3
# Пример: сохранение многомерного массива с помощью numpy (если разрешён сторонний модуль)
import numpy as np
arr = np.random.rand(3, 4)
np.savetxt('matrix.csv', arr, delimiter=',', fmt='%.6f')
loaded_arr = np.loadtxt('matrix.csv', delimiter=',')
print(loaded_arr.shape)(3, 4)
В этом разделе приведены нестандартные приёмы: кастомный кодировщик для JSON, работа с JSON Lines для потоковой обработки, проверка версий в pickle, интеграция с numpy. Каждый из них решает специфическую задачу, которая возникает в реальных проектах.