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


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.