복붙노트

[PYTHON] 파이썬 클래스 장식 자

PYTHON

파이썬 클래스 장식 자

파이썬 2.5에서는 클래스를 꾸미는 데코레이터를 만드는 방법이 있습니까? 특히, 데코레이터를 사용하여 클래스에 멤버를 추가하고 생성자를 변경하여 해당 멤버의 값을 가져 오려고합니다.

다음과 같은 것을 찾으십시오 ( 'class Foo :'에 구문 오류가 있습니다 :

def getId(self): return self.__id

class addID(original_class):
    def __init__(self, id, *args, **kws):
        self.__id = id
        self.getId = getId
        original_class.__init__(self, *args, **kws)

@addID
class Foo:
    def __init__(self, value1):
        self.value1 = value1

if __name__ == '__main__':
    foo1 = Foo(5,1)
    print foo1.value1, foo1.getId()
    foo2 = Foo(15,2)
    print foo2.value1, foo2.getId()

내가 겪은 일은 파이썬에서 C # 인터페이스와 같은 일을하는 방법이다. 내가 생각하는 패러다임을 전환해야합니다.

해결법

  1. ==============================

    1.앞서 설명한 접근 방식 대신 하위 클래스를 고려할 수도 있다는 개념이 두 번째입니다. 그러나 특정 시나리오를 모르는 경우 YMMV :-)

    앞서 설명한 접근 방식 대신 하위 클래스를 고려할 수도 있다는 개념이 두 번째입니다. 그러나 특정 시나리오를 모르는 경우 YMMV :-)

    당신이 생각하는 것은 메타 클래스입니다. 메타 클래스의 __new__ 함수는 클래스의 제안 된 전체 정의에 전달되며 클래스 정의가 작성되기 전에 다시 작성할 수 있습니다. 그 때 생성자를 새 것으로 만들 수 있습니다.

    예:

    def substitute_init(self, id, *args, **kwargs):
        pass
    
    class FooMeta(type):
    
        def __new__(cls, name, bases, attrs):
            attrs['__init__'] = substitute_init
            return super(FooMeta, cls).__new__(cls, name, bases, attrs)
    
    class Foo(object):
    
        __metaclass__ = FooMeta
    
        def __init__(self, value1):
            pass
    

    생성자를 교체하는 것은 다소 극적 일 수 있지만이 언어는 이러한 종류의 심층적 인 내성 및 동적 수정을 지원합니다.

  2. ==============================

    2.클래스 데코레이터가 문제에 대한 올바른 해결책인지 여부는 별개입니다.

    클래스 데코레이터가 문제에 대한 올바른 해결책인지 여부는 별개입니다.

    Python 2.6 이상에서는 @ -yntax가있는 클래스 데코레이터가 있으므로 다음과 같이 작성할 수 있습니다.

    @addID
    class Foo:
        pass
    

    이전 버전에서는 다음과 같이 할 수 있습니다.

    class Foo:
        pass
    
    Foo = addID(Foo)
    

    그러나 이것은 함수 데코레이터에서와 똑같이 작동하며 데코레이터는 예제에서 수행하지 않은 새로운 (또는 수정 된 원본) 클래스를 리턴해야한다는 점에 유의하십시오. addID 데코레이터는 다음과 같습니다.

    def addID(original_class):
        orig_init = original_class.__init__
        # Make copy of original __init__, so we can call it without recursion
    
        def __init__(self, id, *args, **kws):
            self.__id = id
            self.getId = getId
            orig_init(self, *args, **kws) # Call the original __init__
    
        original_class.__init__ = __init__ # Set the class' __init__ to the new one
        return original_class
    

    그런 다음 위에서 설명한대로 Python 버전에 적절한 구문을 사용할 수 있습니다.

    하지만 당신이 __init__를 오버라이드하고 싶다면 상속이 더 적합하다고 다른 사람들과 동의합니다.

  3. ==============================

    3.그건 좋은 습관이 아니기 때문에 그렇게하는 메커니즘이 없습니다. 당신이 원하는 것을 성취하는 올바른 방법은 상속입니다.

    그건 좋은 습관이 아니기 때문에 그렇게하는 메커니즘이 없습니다. 당신이 원하는 것을 성취하는 올바른 방법은 상속입니다.

    수업 자료를 살펴보십시오.

    작은 예 :

    class Employee(object):
    
        def __init__(self, age, sex, siblings=0):
            self.age = age
            self.sex = sex    
            self.siblings = siblings
    
        def born_on(self):    
            today = datetime.date.today()
    
            return today - datetime.timedelta(days=self.age*365)
    
    
    class Boss(Employee):    
        def __init__(self, age, sex, siblings=0, bonus=0):
            self.bonus = bonus
            Employee.__init__(self, age, sex, siblings)
    

    이 방법으로 Boss는 Employee가 가지고있는 모든 것을 가지고 있으며, 또한 자신의 __init__ 메소드와 멤버를 보유하고 있습니다.

  4. ==============================

    4.클래스를 동적으로 정의 할 수 있다고 설명하는 사람은 없습니다. 따라서 하위 클래스를 정의하고 리턴하는 데코레이터를 가질 수 있습니다.

    클래스를 동적으로 정의 할 수 있다고 설명하는 사람은 없습니다. 따라서 하위 클래스를 정의하고 리턴하는 데코레이터를 가질 수 있습니다.

    def addId(cls):
    
        class AddId(cls):
    
            def __init__(self, id, *args, **kargs):
                super(AddId, self).__init__(*args, **kargs)
                self.__id = id
    
            def getId(self):
                return self.__id
    
        return AddId
    

    어떤 것을 파이썬 2에서 사용할 수 있는가? (Blckknght의 코멘트, 2.6+에서 이것을 계속해야하는 이유를 설명한다) :

    class Foo:
        pass
    
    FooId = addId(Foo)
    

    그리고 파이썬 3에서는 이렇게합니다 (그러나 클래스에서 super ()를 사용하도록주의하십시오).

    @addId
    class Foo:
        pass
    

    그래서 여러분은 여러분의 케이크를 가지고 그것을 먹을 수 있습니다 - 상속과 장식 자들!

  5. ==============================

    5.실제로 클래스 데코레이터의 구현은 매우 훌륭합니다.

    실제로 클래스 데코레이터의 구현은 매우 훌륭합니다.

    https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py

    실제로 이것이 꽤 흥미로운 구현이라고 생각합니다. 클래스가 꾸미는 클래스를 서브 클래스하기 때문에 isinstance 체크와 같은 클래스에서와 똑같이 동작합니다.

    추가 장점이 있습니다 : 커스텀 장고 양식의 __init__ 문이 self.fields를 수정하거나 추가하는 것은 드문 일이 아니므로, 문제의 클래스에 대해 __init__가 모두 실행 된 후에 self.fields에 대한 변경이 발생하는 것이 더 좋습니다.

    매우 영리한.

    그러나 클래스에서 실제로 데코레이션이 생성자를 변경하기를 원합니다. 클래스 데코레이터의 좋은 사용 사례는 아닌 것 같습니다.

  6. ==============================

    6.나는 상속이 문제 제기에 더 잘 어울리는 것에 동의한다.

    나는 상속이 문제 제기에 더 잘 어울리는 것에 동의한다.

    이 질문은 클래스 꾸미기에 정말 편리하다고 생각합니다. 모두 감사합니다.

    상속이 Python 2.7에서 영향을 미치는 방법 (원래 함수의 docstring을 유지하는 @wraps 등)을 비롯한 몇 가지 예제가 있습니다.

    def dec(klass):
        old_foo = klass.foo
        @wraps(klass.foo)
        def decorated_foo(self, *args ,**kwargs):
            print('@decorator pre %s' % msg)
            old_foo(self, *args, **kwargs)
            print('@decorator post %s' % msg)
        klass.foo = decorated_foo
        return klass
    
    @dec  # No parentheses
    class Foo...
    

    데코레이터에 매개 변수를 추가하려는 경우가 종종 있습니다.

    from functools import wraps
    
    def dec(msg='default'):
        def decorator(klass):
            old_foo = klass.foo
            @wraps(klass.foo)
            def decorated_foo(self, *args ,**kwargs):
                print('@decorator pre %s' % msg)
                old_foo(self, *args, **kwargs)
                print('@decorator post %s' % msg)
            klass.foo = decorated_foo
            return klass
        return decorator
    
    @dec('foo decorator')  # You must add parentheses now, even if they're empty
    class Foo(object):
        def foo(self, *args, **kwargs):
            print('foo.foo()')
    
    @dec('subfoo decorator')
    class SubFoo(Foo):
        def foo(self, *args, **kwargs):
            print('subfoo.foo() pre')
            super(SubFoo, self).foo(*args, **kwargs)
            print('subfoo.foo() post')
    
    @dec('subsubfoo decorator')
    class SubSubFoo(SubFoo):
        def foo(self, *args, **kwargs):
            print('subsubfoo.foo() pre')
            super(SubSubFoo, self).foo(*args, **kwargs)
            print('subsubfoo.foo() post')
    
    SubSubFoo().foo()
    

    출력 :

    @decorator pre subsubfoo decorator
    subsubfoo.foo() pre
    @decorator pre subfoo decorator
    subfoo.foo() pre
    @decorator pre foo decorator
    foo.foo()
    @decorator post foo decorator
    subfoo.foo() post
    @decorator post subfoo decorator
    subsubfoo.foo() post
    @decorator post subsubfoo decorator
    

    함수 장식자를 좀 더 간결하게 사용 했으므로 함수 장식자를 사용했습니다. 다음은 클래스를 꾸미는 클래스입니다.

    class Dec(object):
    
        def __init__(self, msg):
            self.msg = msg
    
        def __call__(self, klass):
            old_foo = klass.foo
            msg = self.msg
            def decorated_foo(self, *args, **kwargs):
                print('@decorator pre %s' % msg)
                old_foo(self, *args, **kwargs)
                print('@decorator post %s' % msg)
            klass.foo = decorated_foo
            return klass
    

    이러한 괄호를 검사하고 데코 레이팅 된 클래스에 메서드가없는 경우 작동하는보다 강력한 버전입니다.

    from inspect import isclass
    
    def decorate_if(condition, decorator):
        return decorator if condition else lambda x: x
    
    def dec(msg):
        # Only use if your decorator's first parameter is never a class
        assert not isclass(msg)
    
        def decorator(klass):
            old_foo = getattr(klass, 'foo', None)
    
            @decorate_if(old_foo, wraps(klass.foo))
            def decorated_foo(self, *args ,**kwargs):
                print('@decorator pre %s' % msg)
                if callable(old_foo):
                    old_foo(self, *args, **kwargs)
                print('@decorator post %s' % msg)
    
            klass.foo = decorated_foo
            return klass
    
        return decorator
    

    Assert는 데코레이터가 괄호없이 사용되지 않았는지 확인합니다. 있다면, 데코 레이팅되는 클래스는 데코레이터의 msg 매개 변수에 전달되어 AssertionError를 발생시킵니다.

    @decorate_if는 조건이 True로 평가되는 경우에만 데코레이터를 적용합니다.

    getattr, callable test 및 @decorate_if는 데코레이터가 장식 될 클래스에 foo () 메서드가 없으면 데코레이터가 중단되지 않도록하기 위해 사용됩니다.

  7. from https://stackoverflow.com/questions/681953/python-class-decorator by cc-by-sa and MIT license