Lớp trang trí phương pháp với tự tranh luận?


154

Làm cách nào để tôi 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ư:

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 bản thân không tồn tại để chuyển self.urlđến trang trí. Có cách nào để giái quyết vấn đề này không?


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

Đó là người trang trí của tôi, vì vậy tôi có toàn quyền kiểm soát nó
Đánh dấu

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

7
Vấn đề là bản thân không tồn tại vào thời điểm định nghĩa hàm. Bạn cần làm cho nó thành một chức năng một phần.
Antimon

Câu trả lời:


208

Đúng. Thay vì chuyển vào thuộc tính cá thể tại thời điểm định nghĩa lớp, hãy kiểm tra nó trong 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à ví dụ, vì vậy nó đọc thuộc tính đó. Bạn có thể chuyển tên thuộc tính dưới dạng chuỗi cho trình trang trí và sử dụng getattrnếu bạn không muốn mã hóa 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

38
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


@raphael Trong thiết lập này dường như tôi 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

1
@Raphael: Làm thế nào tôi có thể làm tương tự cho một classmethod, vì ở đây tất cả các phương thức là các phương thức thể hiện.
Apurva Kunkulol

38

Một ví dụ ngắn gọn hơn có thể như sau:

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

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

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

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

Mà sẽ in:

kitty!

6

Một lựa chọn khác là từ bỏ đường cú pháp và trang trí trong __init__lớp.

def countdown(number):
    def countdown_decorator(func):
        def func_wrapper():
            for index in reversed(range(1, number+1)):
                print("{}".format(index))
            func()
        return func_wrapper
    return countdown_decorator

class MySuperClass():
    def __init__(self, number):
        self.number = number
        self.do_thing = countdown(number)(self.do_thing)

    def do_thing(self):
        print('im doing stuff!')


myclass = MySuperClass(3)

myclass.do_thing()

cái nào sẽ in

3
2
1
im doing stuff!

4

Bạn không thể. Không có selftrong cơ thể lớp, bởi vì không có trường hợp nào tồn tại. Bạn cần phải truyền nó, giả sử, strcó chứa tên thuộc tính để tra cứu trên thể hiện, mà hàm trả về sau đó có thể thực hiện hoặc sử dụng hoàn toàn một phương thức khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.