Травление определения класса - PullRequest
7 голосов
/ 13 апреля 2010

Есть ли способ выбрать определение класса?

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

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

Ответы [ 3 ]

2 голосов
/ 22 января 2015

Если вы используете dill, это позволяет вам обрабатывать __main__, как если бы это был модуль python (по большей части). Следовательно, вы можете сериализовать интерактивно определенные классы и тому подобное. dill также (по умолчанию) может транспортировать определение класса как часть рассола.

>>> class MyTest(object):
...   def foo(self, x):
...     return self.x * x
...   x = 4
... 
>>> f = MyTest() 
>>> import dill
>>>
>>> with open('test.pkl', 'wb') as s:
...   dill.dump(f, s)
... 
>>> 

Затем выключите интерпретатор и отправьте файл test.pkl по TCP. На удаленной машине теперь вы можете получить экземпляр класса.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('test.pkl', 'rb') as s:
...   f = dill.load(s)
... 
>>> f
<__main__.MyTest object at 0x1069348d0>
>>> f.x
4
>>> f.foo(2)
8
>>>             

Но как получить определение класса? Так что это не совсем то, что вы хотели. Однако следующее.

>>> class MyTest2(object):
...   def bar(self, x):
...     return x*x + self.x
...   x = 1
... 
>>> import dill
>>> with open('test2.pkl', 'wb') as s:
...   dill.dump(MyTest2, s)
... 
>>>

Затем, после отправки файла ... вы можете получить определение класса.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('test2.pkl', 'rb') as s:
...   MyTest2 = dill.load(s)
... 
>>> print dill.source.getsource(MyTest2)
class MyTest2(object):
  def bar(self, x):
    return x*x + self.x
  x = 1

>>> f = MyTest2()
>>> f.x
1
>>> f.bar(4)
17

Итак, в dill есть dill.source, и у него есть методы, которые могут обнаруживать зависимости функций и классов и брать их вместе с pickle (по большей части).

>>> def foo(x):
...   return x*x
... 
>>> class Bar(object):
...   def zap(self, x):
...     return foo(x) * self.x
...   x = 3
... 
>>> print dill.source.importable(Bar.zap, source=True)
def foo(x):
  return x*x
def zap(self, x):
  return foo(x) * self.x

Так что это не "идеально" (или, возможно, не то, что ожидалось) ... но оно сериализует код для динамически создаваемого метода и его зависимостей. Вы просто не получаете остальную часть класса - но остальная часть класса в этом случае не нужна.

Если вы хотите получить все, вы можете просто засолить весь сеанс.

>>> import dill
>>> def foo(x):
...   return x*x
... 
>>> class Blah(object):
...   def bar(self, x):
...     self.x = (lambda x:foo(x)+self.x)(x)
...   x = 2
... 
>>> b = Blah()
>>> b.x
2
>>> b.bar(3)
>>> b.x
11
>>> dill.dump_session('foo.pkl')
>>> 

Затем на удаленной машине ...

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foo.pkl')
>>> b.x
11
>>> b.bar(2)
>>> b.x
15
>>> foo(3)
9

Наконец, если вы хотите, чтобы транспорт был «прозрачным» для вас, вы можете использовать pathos.pp или ppft, которые предоставляют возможность отправлять объекты на второй сервер Python (на удаленной машине) или Python. процесс. Они используют dill под капотом и просто передают код по проводам.

>>> class More(object):
...   def squared(self, x):
...     return x*x
... 
>>> import pathos
>>> 
>>> p = pathos.pp.ParallelPythonPool(servers=('localhost,1234',))
>>> 
>>> m = More()
>>> p.map(m.squared, range(5))
[0, 1, 4, 9, 16]

Аргумент servers является необязательным, и здесь он просто подключается к локальному компьютеру через порт 1234… но если вы вместо этого (или также) используете имя и порт удаленного компьютера, вы переключитесь на удаленная машина - «без усилий».

Получите dill, pathos и ppft здесь: https://github.com/uqfoundation

2 голосов
/ 13 апреля 2010

Увы, не напрямую. Вы можете отправить строковую форму оператора class или форму байт-кода и «перегидрировать» ее с exec на принимающей стороне.

1 голос
/ 13 апреля 2010

Документация довольно хорошо объясняет, что можно, а что нельзя, и почему это так.

http://docs.python.org/library/pickle.html#what-can-be-pickled-and-unpickled

По сути, если класс или модуль импортируется по имени, когда он не выбран, он должен работать, если только вы не планируете изменить определение класса в промежутке времени до того момента, когда вы его не выберете. В приведенном ниже определении класса будут выбраны только имя класса «Test» и имя метода «mymethod». Если вы выберете определение класса, а затем измените определение так, чтобы attr было другим значением, а mymethod делает что-то совершенно иное, метод выбора выберет новое определение.

class Test(object):
    attr = 5

    def mymethod(self, arg):
        return arg
...