클래스의 파이썬 데코레이터


140

다음과 같은 것을 쓸 수 있습니까?

class Test(object):
    def _decorator(self, foo):
        foo()

    @self._decorator
    def bar(self):
        pass

이것은 실패합니다 : @self의 self는 알 수 없습니다

나는 또한 시도했다 :

@Test._decorator(self)

또한 실패 : 테스트 알 수 없음

데코레이터에서 일부 인스턴스 변수를 임시로 변경 한 다음 데코 레이팅 된 메소드를 다시 변경하기 전에 실행하고 싶습니다.

답변:


268

이와 같은 것이 필요한 일을합니까?

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

test = Test()

test.bar()

이렇게하면 데코레이터에 액세스하기 위해 self를 호출하지 않고 일반 네임 스페이스로 클래스 네임 스페이스에 숨겨집니다.

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>> 

의견의 질문에 답변하기 위해 편집 :

다른 클래스에서 숨겨진 데코레이터를 사용하는 방법

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

    _decorator = staticmethod( _decorator )

class TestB( Test ):
    @Test._decorator
    def bar( self ):
        print "override bar in"
        super( TestB, self ).bar()
        print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

산출:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic

7
데코레이터 또는 데코레이션 기능? 인스턴스에서 "bar"를 호출 할 때 랩 바를 반환하는 반환 된 "magic"함수는 위의 자체 변수를 수신하고 "bar"라는 호출 전후에 원하는 인스턴스 변수에 대해 무엇이든 할 수 있습니다. . 클래스를 선언 할 때 인스턴스 변수와 같은 것은 없습니다. 데코레이터 내에서 수업에 무언가를 하시겠습니까? 나는 그것이 관용어라고 생각하지 않습니다.
Michael Speer

1
고마워 마이클 지금은 이것이 내가 원하는 것임을 알았습니다.
hcvst

2
이 솔루션은 정의 된 시점에서 @ decorator 구문을 사용할 수 있기 때문에 허용 된 답변보다 훨씬 좋습니다. 수업이 끝날 때 데코레이터 호출을 해야하는 경우 함수가 장식되고 있는지 확실하지 않습니다. 데코레이터 자체에서 @staticmethod를 사용할 수는 없지만 조금 이상합니다.
mgiuca April

1
상속 된 Test 클래스를 만들면 작동하지 않는다고 생각합니다. 예 : class TestB (Test) : @_decorator def foobar (self) : print "adsf"해결 방법이 있습니까?
extraeee

1
@extraeee : 내가 편집 한 것을 확인하십시오. 주어진 데코레이터를 staticmethod로 한정해야하지만, 그것을 사용한 후에 (또는 staticmethod 버전을 다른 이름으로 할당 한 후에)
Michael Speer

58

당신이하고 싶은 것은 불가능합니다. 예를 들어 아래 코드가 유효한지 여부를 확인하십시오.

class Test(object):

    def _decorator(self, foo):
        foo()

    def bar(self):
        pass
    bar = self._decorator(bar)

물론 self해당 시점에서 정의 되지 않았으므로 유효하지 않습니다 . Test클래스 자체가 정의 될 때까지 정의되지 않을 때 와 동일 합니다 (과정 중). 데코레이터 스 니펫이 변환되는 코드이기 때문에이 코드 스 니펫을 보여 드리겠습니다 .

보시다시피, 데코레이터에서 인스턴스에 액세스하는 것은 실제로 가능하지 않습니다. 데코레이터는 인스턴스화 중이 아닌 연결된 함수 / 메소드를 정의하는 동안 적용되기 때문에 불가능합니다.

클래스 수준의 액세스 가 필요한 경우 다음을 시도하십시오.

class Test(object):

    @classmethod
    def _decorator(cls, foo):
        foo()

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)

5
아래의보다 정확한 답변을 참조하도록 업데이트해야합니다.
Nathan Buesgens

1
좋은. 당신의 산문은 불가능하다고 말하지만, 코드는 그것을 수행하는 방법을 거의 보여줍니다.
Mad Physicist

22
import functools


class Example:

    def wrapper(func):
        @functools.wraps(func)
        def wrap(self, *args, **kwargs):
            print("inside wrap")
            return func(self, *args, **kwargs)
        return wrap

    @wrapper
    def method(self):
        print("METHOD")

    wrapper = staticmethod(wrapper)


e = Example()
e.method()

1
TypeError : 'staticmethod'객체를 호출 할 수 없음
wyx

@wyx는 데코레이터를 호출하지 않습니다. 예를 들어, 그것은해야 @foo하지@foo()
docyoda

첫 번째 인수는하지해야 wrapperself?
CpILL

7

일부 디버깅 상황 에서이 유형의 데코레이터를 사용하면 호출 함수를 찾을 필요없이 장식하여 클래스 속성을 재정의 할 수 있습니다.

class myclass(object):
    def __init__(self):
        self.property = "HELLO"

    @adecorator(property="GOODBYE")
    def method(self):
        print self.property

여기 데코레이터 코드가 있습니다

class adecorator (object):
    def __init__ (self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        def newf(*args, **kwargs):

            #the 'self' for a method function is passed as args[0]
            slf = args[0]

            # replace and store the attributes
            saved = {}
            for k,v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf,k)
                    setattr(slf, k, v)

            # call the method
            ret = func(*args, **kwargs)

            #put things back
            for k,v in saved.items():
                setattr(slf, k, v)

            return ret
        newf.__doc__ = func.__doc__
        return newf 

참고 : 클래스 데코레이터를 사용했기 때문에 데코레이터 클래스 생성자에 인수를 전달하지 않더라도 함수를 장식하기 위해 괄호와 함께 @adecorator ()를 사용해야 합니다.


7

이것은 같은 클래스 내에 정의 된 내부 self에서 액세스하고 사용하는 한 가지 방법입니다 decorator.

class Thing(object):
    def __init__(self, name):
        self.name = name

    def debug_name(function):
        def debug_wrapper(*args):
            self = args[0]
            print 'self.name = ' + self.name
            print 'running function {}()'.format(function.__name__)
            function(*args)
            print 'self.name = ' + self.name
        return debug_wrapper

    @debug_name
    def set_name(self, new_name):
        self.name = new_name

출력 (에서 테스트 Python 2.7.10) :

>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'

위의 예제는 바보이지만 작동합니다.


4

나는 매우 비슷한 문제를 연구 하면서이 질문을 발견했습니다. 내 해결책은 문제를 두 부분으로 나누는 것입니다. 먼저, 클래스 메소드와 연관시킬 데이터를 캡처해야합니다. 이 경우 handler_for는 Unix 명령을 해당 명령의 출력에 대한 핸들러와 연결합니다.

class OutputAnalysis(object):
    "analyze the output of diagnostic commands"
    def handler_for(name):
        "decorator to associate a function with a command"
        def wrapper(func):
            func.handler_for = name
            return func
        return wrapper
    # associate mount_p with 'mount_-p.txt'
    @handler_for('mount -p')
    def mount_p(self, slurped):
        pass

이제 각 클래스 메소드와 일부 데이터를 연관 시켰으므로 해당 데이터를 수집하여 클래스 속성에 저장해야합니다.

OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
    try:
        OutputAnalysis.cmd_handler[value.handler_for] = value
    except AttributeError:
        pass

4

다음은 Michael Speer의 답변을 확장하여 몇 단계 더 나아가는 것입니다.

인수를 가져 와서 인수와 반환 값이있는 함수에 작용하는 인스턴스 메소드 데코레이터.

class Test(object):
    "Prints if x == y. Throws an error otherwise."
    def __init__(self, x):
        self.x = x

    def _outer_decorator(y):
        def _decorator(foo):
            def magic(self, *args, **kwargs) :
                print("start magic")
                if self.x == y:
                    return foo(self, *args, **kwargs)
                else:
                    raise ValueError("x ({}) != y ({})".format(self.x, y))
                print("end magic")
            return magic

        return _decorator

    @_outer_decorator(y=3)
    def bar(self, *args, **kwargs) :
        print("normal call")
        print("args: {}".format(args))
        print("kwargs: {}".format(kwargs))

        return 27

그리고

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)

3

데코레이터 는 일반적으로 인스턴스 속성에 의존 하는 객체 메소드 의 기능과 비교하여 전체 객체 (함수 객체 포함) 의 기능을 수정하는 것이 더 적합 해 보입니다 . 예를 들면 다음과 같습니다.

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"

출력은 다음과 같습니다.

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

1

데코레이터를 장식 할 수 있습니다 :

import decorator

class Test(object):
    @decorator.decorator
    def _decorator(foo, self):
        foo(self)

    @_decorator
    def bar(self):
        pass

1

도움이 될 수있는 데코레이터 구현이 있습니다.

    import functools
    import datetime


    class Decorator(object):

        def __init__(self):
            pass


        def execution_time(func):

            @functools.wraps(func)
            def wrap(self, *args, **kwargs):

                """ Wrapper Function """

                start = datetime.datetime.now()
                Tem = func(self, *args, **kwargs)
                end = datetime.datetime.now()
                print("Exection Time:{}".format(end-start))
                return Tem

            return wrap


    class Test(Decorator):

        def __init__(self):
            self._MethodName = Test.funca.__name__

        @Decorator.execution_time
        def funca(self):
            print("Running Function : {}".format(self._MethodName))
            return True


    if __name__ == "__main__":
        obj = Test()
        data = obj.funca()
        print(data)

1

내부 클래스로 선언하십시오. 이 솔루션은 매우 견고하므로 권장됩니다.

class Test(object):
    class Decorators(object):
    @staticmethod
    def decorator(foo):
        def magic(self, *args, **kwargs) :
            print("start magic")
            foo(self, *args, **kwargs)
            print("end magic")
        return magic

    @Decorators.decorator
    def bar( self ) :
        print("normal call")

test = Test()

test.bar()

결과:

>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>> 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.