2012-07-30 24 views
66

Làm cách nào để chuyển một trường lớp cho một trình trang trí trên một phương thức lớp làm đối số? Những gì tôi muốn làm là một cái gì đó như:Trình trang trí phương thức lớp Python với các đối số tự?

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization("some_attr", self.url) 
    def get(self): 
     do_work() 

Nó phàn nàn rằng tự không tồn tại khi tự chuyển giao cho trang trí. Có cách nào để giái quyết vấn đề này không?

+0

Đó có phải là trang trí tùy chỉnh mà bạn có quyền kiểm soát hoặc trang trí bạn không thể thay đổi không? –

+0

Đó là trang trí của tôi, vì vậy tôi có toàn quyền kiểm soát nó – Mark

+0

Nó được gọi trước khi init tôi nghĩ là vấn đề ... –

Trả lời

98

Có. Thay vì truyền trong thuộc tính instance tại thời gian định nghĩa lớp, hãy kiểm tra nó tại thời gian chạy:

def check_authorization(f): 
    def wrapper(*args): 
     print args[0].url 
     return f(*args) 
    return wrapper 

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization 
    def get(self): 
     print 'get' 

>>> Client('http://www.google.com').get() 
http://www.google.com 
get 

Trình trang trí chặn các đối số phương thức; đối số đầu tiên là cá thể, vì vậy nó đọc thuộc tính của nó. Bạn có thể vượt qua trong tên thuộc tính như một chuỗi các trang trí và sử dụng getattr nếu bạn không muốn hardcode tên thuộc tính:

def check_authorization(attribute): 
    def _check_authorization(f): 
     def wrapper(self, *args): 
      print getattr(self, attribute) 
      return f(self, *args) 
     return wrapper 
    return _check_authorization 
2

Bạn không thể. Không có self trong nội dung lớp học, vì không có trường hợp nào tồn tại. Bạn sẽ cần phải chuyển nó, giả sử, một str có chứa tên thuộc tính để tra cứu trên cá thể, mà hàm trả về có thể làm hoặc sử dụng một phương thức hoàn toàn khác.

15
from re import search 
from functools import wraps 

def is_match(_lambda, pattern): 
    def wrapper(f): 
     @wraps(f) 
     def wrapped(self, *f_args, **f_kwargs): 
      if callable(_lambda) and search(pattern, (_lambda(self) or '')): 
       f(self, *f_args, **f_kwargs) 
     return wrapped 
    return wrapper 

class MyTest(object): 

    def __init__(self): 
     self.name = 'foo' 
     self.surname = 'bar' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'foo') 
    def my_rule(self): 
     print 'my_rule : ok' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'bar') 
    def my_rule2(self): 
     print 'my_rule2 : ok' 



test = MyTest() 
test.my_rule() 
test.my_rule2() 

ouput: my_rule2: ok

+1

Cảm ơn bạn rất nhiều! Đây sẽ là câu trả lời được chấp nhận. Tôi đã rách tóc ra cố gắng tìm ra những người trang trí cho các phương pháp lớp học. – benshepherd

+0

@raphael Trong thiết lập này tôi dường như không thể truy cập _lambda hoặc mẫu. Làm thế nào tôi có thể khắc phục điều đó. – Jonathan

13

Một hơn Ví dụ súc tích có thể là như sau:

#/usr/bin/env python3 
from functools import wraps 

def wrapper(method): 
    @wraps(method) 
    def _impl(self, *method_args, **method_kwargs): 
     return method(self, *method_args, **method_kwargs) + "!" 
    return _impl 

class Foo: 
    @wrapper 
    def bar(self, word): 
     return word 

f = Foo() 
result = f.bar("kitty") 
print(result) 

nào sẽ in:

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