python - 장식기/문법사탕/함수 봉인에 대한 생각

12193 단어

장식기


장식기는 본질적으로 파이톤 함수로 다른 함수들이 코드 변동이 필요 없는 전제에서 추가 기능을 추가할 수 있고 장식기의 반환값도 함수 대상이다.로그 삽입, 성능 테스트, 사무 처리, 캐시, 권한 검사 등 장면에 자주 사용된다.장식기는 이런 문제를 해결하는 절호의 디자인이다. 장식기가 있으면 우리는 함수 기능 자체와 무관한 대량의 비슷한 코드를 추출하여 계속 다시 사용할 수 있다.개괄적으로 말하면 장식기의 역할은 이미 존재하는 대상을 위해 추가 기능을 추가하는 것이다.
def foo():
    print('i am foo')

함수의 실행 로그를 기록할 수 있는 새로운 요구 사항이 생겨 코드에 로그 코드를 추가합니다.
def foo():
    print('i am foo')
    logging.info("foo is running")
bar(), bar2()도 비슷한 수요가 있는데 어떻게 해야 하나요?bar 함수에 logging을 하나 더 쓸까요?이렇게 하면 대량의 공통된 코드를 만들 수 있다. 중복 코드를 줄이기 위해 우리는 이렇게 해서 함수를 다시 정의할 수 있다. 로그를 전문적으로 처리하고 로그를 처리한 후에 진정한 업무 코드를 집행한다.
def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()

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

use_logging(bar)

논리적으로 이해하기 어렵지 않지만, 이렇게 되면 우리는 매번 하나의 함수를 매개 변수로use 에 전달해야 한다.logging 함수.그리고 이런 방식은 기존의 코드 논리 구조를 파괴했다. 이전에 업무 논리를 실행할 때bar()를 실행했지만 지금은use 로 바뀌어야 한다.logging(bar).그럼 더 좋은 방법은 없을까요?당연히 있지, 답은 장식기야.
def use_logging(func):      #             def     func,       ,   def         ,   def    func,    (    )
#           ,  return    
    def wrapper(*args, **kwargs):
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

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

bar = use_logging(bar)
bar()

함수use_logging는 바로 장식기입니다. 진정한 업무 방법을 수행하는func를 함수 안에 감싸서 bar이불use_logging처럼 장식되어 있습니다.이 예에서 함수가 들어오고 끝날 때 횡단면(Aspect)이라고 하는데 이런 프로그래밍 방식은 절단면을 위한 프로그래밍(Aspect-Oriented Programming)이라고 부른다.
# 기호는 장식기의 문법 사탕으로 함수를 정의할 때 사용되며 다시 값을 부여하는 동작을 피한다
def use_logging(func):
#             def     func,       ,   def         ,   def    func,    (    )
    def wrapper(*args, **kwargs):
        logging.warn("%s is running" % func.__name__)
        return func(*args)
    return wrapper    # return  ,       func

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

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

bar()          #       `bar = use_logging(bar)`

위에서 보듯이 우리는 bar = use_logging(bar)라는 문장을 생략하고 bar()를 직접 호출하면 원하는 결과를 얻을 수 있다.만약 우리가 다른 유사한 함수가 있다면, 우리는 함수를 반복해서 수정하거나 새로운 봉인을 추가하지 않고, 장식기를 계속 호출해서 함수를 수식할 수 있다.이렇게 하면 우리는 프로그램의 중복 이용성을 향상시키고 프로그램의 가독성을 증가시켰다.
파라미터가 있는 장식기 장식기는 더 큰 유연성을 가진다. 예를 들어 파라미터가 있는 장식기: 위의 장식기 호출에서 예를 들어@use_logging 이 장식기의 유일한 파라미터는 업무를 수행하는 함수이다.장식기의 문법은 우리가 호출할 때 다른 매개 변수를 제공할 수 있도록 한다. 예를 들어 @decorator(a).이렇게 하면 장식기의 작성과 사용에 더욱 큰 유연성을 제공할 수 있다.
In [10]: import logging

In [11]: def use_logging(level):
    ...:     def decorator(func):
    ...:         def wrapper(*args, **kwargs):
    ...:             if level == 'warn':
    ...:                 logging.warn('%s is running'% func.__name__)
    ...:             return func(*args)
    ...:         return wrapper
    ...:     return decorator
    ...:

In [12]: @use_logging(level="warn")
    ...: def foo(name="foo"):
    ...:     print('i am %s'% name)
    ...:

In [13]: foo()
/usr/local/bin/ipython3:5: DeprecationWarning: The 'warn' function is deprecated, use 'warning' instead
  import sys
WARNING:root:foo is running
i am foo

In [14]: @use_logging(level)
    ...: def foo(name="foo"):
    ...:     print('i am %s'% name)
    ...:
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
 in ()
----> 1 @use_logging(level)
      2 def foo(name="foo"):
      3     print('i am %s'% name)
      4

NameError: name 'level' is not defined

위의 use_logging는 파라미터를 허용하는 장식기입니다.그것은 실제적으로 원래의 장식기의 함수를 봉하여 장식기로 되돌려준다.우리는 그것을 파라미터를 포함하는 패키지로 이해할 수 있다.우리가 @use_logging(level="warn") 호출을 사용할 때, 파이톤은 이 층의 봉인을 발견하고 파라미터를 장식기 환경에 전달할 수 있다.
클래스 장식기는 클래스 장식기를 다시 한 번 살펴보자. 함수 장식기에 비해 클래스 장식기는 유연성이 크고 내부 집합이 높으며 봉인성 등 장점이 있다.클래스 장식기를 사용하면 클래스 내부의 에 의존할 수 있다call__방법, @ 형식으로 장식기를 함수에 추가할 때 이 방법을 사용합니다.
In [19]: class Foo(object):
    ...:     def __init__(self,func):
    ...:         self._func = func
    ...:     def __call__(self):      #        
    ...:         print('class decorator running')
    ...:         self._func()
    ...:         print('class decorator ending')
    ...:

In [20]: @Foo
    ...: def bar():
    ...:     print('bar')
    ...:

In [21]: bar()
class decorator running
bar
class decorator ending

functools.wraps는 장식기를 사용하여 코드를 크게 복용했지만 그의 단점 중 하나는 원 함수의 원 정보가 없어졌다는 것이다. 예를 들어 함수의 docstring, __name__, 매개 변수 목록이다. 먼저 예를 들어 장식기
def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

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

함수 완성은 다음과 같습니다.
def f(x):
    """does some math"""
    return x + x * x
f = logged(f)

함수fwith_logging로 대체되었음을 발견하기 어렵지 않다. 물론 함수docstring__name__는 함수with_logging 함수의 정보로 바뀌었다.
print f.__name__    # prints 'with_logging'
print f.__doc__     # prints None

이 문제는 비교적 심각하다. 다행히도 우리는functools.wrapswraps 자체도 하나의 장식기이다. 이것은 원 함수의 원 정보를 장식기 함수에 복사할 수 있기 때문에 장식기 함수도 원 함수와 같은 원 정보를 가지게 된다.
from functools import wraps
def logged(func):
    @wraps(func)      #    wraps,      ,     
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

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

print f.__name__  # prints 'f'
print f.__doc__   # prints 'does some math'

내장 장식기 @staticmathod, @classmethod, @property
장식기의 순서
@a
@b
@c
def f ():

해당f = a(b(c(f)))함수와 방법류의 함수를 방법(method)이라고 하고 모듈의 함수를 함수(function)라고 한다.무릇def foo() 이런 것은 모두 함수이고 클래스에서 정의한 함수가 바로 방법이다.모든 가방, 모듈, 클래스, 함수, 방법은 문서를 포함해야 한다. 클래스 포함__init__ 방법
특수 함수__call__모든 함수는 호출 대상이다.하나의 클래스 실례도 하나의 호출 대상이 될 수 있으며 특수한 방법__call__()만 실현할 수 있다.
Person 클래스를 호출 가능한 객체로 만듭니다.
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, friend):
        print 'My name is %s...' % self.name
        print 'My friend is %s...' % friend

이제 Person 인스턴스를 직접 호출할 수 있습니다.
>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...
p('Tim')만 봐도 p가 함수인지 클래스의 실례인지 확정할 수 없기 때문에 파이톤에서 함수도 대상이고 대상과 함수의 차이는 현저하지 않다.
함수 패키지
함수 봉인은 일종의 함수 기능으로 한 프로그래머가 쓴 하나 이상의 기능을 함수, 클래스의 방식으로 봉하여 대외적으로 간단한 함수 인터페이스만 제공한다.프로그래머가 프로그램을 쓰는 과정에서 같은 조작을 수행해야 할 때 프로그래머(호출자)는 같은 함수를 써서 호출할 필요가 없고 함수 라이브러리에서 직접 호출할 수 있다.프로그래머도 네트워크에서 다운로드한 기능 함수를 [컴파일러]의 [라이브러리 함수]에 봉하여 이 기능을 실행할 함수가 필요할 때 직접 호출하면 된다.프로그래머는 함수 내부에서 어떻게 이루어지는지 알 필요가 없고 이 함수나 클래스가 어떤 기능을 제공하는지 알기만 하면 된다.
문법 사탕
문법사탕(Syntactic sugar)은 Peter J. Landin(툴링과 같은 천재적인 인물로 그가 가장 먼저 Lambda 연산을 발견하고 함수식 프로그래밍을 창립한 단어로 컴퓨터 언어에 새로운 기능을 추가하지 않고 인간에게 더 달콤한 문법을 뜻한다.문법사탕은 종종 프로그래머에게 더욱 실용적인 인코딩 방식을 제공하여 더욱 좋은 인코딩 스타일에 유익하고 읽기 쉽다.그러나 언어에 새로운 것을 첨가하지는 않았다.
리스트에 대해 list1=[1,2],[3,4,5],[6,7],[8],[9]]를 목록list 로 전환2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]의 질문.
일반적 방법
list_1 = [[1, 2], [3, 4, 5], [6, 7], [8], [9]]
list_2 = []
for _ in list_1:
    list_2 += _
print(list_2)

더 Pythonic 방법 2, 목록 유도
list_1 = [[1, 2], [3, 4, 5], [6, 7], [8], [9]]
[i for k in list_1 for i in k]       #  ⚠️  ,     ,  

추상적인 용법.
list_1 = [[1, 2], [3, 4, 5], [6, 7], [8], [9]]
sum(list_1, [])
sum의 첫 번째 파라미터는 교체 대상이면 되고, 두 번째 파라미터는 기본적으로 0이다.
lambda 표현식
함수식 그 흑마법 - 문법사탕
+1 함수
f=lambda x:x+1
max 함수(조건문장의 쓰기 방법은 다음과 같다)
f_max=lambda x,y:x if x>y else y

Filter,map,reducefilter 함수는 두 개의 매개 변수를 받아들인다. 첫 번째는 필터 함수이고, 두 번째는 범람 가능한 대상이며, 필터 조건을 충족시키는 모든 요소를 선택하는 데 사용된다.
소문자 제거
s=filter(lambda x:not str(x).islower(),"asdasfAsfBsdfC")
for ch in s:
    print(ch)
map 함수가 받아들인 매개 변수의 유형은 filter과 유사하며 함수를 범람 가능한 대상의 모든 요소에 작용하는 데 사용된다.수학에서 비추는 개념과 유사하다.예: y=2x+1 구하기
s=map(lambda x:2*x+1,range(6))
for x in s:
    print(x)

range(6) = range(0,6) = [0,1,2,3,4,5] 함수 원형: range(start,end,scan): range() 함수는 정수 목록을 만들 수 있습니다. 일반적으로 for 순환에서 start에서 end로 끝까지의 계수를 계산하는데 end,scan을 포함하지 않습니다. 매번 점프하는 간격은 기본적으로 1입니다.reduce 함수는 모든 원소를 누적 조작하는데 첫 번째 파라미터는 반드시 두 개의 파라미터가 있는 함수여야 한다.
In [54]: from functools import reduce
In [55]: s = reduce( lambda x,y: x+y, range(1,6) )
In [56]: print(s)
15

곱셈 구하기 (세 번째 선택할 수 있는 매개 변수는 누적 변수의 초기 값을 표시합니다)
from functools import reduce
s=reduce(lambda x,y:x*y,range(1,6),1)
print(s)
# 120

커리화 (curry) 함수 한 함수에 두 개의 매개 변수가 필요하고 한 개의 매개 변수만 입력하면 커리화 함수를 얻을 수 있습니다. 이것은 함수식 프로그래밍 언어의 중요한 특성 중 하나*3 함수
f_mul=lambda x,y:x*y
from functools import partial
mul3=partial(f_mul,3)
print(mul3(1))
print(mul3(6))

주: 익명 함수 과다 는 코드 효율 에 영향 을 줄 수 있다.

랴오신의 장식기에 대한 해석


만약 decorator 자체가 매개 변수를 입력해야 한다면, decorator를 되돌려 주는 고급 함수를 작성해야 하며, 쓰기가 더욱 복잡할 것이다.예를 들어, log의 텍스트를 사용자화할 경우:
def log(text):       # text          ,func             ,       
    def decorator(func):          #     !!!
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

이 3층 내포된 decorator는 다음과 같이 사용됩니다.
@log('execute')
def now():
    print('2015-3-25')

결과는 다음과 같습니다.
>>> now()
execute now():
2015-3-25

3레이어 네스트의 효과는 두 레이어의 decorator에 비해 다음과 같습니다.
>>> now = log('execute')(now)

우리는 위의 문장을 분석하고 먼저 집행log('execute')하고 되돌아오는 함수decorator 함수를 다시 호출하고 되돌아오는 함수를 호출한다. 매개 변수는 now 함수이고 되돌아오는 값은 최종적으로wrapper 함수이다.상기 두 가지 데코레이터의 정의는 모두 문제가 없지만 아직 마지막 단계가 남았다.우리가 함수도 대상이라고 말했기 때문에 __name__ 등 속성이 있지만 decorator 장식을 거친 함수를 보면 그것들의 __name__은 원래의'now'에서'wrapper'로 바뀌었다.
>>> now.__name__
'wrapper'

되돌아오는 wrapper() 함수 이름이'wrapper'이기 때문에 원시 함수__name__ 등 속성을 wrapper() 함수에 복사해야 합니다. 그렇지 않으면 함수 서명에 의존하는 코드가 실행될 수 있습니다.
이런 코드를 작성할 필요가 없다wrapper.__name__ = func.__name__. 파이톤에 내장된 functools.wraps는 바로 이 일을 하는 것이다. 그러므로 완전한 decorator의 작성법은 다음과 같다.
import functools

def log(func):
    @functools.wraps(func)   #         ,      
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

또는 매개변수가 있는 decorator에 대해 다음을 수행합니다.
import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)     #         ,      
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

좋은 웹페이지 즐겨찾기