2017-02-15 6 views
2

오랜 시간 컨설팅을 한 후 여기가 저의 첫 번째 질문입니다. 나는이 문제에 대해 많은 것을 봤는데 나는 그것을 다루는 유용한 정보를 찾지 못했다고 생각한다.functools.wraps가 mappingproxy 유형이 아닌 dict 유형의 __dict__을 반환했습니다.

Python 3에서 내 코드에 대한 일반적인 deprecation python 데코레이터를 만들려고합니다. 목적은 두 가지 일을하는 함수 또는 클래스를 올바르게 래핑하는 것입니다 : deprecation에 대한 경고를 인쇄하고 __doc__ 앞에 비추천 정보 sphinx으로도 처리 할 수 ​​있습니다). 나는 내가 원하는 것을 대부분 다 써 버렸다고 생각한다. 스핑크스와 여전히 고심하고 있지만, functools.wraps의 행동에 대해 궁금한 점이있다. 기본적으로 래핑 된 클래스의 __dict__dict이고 래핑되지 않은 클래스는 mappingproxy 유형입니다.

import functools 

class Deprecated(object): 
    def __call__(self, cls): 
     myassigns = functools.WRAPPER_ASSIGNMENTS 
     myassigns += ('__mro__',) 

     @functools.wraps(cls, assigned=myassigns) 
     def new_func(*args, **kwargs): 
      warnings.warn(message, category=DeprecationWarning, stacklevel=2) 
      return cls(*args, **kwargs) 

     return new_func 


@Deprecated() 
class SomeOldClass: 
    def some_method(self, x, y): 
     return x + y 

class SomeClass: 
    def some_method(self, x, y): 
     return x + y 

print(type(SomeOldClass.__dict__)) 
print(type(SomeClass.__dict__)) 

그리고 출력 :

<class 'dict'> 
<class 'mappingproxy'> 

functools.wraps 오작동이 아니면 내가 뭔가 잘못하고있는 중이 야 여기

는 일부 쇼케이스 코드?

+3

"랩된 클래스"는 함수입니다. 'functools.wraps'는 무엇을 기대 했습니까? – user2357112

+0

'some_method'을 감쌀려고 했습니까? 왜냐하면, 당신은 클래스 자체를 감쌌다. 어떤 종류의 작품 (깊이 보이지는 않았지만 인스턴스를 생성 할 때 경고해야한다고 생각한다.) 또한 이름이 클래스가 아니라 래퍼 함수를 ​​참조한다는 것을 의미한다. 더 이상 클래스에 관련된 것들은'SomeOldClass'에 명시 적으로'__wrapped__' 속성으로 드릴 스루하지 않고 볼 수 있습니다. 클래스를 이런 식으로 사용하지 않으려면 래퍼가 클래스로 들어가 클래스를 래핑하는 함수를 반환하지 않고 명시 적으로'__init__' 또는'__new__'을 명시 적으로 래핑 (원래'cls'를 반환)해야합니다. – ShadowRanger

+0

@ user2357112 필자는 필자의 래핑 (wrapping) 기능을 감추기를 기대하며, 필자의 변경 사항을 제외하고는 그것이 원래의 클래스 인 것처럼 보인다. 구체적으로, 나는'help()'와 같은 함수가 내 비추천 클래스에서 제대로 작동하기를 원했다. – j3mdamas

답변

0

난 강력하게 당신의 래퍼 클래스를 인식하고 좋을 것, 그래서 클래스 자체 포장하지 않습니다 모호하지 않고

@Deprecated() 
class SomeOldClass: 
    def some_method(self, x, y): 
     return x + y 

:이 수

class Deprecated(object): 
    def __init__(self, messagefmt='{wrapped} is deprecated'): 
     self.messagefmt = messagefmt 

    def __call__(self, wrapped): 
     message = self.messagefmt.format(wrapped=wrapped.__name__) 
     wrappingclass = isinstance(wrapped, type) 
     if wrappingclass: 
      wrappedfunc = wrapped.__init__ 
     else: 
      wrappedfunc = wrapped 

     @functools.wraps(wrappedfunc) 
     def new_func(*args, **kwargs): 
      warnings.warn(message, category=DeprecationWarning, stacklevel=2) 
      return wrappedfunc(*args, **kwargs) 
     wrapped.__doc__ = 'Deprecated\n\n' + message + '\n\n' + (wrapped.__doc__ or '') 

     if wrappingclass: 
      wrapped.__init__ = new_func 
      return wrapped 
     else: 
      return new_func 

당신이 두 클래스와 함께 사용을 클래스 비헤이비어 및 함수 (대개 사용자 설정되는 지원 중단 메시지 포함) :

@Deprecated('foo is dead') 
def foo(): 
    pass 

그래서 당신이 그들을 사용하는 경우 :

>>> import warnings 
>>> warnings.filterwarnings('always', category=DeprecationWarning) 
>>> SomeOldClass() 
/usr/local/bin/ipython3:1: DeprecationWarning: SomeOldClass is deprecated 
    #!/usr/bin/python3 
<__main__.SomeOldClass at 0x7fea8f744a20> 
>>> foo() 
/usr/local/bin/ipython3:1: DeprecationWarning: foo is dead 
    #!/usr/bin/python3 

와 유사하게, 모두를위한 __doc__가 중단 통지를 씁니다 수정이 (당신이 그것을 Sphinxify을 조정할 수, 난 그냥 기본 예제를 제공했다).

+0

정말 고마워요! 이것이 제가 찾고 있던 해답입니다! 나는 그것을 시험 할 것이지만 그것이 효과가 있다고 확신한다. – j3mdamas

+0

@ j3mdamas : 필자는 표시된 경우에이를 테스트하여 ('__doc__ '문자열을 적절하게 수정할 수 있는지,'class'가 클래스로 남아 있는지 등을 확인했습니다.) 몇 가지 뚜렷한 예가 있습니다. 예를 들어, 클래스 대 비 클래스에 대한 테스트는 Py2에서 친숙한 구식 클래스가 아니므로 구식 클래스를 지원하기 위해 약간 조정할 필요가 있습니다. 그럼에도 불구하고 최소한의 미세 조정이 필요하며 비교적 가깝습니다. – ShadowRanger

+0

작은 댓글 : 메서드의 경우 '__doc__'이 (가) 업데이트되지 않았습니다. 나는'@ functools.wraps (wrappedfunc)'전에 과제를 옮기고 작업을 시작했습니다. – j3mdamas

0

functools.wrap은 기술적으로 이상한 일이 아니며, 이라고 불리는 기능을 반환했습니다.은 인스턴스를 반환합니다.

>>> SomeOldClass 
<function __main__.SomeOldClass> 

하지 클래스 : 포장 후

, SomeOldClass는 포장 및 반환 기능 new_func입니다. 장식되지 않은 SomeClass__dict__mappingproxy 인 클래스로 보존됩니다.

+0

당신의 대답에 감사드립니다! 그래도 데코레이션 클래스의 작업 예제를 찾는 것은 어렵습니다. 그래서이 접근 방식을 사용했습니다. – j3mdamas