Kendi kendine argümanları olan sınıf yöntemi dekoratörü?


165

Bir sınıf alanını bir sınıf yöntemindeki bir dekoratöre bağımsız değişken olarak nasıl iletirim? Yapmak istediğim şey şuna benzer:

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

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

self.urlDekoratöre geçmek için benliğin var olmadığından şikayet eder . Bunun bir yolu var mı?


Bu sizin kontrolünüzde olan özel bir dekoratör mü yoksa değiştiremeyeceğiniz bir dekoratör mü?
Joel Cornett

1
Bu benim dekoratörüm, bu yüzden üzerinde tam kontrole sahibim
Mark

Başlangıçtan önce çağrılır, sanırım sorun bu ...
Joran Beasley

8
Sorun, işlev tanımlama zamanında kendinin var olmamasıdır. Kısmi bir işleve dönüştürmeniz gerekiyor.
Antimony

Yanıtlar:


222

Evet. Örnek özniteliğini sınıf tanımlama zamanında geçirmek yerine çalışma zamanında kontrol edin:

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

Dekoratör, yöntem argümanlarını yakalar; ilk argüman örnektir, bu yüzden ondan özniteliği okur. Öznitelik adını dekoratöre bir dize olarak iletebilir ve öznitelik adını getattrsabit kodlamak istemiyorsanız kullanabilirsiniz:

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

41

Daha kısa bir örnek aşağıdaki gibi olabilir:

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

Hangi yazdıracak:

kitty!

1
IMO, bu stackoverflow.com/a/11731208/257924'ten daha üstündür . İç işlevin _implbunu herhangi bir amaçla selfdeğiştirmek selfiçin nasıl erişebileceğini gösterir . Bir sınıftaki yöntemlerin self.idbir alt kümesinde a'yı artıran basit bir yöntem dekoratörü ve yalnızca "@" dekorasyon sözdiziminin uygulandığı bir sınıftaki yöntemleri geliştirmem gerekiyordu. Sözdizimsel Şeker, o şekeri bırakan ve yöntemin derinliklerine bakmamı gerektiren stackoverflow.com/a/56322968/257924 ile karşılaştırıldığında, bunu Gelecek Benliğime ödüyor __init__.
bgoodr

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

çıktı: my_rule2: tamam


@raphael Bu kurulumda _lambda veya pattern'e erişemiyorum. Bunu nasıl düzeltebilirim?
Jonathan

1
@Raphael: Bir sınıf yöntemi için aynısını nasıl yapabilirim, çünkü buradaki tüm yöntemler örnek yöntemleridir.
Apurva Kunkulol

6

Diğer bir seçenek de sözdizimsel şekeri terk etmek __init__ve sınıfta süslemek olabilir .

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

hangisi basar

3
2
1
im doing stuff!

4

Yapamazsın. Hiçbir Orada selfhiçbir örneği bulunduğundan, sınıf vücutta. Bir, geçmek demek gerekiyordu strdönen fonksiyon sonra yapın veya tamamen farklı bir yöntem kullanabilirsiniz Örneğin, üzerinde arama için öznitelik adını içeren.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.