[PYTHON] __new__ 대신 metaclass의 __call__ 메소드를 사용합니까?
PYTHON__new__ 대신 metaclass의 __call__ 메소드를 사용합니까?
메타 클래스에 관해 논의 할 때 문서의 내용은 다음과 같습니다.
내 질문은 : 클래스를 호출 할 때 사용자 지정 동작 (예 : 신선한 개체를 만드는 대신 캐싱)을 사용한다고 가정합니다. 클래스의 __new__ 메서드를 재정 의하여이 작업을 수행 할 수 있습니다. 대신 __call__을 사용하여 메타 클래스를 정의하고 싶습니다. 이 접근 방식은 __new__에서 달성 할 수없는 것이 무엇입니까?
해결법
-
==============================
1.질문에 대한 직접적인 대답은 인스턴스 작성을 사용자 정의하는 것 이상의 작업을 수행하려는 경우 또는 클래스 작성 방법과 인스턴스 작성 방법을 분리하려는 경우입니다.
질문에 대한 직접적인 대답은 인스턴스 작성을 사용자 정의하는 것 이상의 작업을 수행하려는 경우 또는 클래스 작성 방법과 인스턴스 작성 방법을 분리하려는 경우입니다.
파이썬에서 싱글 톤 만들기와 관련 토론에 대한 제 대답을보십시오.
몇 가지 장점이 있습니다.
단일 책임 원리에 대해 걱정하지 않는다면 __new__ 커스터마이징이 잘 작동하는 경우가 많이 있습니다.
그러나 인스턴스가 생성 될 때가 아니라 클래스가 생성 될 때 이전에 발생해야하는 다른 유스 케이스가 있습니다. 메타 클라스 (metaclass)가 필요하다는 것이 이들이 등장 할 때입니다. 파이썬에서 메타 실용 (구체적) 사용 사례는 무엇입니까?를보십시오. 훌륭한 예제가 많이 있습니다.
-
==============================
2.한가지 차이점은 metaclass __call__ 메쏘드를 정의하면 클래스 나 서브 클래스의 __new__ 메쏘드 중 어떤 것이 호출 될 수 있기 전에 그것이 호출되도록 요구한다는 것입니다.
한가지 차이점은 metaclass __call__ 메쏘드를 정의하면 클래스 나 서브 클래스의 __new__ 메쏘드 중 어떤 것이 호출 될 수 있기 전에 그것이 호출되도록 요구한다는 것입니다.
class MetaFoo(type): def __call__(cls,*args,**kwargs): print('MetaFoo: {c},{a},{k}'.format(c=cls,a=args,k=kwargs)) class Foo(object): __metaclass__=MetaFoo class SubFoo(Foo): def __new__(self,*args,**kwargs): # This never gets called print('Foo.__new__: {a},{k}'.format(a=args,k=kwargs)) sub=SubFoo() foo=Foo() # MetaFoo: <class '__main__.SubFoo'>, (),{} # MetaFoo: <class '__main__.Foo'>, (),{}
SubFoo .__ new__는 결코 호출되지 않습니다. 반대로 메타 클래스없이 Foo .__ new__를 정의하면 하위 클래스가 Foo .__ new__를 재정의 할 수 있습니다.
물론, 당신은 MetaFoo .__ call__을 정의하여 cls .__ new__를 호출 할 수 있습니다.하지만 그건 당신에게 달렸습니다. 이를 거부하면 하위 클래스에서 __new__ 메서드를 호출하지 못하도록 방지 할 수 있습니다.
나는 여기서 메타 클래스를 사용하는 것에 대한 강력한 이점을 보지 못했다. 그리고 "Simple은 복잡한 것보다 낫다"때문에 __new__을 사용하는 것이 좋습니다.
-
==============================
3.미묘한 차이점은 이러한 메소드의 실행 순서를 신중하게 관찰 할 때 조금 더 눈에 띄게됩니다.
미묘한 차이점은 이러한 메소드의 실행 순서를 신중하게 관찰 할 때 조금 더 눈에 띄게됩니다.
class Meta_1(type): def __call__(cls, *a, **kw): print "entering Meta_1.__call__()" rv = super(Meta_1, cls).__call__(*a, **kw) print "exiting Meta_1.__call__()" return rv class Class_1(object): __metaclass__ = Meta_1 def __new__(cls, *a, **kw): print "entering Class_1.__new__()" rv = super(Class_1, cls).__new__(cls, *a, **kw) print "exiting Class_1.__new__()" return rv def __init__(self, *a, **kw): print "executing Class_1.__init__()" super(Class_1,self).__init__(*a, **kw)
위의 코드는 실제로 우리가하는 일을 로그하는 것 외에는 아무 것도하지 않습니다. 각 메소드는 상위 구현, 즉 기본값으로 연기됩니다. 따라서 로깅 옆에 단순히 다음과 같이 선언 한 것처럼 효과적입니다.
class Meta_1(type): pass class Class_1(object): __metaclass__ = Meta_1
이제 Class_1 인스턴스를 만듭니다.
c = Class_1() # entering Meta_1.__call__() # entering Class_1.__new__() # exiting Class_1.__new__() # executing Class_1.__init__() # exiting Meta_1.__call__()
그러므로 type이 Meta_1의 부모라면 우리는 .__ call __ ()과 같은 유형의 의사 구현을 상상할 수 있습니다 :
class type: def __call__(cls, *args, **kwarg): # ... a few things could possibly be done to cls here... maybe... or maybe not... # then we call cls.__new__() to get a new object obj = cls.__new__(cls, *args, **kwargs) # ... a few things done to obj here... maybe... or not... # then we call obj.__init__() obj.__init__(*args, **kwargs) # ... maybe a few more things done to obj here # then we return obj return obj
Meta_1 .__ call __ () (또는이 경우에는 .__ call __ ())을 입력하면 Class_1 .__ new __ () 및 Class_1 .__ init __ ()에 대한 호출이 최종적으로 수행되는지 여부에 영향을 줄 수 있습니다. Meta_1 .__ call __ ()을 실행하는 동안 어느 쪽이든 손도 닿지 않은 객체를 반환 할 수 있습니다. 싱글 톤 패턴에 대한이 접근법을 예로 들어 보겠습니다.
class Meta_2(type): __Class_2_singleton__ = None def __call__(cls, *a, **kw): # if the singleton isn't present, create and register it if not Meta_2.__Class_2_singleton__: print "entering Meta_2.__call__()" Meta_2.__Class_2_singleton__ = super(Meta_2, cls).__call__(*a, **kw) print "exiting Meta_2.__call__()" else: print ("Class_2 singleton returning from Meta_2.__call__(), " "super(Meta_2, cls).__call__() skipped") # return singleton instance return Meta_2.__Class_2_singleton__ class Class_2(object): __metaclass__ = Meta_2 def __new__(cls, *a, **kw): print "entering Class_2.__new__()" rv = super(Class_2, cls).__new__(cls, *a, **kw) print "exiting Class_2.__new__()" return rv def __init__(self, *a, **kw): print "executing Class_2.__init__()" super(Class_2, self).__init__(*a, **kw)
Class_2 유형의 객체를 반복적으로 만들려고 할 때 어떤 일이 일어나는지 관찰 해 봅시다.
a = Class_2() # entering Meta_2.__call__() # entering Class_2.__new__() # exiting Class_2.__new__() # executing Class_2.__init__() # exiting Meta_2.__call__() b = Class_2() # Class_2 singleton returning from Meta_2.__call__(), super(Meta_2, cls).__call__() skipped c = Class_2() # Class_2 singleton returning from Meta_2.__call__(), super(Meta_2, cls).__call__() skipped print a is b is c True
이제 같은 구현을 시도하기 위해 클래스의 __new __ () 메소드를 사용하여이 구현을 관찰하십시오.
import random class Class_3(object): __Class_3_singleton__ = None def __new__(cls, *a, **kw): # if singleton not present create and save it if not Class_3.__Class_3_singleton__: print "entering Class_3.__new__()" Class_3.__Class_3_singleton__ = rv = super(Class_3, cls).__new__(cls, *a, **kw) rv.random1 = random.random() rv.random2 = random.random() print "exiting Class_3.__new__()" else: print ("Class_3 singleton returning from Class_3.__new__(), " "super(Class_3, cls).__new__() skipped") return Class_3.__Class_3_singleton__ def __init__(self, *a, **kw): print "executing Class_3.__init__()" print "random1 is still {random1}".format(random1=self.random1) # unfortunately if self.__init__() has some property altering actions # they will affect our singleton each time we try to create an instance self.random2 = random.random() print "random2 is now {random2}".format(random2=self.random2) super(Class_3, self).__init__(*a, **kw)
위의 구현은 클래스에 싱글 톤을 성공적으로 등록하더라도 __init __ ()가 호출되는 것을 막지는 못하지만, __init __ () 유형 (암시가없는 경우 기본 메타 클래스 임)에서 암시 적으로 발생합니다. 이로 인해 원하지 않는 결과가 발생할 수 있습니다.
a = Class_3() # entering Class_3.__new__() # exiting Class_3.__new__() # executing Class_3.__init__() # random1 is still 0.282724600824 # random2 is now 0.739298365475 b = Class_3() # Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped # executing Class_3.__init__() # random1 is still 0.282724600824 # random2 is now 0.247361634396 c = Class_3() # Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped # executing Class_3.__init__() # random1 is still 0.282724600824 # random2 is now 0.436144427555 d = Class_3() # Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped # executing Class_3.__init__() # random1 is still 0.282724600824 # random2 is now 0.167298405242 print a is b is c is d # True
-
==============================
4.라이프 사이클 단계의 문제이며 액세스 권한이 있습니다. __call__은 __new__ 뒤에 호출되고 초기화 매개 변수가 전달되어 __init__에 전달되기 전에 조작 매개 변수를 조작 할 수 있습니다. 이 코드를 시험해보고 그 결과를 연구하십시오 :
라이프 사이클 단계의 문제이며 액세스 권한이 있습니다. __call__은 __new__ 뒤에 호출되고 초기화 매개 변수가 전달되어 __init__에 전달되기 전에 조작 매개 변수를 조작 할 수 있습니다. 이 코드를 시험해보고 그 결과를 연구하십시오 :
class Meta(type): def __new__(cls, name, bases, newattrs): print "new: %r %r %r %r" % (cls, name, bases, newattrs,) return super(Meta, cls).__new__(cls, name, bases, newattrs) def __call__(self, *args, **kw): print "call: %r %r %r" % (self, args, kw) return super(Meta, self).__call__(*args, **kw) class Foo: __metaclass__ = Meta def __init__(self, *args, **kw): print "init: %r %r %r" % (self, args, kw) f = Foo('bar') print "main: %r" % f
-
==============================
5.pyroscope의 대답에 대한 파이썬 3 버전이 복사, 붙여 넣기 및 해킹 (누군가가이 페이지에서 다시 6 개월 만에 다시 돌아올 때 나를 만났을 때)으로 편리 할 수 있다고 생각했습니다. 이 기사에서 가져온 것입니다 :
pyroscope의 대답에 대한 파이썬 3 버전이 복사, 붙여 넣기 및 해킹 (누군가가이 페이지에서 다시 6 개월 만에 다시 돌아올 때 나를 만났을 때)으로 편리 할 수 있다고 생각했습니다. 이 기사에서 가져온 것입니다 :
class Meta(type): @classmethod def __prepare__(mcs, name, bases, **kwargs): print(' Meta.__prepare__(mcs=%s, name=%r, bases=%s, **%s)' % ( mcs, name, bases, kwargs )) return {} def __new__(mcs, name, bases, attrs, **kwargs): print(' Meta.__new__(mcs=%s, name=%r, bases=%s, attrs=[%s], **%s)' % ( mcs, name, bases, ', '.join(attrs), kwargs )) return super().__new__(mcs, name, bases, attrs) def __init__(cls, name, bases, attrs, **kwargs): print(' Meta.__init__(cls=%s, name=%r, bases=%s, attrs=[%s], **%s)' % ( cls, name, bases, ', '.join(attrs), kwargs )) super().__init__(name, bases, attrs) def __call__(cls, *args, **kwargs): print(' Meta.__call__(cls=%s, args=%s, kwargs=%s)' % ( cls, args, kwargs )) return super().__call__(*args, **kwargs) print('** Meta class declared') class Class(metaclass=Meta, extra=1): def __new__(cls, myarg): print(' Class.__new__(cls=%s, myarg=%s)' % ( cls, myarg )) return super().__new__(cls) def __init__(self, myarg): print(' Class.__init__(self=%s, myarg=%s)' % ( self, myarg )) self.myarg = myarg super().__init__() def __str__(self): return "<instance of Class; myargs=%s>" % ( getattr(self, 'myarg', 'MISSING'), ) print('** Class declared') Class(1) print('** Class instantiated')
출력 :
** Meta class declared Meta.__prepare__(mcs=<class '__main__.Meta'>, name='Class', bases=(), **{'extra': 1}) Meta.__new__(mcs=<class '__main__.Meta'>, name='Class', bases=(), attrs=[__module__, __qualname__, __new__, __init__, __str__, __classcell__], **{'extra': 1}) Meta.__init__(cls=<class '__main__.Class'>, name='Class', bases=(), attrs=[__module__, __qualname__, __new__, __init__, __str__, __classcell__], **{'extra': 1}) ** Class declared Meta.__call__(cls=<class '__main__.Class'>, args=(1,), kwargs={}) Class.__new__(cls=<class '__main__.Class'>, myarg=1) Class.__init__(self=<instance of Class; myargs=MISSING>, myarg=1) ** Class instantiated
같은 기사에서 강조된 또 다른 훌륭한 자료는 David Beazley의 PyCon 2013 Python 3 Metaprogramming 튜토리얼입니다.
from https://stackoverflow.com/questions/6966772/using-the-call-method-of-a-metaclass-instead-of-new by cc-by-sa and MIT license
'PYTHON' 카테고리의 다른 글
[PYTHON] 파이썬에서 한 번에 문자열 2 (또는 n) 문자를 반복 할 수 있습니다. (0) | 2018.11.12 |
---|---|
[PYTHON] 파이썬 - Ceil, datetime, 다음 1/4 분기 (0) | 2018.11.12 |
[PYTHON] tkinter에서 날짜 선택 도구를 어떻게 만듭니 까? (0) | 2018.11.12 |
[PYTHON] 파이프를 통해 stdin을 읽는 스크립트에서 pdb.set_trace ()를 사용하십시오. (0) | 2018.11.12 |
[PYTHON] 파이썬, 열거 형은 무엇에 좋은가요? [복제] (0) | 2018.11.12 |