파이썬 함수를 피클 (또는 코드를 직렬화)하는 쉬운 방법이 있습니까?


100

네트워크 연결을 통해 기능을 전송하려고합니다 (asyncore 사용). 이와 같은 전송을 위해 Python 함수 (이 경우 적어도 부작용이없는 함수)를 직렬화하는 쉬운 방법이 있습니까?

이상적으로는 다음과 같은 한 쌍의 기능을 갖고 싶습니다.

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

답변:


120

함수 바이트 코드를 직렬화 한 다음 호출자에서 재구성 할 수 있습니다. 정렬 화 모듈은 다음의 함수로 재 조립 될 수 직렬화를 코드 오브젝트로 사용할 수있다. 즉 :

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

그런 다음 원격 프로세스에서 (code_string 전송 후) :

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

몇 가지주의 사항 :

  • marshal의 형식 (해당 문제에 대한 모든 파이썬 바이트 코드)은 주요 파이썬 버전간에 호환되지 않을 수 있습니다.

  • cpython 구현에서만 작동합니다.

  • 함수가 선택해야하는 전역 (가져온 모듈, 기타 함수 등 포함)을 참조하는 경우 이들도 직렬화하거나 원격 측에서 다시 만들어야합니다. 내 예제는 원격 프로세스의 전역 네임 스페이스를 제공합니다.

  • 클로저 또는 생성기 함수와 같은 더 복잡한 경우를 지원하려면 더 많은 작업을 수행해야 할 것입니다.


1
Python 2.5에서 "new"모듈은 더 이상 사용되지 않습니다. 'new.function'은 'import types'다음에 'types.FunctionType'으로 대체되어야합니다.
Eric O Lebigot

2
감사. 이것이 바로 제가 찾던 것입니다. 몇 가지 간단한 테스트를 기반으로 생성기의 경우 그대로 작동합니다.
Michael Fairley

2
marshal 모듈에서 처음 몇 개의 단락을 읽으면 대신 pickle을 사용하는 것이 좋습니다. 피클 페이지도 마찬가지입니다. docs.python.org/2/library/marshal.html
dgorissen

1
으로 marshal초기화 된 사전 사전을 직렬화하는 모듈 을 적용하려고합니다 defaultdict(lambda : defaultdict(int)). 그러나 그것은 오류를 반환합니다 ValueError: unmarshallable object. 참고 저는 python2.7을 사용하고 있습니다. 어떤 생각? 감사합니다
user17375 2013 년

2
파이썬 3.5.3에서 foo.func_code발생합니다 AttributeError. 기능 코드를 얻는 다른 방법이 있습니까?
AlQuemist

41

Python의 pickle 라이브러리를 확장하여 함수를 포함한 다양한 유형을 지원하는 Dill을 확인하십시오 .

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

또한 함수의 클로저에서 객체에 대한 참조를 지원합니다.

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

2
dill은 또한 함수와 람다에서 소스 코드를 가져 와서이를 디스크에 저장하는 작업을 꽤 잘 수행합니다.
Mike McKerns 2014 년

14

이 특정 프로젝트의 표준 라이브러리를 고수해야합니다.
Michael Fairley

21
그러나 당신이 할 수없는 것을 의미하지 않는다 이 : 수행하는 방법을 볼 파이의 코드
아론 Digulla을

4
@ AaronDigulla- 사실이지만 다른 사람이 게시 한 코드 한 줄을 읽기 전에 항상 소프트웨어 라이센스를 확인해야한다는 점을 언급 할 가치가 있습니다. 다른 사람의 코드를 읽고 소스를 인용하지 않고 아이디어를 재사용하거나 라이선스 / 복사 제약을 준수하는 것은 많은 경우 표절 및 / 또는 저작권 위반으로 간주 될 수 있습니다.
mdscruggs

12

가장 간단한 방법은 아마도 inspect.getsource(object)( inspect 모듈 참조 ) 함수 나 메서드의 소스 코드와 함께 문자열을 반환하는 것입니다.


함수 이름이 코드에 명시 적으로 정의되어 있다는 점을 제외하고는보기 좋지만 약간 문제가 있습니다. 코드의 첫 번째 줄을 제거 할 수 있지만 'def \ / n func () :'와 같은 작업을 수행하면 깨질 수 있습니다. 함수 자체로 함수 이름을 피클 할 수는 있지만 이름이 충돌하지 않을 것이라는 보장이 없거나 함수를 래퍼에 넣어야합니다. 그래도 가장 깨끗한 솔루션은 아니지만 해야 할 수도 있습니다.
Michael Fairley

1
inspect 모듈은 실제로 함수가 정의 된 위치를 묻고 소스 코드 파일에서 해당 행을 읽는 것입니다. 거의 정교하지 않습니다.
너무 많은 PHP

1
.__ name__ 속성을 사용하여 함수의 이름을 찾을 수 있습니다. ^ def \ s * {name} \ s * (에서 정규식 대체를 수행하고 원하는 이름을 지정할 수 있습니다. 완벽하지는 않지만 대부분의 작업에서 작동합니다.
too much php

6

그것은 모두 런타임에 함수를 생성하는지 여부에 달려 있습니다.

그렇게하면- 파일 inspect.getsource(object)에서 객체의 소스를 가져 오므로 동적으로 생성 된 함수에 대해 작동하지 않으므로 .py실행 전에 정의 된 함수 만 소스로 검색 할 수 있습니다.

어쨌든 함수가 파일에 배치되어 있다면 수신기 액세스 권한을 부여하고 모듈 및 함수 이름 만 전달하십시오.

내가 생각할 수있는 동적으로 생성 된 함수에 대한 유일한 해결책은 전송 전에 문자열로 함수를 구성하고 소스를 전송 한 다음 eval()수신기 측에서 함수를 구성하는 것입니다 .

편집 : marshal솔루션도 꽤 똑똑해 보이며 내장 된 다른 것을 직렬화 할 수 있다는 것을 몰랐습니다.



2
code_string = '' '
def foo (x) :
    반환 x * 2
데프 바 (x) :
    반환 x ** 2
'' '

obj = pickle.dumps (코드 _ 문자열)

지금

exec (pickle.loads (obj))

foo (1)
> 2
바 (3)
> 9

2

다음과 같이 할 수 있습니다.

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

이제 모듈 이름에 대한 참조 대신 transmit(fn_generator())실제 정의를 보냅니다 fn(x,y).

동일한 트릭을 사용하여 네트워크를 통해 클래스를 보낼 수 있습니다.


1

이 모듈에 사용되는 기본 기능은 쿼리를 다루고 유선을 통해 최상의 압축을 얻을 수 있습니다. 유익한 소스 코드를 참조하십시오.

y_serial.py 모듈 :: SQLite로 Python 객체웨어 하우스

"Serialization + persistance :: 몇 줄의 코드로 Python 객체를 SQLite로 압축하고 주석을 단 다음 나중에 SQL없이 키워드로 시간순으로 검색합니다. 데이터베이스가 스키마없는 데이터를 저장하는 데 가장 유용한"표준 "모듈입니다."

http://yserial.sourceforge.net


1

Cloudpickle 은 아마도 당신이 찾고있는 것일 것입니다. Cloudpickle은 다음과 같이 설명됩니다.

cloudpickle은 데이터에 가까운 원격 호스트에서 실행하기 위해 Python 코드가 네트워크를 통해 제공되는 클러스터 컴퓨팅에 특히 유용합니다.

사용 예 :

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

0

다음은 함수를 피클 가능하게 만들기 위해 래핑하는 데 사용할 수있는 도우미 클래스입니다. 이미 언급 된주의 사항 marshal이 적용되지만 가능할 때마다 피클을 사용하도록 노력하고 있습니다. 직렬화에서 전역 또는 클로저를 보존하려는 노력은 없습니다.

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

        def __call__(self, *args, **kwargs):
            return self._fun(*args, **kwargs)

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.