2009-12-22 8 views
30

중첩 된 클래스가 있습니다.파이썬에서 중첩 된 클래스를 피클 링하려면 어떻게해야합니까?

 
class WidgetType(object): 

    class FloatType(object): 
     pass 

    class TextType(object): 
     pass 

..이

 
class ObjectToPickle(object): 
    def __init__(self): 
     self.type = WidgetType.TextType 

같은 중첩 클래스 형식 (인스턴스가 아닌) 참조하는 oject ObjectToPickle 클래스의 인스턴스를 serialize하는 중입니다. 그 결과는 다음과 같습니다 :

PicklingError: Can't pickle <class 'setmanager.app.site.widget_data_types.TextType'>

파이썬에서 중첩 된 클래스를 피클 할 수있는 방법이 있습니까?

+1

도니는 다르게, 사용자 이름이 우연의 일치인가? : p –

+0

파이썬 피클 문서를 참고하십시오 : http://docs.python.org/library/pickle.html#the-pickle-protocol – joeforker

+0

파이썬 3.4 이상에서,'inst = ObjectToPickle(); pickle.dumps (inst, 4)'는 잘 동작합니다. – Eponymous

답변

1

Pickle은 모듈 범위 (최상위 레벨)에 정의 된 클래스에서만 작동합니다. 이 경우 코드에서 중첩 된 클래스를 정의한 다음 WidgetType에서 속성으로 설정할 수있는 것처럼 보입니다. 코드에 TextTypeFloatType을 참조하지 않는 이유가 있다고 가정합니다. 또는 모듈을 가져오고 widget_type.TextTypewidget_type.FloatType을 사용하십시오.

+0

[..] WidgetType의 속성으로 설정합니다. [..] 이렇게하면 다음과 같은 결과가 나타납니다. "TypeError : 속성 개체를 피클링 할 수 없습니다." – prinzdezibel

24

pickle 모듈이 모듈에서 TextType 클래스를 가져 오려고합니다. 그러나 클래스가 중첩되어 있으므로 작동하지 않습니다. jasonjs의 제안이 효과가 있습니다. 다음은 오류 메시지에 대한 pickle.py의 라인을 책임 :

try: 
     __import__(module) 
     mod = sys.modules[module] 
     klass = getattr(mod, name) 
    except (ImportError, KeyError, AttributeError): 
     raise PicklingError(
      "Can't pickle %r: it's not found as %s.%s" % 
      (obj, module, name)) 

klass = getattr(mod, name) 물론 중첩 된 클래스의 경우에는 작동하지 않습니다. 무슨 일이 벌어지고 있는지 보여주기 위해 인스턴스를 pickling하기 전에 다음 줄을 추가해보십시오 :

import sys 
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType) 

이 코드는 TextType을 모듈에 속성으로 추가합니다. 산 세척은 잘 작동합니다. 나는이 해킹을 사용하도록 조언하지 않습니다.

+0

A +. 이것에 입안을 맞춰라. 그리고 요즘은 피클을 사용하는 것이 나쁘다. 또한 정말 끝내주는 터미널 진행 표시 줄을 발견했습니다! – Kumba

+2

'TextType = WidgetType.TextType'을 사용하여 모듈의 끝에있는 내부 클래스 이름을 별명을 짓는 것이 약간 간단합니다. 그러나 더 적은 해킹을하지는 않지만 여전히 "내부 클래스를 제거하십시오"라고 소리 지르는 것입니다. – Amoss

1

나디아의 대답은 꽤 완벽합니다. 실제로하고 싶은 것은 아닙니다. 중첩 클래스 대신 WidgetTypes에서 상속을 사용할 수 없습니까?

중첩 클래스를 사용하는 유일한 이유는 함께 긴밀하게 협력하는 클래스를 캡슐화하는 것입니다. 특정 예제는 내게 즉시 상속 후보자처럼 보입니다. WidgetType 클래스를 함께 중첩하는 것은 이점이 없습니다. 그들을 모듈에 넣고 WidgetType 대신 상속받습니다.

+1

주어진 객체 인스턴스의 해시를 얻기 위해'cPickle'과'hashlib.sha1()'을 사용했기 때문에 이것에 마주 쳤습니다. 타사 모듈 인 [dpkt] (http://code.google.com/p/dpkt/)를 사용하여 libpcap 파일에서 ICMP 데이터를 구문 분석해야하는 필요성에 부 닥쳤으며 dpkt가 두 클래스 모두에서 중첩 클래스를 사용한다는 것을 발견했습니다. ICMP()'와'ICMP6()'를 사용한다. dpkt의 icmp.py에서 코드를 변경하면이 문제가 발생하지만,이 방법은 내가 제어하지 않는 다른 시스템에서는 실용적인 해결책이 아닙니다. 그러므로 당신의 대답은 합리적인 동안 다른 사람들이 자신의 코드를 작성하는 방법 때문에 모든 경우에 적용되지 않을 것입니다. – Kumba

4

현자 (www.sagemath.org)에서 우리는이 피클 링 문제에 대해 많이 알고 있습니다. 우리가 체계적으로 해결하기로 결정한 방식은 바깥 쪽 클래스를 해킹 구현 및 숨기기를 목표로하는 특정 메타 클래스 안에 넣는 것입니다. 여러 수준의 중첩이 있으면 중첩 된 클래스를 통해 자동으로 전파됩니다.

22

나는 이것이 의 매우 좋은이라는 것을 알고 있지만 분명히 코드를 재 구조화하는 명백하고 확실한 답변 이외에이 질문에 대한 만족스러운 해결책을 결코 본 적이 없습니다.

불행히도, 그런 일을하는 것이 항상 실용적인 것은 아니며, 최후의 수단으로 입니다. 다른 클래스 내에 정의 된 클래스의 인스턴스를 피클링하는 것이 가능합니다.

당신은 따라서

A callable object that will be called to create the initial version of the object. The next element of the tuple will provide arguments for this callable.

를 반환 할 수 __reduce__ function 상태의 파이썬 문서는 당신이 필요로하는 모든 해당 클래스의 인스턴스를 반환 할 수있는 개체입니다. 이 클래스 합니다 (__main__ 수준에 살아야한다, 따라서) 그 자체가 picklable 수해야하며, 것처럼 간단 할 수있다 : 모든 것은 따라서 남아

class _NestedClassGetter(object): 
    """ 
    When called with the containing class as the first argument, 
    and the name of the nested class as the second argument, 
    returns an instance of the nested class. 
    """ 
    def __call__(self, containing_class, class_name): 
     nested_class = getattr(containing_class, class_name) 
     # return an instance of a nested_class. Some more intelligence could be 
     # applied for class construction if necessary. 
     return nested_class() 

,에 __reduce__ 방법에 적절한 인수를 반환하는 것입니다 FloatType :

class WidgetType(object): 

    class FloatType(object): 
     def __reduce__(self): 
      # return a class which can return this class when called with the 
      # appropriate tuple of arguments 
      return (_NestedClassGetter(), (WidgetType, self.__class__.__name__,)) 

결과는 중첩되지만 인스턴스 절인 할 수있는 클래스입니다 (추가 작업이 __state__ 정보를로드/덤프 필요하지만 이것이 __reduce__ 문서에 따라 비교적 간단합니다).

심하게 중첩 된 클래스의 경우이 동일한 기법 (약간의 코드 수정)을 적용 할 수 있습니다. 이에

import pickle 


class ParentClass(object): 

    class NestedClass(object): 
     def __init__(self, var1): 
      self.var1 = var1 

     def __reduce__(self): 
      state = self.__dict__.copy() 
      return (_NestedClassGetter(), 
        (ParentClass, self.__class__.__name__,), 
        state, 
        ) 


class _NestedClassGetter(object): 
    """ 
    When called with the containing class as the first argument, 
    and the name of the nested class as the second argument, 
    returns an instance of the nested class. 
    """ 
    def __call__(self, containing_class, class_name): 
     nested_class = getattr(containing_class, class_name) 

     # make an instance of a simple object (this one will do), for which we can change the 
     # __class__ later on. 
     nested_instance = _NestedClassGetter() 

     # set the class of the instance, the __init__ will never be called on the class 
     # but the original state will be set later on by pickle. 
     nested_instance.__class__ = nested_class 
     return nested_instance 



if __name__ == '__main__': 

    orig = ParentClass.NestedClass(var1=['hello', 'world']) 

    pickle.dump(orig, open('simple.pickle', 'w')) 

    pickled = pickle.load(open('simple.pickle', 'r')) 

    print type(pickled) 
    print pickled.var1 

내 최종 문서는 다른 답변이 말한 것을 기억하는 것입니다 :

A가 완전히 예를 들어 일이 dill 대신 pickle를 사용하는 경우

If you are in a position to do so, consider re-factoring your code to avoid the nested classes in the first place.

5

을, 그것을 작동합니다. 여기

>>> import dill 
>>> 
>>> class WidgetType(object): 
... class FloatType(object): 
...  pass 
... class TextType(object): 
...  pass 
... 
>>> class ObjectToPickle(object): 
... def __init__(self): 
...  self.type = WidgetType.TextType 
... 
>>> x = ObjectToPickle() 
>>> 
>>> _x = dill.dumps(x) 
>>> x_ = dill.loads(_x) 
>>> x_ 
<__main__.ObjectToPickle object at 0x10b20a250> 
>>> x_.type 
<class '__main__.TextType'> 

가져 오기 딜 : https://github.com/uqfoundation/dill

+0

Dill이 다른 네임 스페이스에서 피클 링 처리에 관한 정보를 찾지 못했습니다 (참조 : [this] (http://stackoverflow.com/questions/26696695/store-object-using-python-pickle-and-load-it-into -different-namespace) 질문)? – Jens

+0

'dill'은 참조 또는 클래스 정의 (즉, 클래스 소스 코드를 피클 할 수 있음)로 클래스를 피클 할 수 있습니다. –

관련 문제