Dekorator metode kelas dengan argumen sendiri?

154

Bagaimana saya meneruskan bidang kelas ke dekorator pada metode kelas sebagai argumen? Yang ingin saya lakukan adalah sesuatu seperti:

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

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

Mengeluh bahwa diri tidak ada untuk lewat self.urlke dekorator. Apakah ada jalan keluarnya?

Menandai
sumber
Apakah itu dekorator khusus yang Anda kontrol, atau yang tidak dapat Anda ubah?
Joel Cornett
Ini dekorator saya, jadi saya memiliki kendali penuh atas hal itu
Mark
Itu dipanggil sebelum init saya pikir masalahnya ...
Joran Beasley
7
Masalahnya adalah bahwa diri tidak ada pada waktu definisi fungsi. Anda perlu membuatnya menjadi fungsi parsial.
Antimony

Jawaban:

209

Iya. Alih-alih meneruskan atribut instance pada waktu definisi kelas, periksa saat runtime:

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

Dekorator memotong argumen metode; argumen pertama adalah instance, jadi ia membaca atributnya. Anda bisa memasukkan nama atribut sebagai string ke dekorator dan menggunakan getattrjika Anda tidak ingin membuat kode nama atribut:

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
li.davidm
sumber
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

Raphaël
sumber
@raphael Dalam pengaturan ini sepertinya saya tidak dapat mengakses _lambda atau pola. Bagaimana saya bisa memperbaikinya?
Jonathan
1
@ Raphael: Bagaimana saya bisa melakukan hal yang sama untuk metode classmet, karena di sini semua metode adalah metode instance.
Apurva Kunkulol
38

Contoh yang lebih ringkas adalah sebagai berikut:

#/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)

Yang akan dicetak:

kitty!
maxywb
sumber
6

Pilihan lain adalah meninggalkan gula sintaksis dan menghias di __init__kelas.

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()

yang akan dicetak

3
2
1
im doing stuff!
Arwalk
sumber
4

Kamu tidak bisa Tidak ada selfdalam tubuh kelas, karena tidak ada contoh ada. Anda harus meneruskannya, misalnya, yang strberisi nama atribut untuk mencari pada instance, yang kemudian dapat dilakukan fungsi yang dikembalikan, atau menggunakan metode yang berbeda sama sekali.

Julian
sumber