Как сериализовать scandir.DirEntry в Python для отправки через сетевой сокет?

У меня есть серверные и клиентские программы, которые общаются друг с другом через сетевой сокет.

Я хочу отправить запись каталога (scandir.DirEntry), полученную от scandir.scandir(), через сокет.

На данный момент я использую модули pickle и cPickle и придумал следующее (только отрывок):

import scandir, pickle

s = scandir.scandir("D:\\PYTHON")
entry = s.next()
data = pickle.dumps(entry)

Однако я получаю следующий стек ошибок:

File "untitled.py", line 5, in <module>
  data = pickle.dumps(item)
File "C:\Python27\Lib\pickle.py", line 1374, in dumps
  Pickler(file, protocol).dump(obj)
File "C:\Python27\Lib\pickle.py", line 224, in dump
  self.save(obj)
File "C:\Python27\Lib\pickle.py", line 306, in save
  rv = reduce(self.proto)
File "C:\Python27\Lib\copy_reg.py", line 70, in _reduce_ex
  raise TypeError, "can't pickle %s objects" % base.__name__

TypeError: can't pickle DirEntry objects

Как я могу избавиться от этой ошибки?

Я слышал об использовании marshall или JSON. ОБНОВЛЕНИЕ: JSON не выгружает все данные внутри объекта.

Есть ли совершенно другой способ отправить объект через сокет?

Заранее благодарю за любую помощь.


person Community    schedule 28.09.2016    source источник
comment
Почему вы хотите отправить объект через сокет, а не соответствующие данные внутри объекта?   -  person Simon Black    schedule 29.09.2016
comment
@SimonBlack: Точно, я частично понял это, отправив только доступные данные. Но как эмулировать поведение методов класса, например is_dir()? Где фактические данные для этого is_dir() метода?   -  person    schedule 29.09.2016


Ответы (2)


Да, объекты os.DirEntry предназначены для недолгого существования, их нельзя хранить или сериализовать. Если вам нужно, чтобы данные в них были сериализованы, похоже, вы поняли это в своем собственном ответе - сериализуйте (замаринуйте) версию dict необходимых вам атрибутов.

Чтобы десериализовать объект, который ходит и крякает, как экземпляр os.DirEntry, создайте класс PseudoDirEntry, который имитирует то, что вам нужно.

Обратите внимание, что вы уже можете напрямую сериализовать объект статистики, что избавляет вас от выбора полей из этого.

В совокупности это будет выглядеть так:

class PseudoDirEntry:
    def __init__(self, name, path, is_dir, stat):
        self.name = name
        self.path = path
        self._is_dir = is_dir
        self._stat = stat

    def is_dir(self):
        return self._is_dir

    def stat(self):
        return self._stat

А потом:

>>> import os, pickle
>>> entry = list(os.scandir())[0]
>>> pickled = pickle.dumps({'name': entry.name, 'path': entry.path, 'is_dir': entry.is_dir(), 'stat': entry.stat()})
>>> loaded = pickle.loads(pickled)
>>> pseudo = PseudoDirEntry(loaded['name'], loaded['path'], loaded['is_dir'], loaded['stat'])
>>> pseudo.name
'.DS_Store'
>>> pseudo.is_dir()
False
>>> pseudo.stat()
os.stat_result(st_mode=33188, st_ino=8370294, st_dev=16777220, st_nlink=1, st_uid=502, st_gid=20, st_size=8196, st_atime=1478356967, st_mtime=1477601172, st_ctime=1477601172)
person Ben Hoyt    schedule 15.11.2016

Что ж, я сам понял, что для экземпляров нестандартных классов, подобных этому scandir.DirEntry, лучший способ — преобразовать данные члена класса в (возможно, вложенную) комбинацию стандартных объектов, например (list, dict , так далее.).

Например, в частном случае scandir.DirEntry это можно сделать следующим образом.

import scandir, pickle

s = scandir.scandir("D:\\PYTHON")
entry = s.next()

# first convert the stat object to st_
st = entry.stat()
st_ = {'st_mode':st.st_mode, 'st_size':st.st_size,\
   'st_atime':st.st_atime, 'st_mtime':st.st_mtime,\
   'st_ctime':st.st_ctime}

# now convert the entry object to entry_
entry_ = {'name':entry.name, 'is_dir':entry.is_dir(), \
 'path':entry.path, 'stat':st_}

# one may need some other class member data also as necessary

# now pickle the converted entry_
data = pickle.dumps(entry_)

Хотя для моей цели мне нужны только данные, после распаковки на другом конце может потребоваться реконструировать нераспакованный entry_ в незащищенный scandir.DirEntry объект 'entry'. Однако мне еще предстоит выяснить, как реконструировать экземпляр класса и установить данные для поведения таких методов, как is_dir(), stat().

person Community    schedule 29.09.2016