2011-09-20 36 views
11

Tôi đang cố chuyển đối số tùy chọn cho trình trang trí lớp của mình trong python. Dưới đây là đoạn code tôi hiện có:Đối số trang trí lớp học Python

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 

Các trang trí thứ hai với lý lẽ để ghi đè lên một mặc định (max_hits=10, timeout=5 chức năng __init__ của tôi), không hoạt động và tôi đã nhận ngoại trừ TypeError: __init__() takes at least 2 arguments (3 given). Tôi đã thử nhiều giải pháp và đọc các bài viết về nó, nhưng ở đây tôi vẫn không thể làm cho nó hoạt động được.

Bất kỳ ý tưởng nào để giải quyết vấn đề này? Cảm ơn!

Trả lời

12

@Cache(max_hits=100, timeout=50) gọi __init__(max_hits=100, timeout=50), vì vậy bạn không thỏa mãn đối số function.

Bạn có thể triển khai trang trí của mình thông qua phương thức trình bao bọc đã phát hiện xem có chức năng nào không. Nếu nó tìm thấy một hàm, nó có thể trả về đối tượng Cache. Nếu không, nó có thể trả về một hàm bao bọc sẽ được sử dụng làm trang trí.

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

Xin cảm ơn các bạn và @lunixbochs về giải pháp của bạn! Hoạt động như một nét duyên dáng :) – Dachmt

+3

Nếu nhà phát triển gọi'Cache' với vị trí thay vì đối số từ khóa (ví dụ: '@Cache (100,50)') thì 'hàm' sẽ được gán giá trị 100 và' max_hits' 50. An lỗi sẽ không được nâng lên cho đến khi hàm được gọi. Điều này có thể được coi là hành vi đáng ngạc nhiên vì hầu hết mọi người mong đợi thống nhất vị trí và ngữ nghĩa từ khóa. – unutbu

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

tương đương với

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

Trong khi

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

tương đương với

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

Cache(max_hits=100, timeout=50)(double) có ngữ nghĩa rất khác so với Cache(double).

Đó là không khôn ngoan để cố gắng làm cho Cache xử lý cả hai trường hợp sử dụng.

Bạn có thể sử dụng thay vì một nhà máy trang trí có thể mất tùy chọn max_hitstimeout đối số, và trả về một trang trí:

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. Nếu lớp Cache không có phương pháp nào khác ngoài __init____call__, bạn có thể di chuyển tất cả mã bên trong hàm _cache và loại bỏ hoàn toàn Cache.

+1

không khôn ngoan hay không ... nếu nhà phát triển vô tình sử dụng @cache thay vì cache(), nó sẽ tạo ra một lỗi lạ khi họ cố gắng gọi hàm kết quả. việc thực hiện khác thực sự hoạt động như cả cache và cache() – lunixbochs

+0

Cảm ơn @unutbu, giải pháp tốt quá. – Dachmt

+1

@lunixbochs: Nhà phát triển nhầm lẫn 'cache_hits' (nee' cache') với 'cache_hits()' có khả năng gây nhầm lẫn với bất kỳ đối tượng hàm nào có cuộc gọi hàm hoặc nhầm lẫn trình tạo với trình lặp. Ngay cả các lập trình viên Python có kinh nghiệm vừa phải nên được sử dụng để chú ý đến differenc. – unutbu

0

Tôi đã học được rất nhiều từ câu hỏi này, cảm ơn tất cả. Không phải là câu trả lời chỉ để đặt dấu ngoặc trống trên @Cache đầu tiên? Sau đó, bạn có thể di chuyển thông số function thành __call__.

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 

Mặc dù tôi nghĩ rằng phương pháp này là đơn giản và ngắn gọn hơn:

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 

Nếu bạn quên dấu ngoặc đơn khi sử dụng trang trí, không may bạn vẫn không gặp một lỗi khi chạy, như bên ngoài các tham số trang trí được chuyển qua hàm bạn đang cố gắng trang trí.Sau đó, khi chạy các trang trí nội phàn nàn:

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

Tuy nhiên bạn có thể bắt này, nếu bạn biết các thông số của trang trí của bạn sẽ không bao giờ trở thành một callable:

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 

Nếu bây giờ bạn thử:

@cache 
def some_method() 
    pass 

Bạn nhận được AssertionError khi khai báo.

Trên tổng số ốp, tôi đi qua bài đăng này tìm kiếm trang trí trang trí các lớp học, thay vì các lớp học trang trí. Trong trường hợp bất kỳ ai khác cũng vậy, this question rất hữu ích.

Các vấn đề liên quan