Ved å bruke den samme arkitekt (med argumenter) med funksjoner og fremgangsmåter

stemmer
14

Jeg har prøvd å lage en dekoratør som kan brukes med både funksjoner og metoder i python. Dette på sin egen er ikke så vanskelig, men når du oppretter en dekoratør som tar argumenter, det synes å være.

class methods(object):
    def __init__(self, *_methods):
        self.methods = _methods

    def __call__(self, func): 
        def inner(request, *args, **kwargs):
            print request
            return func(request, *args, **kwargs)
        return inner

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        new_func = self.func.__get__(obj, type)
        return self.__class__(new_func)

Den ovennevnte kode brytes funksjonen / metoden på riktig måte, men i tilfelle av en metode, den requester argumentet forekomsten den opererer på, ikke er den første ikke-selv argument.

Er det en måte å fortelle om dekoratør blir brukt til en funksjon i stedet for en metode, og håndtere tilsvarende?

Publisert på 17/08/2009 klokken 15:08
kilden bruker
På andre språk...                            


5 svar

stemmer
4

Dekoratør alltid brukes på en funksjon objekt - har dekoratør printtypen av sin argumentasjon, og du vil være i stand til å bekrefte at; og det bør generelt returnere en funksjon objekt, også (som allerede er en dekoratør med riktig __get__-) selv om det finnes unntak fra sistnevnte.

Dvs. i koden:

class X(object):

  @deco
  def f(self): pass

deco(f)kalles i klassen kroppen, og mens du er der fortsatt, fer en funksjon, ikke en forekomst av en metode type. (Metoden er produsert og returneres i f's __get__når senere fåpnes som en attributt for Xeller en forekomst derav).

Kanskje du kan bedre forklare en leken bruk du ønsker for din dekoratør, slik at vi kan være til mer hjelp ...?

Edit : Dette gjelder for dekoratører med argumenter, også, det vil si

class X(object):

  @deco(23)
  def f(self): pass

så er det deco(23)(f)som kalles i klassen kroppen, fer fortsatt en funksjon objekt når sendes som argument til hva callable deco(23)avkastning, og at Callable bør fortsatt returnere en funksjon objekt (vanligvis - med unntak ;-).

Svarte 17/08/2009 kl. 15:16
kilden bruker

stemmer
4

Siden du allerede definerer en __get__å bruke dekoratør på Bound Method, kan du passerer et flagg fortelle det hvis det blir brukt på en metode eller funksjon.

class methods(object):
    def __init__(self, *_methods, called_on_method=False):
        self.methods = _methods
        self.called_on_method

    def __call__(self, func):
        if self.called_on_method:
            def inner(self, request, *args, **kwargs):
                print request
                return func(request, *args, **kwargs)
        else:
            def inner(request, *args, **kwargs):
                print request
                return func(request, *args, **kwargs)
        return inner

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        new_func = self.func.__get__(obj, type)
        return self.__class__(new_func, called_on_method=True)
Svarte 17/08/2009 kl. 15:24
kilden bruker

stemmer
1

En delvis (spesifikk) oppløsning jeg har kommet opp med er avhengig av unntakshåndtering. Jeg forsøker å skape en dekoratør å bare tillate visse HttpRequest metoder, men at det skal fungere med begge funksjonene som er utsikt, og metoder som er visninger.

Så vil denne klassen gjøre hva jeg vil:

class methods(object):
    def __init__(self, *_methods):
        self.methods = _methods

    def __call__(self, func): 
        @wraps(func)
        def inner(*args, **kwargs):
            try:
                if args[0].method in self.methods:
                    return func(*args, **kwargs)
            except AttributeError:
                if args[1].method in self.methods:
                    return func(*args, **kwargs)
            return HttpResponseMethodNotAllowed(self.methods)
        return inner

Her er de to brukstilfeller: dekorere en funksjon:

@methods("GET")
def view_func(request, *args, **kwargs):
    pass

og dekorere metoder i en klasse:

class ViewContainer(object):
    # ...

    @methods("GET", "PUT")
    def object(self, request, pk, *args, **kwargs):
        # stuff that needs a reference to self...
        pass

Finnes det en bedre løsning enn å bruke avvikshåndtering?

Svarte 17/08/2009 kl. 15:28
kilden bruker

stemmer
14

Å utvide på __get__tilnærming. Dette kan generaliseres til en dekoratør dekoratør.

class _MethodDecoratorAdaptor(object):
    def __init__(self, decorator, func):
        self.decorator = decorator
        self.func = func
    def __call__(self, *args, **kwargs):
        return self.decorator(self.func)(*args, **kwargs)
    def __get__(self, instance, owner):
        return self.decorator(self.func.__get__(instance, owner))

def auto_adapt_to_methods(decorator):
    """Allows you to use the same decorator on methods and functions,
    hiding the self argument from the decorator."""
    def adapt(func):
        return _MethodDecoratorAdaptor(decorator, func)
    return adapt

På denne måten kan du bare gjøre dekoratør automatisk tilpasse seg de forholdene det brukes i.

def allowed(*allowed_methods):
    @auto_adapt_to_methods
    def wrapper(func):
        def wrapped(request):
            if request not in allowed_methods:
                raise ValueError("Invalid method %s" % request)
            return func(request)
        return wrapped
    return wrapper

Legg merke til at wrapper funksjonen kalles på alle funksjonskall, så ikke gjør noe dyrt der.

Bruk av dekoratør:

class Foo(object):
    @allowed('GET', 'POST')
    def do(self, request):
        print "Request %s on %s" % (request, self)

@allowed('GET')
def do(request):
    print "Plain request %s" % request

Foo().do('GET')  # Works
Foo().do('POST') # Raises
Svarte 17/08/2009 kl. 16:15
kilden bruker

stemmer
0

Her er en generell måten jeg fant å oppdage om en dekorert Callable er en funksjon eller metode:

import functools

class decorator(object):

  def __init__(self, func):
    self._func = func
    self._obj = None
    self._wrapped = None

  def __call__(self, *args, **kwargs):
    if not self._wrapped:
      if self._obj:
        self._wrapped = self._wrap_method(self._func)
        self._wrapped = functools.partial(self._wrapped, self._obj)
      else:
        self._wrapped = self._wrap_function(self._func)
    return self._wrapped(*args, **kwargs)

  def __get__(self, obj, type=None):
    self._obj = obj
    return self

  def _wrap_method(self, method):
    @functools.wraps(method)
    def inner(self, *args, **kwargs):
      print('Method called on {}:'.format(type(self).__name__))
      return method(self, *args, **kwargs)
    return inner

  def _wrap_function(self, function):
    @functools.wraps(function)
    def inner(*args, **kwargs):
      print('Function called:')
      return function(*args, **kwargs)
    return inner

Eksempler på bruk:

class Foo(object):
  @decorator
  def foo(self, foo, bar):
    print(foo, bar)

@decorator
def foo(foo, bar):
  print(foo, bar)

foo(12, bar=42)      # Function called: 12 42
foo(12, 42)          # Function called: 12 42
obj = Foo()
obj.foo(12, bar=42)  # Method called on Foo: 12 42
obj.foo(12, 42)      # Method called on Foo: 12 42
Svarte 13/01/2018 kl. 13:54
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more