Это своего рода вопрос, но я также просто надеюсь, что мне не нужно писать кучу кода, чтобы получить желаемое поведение. (Кроме того, если он уже существует, он, вероятно, работает быстрее, чем то, что я написал бы в любом случае.) У меня есть несколько больших списков чисел, которые не могут уместиться в памяти — по крайней мере, не все одновременно. И это нормально, потому что мне нужна только небольшая часть каждого списка за раз, и я знаю, как сохранять списки в файлы и зачитывать часть списка, которая мне нужна. Проблема в том, что мой метод сделать это несколько неэффективен, поскольку он включает в себя итерацию по файлу для той части, которую я хочу. Итак, мне было интересно, есть ли какая-то библиотека или что-то еще, что я не нашел, что позволяет мне индексировать файл, как если бы это был список, используя нотацию []
, с которой я знаком. Так как я сам пишу файлы, я могу сделать из них форматирование, какое мне нужно, но в настоящее время мои файлы не содержат ничего, кроме элементов списка с \n
в качестве разделителя между значениями.
Просто напомнить, что я ищу/сделать это более конкретным.
f[1:3]
) должен возвращаться как объект списка python в памяти.f[i] = x
должен записать значение x
в файл f
в месте, соответствующем индексу i
)Честно говоря, я не ожидаю, что такое может существовать, но никогда не знаешь, когда упускаешь что-то в своем исследовании. Итак, я решил спросить. Кстати, если этого не существует, можно ли перегрузить оператор []
в python?
Как минимум, чтобы сделать этот эффективный, вам нужно записывать записи с фиксированной длиной кадра, чтобы можно было использовать seek
для поиска нужных элементов без необходимости учитывать переменную длину строки.
@gmds Под индексом я подразумеваю позицию в списке, сохраняемую в файле. В настоящее время для меня означает номер строки, так как это то, что разделяет элементы списка, но в настоящее время я думаю, что хочу сделать то, что предлагает donkopotamus, с фиксированной длиной кадра (что означает, что я должен переключиться на двоичный файл?), но мое понимание " позиция в файле" - это точный символ/байт, в котором я нахожусь, а это не то, что мне нужно, поскольку каждый кадр занимает несколько байтов. Я думаю, что я хочу использовать seek
и struct
для чтения/записи в файл с использованием форматирования c-типа, но мне нужно провести дополнительные исследования, чтобы быть уверенным.
Если ваши данные чисто числовые, вы можете рассмотреть возможность использования массивов numpy
и сохранения данных в формате npy
. После сохранения в этом формате вы можете загрузить отображаемый в память файл как:
>>> X = np.load("some-file.npy", mmap_mode = "r")
>>> X[1000:1003]
memmap([4, 5, 6])
Этот доступ будет загружаться непосредственно с диска, не требуя загрузки начальных данных.
Если OP не может поместить все свои данные в память, есть ли способ вообще записать этот файл?
Я думаю, что это может быть полезно включить в ваше решение, была моя точка зрения.
Похоже, это может быть то, что мне нужно, но просмотр документации memmap предупредил меня о модуле python mmap
. Прямо сейчас я не совсем уверен, какой из них мне нужен, потому что они, похоже, делают то же самое, что и memmap, только для numpy
. Есть ли причина, по которой вы специально предложили memmap вместо mmap?
Только потому, что для быстрого доступа вам нужен фреймовый протокол, который будет использовать записи фиксированной длины для хранения элементов (это означает, что реализация может быстро искать нужное место) ... если вы просто mmap
свой текстовый файл, у вас будет нет записи фиксированной длины и не может оптимизировать поиск правильного индекса... существует множество альтернатив разное, таких как hdf5
На самом деле вы можете сделать это, написав простой класс, я думаю:
class FileWrapper:
def __init__(self, path, **kwargs):
self._file = open(path, 'r+', **kwargs)
def _do_single(self, where, s=None):
if where >= 0:
self._seek(where)
else:
self._seek(where, 2)
if s is None:
return self._read(1)
else:
return self._write(s)
def _do_slice_contiguous(self, start, end, s=None):
if start is None:
start = 0
if end is None:
end = -1
self._seek(start)
if s is None:
return self._read(end - start)
else:
return self._write(s)
def _do_slice(self, where, s=None):
if s is None:
result = []
for index in where:
file._seek(index)
result.append(file.read(1))
return result
else:
for index, char in zip(where, s):
file._seek(index)
file._write(char)
return len(s)
def __getitem__(self, key):
if isinstance(key, int):
return self._do_single(key)
elif isinstance(key, slice):
if self._is_contiguous(key):
return self._do_slice_contiguous(key.start, key.stop)
else:
return self._do_slice(self._process_slice(key))
else:
raise ValueError('File indices must be ints or slices.')
def __setitem__(self, key, value):
if isinstance(key, int):
return self._do_single(key, value)
elif isinstance(key, slice):
if self._is_contiguous(key):
return self._do_slice_contiguous(key.start, key.stop, value)
else:
where = self._process_slice(key)
if len(where) == len(value):
return self._do_slice(where, value)
else:
raise ValueError('Length of slice not equal to length of string to be written.')
def __del__(self):
self._file.close()
def _is_contiguous(self, key):
return key.step is None or key.step == 1
def _process_slice(self, key):
return range(key.start, key.stop, key.step)
def _read(self, size):
return self._file.read(size)
def _seek(self, offset, whence=0):
return self._file.seek(offset, whence)
def _write(self, s):
return self._file.write(s)
Я уверен, что можно было бы сделать много оптимизаций, так как я торопился с этим, но писать было весело.
Это не отвечает на вопрос полностью, потому что он поддерживает произвольный доступ к персонажи, как предполагается, к строкам, которые находятся на более высоком уровне абстракции и более сложны в обработке (поскольку они могут быть переменной длины)
Вы можете перегрузить оператор
[]
, определив__getitem__
. Кроме того, когда вы говорите «индекс», вы имеете в виду «позицию в файле» (в отличие от номера строки), верно?