Python 함수 장식기의 원리와 용법에 대한 상세한 설명

9679 단어
본고의 실례는 파이톤 함수 장식기의 원리와 용법을 서술하였다.여러분에게 참고하도록 공유하겠습니다. 구체적으로는 다음과 같습니다.
장식기는 본질적으로 하나의 함수이다. 이 함수는 다른 함수를 처리하는 데 사용된다. 다른 함수는 코드를 수정할 필요가 없는 전제에서 추가 기능을 추가할 수 있고 장식기의 반환값도 하나의 함수 대상이다.이것은 로그 삽입, 성능 테스트, 사무 처리, 캐시, 권한 검사 등 응용 장면에 자주 사용된다.장식기는 이런 문제를 해결하는 절호의 디자인이다. 장식기가 있으면 우리는 함수 기능 자체와 무관한 대량의 비슷한 코드를 추출하여 계속 다시 사용할 수 있다.개괄적으로 말하면 장식기의 역할은 이미 존재하는 대상을 위해 추가 기능을 추가하는 것이다.
엄밀히 말하면 장식기는 문법사탕일 뿐이고 장식기는 호출 가능한 대상이며 일반적인 호출 가능한 대상처럼 호출할 수 있다. 특수한 점은 장식기의 매개 변수는 함수이다
이제 함수의 실행 시간을 기록할 수 있는 새로운 요구가 생겨서 코드에 로그 코드를 추가합니다.

import time
#        
def foo():
  start = time.time()
  # print(start) # 1504698634.0291758 1970 1 1       ,  Unix  
  time.sleep(3)
  end = time.time()
  print('spend %s'%(end - start))
foo()


bar(), bar2()도 비슷한 수요가 있는데 어떻게 해야 하나요?다시 bar 함수에서 시간 함수를 호출합니까?이렇게 하면 대량의 동일한 코드를 만들 수 있다. 중복 코드를 줄이기 위해 우리는 이렇게 해서 함수를 다시 정의할 수 있다. 즉, 전문적으로 시간을 설정한다.

import time
def show_time(func):
  start_time=time.time()
  func()
  end_time=time.time()
  print('spend %s'%(end_time-start_time))
def foo():
  print('hello foo')
  time.sleep(3)
show_time(foo)


그러나 이렇게 되면 당신의 기초 플랫폼의 함수가 이름을 수정하여 업무 라인의 사람들에게 고소당하기 쉽다. 왜냐하면 우리는 매번 하나의 함수를 매개 변수로 show 에 전달해야 하기 때문이다.time 함수.그리고 이런 방식은 기존의 코드 논리 구조를 파괴했다. 이전에 업무 논리를 실행할 때foo()를 실행했지만 지금은 show 로 바꿀 수 밖에 없다.time(foo).그럼 더 좋은 방법은 없을까요?당연히 있지, 답은 장식기야.

def show_time(f):
  def inner():
    start = time.time()
    f()
    end = time.time()
    print('spend %s'%(end - start))
  return inner
@show_time #foo=show_time(f)
def foo():
  print('foo...')
  time.sleep(1)
foo()
def bar():
  print('bar...')
  time.sleep(2)
bar()


결과 출력:
foo... spend 1.0005607604980469 bar...
함수 show타임은 장식기입니다. 진정한 업무 방법 f를 함수에 감싸서 포오가 상하 시간 함수에 장식된 것처럼 보입니다.이 예에서 함수가 들어오고 끝날 때 횡단면(Aspect)이라고 하는데 이런 프로그래밍 방식은 절단면을 위한 프로그래밍(Aspect-Oriented Programming)이라고 부른다.
# 기호는 장식기의 문법 사탕으로 함수를 정의할 때 사용되며 다시 값을 부여하는 동작을 피한다
장식기가 파이톤에서 이렇게 편리하게 사용되는 것은 파이톤의 함수가 일반적인 대상처럼 매개 변수로 다른 함수에 전달되고 다른 변수에 값을 부여받을 수 있으며 되돌아오는 값으로 다른 함수에 정의될 수 있기 때문이다.
장식기는 두 가지 특성이 있는데, 하나는 장식된 함수를 다른 함수로 바꿀 수 있고, 다른 하나는 모듈을 불러올 때 바로 실행할 수 있다.

def decorate(func):
  print('running decorate', func)
  def decorate_inner():
    print('running decorate_inner function')
    return func()
  return decorate_inner
@decorate
def func_1():
  print('running func_1')
if __name__ == '__main__':
  print(func_1)
  #running decorate 
  # .decorate_inner at 0x000001904743DF28>
  func_1()
  #running decorate_inner function
  # running func_1


args와 *kwargs를 통해 수식 함수의 매개 변수를 전달합니다

def decorate(func):
  def decorate_inner(*args, **kwargs):
    print(type(args), type(kwargs))
    print('args', args, 'kwargs', kwargs)
    return func(*args, **kwargs)
  return decorate_inner
@decorate
def func_1(*args, **kwargs):
  print(args, kwargs)
if __name__ == '__main__':
  func_1('1', '2', '3', para_1='1', para_2='2', para_3='3')
#    
# 
# args ('1', '2', '3') kwargs {'para_1': '1', 'para_2': '2', 'para_3': '3'}
# ('1', '2', '3') {'para_1': '1', 'para_2': '2', 'para_3': '3'}


파라미터가 있는 장식 함수

import time
#   
def show_time(f):
  def inner(x,y):
    start = time.time()
    f(x,y)
    end = time.time()
    print('spend %s'%(end - start))
  return inner
@show_time
def add(a,b):
  print(a+b)
  time.sleep(1)
add(1,2)


길지 않다

import time
#   
def show_time(f):
  def inner(*x,**y):
    start = time.time()
    f(*x,**y)
    end = time.time()
    print('spend %s'%(end - start))
  return inner
@show_time
def add(*a,**b):
  sum=0
  for i in a:
    sum+=i
  print(sum)
  time.sleep(1)
add(1,2,3,4)


패라메트릭 장식기
위의 장식기 호출 중, 예를 들어 @showtime, 이 장식기의 유일한 매개 변수는 업무를 수행하는 함수입니다.장식기의 문법은 @decorator (a) 와 같은 다른 인자를 호출할 때 제공할 수 있도록 합니다.이렇게 하면 장식기의 작성과 사용에 더욱 큰 유연성을 제공할 수 있다.

import time
def time_logger(flag=0):
  def show_time(func):
    def wrapper(*args, **kwargs):
      start_time = time.time()
      func(*args, **kwargs)
      end_time = time.time()
      print('spend %s' % (end_time - start_time))
      if flag:
        print('              ')
    return wrapper
  return show_time
@time_logger(flag=1)
def add(*args, **kwargs):
  time.sleep(1)
  sum = 0
  for i in args:
    sum += i
  print(sum)
add(1, 2, 5)


@time_logger(flag=1)는 두 가지 일을 했다.
(1)time_logger(1): 패키지 함수 show 얻기time, 환경 변수 flag 저장
(2)@show_time   :add=show_time(add)
위의 타임logger는 파라미터가 있는 장식기입니다.그것은 실제적으로 기존 장식기의 함수를 봉인하고 장식기 (파라미터를 포함하는 패키지 함수) 를 되돌려줍니다.@timelogger(1)를 호출할 때 파이톤은 이 층의 봉인을 발견하고 파라미터를 장식기 환경에 전달할 수 있습니다.
장식기를 겹쳐 놓다.
실행 순서가 뭐예요?
만약 한 함수가 여러 개의 장식기에 수식된다면, 사실은 이 함수가 가장 안쪽의 장식기에 먼저 수식된 후 (아래 예에서 함수main()가 먼저 inner에 의해 장식되고 새로운 함수로 변한 후, 다시 장식기에 수식되어야 한다)

def outer(func):
  print('enter outer', func)
  def wrapper():
    print('running outer')
    func()
  return wrapper
def inner(func):
  print('enter inner', func)
  def wrapper():
    print('running inner')
    func()
  return wrapper
@outer
@inner
def main():
  print('running main')
if __name__ == '__main__':
  main()
#    
# enter inner 
# enter outer .wrapper at 0x000001A9F2BD5048>
# running outer
# running inner
# running main


클래스 장식기
함수 장식기에 비해 클래스 장식기는 유연성이 크고 내부 집합이 높으며 봉인성 등 장점이 있다.클래스 장식기를 사용하면 클래스 내부의 에 의존할 수 있다call__방법, @ 형식으로 장식기를 함수에 추가할 때 이 방법을 사용합니다.

import time
class Foo(object):
  def __init__(self, func):
    self._func = func
  def __call__(self):
    start_time=time.time()
    self._func()
    end_time=time.time()
    print('spend %s'%(end_time-start_time))
@Foo #bar=Foo(bar)
def bar():
  print ('bar')
  time.sleep(2)
bar()  #bar=Foo(bar)()>>>>>>>       ,  active Foo  __call__  


표준 창고에는 여러 종류의 장식기가 있다
예를 들어 장식 방법의 함수는property,classmethod,staticmethod가 있다.functools 모듈의 lrucache,singledispatch,wraps 등
from functools import lru_cache from functools import singledispatch from functools import wraps
functools.랩스는 장식기를 사용하여 코드를 크게 복용했지만, 그의 단점은 원래 함수의 원 정보가 보이지 않는다는 것이다. 예를 들어 함수의docstring,name__、매개변수 목록의 예:

def foo():
  print("hello foo")
print(foo.__name__)# foo
def logged(func):
  def wrapper(*args, **kwargs):
    print (func.__name__ + " was called")
    return func(*args, **kwargs)
  return wrapper
@logged
def cal(x):
  resul=x + x * x
  print(resul)
cal(2)
#6
#cal was called
print(cal.__name__)# wrapper
print(cal.__doc__)#None
#  f wrapper   ,    docstring,__name__     wrapper      。


다행히 우리는 functools가 있다.wraps, wraps 자체도 하나의 장식기로서 원 함수의 원 정보를 장식기 함수에 복사할 수 있다. 그래서 장식기 함수도 원 함수와 같은 원 정보를 가지게 된다.

from functools import wraps
def logged(func):
  @wraps(func)
  def wrapper(*args, **kwargs):
    print(func.__name__ + " was called")
    return func(*args, **kwargs)
  return wrapper
@logged
def cal(x):
  return x + x * x
print(cal.__name__) # cal


장식기를 사용하면 우리가 원하지 않는 부작용이 발생할 수 있다. 예를 들어 수식된 함수 이름을 바꾸고 디버거나 대상 서열화기 등 내성 메커니즘을 사용해야 하는 도구가 정상적으로 작동하지 못할 수도 있다.
사실 장식기를 호출하면 같은 작용역에서 원래 함수와 같은 이름의 변수(예를 들어 아래func 1)를 장식기가 되돌아오는 대상으로 재할당한다.\wraps를 사용하면 내부 함수(예를 들어 아래func 1)와 관련된 중요한 메타데이터를 모두 외곽 함수(예를 들어 아래decorate inner)로 복사합니다

from functools import wraps
def decorate(func):
  print('running decorate', func)
  @wraps(func)
  def decorate_inner():
    print('running decorate_inner function', decorate_inner)
    return func()
  return decorate_inner
@decorate
def func_1():
  print('running func_1', func_1)
if __name__ == '__main__':
  func_1()
#    
#running decorate 
# running decorate_inner function 
# running func_1 


파이썬 관련 내용에 관심이 있는 독자는 본 사이트의 주제를 보실 수 있습니다.,,,,,,,,,,,,,,,,
본고에서 서술한 것이 여러분의 파이톤 프로그램 설계에 도움이 되었으면 합니다.

좋은 웹페이지 즐겨찾기