Python, Decorator 써본 이야기

어쩌다 python-django 로 웹 어플리케이션 하나를 만들게 되었는데 이거 은근 신세계다. django 같은 프레임워크야 뭐 많으니까 놀랄게 없더라도 python의 decorator는 나에게 뭔가 신세계를 보여줬다. java-spring에서 그렇게 복잡했던 AOP가 decorator를 쓰면 그냥 별 고민 없이 끝나버린다. 이걸 AOP라고 해도 될런지 모르겠지만 말이다. 여튼 decorator에 감탄한 나머지 까먹기 전에 decorator 파트만 정리해본다.

일반 데코레이터

  1. 이렇게 코딩하고 실행하면
    # 얘가 데코레이터
    def decorator(func):
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__ , "after"))
            return result
        return decorator
    
    # 함수에 데코레이터를 붙여준다.
    @decorator
    def func(x, y):
        print(x + y)
        return x + y
    
    func(1,2)
  2. 이런 결과가 나온다. 아! 신통방통 하다!
    func before
    3
    func after
  3. @데코레이터는 사실 이거랑 같은 의미라고 한다
    def decorator(func):
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__ , "after"))
            return result
        return decorator
    
    def func(x, y):
        print(x + y)
        return x + y
    
    func2 = decorator(func)
    func2(1,2)

파라메터를 가지는 데코레이터

  1. 데코레이터에 뭔가 파라메터를 전달하고 싶을데는 약간 복잡하긴 하지만 역시 다 된다! function을 감싸는 decorator를 다시 감싸주면 된다.
    # 얘가 파라메터도 붙는 데코레이터
    def decorator_with_param(param):
        def wrapper(func):
            def decorator(*args, **kwargs):
                print(param)
                print("%s %s" % (func.__name__, "before"))
                result = func(*args, **kwargs)
                print("%s %s" % (func.__name__ , "after"))
                return result
            return decorator
        return wrapper
    
    @decorator_with_param("hello, decorator!")
    def func(x, y):
        print(x + y)
        return x + y
    
    func(1,2)
  2. 결과는 이렇게 나온다!
    hello, decorator!
    func before
    3
    func after

그런데 func.__doc__이 나오지 않는다. 아,  망했다!

  1. 원랜 이렇게 나와야한다. 그래야 Swagger UI 같은애랑 붙일때 자동으로 문서화가 된다.
    def func(x, y):
        """
        x와 y를 더합니다.
        :param x:
        :param y:
        :return:
        """
        print(x + y)
        return x + y
    
    print(func.__doc__)
    
    ---- 출력 ----
    
     x와 y를 더합니다.
     :param x:
     :param y:
     :return:
  2. 그런데 데코레이션을 붙이는 순강 망한다. __doc__이 안나온다. 실제로 django api application을 만들면서 api endpoint 메소드들을 decorator로 신나게 감쌌더니 Sweager UI에서 doc 처리하지 못해 공백 API 가이드만 한가득 나왔다.
    def decorator(func):
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__ , "after"))
            return result
        return decorator
    
    @decorator
    def func(x, y):
        """
        x와 y를 더합니다.
        :param x:
        :param y:
        :return:
        """
        print(x + y)
        return x + y
    
    print(func.__doc__)
    
    ---- 출력 ----
    None # 아, 망했어요!
  3. 생각해보면 당연한 일이다. 실행시간에 실제로 접근하는 메타데이터는 func가 아니라 데코레이터가 만들어준 wrapper의 메터데이터니 제대로 나올리가 없다. 그렇다. 우린 망했다.
  4. 그렇다고 진짜 망한건 아니다. decorator에 @wraps 달아주면 모든것이 해결된다. 모든 decorator에는 반드시 @wraps를 달아주자. 그것이 모두가 행복해지는 길이다. 이유는 찾아보기 귀찮아서 생략. (대충 소스 보니 func의 __doc__ 같은 meta 정보를 wrapper에 복사해 넣는거 같은데 확실한건 아님!)
    from functools import wraps
    
    # 파라메터 없는 데코레이터에도 @wraps 붙여주고
    def decorator(func):
        @wraps(func)
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__, "after"))
            return result
        return decorator
    
    
    # 파라메터 있는 데코레이터에도 @wraps 붙여주고
    def decorator_with_param(param):
        def wrapper(func):
            @wraps(func)
            def decorator(*args, **kwargs):
                print(param)
                print("%s %s" % (func.__name__, "before"))
                result = func(*args, **kwargs)
                print("%s %s" % (func.__name__ , "after"))
                return result
            return decorator
        return wrapper
    
    
    @decorator
    def func(x, y):
        """
        x와 y를 더합니다.
        :param x:
        :param y:
        :return:
        """
        print(x + y)
        return x + y
    
    
    @decorator_with_param("hello, decorator!")
    def func2(x, y):
        """
        x와 y를 더합니다.
        :param x:
        :param y:
        :return:
        """
        print(x + y)
        return x + y
    
    
    print(func.__doc__)
    func(1, 2)
    print(func2.__doc__)
    func2(1, 2)
  5. 실행하니 잘 나오네!
      x와 y를 더합니다.
     :param x:
     :param y:
     :return:
     
    func before
    3
    func after
    
     x와 y를 더합니다.
     :param x:
     :param y:
     :return:
     
    hello, decorator!
    func2 before
    3
    func2 after
    
    

class로도 decorator 선언이 가능하다고도 합니다.

class로 만드는게 뭔가 낙타표기도 되고 그래서 뭔가 그 뭔가 멋져보이는거 같은데 여기엔 치명적인 단점이 있다. @wraps를 붙일수가 없다. 그나마 parameter를 가지는 데코레이터의 경우 __call__ 시점에서 wrapper를 만들면서 @wraps를 붙여줄수 있는데 parameter가 없는 데코레이터의 경우 wraps를 붙일 방법이 보이질 않는다. 이거 저거 찾아보니 결국 __doc__, __name__들을 복사해 넣는데 이럴거면 그냥 function으로 데코레이터 만들란다.

from functools import wraps

# 그냥 Class 데코레이터, @wraps를 붙일만한데가 보이지 않는다.
class Decorator:
    def __init__ (self, func):
        self.func = func

    def __call__ (self, *args, **kwargs):
        print("%s %s" % (self.func.__name__, "before"))
        result = self.func(*args, **kwargs)
        print("%s %s" % (self.func.__name__, "after"))
        return result


# 파레매터를 가지는 Class 데코레이터, __call__에서 @wraps를 넣어준다.
class DecoratorWithParam:
    def __init__ (self, param):
        self.param = param

    def __call__ (self, func):
        @wraps(func)
        def decorator(*args, **kwargs):
            print(self.param)
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__, "after"))
        return decorator


@Decorator
def func(x, y):
    """
    x와 y를 더합니다.
    :param x:
    :param y:
    :return:
    """
    print(x + y)
    return x + y

@DecoratorWithParam("hello, decorator!")
def func2(x, y):
    """
    x와 y를 더합니다.
    :param x:
    :param y:
    :return:
    """
    print(x + y)
    return x + y


print(func.__doc__)
func(1,2)
print(func2.__doc__)
func2(1,2)

---- 출력 ----
None # 아, 이거 짜증나네
func before
3
func after

 x와 y를 더합니다.
 :param x:
 :param y:
 :return:
 
hello, decorator!
func2 before
3
func2 after

끝!

git log graph 설정

vi ~/.gitconfig

[alias]
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
lg = !"git lg1"

인터넷 뒤져서 나온 제일 맘에 드는 설정……

주짓수 수련기 7 – Side Mount Escape

요즘 사이드 마운트만 당했다 하면 빠져나오지 못하고 너무 허부적 댄다. 원래 빠져나오기 어려운 자세라고 하지만 연습도 지식도 많이 부족한거 같다. 다음 운동떄는 사이드 마운트 내어 주고 스파링을 시작해볼까 한다.

  1.  제일 처음 배운 기본적인 탈출법
    상대와 내 몸 사이 틈을 만들고 엉덩이 빼기로 틈을 더욱 크게 벌린다. 그리고 재빨리 무릎을 상대 허리 깊숙이 밀어넣어 가드 자세를 취한다. 나의 경우는 무릎 넣는 타이밍에 문제가 있는지 허리를 빼앗기 전에 다시 사이드 마운트로 원상 복귀가 되어버린다. 초반 빈공간 만들때 체력 소모가 좀 큰게 단점인거 같다.

    – 공간 만들기 위해 몸을 좌우 브릿주 해주는게 포인트인 영상

    – 빈틈을 만들기 위해 브릿지를 강조해주는 영상

    – 기본 외에 몇가지 응용이 있는 영상
  2. 힙롤 이라고 하는거 같던데 1번보다 체력소모도 작고 효과도 확실한거 같다. 다만 삑사리 나면 백을 잡혀버리는 리스크가 있다. 몇번 시도했는데 아직 성공한적은 없다.

  3. Elbo Push 라는데 안배워서 그냥 혼자 시도해봐야 겠음
  4. 음 이건 색다른데?
  5. 그외, 참조하면 좋은 영상

주짓수 수련기 6 – Lasso Guard

얼마전에 배우게 된 라쏘 가드

한팔(왼팔)로 상대의 맞은편 팔(오른팔) 소매를 잡고 같은 방향 다리(왼팔)로 다시 얽어매어 고정시키는 형태의 가드. 소매를 강하게 잡고 있어야 해서 손가락에 부담이 꽤 되는데 다리로 옭아매는 부분 때문인지 상대를 압박하는 힘이 은근 강력하다.

  1. 기본 세팅 – 일반 오픈(혹은 클로즈) 가드 상태에서 라소 가드로 전환,
  2. 제일 처음 배운 스윕
  3. 두번째로 배운 오모플라타 전환
  4. 세번째로 배운 lassor to x-guard 스윕
  5. 오모플라타 전환 좀 어려운거 (데굴데굴?)
  6. 기타 등등등……
  7. 라소 가드 패스 – 이건 아직 제대로 못배워서 @_@

주짓수 수련기 5 – X-Guard Sweep

본 주짓수 도장에서 배운 X-Guard 후 백으로 넘어가는 것 외에 몇개가 더있어서 퍼옴

대충 4가지 sweep 방법이 있는듯 하다.

1. 본 주짓수에서 배운 x가드 후 상대 등으로 넘어가기 영상

2. x가드후 상대를 전방으로 넘어뜨린 후 가드 패스
3. x가드후 상대를 위쪽으로 넘긴 후 가드 패스
4. x가드후 상대 등쪽으로 넘기고 가드 패스
x가드 까지 가는 과정이 좀더 상세히 나온 영상

연습 부족으로 블루 벨트에게 써먹다가 역공당했던 기술 ㅠ.ㅠ

LeedCode – Longest Substring Without Repeating Characters 문제풀이 (난이도:중)

LeedCode – Longest Substring Without Repeating Characters 문제풀이 (난이도:중)

Given a string, find the length of the longest substring without repeating characters.

Examples:
Given “abcabcbb”, the answer is “abc”, which the length is 3.
Given “bbbbb”, the answer is “b”, with the length of 1.
Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        boolean[] cache = new boolean[255];
        int count = 0;
        int maxCount = 0;
        int head = 0, tail = 0;
        while(head < s.length()){
            if(cache[s.charAt(head)] != true){
                cache[s.charAt(head)] = true;
                maxCount = Math.max(head-tail+1, maxCount);
                head++;
            }
            else{
                cache[s.charAt(tail)] = false;
                tail++;
            }
        }
        
        return maxCount;
    }
}

주짓수 수련기 4 – Close Guard Pass

내 허리를 단단히 조이는 상대의 강력한 클로즈 가드를 뚫고 들어가자!

상대의 양 골반을 골반을 잡을 수 있을때 : 양 손으로 상대의 골반을 단단히 고정하고 몸을 틀어 그 힘으로 가드를 부수고 들어간다.

상대의 겨드랑이를 잡을 수 있을때 : 한쪽 무릎을 상대의 가랑이 사이에 넣어 그 힘으로 가드를 부수고 들어간다.

상대의 한쪽 팔을 컨트롤 할 수 있을때 상대의 한손을 제압하고 그쪽 방향의 다리를 축으로 일어서 중력의 힘으로 가드를 부수고 들어간다.

몇가지 변형들이 있는데 위에 것드이 가장 기본적인거 같다.

주짓수 수련기 3 – Scissors Sweep

주짓수 수련기 2 이후 갑작스런 수술로 2달간 주짓수를 또 쉬게 되었다.

다행히 수술도 잘 끝났고 회복 기간이 지난후 의사 선생님도 이제 운동해도 된다고 하셨고,

그후 웨이트 트레이닝을 하며 몸을 테스트 해본결과 별 이상이 느껴지지 않아 당산동 본 주짓수에 다시 나가기로 했다.

2달만에 다시 해본 스파링은 역시 아무것도 기억이 안나 엄청 허부적 대었다. 다행히 기본 가드 자세와 새우빼기, 브릿지는 다 기억이나

기초 스킬부터 다시 연습 해야 할듯 하다.

일단, 맨 처음 배웠던 Scissors Sweep 부터 정리해본다.

Codility – MaxCounters 문제 풀이 (난이도:중)

Codility – MaxCounters 문제 풀이 (난이도:중)

// you can also use imports, for example:
// import java.util.*;

// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");

class Solution {
    public int[] solution(int N, int[] A) {
        
        int maxCounter = N+1;
        int counters[] = new int[N];
        for(int i = 0 ; i < counters.length ; i++){
            counters[i] = 0;   
        }
        
        int nextMax = 0;
        int curMax = 0;
        for(int i = 0 ; i < A.length ; i++){
            int counterNumber = A[i];
            int counterIndex = counterNumber - 1;
            
            if(counterNumber < maxCounter){
                if(counters[counterIndex] <= curMax){
                    counters[counterIndex] = curMax;
                }
                counters[counterIndex]++;
                nextMax = Math.max(nextMax, counters[counterIndex]);
            }
            else{
                curMax = nextMax;
            }
        }
        
        for(int i = 0 ; i < counters.length ; i++){
            if(counters[i] < curMax){
                counters[i] = curMax;   
            }
        }
        
        return counters;
    }
}

Codility – FrogRiverOne 문제 풀이 (난이도 : 하)

Codility – FrogRiverOne 문제 풀이 (난이도 : 하)

class Solution {
    public int solution(int X, int[] A) {
        // write your code in Java SE 8
        int leafList[] = new int[X+1];
        for(int i = 0 ; i < leafList.length ; i++){
            leafList[i] = -1;   
        }
        
        for(int i = 0 ; i < A.length ; i++){
            int leaf = A[i];
            if(leaf <= X ){
                if(leafList[leaf] == -1){
                    leafList[leaf] = i;
                }
                else{
                    leafList[leaf] = Math.min(i, leafList[leaf]);    
                }
            }
        }
        
        int result = 0;
        for(int i = 1 ; i < leafList.length ; i++){
            if(leafList[i] == -1){
                result = -1;
                break;
            }
            result = Math.max(result, leafList[i]);
        }
        
        return result;
    }
}