Python 장식 기 깊이 이해

8466 단어 Python장식 기
Python 장식 기 를 이야기 하기 전에 예 를 들 고 싶 습 니 다.좀 더러 우 나 장식 기 라 는 화제 와 잘 어 울 립 니 다.
누구나 가지 고 있 는 팬티 의 주요 기능 은 부끄러움 을 가 리 는 것 이지 만 겨울 이 되면 바람 과 추 위 를 막 아 줄 수 없 으 니 어떻게 해 야 합 니까?우리 가 생각 하 는 방법 은 팬 티 를 개조 해서 더 두 껍 고 길 게 만 드 는 것 이다.그러면 부끄러움 을 가 리 는 기능 도 있 을 뿐만 아니 라 보온 도 할 수 있다.그러나 문제 가 있다.이 팬 티 는 우리 가 긴 바지 로 개조 한 후에 부끄러움 을 가 리 는 기능 도 있 지만 본질 적 으로 더 이상 진정한 팬티 가 아니다.그래서 똑똑 한 사람들 이 긴 바 지 를 발 명 했 습 니 다.팬티 에 영향 을 주지 않 는 전제 에서 긴 바 지 를 팬티 밖 에 입 었 습 니 다.이렇게 팬티 인지 팬티 인지 긴 바지 가 생기 면 아 기 는 더 이상 춥 지 않 습 니 다.장식 기 는 우리 가 말 한 긴 바지 처럼 팬티 의 역할 에 영향 을 주지 않 는 전제 에서 우리 몸 을 따뜻 하 게 해 준다.
장식 기 에 대해 이야기 하기 전에 파 이 썬 의 함수 와 자바,C++가 다르다 는 것 을 알 아야 합 니 다.파 이 썬 의 함 수 는 일반 변수 처럼 매개 변수 로 다른 함수 에 전달 할 수 있 습 니 다.예 를 들 어:

def foo():
  print("foo")

def bar(func):
  func()

bar(foo)

본 격 적 으로 우리 의 주제 로 돌아가다.장식 기 는 본질 적 으로 Python 함수 나 클래스 로 다른 함수 나 클래스 가 코드 수정 이 필요 없 는 전제 에서 추가 기능 을 추가 할 수 있 습 니 다.장식 기의 반환 값 도 함수/클래스 대상 입 니 다.이 는 로그 삽입,성능 테스트,사무 처리,캐 시,권한 검사 등 장면 에 자주 사용 되 는데 장식 기 는 이런 문 제 를 해결 하 는 가장 좋 은 디자인 이다.장식 기 가 있 으 면 우 리 는 함수 기능 자체 와 무관 한 대량의 유사 코드 를 추출 하여 장식 기 에 계속 재 활용 할 수 있다.요약 하면 장식 기의 역할 은 이미 존재 하 는 대상 에 게 추가 적 인 기능 을 추가 하 는 것 이다.
먼저 간단 한 예 를 들 어 실제 코드 는 이것 보다 훨씬 복잡 할 수 있 지만:

def foo():
  print('i am foo')
함수 의 실행 로 그 를 기록 하고 싶 어서 코드 에 로그 코드 를 추가 합 니 다.

def foo():
  print('i am foo')
  logging.info("foo is running")
함수 bar(),bar 2()도 비슷 한 수요 가 있다 면 어떻게 합 니까?하나 더 써 볼 까요?이렇게 하면 대량의 유사 한 코드 를 만 들 수 있 습 니 다.중복 코드 를 줄 이기 위해 서 우 리 는 이렇게 할 수 있 습 니 다.새로운 함 수 를 다시 정의 할 수 있 습 니 다.로 그 를 전문 적 으로 처리 하고 로 그 를 처리 한 후에 진정한 업무 코드 를 실행 할 수 있 습 니 다.

def use_logging(func):
  logging.warn("%s is running" % func.__name__)
  func()

def foo():
  print('i am foo')

use_logging(foo)

이렇게 하 는 것 은 논리 적 으로 문제 가 없고 기능 은 실현 되 었 지만 우리 가 호출 할 때 진정한 업무 논리 foo 함 수 를 호출 하 는 것 이 아니 라 use 로 바 뀌 었 다.logging 함수,이것 은 원래 의 코드 구 조 를 파괴 합 니 다.지금 우 리 는 매번 원래 의 foo 함 수 를 매개 변수 로 use 에 전달 해 야 합 니 다.logging 함수,그럼 더 좋 은 방법 이 있 나 요?물론 있 습 니 다.정 답 은 장식 기 입 니 다.
단순 장식 기

def use_logging(func):

def wrapper():
    logging.warn("%s is running" % func.__name__)
return func()  #   foo          ,  func()      foo()
return wrapper

def foo():
  print('i am foo')

foo = use_logging(foo) #       use_logging(foo)          wrapper,        foo = wrapper
foo()          #   foo()       wrapper()
use_logging 은 하나의 장식 기 입 니 다.일반적인 함수 입 니 다.진정한 업무 논 리 를 수행 하 는 함수 func 를 감 싸 서 foo 가 use 에 의 해 보 입 니 다.logging 이 장식 한 것 처럼 uselogging 이 돌아 온 것 도 함수 입 니 다.이 함수 의 이름 은 wrapper 입 니 다.이 예 에서 함수 가 들 어가 고 종료 할 때 횡단면 이 라 고 불 리 며,이러한 프로 그래 밍 방식 은 절단면 을 위 한 프로 그래 밍 이 라 고 불 린 다.
문법 사탕
Python 을 접 한 지 오래 되 었 다 면@기호 가 낯 설 지 않 을 것 입 니 다.맞습니다.@기 호 는 장식 기의 문법 사탕 입 니 다.함수 가 정 의 를 시작 하 는 곳 에 두 면 마지막 으로 다시 할당 하 는 동작 을 생략 할 수 있 습 니 다.

def use_logging(func):

def wrapper():
    logging.warn("%s is running" % func.__name__)
return func()
return wrapper

@use_logging
def foo():
  print("i am foo")

foo()

위 에서 보 듯 이@이 있 으 면 우 리 는 foo=use 를 절약 할 수 있다.logging(foo)이라는 말 은 foo()를 직접 호출 하면 원 하 는 결 과 를 얻 을 수 있 습 니 다.보 셨 습 니까?foo()함 수 는 수정 할 필요 가 없습니다.정 의 된 곳 에 장식 기 를 추가 하고 호출 할 때 도 예전 과 같 습 니 다.만약 에 우리 가 다른 유사 한 함수 가 있다 면 우 리 는 장식 기 를 계속 호출 하여 함 수 를 수식 할 수 있 습 니 다.함 수 를 중복 수정 하거나 새로운 패 키 지 를 추가 하지 않 아 도 됩 니 다.이렇게 해서 우 리 는 프로그램의 중복 이용 성 을 높이 고 프로그램의 가 독성 을 증가 시 켰 다.
장식 기 가 Python 에서 이렇게 편리 하 게 사용 하 는 것 은 Python 의 함수 가 일반적인 대상 처럼 매개 변수 로 다른 함수 에 전달 할 수 있 기 때 문 입 니 다.다른 변수 에 값 을 부여 할 수 있 고 반환 값 으로 다른 함수 에 정의 할 수 있 습 니 다.
*args、**kwargs
제 업무 논리 함수 foo 에 인자 가 필요 하 다 면 어떻게 하 시 겠 습 니까?예 를 들 면:

def foo(name):
  print("i am %s" % name)
wrapper 함 수 를 정의 할 때 인 자 를 지정 할 수 있 습 니 다:

def wrapper(name):
    logging.warn("%s is running" % func.__name__)
return func(name)
return wrapper
이렇게 foo 함수 가 정의 하 는 매개 변 수 는 wrapper 함수 에 정의 할 수 있 습 니 다.이때 또 누군가가 물 어 볼 것 이다.만약 foo 함수 가 두 개의 인 자 를 받는다 면?세 개의 인 자 는 요?더 심 한 것 은 내 가 많은 것 을 전 할 수 있다.장식 기 가 foo 에 몇 개의 인자 가 있 는 지 모 를 때 우 리 는*args 로 대체 할 수 있 습 니 다.

def wrapper(*args):
    logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
이렇게 되면 foo 가 몇 개의 인 자 를 정 의 했 든 지 간 에 나 는 func 에 완전 하 게 전달 할 수 있다.이렇게 하면 foo 의 업무 논리 에 영향 을 주지 않 습 니 다.이때 또 독자 들 은 foo 함수 가 키워드 인 자 를 정의 했다 면?예 를 들 면:

def foo(name, age=None, height=None):
  print("I am %s, age %s, height %s" % (name, age, height))
이 때,wrapper 함 수 를 키워드 함수 로 지정 할 수 있 습 니 다.

def wrapper(*args, **kwargs):
# args     ,kwargs    
    logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
매개 변수 장식 기
장식 기 는 더욱 큰 유연성 이 있다.예 를 들 어 파 라 메 터 를 가 진 장식 기 는 위의 장식 기 호출 에서 이 장식 기 는 유일한 파 라 메 터 를 받 는 것 이 바로 업 무 를 수행 하 는 함수 foo 이다.장식 기의 문법 은 우리 가 호출 할 때@decorator(a)와 같은 다른 인 자 를 제공 할 수 있 도록 합 니 다.이렇게 하면 장식 기의 작성 과 사용 에 더욱 큰 유연성 을 제공 할 수 있다.예 를 들 어,우 리 는 인 테 리 어 에서 로그 의 등급 을 지정 할 수 있 습 니 다.업무 함수 에 따라 필요 한 로그 등급 이 다 를 수 있 기 때 문 입 니 다.

def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
        logging.warn("%s is running" % func.__name__)
elif level == "info":
        logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper

return decorator

@use_logging(level="warn")
def foo(name='foo'):
  print("i am %s" % name)

foo()

위의 uselogging 은 인 자 를 허용 하 는 장식 기 입 니 다.그것 은 실제로 기 존 장식 기의 함수 에 대한 패키지 이 며,장식 기 를 되 돌려 줍 니 다.우 리 는 그것 을 매개 변 수 를 포함 한 패키지 로 이해 할 수 있다.우리 가@use 를 사용 할 때logging(level="warn")이 호출 될 때 Python 은 이 층 의 패 키 징 을 발견 하고 인 자 를 장식 기 환경 에 전달 할 수 있 습 니 다.
@use_logging(level="warn")은@decorator 와 같 습 니 다.
클래스 장식 기
맞아요.장식 기 는 함수 일 뿐만 아니 라 클래스 일 수도 있어 요.함수 장식 기 에 비해 클래스 장식 기 는 유연성 이 크 고 내부 집적,포장 성 등 장점 이 있어 요.클래스 장식 기 를 사용 하 는 것 은 주로 클래스 에 의존 하 는call__방법 은@형식 으로 장식 기 를 함수 에 추가 할 때 이 방법 을 사용 합 니 다.

class Foo(object):
def __init__(self, func):
    self._func = func

def __call__(self):
print ('class decorator runing')
    self._func()
print ('class decorator ending')

@Foo
def bar():
print ('bar')

bar()

functools.wraps
장식 기 를 사용 하여 코드 를 크게 재 활용 하 였 으 나,그 는 원 함수 의 메타 정보 가 보이 지 않 는 다 는 단점 이 있 습 니 다.예 를 들 어 함수 의 docstring,name__、매개 변수 목록

#    
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__   #    'with_logging'
print func.__doc__    #    None
return func(*args, **kwargs)
return with_logging

#   
@logged
def f(x):
"""does some math"""
return x + x * x

logged(f)

발견 하기 어렵 지 않 습 니 다.함수 f 피 withlogging 이 대 체 했 습 니 다.물론 docstring,name__바로 withlogging 함수 정보 입 니 다.다행히 우 리 는 funtools.wraps 가 있 습 니 다.wraps 자체 도 장식 기 입 니 다.이 는 원 함수 의 메타 정 보 를 장식 기 안의 func 함수 에 복사 할 수 있 습 니 다.이 로 인해 장식 기 안의 func 함수 도 원 함수 foo 와 같은 메타 정 보 를 가지 게 되 었 습 니 다.

from functools import wraps
def logged(func):
  @wraps(func)
def with_logging(*args, **kwargs):
print func.__name__   #    'f'
print func.__doc__    #    'does some math'
return func(*args, **kwargs)
return with_logging

@logged
def f(x):
"""does some math"""
return x + x * x

장식 기 순서
하나의 함수 가 여러 개의 장식 기 를 동시에 정의 할 수 있 습 니 다.예 를 들 어:

@a
@b
@c
def f ():
  pass
그것 의 실행 순 서 는 안에서 밖으로 가장 안쪽 장식 기 를 가장 먼저 호출 하고 마지막 으로 가장 바깥쪽 장식 기 를 호출 하 는 것 이다.이것 은 같은 효 과 를 가진다.

f = a(b(c(f)))
읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기