파이썬 데코레이터의 일반적인 용도는 무엇입니까? [닫은]


337

필자는 자신을 합리적으로 유능한 Python 코더로 생각하고 싶지만, 결코 그런 적이 없었던 언어의 한 측면은 데코레이터입니다.

나는 그들이 무엇인지 (피상적으로) 알고, 스택 오버플로에 대한 자습서, 예제, 질문을 읽었으며 구문을 이해하고 내 자신을 작성할 수 있으며 때로는 @classmethod 및 @staticmethod를 사용할 수 있지만 내 자신의 파이썬 코드에서 문제를 해결하는 데코레이터. "흠 ... 이것은 데코레이터의 직업처럼 보입니다!"

여러분의 프로그램에서 데코레이터를 어디에서 사용했는지에 대한 예제를 제공 할 수 있을지 궁금합니다. "A-ha!" 순간과 그들을 얻을 .


5
또한 데코레이터는 Memoizing에 유용합니다. 함수의 계산 결과를 느리게 캐싱합니다. 데코레이터는 입력을 확인하는 함수를 반환하고 이미 제공된 경우 캐시 된 결과를 반환 할 수 있습니다.
Peter

1
파이썬에는 내장 데코레이터가 있는데 functools.lru_cache, 이것은 파이썬 3.2 이후 2011 년 2 월에 출시 된 Peter의 말을 정확하게 수행합니다.
태경

Python Decorator Library 의 내용은 다른 용도에 대한 좋은 아이디어를 제공해야합니다.
martineau

답변:


126

나는 주로 타이밍 목적으로 데코레이터를 사용합니다.

def time_dec(func):

  def wrapper(*arg):
      t = time.clock()
      res = func(*arg)
      print func.func_name, time.clock()-t
      return res

  return wrapper


@time_dec
def myFunction(n):
    ...

13
유닉스에서 time.clock()CPU 시간을 측정합니다. time.time()벽시계 시간을 측정하려는 경우 대신 사용할 수 있습니다 .
Jabba

20
좋은 예입니다! 그래도 뭔지 모르겠다. 거기에서 무엇을하고 있는지, 데코레이터가 어떻게 문제를 해결하는지 설명하면 매우 좋습니다.
MeLight

7
글쎄, 그것은 myFunction실행 하는 데 걸리는 시간을 측정합니다 ...
RSabet

98

동기화에 사용했습니다.

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            lock.acquire()
            try:
                return f(*args, **kw)
            finally:
                lock.release()
        return newFunction
    return wrap

주석에서 지적했듯이 Python 2.5부터는 (또는 버전 2.6 이후) 객체 with와 함께 명령문 을 사용하여 데코레이터의 구현을 다음과 같이 단순화 할 수 있습니다.threading.Lockmultiprocessing.Lock

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            with lock:
                return f(*args, **kw)
        return newFunction
    return wrap

어쨌든 다음과 같이 사용하십시오.

import threading
lock = threading.Lock()

@synchronized(lock)
def do_something():
  # etc

@synchronzied(lock)
def do_something_else():
  # etc

기본적으로 함수 호출의 양쪽 에 lock.acquire()/ lock.release()를 넣습니다 .


18
아마도 정당화 될 수 있지만 데코레이터는 본질적으로 혼란스러워합니다. 당신 뒤에 와서 코드를 수정하려는 첫해 멍청한 놈들에게. 이것을 간단하게 피하십시오 : do_something ()은 'with lock :'아래의 블록으로 코드를 묶으면 누구나 자신의 목적을 명확하게 볼 수 있습니다. 데코레이터는 똑똑해 보이고 싶어하는 사람들에 의해 과도하게 사용되지만 (실제로 많은 사람들이), 코드는 단순한 필사자에게 와서 효력을 발생시킵니다.
케빈 J. 라이스

18
@ KevinJ.Rice '첫해 멍청한 놈'이 더 잘 이해할 수 있도록 코드를 제한하십시오. 데코레이터 구문은 훨씬 더 읽기 쉽고 코드를 크게 분리합니다.
TaylerJones

18
@TaylerJones, 코드 가독성은 글을 쓸 때 가장 중요합니다. 코드는 수정 될 때마다 7 번 이상 읽습니다. 이해하기 어려운 코드 (Noobs 또는 시간 압박을 받고있는 전문가를위한)는 누군가가 소스 트리를 방문 할 때마다 지불해야하는 기술 부채입니다.
케빈 J. 라이스

@TaylerJones 프로그래머에게 가장 중요한 작업 중 하나는 명확성을 제공하는 것입니다.
JDOaktown 2016 년

71

일부 RMI를 통해 Python 메서드에 전달되는 형식 검사 매개 변수에 데코레이터를 사용합니다. 따라서 동일한 매개 변수 계산을 반복하는 대신 예외 발생 점보 점보를 반복해서 반복합니다.

예를 들어,

def myMethod(ID, name):
    if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
        raise BlaBlaException() ...

나는 단지 선언한다 :

@accepts(uint, utf8string)
def myMethod(ID, name):
    ...

그리고 accepts()나를 위해 모든 작업을 수행합니다.


15
관심있는 사람은 누구나 @acceptsPEP 318을 구현할 수 있습니다.
martineau

2
오타가 있다고 생각합니다. 첫 번째 방법은 수락해야합니다. "myMethod"로 선언
DevC

1
@DevC 아니요, 오타처럼 보이지 않습니다. "accepts (..)"의 구현이 아니기 때문에 "accepts (..)"는 "myMethod (..)"의 시작 부분에서 두 줄로 수행되는 작업을 수행합니다. 적합한 해석.
Evgeni Sergeev 12

1
범프에 대해 유감스럽게도, 전달 된 인수의 유형을 확인하고 TypeError를 발생시키는 것은 지적합니다. 그렇지 않으면 float 만 검사하는 경우 int를 허용하지 않기 때문에 나쁜 습관으로 간주됩니다. 코드 자체는 최대한의 유연성을 위해 전달 된 다양한 종류의 값에 적응해야합니다.
Gustavo6046

2
Python에서 형식 검사를 수행하는 데 권장되는 방법 isinstance()은 PEP 318 데코레이터 구현 에서와 같이 내장 함수를 사용하는 것입니다 . classinfo인수는 하나 이상의 유형이 될 수 있기 때문에 @ Gustavo6046의 (유효한) 이의 제기를 완화 할 수 있습니다. 파이썬에는 Number추상 기본 클래스가 있으므로 매우 일반적인 테스트 isinstance(42, numbers.Number)가 가능합니다.
martineau

48

데코레이터는 추가 기능으로 투명하게 "포장"하려는 모든 용도로 사용됩니다.

Django는이 기능을 사용하여 뷰 기능 에서 "로그인 필요"기능 을 래핑하고 필터 기능등록합니다 .

클래스 데코레이터를 사용 하여 명명 된 로그를 클래스추가 할 수 있습니다 .

기존 클래스 또는 함수의 동작에 "고착"할 수있는 충분히 일반적인 기능은 장식용 게임입니다.

Python-Dev 뉴스 그룹 에서 PEP 318-함수 및 메소드 데코레이터 (Decoators for Functions and Methods)가 지적한 유스 케이스에 대한 토론도 있습니다 .


Cherrypy는 @ cherrypy.expose를 사용하여 공개되는 함수와 숨겨진 함수를 바로 유지합니다. 그것은 나의 첫 번째 소개 였고 나는 거기에 익숙해졌습니다.
Marc Maxmeister

26

노즈 테스트의 경우 몇 가지 매개 변수 세트로 단위 테스트 기능 또는 방법을 제공하는 데코레이터를 작성할 수 있습니다.

@parameters(
   (2, 4, 6),
   (5, 6, 11),
)
def test_add(a, b, expected):
    assert a + b == expected

23

Twisted 라이브러리는 생성기와 결합 된 데코레이터를 사용하여 비동기 기능이 동기 적이라는 착각을줍니다. 예를 들면 다음과 같습니다.

@inlineCallbacks
def asyncf():
    doStuff()
    yield someAsynchronousCall()
    doStuff()
    yield someAsynchronousCall()
    doStuff()

이를 사용하여 수많은 작은 콜백 함수로 나뉘어 진 코드를 단일 블록으로 자연스럽게 작성할 수 있으므로 이해하고 유지하기가 훨씬 쉽습니다.


14

물론 로깅을위한 한 가지 용도는 다음과 같습니다.

import functools

def log(logger, level='info'):
    def log_decorator(fn):
        @functools.wraps(fn)
        def wrapper(*a, **kwa):
            getattr(logger, level)(fn.__name__)
            return fn(*a, **kwa)
        return wrapper
    return log_decorator

# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
    for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()

10

나는 주로 디버깅 (인수 및 결과를 인쇄하는 함수를 감싸는 함수) 및 검증 (예 : 인수가 올바른 유형인지 또는 웹 응용 프로그램의 경우 사용자가 특정 호출 할 수있는 충분한 권한이 있는지 확인) 방법).


6

함수를 스레드로부터 안전하게 만들기 위해 다음 데코레이터를 사용하고 있습니다. 코드를 더 읽기 쉽게 만듭니다. John Fouhy가 제안한 것과 거의 비슷하지만 차이점은 하나의 기능에 대한 작업이며 잠금 객체를 명시 적으로 만들 필요가 없다는 것입니다.

def threadsafe_function(fn):
    """decorator making sure that the decorated function is thread safe"""
    lock = threading.Lock()
    def new(*args, **kwargs):
        lock.acquire()
        try:
            r = fn(*args, **kwargs)
        except Exception as e:
            raise e
        finally:
            lock.release()
        return r
    return new

class X:
    var = 0

    @threadsafe_function     
    def inc_var(self):
        X.var += 1    
        return X.var

1
이것은 각각의 기능이 장식되어 있고 자체 잠금 기능이 있다는 의미입니까?
슬픔

1
@grieve 예, 데코레이터가 사용될 때마다 (호출되는) 데코레이션되는 함수 / 메소드에 대한 새로운 잠금 객체를 만듭니다.
martineau

5
정말 위험합니다. inc_var () 메소드는 한 번에 한 사람 만 호출 할 수 있다는 점에서 "threadsafe"입니다. 즉,이 방법은 멤버 변수 "var"에서 작동하고 아마도 다른 방법도 멤버 변수 "var"에서 작동 할 수 있으며 이러한 액세스는 잠금이 공유되지 않기 때문에 스레드로부터 안전하지 않습니다. 이런 식으로 작업하면 클래스 X 사용자에게 잘못된 보안 감각이 생깁니다.
밥 반 잔트

단일 잠금을 사용할 때까지 스레드 안전하지 않습니다.
찬두

5

데코레이터는 함수의 속성을 정의하거나이를 변경하는 상용구로 사용됩니다. 완전히 다른 기능을 반환하는 것은 가능하지만 반 직관적입니다. 여기서 다른 응답을 살펴보면 가장 일반적인 용도 중 하나는 로깅, 프로파일 링, 보안 검사 등 다른 프로세스의 범위를 제한하는 것 같습니다.

CherryPy는 객체 디스 패칭을 사용하여 URL을 객체 및 결과적으로 메소드에 일치시킵니다. 이 방법의 데코레이터는 CherryPy가 해당 방법을 사용할 있는지 여부를 나타 냅니다. 예를 들어, 튜토리얼 에서 수정되었습니다 .

class HelloWorld:

    ...

    def secret(self):
        return "You shouldn't be here."

    @cherrypy.expose
    def index(self):
        return "Hello world!"

cherrypy.quickstart(HelloWorld())

사실이 아닙니다. 데코레이터는 함수의 동작을 완전히 변경할 수 있습니다.
재귀

괜찮아. 그러나 데코레이터는 얼마나 자주 함수의 동작을 완전히 바꾸는가? 내가 본 것에서 속성을 지정하는 데 사용되지 않으면 상용구 코드에 사용됩니다. 응답을 편집했습니다.
Nikhil Chelliah

5

소셜 네트워킹 웹 응용 프로그램을 작업하면서 최근에 사용했습니다. 커뮤니티 / 그룹의 경우, 새로운 토론을 만들고 특정 그룹의 회원이어야하는 메시지에 응답 할 수있는 멤버쉽 권한을 부여해야했습니다. 그래서, 나는 데코레이터를 썼고 @membership_required그것을 내가보기에 필요한 곳에 두었습니다.


1

이 데코레이터를 사용하여 매개 변수를 수정합니다.

def fill_it(arg):
    if isinstance(arg, int):
        return "wan" + str(arg)
    else:
        try:
            # number present as string
            if str(int(arg)) == arg:
                return "wan" + arg
            else:
                # This should never happened
                raise Exception("I dont know this " + arg)
                print "What arg?"
        except ValueError, e:
            return arg

def fill_wanname(func):
    def wrapper(arg):
        filled = fill_it(arg)
        return func(filled)
    return wrapper

@fill_wanname
def get_iface_of(wanname):
    global __iface_config__
    return __iface_config__[wanname]['iface']

이 함수를 리팩터링 할 때이 함수는 인수 "wanN"을 전달해야하지만 이전 코드에서는 N 또는 'N'만 전달했습니다.


1

데코레이터를 사용하여 함수 메소드 변수를 쉽게 작성할 수 있습니다.

def static_var(varname, value):
    '''
    Decorator to create a static variable for the specified function
    @param varname: static variable name
    @param value: initial value for the variable
    '''
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

@static_var("count", 0)
def mainCallCount():
    mainCallCount.count += 1

6
귀하의 예를 들어 주셔서 감사하지만 (아포지) 나는 WTF를 말해야합니다-왜 이것을 사용 하시겠습니까? 사람들을 혼동시킬 수있는 거대한 잠재력이 있습니다. 물론, 나는 엣지 케이스 사용의 필요성을 존중하지만 많은 경험이 부족한 파이썬 개발자들이 가지고있는 일반적인 문제에 직면하고 있습니다-클래스를 충분히 사용하지 않습니다. 즉, 간단한 클래스 수를 가지고 초기화하고 사용하십시오. 멍청한 놈은 drop-thru (비 클래스 기반 코드)를 작성하고 정교한 해결 방법으로 클래스 기능 부족에 대처하려고합니다. 제발? 부디? 하프해서 죄송합니다. 답변 해 주셔서 감사합니다.하지만 핫 버튼을 누르십시오.
케빈 J. 라이스

코드 검토에 대한 풀 요청으로 표시되면 -1이 될 것이므로 좋은 파이썬으로도 -1입니다.
Techdragon

귀엽다. 어리석지 만 귀엽습니다. :) 가끔 함수 속성은 신경 쓰지 않지만 일반적인 파이썬 코드에서는 드문 일입니다. 하나를 사용하려고하면 데코레이터 아래에 숨기지 않고 명시 적으로 수행해야합니다.
PM 2Ring
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.