2011-09-20 7 views
11

파이썬에서 클래스 인자에 선택적 인자를 전달하려고합니다. ,파이썬 클래스 장식 자 인자들

class Cache(object): 
    def __init__(self, function, max_hits=10, timeout=5): 
     self.function = function 
     self.max_hits = max_hits 
     self.timeout = timeout 
     self.cache = {} 

    def __call__(self, *args): 
     # Here the code returning the correct thing. 


@Cache 
def double(x): 
    return x * 2 

@Cache(max_hits=100, timeout=50) 
def double(x): 
    return x * 2 

인수가 두 번째 장식가 (내 __init__ 기능에 max_hits=10, timeout=5) 기본 하나를 덮어 작동하지 않으며 나는 예외 TypeError: __init__() takes at least 2 arguments (3 given)을 가지고 : 코드 아래 내가 현재 가지고. 나는 많은 해결책을 시도하고 그것에 관한 기사를 읽었지 만, 나는 여전히 그것을 작동하게 만들 수 없다.

이 문제를 해결할만한 아이디어가 있습니까? 감사!

답변

12

@Cache(max_hits=100, timeout=50)__init__(max_hits=100, timeout=50)이므로 function 인수는 만족스럽지 않습니다.

함수가 있는지 여부를 감지하는 래퍼 메서드를 통해 데코레이터를 구현할 수 있습니다. 함수를 찾으면 Cache 객체를 반환 할 수 있습니다. 그렇지 않으면 데코레이터로 사용될 래퍼 함수를 ​​반환 할 수 있습니다.

class _Cache(object): 
    def __init__(self, function, max_hits=10, timeout=5): 
     self.function = function 
     self.max_hits = max_hits 
     self.timeout = timeout 
     self.cache = {} 

    def __call__(self, *args): 
     # Here the code returning the correct thing. 

# wrap _Cache to allow for deferred calling 
def Cache(function=None, max_hits=10, timeout=5): 
    if function: 
     return _Cache(function) 
    else: 
     def wrapper(function): 
      return _Cache(function, max_hits, timeout) 

     return wrapper 

@Cache 
def double(x): 
    return x * 2 

@Cache(max_hits=100, timeout=50) 
def double(x): 
    return x * 2 
+0

솔루션에 대한 감사 인사와 @lunixbochs! 매력처럼 작동합니다. – Dachmt

+3

개발자가 키워드 인수 대신 위치가 '캐시'를 호출하면 (예 :'@Cache (100,50)') 'function'에는 값 100이 지정되고'max_hits'에는 50이 지정됩니다. 함수가 호출 될 때까지 오류가 발생하지 않습니다. 대부분의 사람들은 균일 한 위치 및 키워드 의미론을 기대하기 때문에 이것은 놀라운 행동으로 간주 될 수 있습니다. – unutbu

11
@Cache 
def double(...): 
    ... 

def double(...): 
    ... 
double = Cache(max_hits=100, timeout=50)(double) 

,276 동등

def double(...): 
    ... 
double=Cache(double) 

@Cache(max_hits=100, timeout=50) 
def double(...): 
    ... 

있지만 동등은 Cache(double)과 매우 다른 의미를 가지고 있습니다.

두 케이스를 모두 Cache이 처리하도록하는 것은 현명하지 않습니다.

는 대신 옵션 max_hitstimeout 인수를 할 수있는 장식 공장을 사용하고 장식 리턴 수 :

class Cache(object): 
    def __init__(self, function, max_hits=10, timeout=5): 
     self.function = function 
     self.max_hits = max_hits 
     self.timeout = timeout 
     self.cache = {} 

    def __call__(self, *args): 
     # Here the code returning the correct thing. 

def cache_hits(max_hits=10, timeout=5): 
    def _cache(function): 
     return Cache(function,max_hits,timeout) 
    return _cache 

@cache_hits() 
def double(x): 
    return x * 2 

@cache_hits(max_hits=100, timeout=50) 
def double(x): 
    return x * 2 

PS합니다. Cache 클래스에 __init____call__ 외에 다른 메서드가없는 경우 _cache 함수 내의 모든 코드를 이동하고 Cache을 모두 제거 할 수 있습니다.

+1

현명하지 않거나 ... 개발자가 실수로 cache() 대신 @cache를 사용하면 결과 함수를 호출 할 때 이상한 오류가 발생합니다. 다른 구현은 실제로 캐시와 캐시 모두로 작동합니다. – lunixbochs

+0

감사합니다. @unutbu, 좋은 해결책입니다. – Dachmt

+1

@lunixbochs :'cache_hits' (예'cache')와'cache_hits()'를 혼동하는 개발자는 함수 객체와 함수 호출을 혼동하거나 생성자를 반복자로 착각 할 가능성이 있습니다. 적당히 숙련 된 파이썬 프로그래머조차도 차이점에주의를 기울여야합니다. – unutbu

0

이 질문에서 많은 것을 배웠습니다. 모두 감사합니다. 첫 번째 @Cache에 빈 괄호를 넣는 것이 답이 아닙니까? 그런 다음 function 매개 변수를 __call__으로 옮길 수 있습니다. 외부로, 데코레이터를 사용할 때 괄호를 잊어 버린 경우

def cache(max_hits=10, timeout=5): 
    def caching_decorator(fn): 
     def decorated_fn(*args ,**kwargs): 
      # Here the code returning the correct thing. 
     return decorated_fn 
    return decorator 

는, 불행히도 당신은 여전히 ​​런타임까지 오류가 발생하지 않습니다

class Cache(object): 
    def __init__(self, max_hits=10, timeout=5): 
     self.max_hits = max_hits 
     self.timeout = timeout 
     self.cache = {} 

    def __call__(self, function, *args): 
     # Here the code returning the correct thing. 

@Cache() 
def double(x): 
    return x * 2 

@Cache(max_hits=100, timeout=50) 
def double(x): 
    return x * 2 

나는이 방법이 간단하고 간결 생각하지만 데코레이터 매개 변수는 데코 레이팅하려는 함수에 전달됩니다.그런 다음 런타임에 내부 장식은 불평 : 당신이 당신의 장식의 매개 변수가 결코 될 것없는 알고있는 경우

TypeError: caching_decorator() takes exactly 1 argument (0 given).

그러나 당신이 잡을 수있는 호출 :

def cache(max_hits=10, timeout=5): 
    assert not callable(max_hits), "@cache passed a callable - did you forget to parenthesize?" 
    def caching_decorator(fn): 
     def decorated_fn(*args ,**kwargs): 
      # Here the code returning the correct thing. 
     return decorated_fn 
    return decorator 

당신은 지금 시도하는 경우 :

@cache 
def some_method() 
    pass 

신고시에 AssertionError이 표시됩니다.

전체 접선에, 나는 장식하는 클래스보다는 장식하는 데코레이터를 찾고있는이 포스트를 보았습니다. 누구도 그렇다고해도 this question이 유용합니다.