Sınıflarda Python dekoratörleri


140

Kişi şöyle bir şey yazabilir mi:

class Test(object):
    def _decorator(self, foo):
        foo()

    @self._decorator
    def bar(self):
        pass

Bu başarısız: self in @self bilinmiyor

Ayrıca denedim:

@Test._decorator(self)

bu da başarısız olur: Test bilinmiyor

Geçici olarak bazı örnek değişkenleri dekoratör değiştirmek ve sonra geri değiştirmeden önce dekore yöntemi çalıştırmak istiyorum.

Yanıtlar:


268

Böyle bir şey ihtiyacınız olanı yapar mı?

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

test = Test()

test.bar()

Bu, dekoratöre erişmek için kendi kendine çağrıyı önler ve düzenli bir yöntem olarak sınıf ad alanında gizli bırakır.

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>> 

yorumlarda soruyu cevaplamak için düzenlendi:

Gizli dekoratör başka bir sınıfta nasıl kullanılır

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

    _decorator = staticmethod( _decorator )

class TestB( Test ):
    @Test._decorator
    def bar( self ):
        print "override bar in"
        super( TestB, self ).bar()
        print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

Çıktı:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic

7
Dekoratör veya dekore edilmiş fonksiyon? Bir örnekte "bar" çağrıldığında ve "bar" olarak adlandırılan (veya sonra da istesin veya istemeseniz de) istediği örnek değişkenleri için herhangi bir şey yapabildiğinde, çubuğu saran döndürülen "sihirli" işlevine dikkat edin. . Sınıfı bildirirken örnek değişkenler diye bir şey yoktur. Dekoratörün içinden sınıfa bir şey yapmak istedin mi? Bunun deyimsel bir kullanım olduğunu düşünmüyorum.
Michael Speer

1
Teşekkürler Michael, sadece şimdi istediğim şeyin bu olduğunu gördüm.
hcvst

2
Bu çözümü kabul edilen cevaptan çok daha güzel buluyorum, çünkü tanım noktasında @ dekoratör sözdiziminin kullanılmasına izin veriyor. Dekoratör çağrılarını sınıfın sonuna koymak zorunda kalırsam, o zaman fonksiyonların süslendiği açık değildir. Dekoratörün üzerinde @staticmethod kullanamamanız biraz garip, ama en azından işe yarıyor.
16gi12

1
Test miras alınan bir sınıf oluşturursanız işe yaradığını sanmıyorum. Örneğin: class TestB (Test): @_decorator def foobar (self): print "adsf" Bir geçici çözüm var mı?
extraeee

1
@extraeee: yaptığım düzenlemeyi kontrol et. verilen dekoratörü statik yöntem olarak nitelendirmeniz gerekir, ancak yalnızca kullanmayı bitirdikten sonra (veya statik yöntem sürümünü farklı bir isme atadıktan sonra)
Michael Speer

58

Yapmak istediğiniz şey mümkün değil. Örneğin, aşağıdaki kodun geçerli olup olmadığını ele alalım:

class Test(object):

    def _decorator(self, foo):
        foo()

    def bar(self):
        pass
    bar = self._decorator(bar)

Elbette, selfo noktada tanımlanmadığı için geçerli değil. Aynı şey Test, sınıfın kendisi tanımlanıncaya kadar tanımlanmayacağı için de geçerlidir . Bu kod snippet'ini gösteriyorum çünkü dekoratör snippet'inizin dönüşümü budur.

Gördüğünüz gibi, dekoratörler böyle bir dekoratörde örneğe erişmek gerçekten mümkün değildir, çünkü dekoratörler örnekleme sırasında bağlı oldukları işlev / yöntemin tanımı sırasında uygulanır ve örnekleme sırasında değil.

Sınıf düzeyinde erişime ihtiyacınız varsa , şunu deneyin:

class Test(object):

    @classmethod
    def _decorator(cls, foo):
        foo()

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)

5
muhtemelen aşağıdaki daha doğru cevaba başvuracak şekilde güncellenmelidir
Nathan Buesgens

1
Güzel. Nesiriniz imkansız diyor, ancak kodunuz bunu nasıl yapacağınızı gösteriyor.
Çılgın Fizikçi

22
import functools


class Example:

    def wrapper(func):
        @functools.wraps(func)
        def wrap(self, *args, **kwargs):
            print("inside wrap")
            return func(self, *args, **kwargs)
        return wrap

    @wrapper
    def method(self):
        print("METHOD")

    wrapper = staticmethod(wrapper)


e = Example()
e.method()

1
TypeError: '
staticmethod

@wyx dekoratörü arama. Örneğin, olmamalı @foo, değil@foo()
docyoda

İlk argüman wrapperolmamalı selfmı?
CpILL

7

Bu tür dekoratörü bazı hata ayıklama durumlarında kullanıyorum, çağrı işlevini bulmadan dekorasyonla sınıf özelliklerini geçersiz kılmaya izin veriyor.

class myclass(object):
    def __init__(self):
        self.property = "HELLO"

    @adecorator(property="GOODBYE")
    def method(self):
        print self.property

İşte dekoratör kodu

class adecorator (object):
    def __init__ (self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        def newf(*args, **kwargs):

            #the 'self' for a method function is passed as args[0]
            slf = args[0]

            # replace and store the attributes
            saved = {}
            for k,v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf,k)
                    setattr(slf, k, v)

            # call the method
            ret = func(*args, **kwargs)

            #put things back
            for k,v in saved.items():
                setattr(slf, k, v)

            return ret
        newf.__doc__ = func.__doc__
        return newf 

Not: Sınıf dekoratörü kullandığım için, dekoratör sınıfı yapıcısına herhangi bir argüman iletmeseniz bile, işlevleri süslemek için parantezle @adecorator () kullanmanız gerekir .


7

Bu (ve kullanılan) erişmek için bir yol olan selfbir içindeki decoratoraynı sınıf içinde tanımlanmış:

class Thing(object):
    def __init__(self, name):
        self.name = name

    def debug_name(function):
        def debug_wrapper(*args):
            self = args[0]
            print 'self.name = ' + self.name
            print 'running function {}()'.format(function.__name__)
            function(*args)
            print 'self.name = ' + self.name
        return debug_wrapper

    @debug_name
    def set_name(self, new_name):
        self.name = new_name

Çıktı (üzerinde test edilmiştir Python 2.7.10):

>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'

Yukarıdaki örnek aptalca, ama işe yarıyor.


4

Çok benzer bir problemi araştırırken bu soruyu buldum. Benim çözümüm, sorunu iki parçaya bölmektir. İlk olarak, sınıf yöntemleriyle ilişkilendirmek istediğiniz verileri yakalamanız gerekir. Bu durumda, handler_for bir Unix komutunu o komutun çıktısı için handler ile ilişkilendirir.

class OutputAnalysis(object):
    "analyze the output of diagnostic commands"
    def handler_for(name):
        "decorator to associate a function with a command"
        def wrapper(func):
            func.handler_for = name
            return func
        return wrapper
    # associate mount_p with 'mount_-p.txt'
    @handler_for('mount -p')
    def mount_p(self, slurped):
        pass

Bazı verileri her sınıf yöntemiyle ilişkilendirdiğimize göre, bu verileri toplamalı ve bir sınıf özniteliğinde saklamalıyız.

OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
    try:
        OutputAnalysis.cmd_handler[value.handler_for] = value
    except AttributeError:
        pass

4

İşte Michael Speer'in cevabını birkaç adım daha ileri götürecek bir genişleme:

Bağımsız değişkenleri alan ve bağımsız değişkenler ve bir dönüş değeri olan bir işleve etki eden bir örnek yöntemi dekoratörü.

class Test(object):
    "Prints if x == y. Throws an error otherwise."
    def __init__(self, x):
        self.x = x

    def _outer_decorator(y):
        def _decorator(foo):
            def magic(self, *args, **kwargs) :
                print("start magic")
                if self.x == y:
                    return foo(self, *args, **kwargs)
                else:
                    raise ValueError("x ({}) != y ({})".format(self.x, y))
                print("end magic")
            return magic

        return _decorator

    @_outer_decorator(y=3)
    def bar(self, *args, **kwargs) :
        print("normal call")
        print("args: {}".format(args))
        print("kwargs: {}".format(kwargs))

        return 27

Ve sonra

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)

3

Dekoratörler, tüm nesnenin işlevselliğini (işlev nesneleri dahil) ve genel olarak örnek niteliklerine bağlı olacak bir nesne yönteminin işlevselliğini değiştirmek için daha uygun görünmektedir . Örneğin:

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"

Çıktı:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

1

Dekoratörü dekore edebilirsiniz:

import decorator

class Test(object):
    @decorator.decorator
    def _decorator(foo, self):
        foo(self)

    @_decorator
    def bar(self):
        pass

1

Yardımcı Olabilecek Dekoratörler Uygulamam var

    import functools
    import datetime


    class Decorator(object):

        def __init__(self):
            pass


        def execution_time(func):

            @functools.wraps(func)
            def wrap(self, *args, **kwargs):

                """ Wrapper Function """

                start = datetime.datetime.now()
                Tem = func(self, *args, **kwargs)
                end = datetime.datetime.now()
                print("Exection Time:{}".format(end-start))
                return Tem

            return wrap


    class Test(Decorator):

        def __init__(self):
            self._MethodName = Test.funca.__name__

        @Decorator.execution_time
        def funca(self):
            print("Running Function : {}".format(self._MethodName))
            return True


    if __name__ == "__main__":
        obj = Test()
        data = obj.funca()
        print(data)

1

İç sınıfta beyan. Bu çözüm oldukça sağlam ve tavsiye edilir.

class Test(object):
    class Decorators(object):
    @staticmethod
    def decorator(foo):
        def magic(self, *args, **kwargs) :
            print("start magic")
            foo(self, *args, **kwargs)
            print("end magic")
        return magic

    @Decorators.decorator
    def bar( self ) :
        print("normal call")

test = Test()

test.bar()

Sonuç:

>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>> 
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.