2017-12-29 50 views
0

Ich möchte einen praktischen Dekorator haben, der überprüft, ob der Wert des an die Methode übergebenen Attributs nicht None ist. Ich möchte es dann als Allzweckdekorator in Klassenmethoden verwenden. Dazu schrieb ich:Wrapping eines Konstruktors in Decorator in Python

def check_empty(name): 
    def wrap_outer(function): 
    def wrapped(*args, **kwargs): 
     if kwargs.get(name, None) is None: 
     raise Exception('{item} cannot be empty'.format(item=name)) 
     return function(args, kwargs) 
    return wrapped 
    return wrap_outer 

Und die Klasse wie folgt definiert:

class Player(object): 
    @check_empty('name') 
    def __init__(self, name): 
    self.name = name 

def __str__(self): 
    return self.name 

Allerdings bedeutet dies nicht funktionieren. Die Vorlage für den Konstruktor und den Dekorator stimmen nicht überein. Wie können wir einen Funktionsdecorator für einen Konstruktor erstellen? Oder ist der klassenbasierte Dekorateur eine bessere Option?


Dank @ ajax1234, habe ich das Problem herausgefunden. Es war in der Art, wie ich meine args und kwargs übergeben wurde. Es hätte sein müssen:

def check_empty(attr): 
    def wrap_outer(function): 
    def wrapped(*args, **kwargs): 
     if (len(args) > 1 and args[1] is None) or (len(args)==1 and len(kwargs)==0): 
     raise Exception('{item} cannot be empty'.format(item=attr)) 
     elif attr in kwargs and kwargs.get(attr, None) is None: 
     raise Exception('{item} cannot be empty'.format(item=attr)) 
    return function(*args, **kwargs) 
    return wrapped 
return wrap_outer 

Diese kümmert sich auf den Parameter zu überprüfen, ob es als Schlüsselwort Argument oder nur ein Positions Argument übergeben wird.

HINWEIS: Es hat eine Einschränkung, dass Positionslogik wird nicht gut skalieren, während die allgemeine Natur beibehalten. (Wir bräuchten in das Positions Argumente der Position suchen inspect.getargspec und anschließend von dort aufzubauen.)

Antwort

0

du versuchen:

def check_empty(attr): 
    def check_method(function): 
    def wrapped(cls, **kwargs): 
     if kwargs.get(attr) is None: 
     raise Exception('{item} cannot be empty'.format(item=attr)) 
     return function(cls, **kwargs) 
    return wrapped 
    return check_method 

class Player(object): 
    @check_empty('name') 
    def __init__(self, **kwargs): 
    self.__dict__ = kwargs 
    def __str__(self): 
    return self.name 

p = Player(name=None) 

Ausgang:

Exception: Name cannot be empty 

jedoch jedem andere Parameter als None funktionieren:

p = Player(name = 'Foo') 
print(p.name) 

Ausgang:

'Foo' 
+0

Hallo, was ich versuche, ist ein allgemeiner Dekorateur, der einen beliebigen Variablennamen überprüfen kann. Zum Beispiel, wenn ich mit 'name' auch eine' age' Variable habe, würde ich gerne den gleichen Dekorateur zweimal benutzen. – Anshul

+0

@Anshul bitte meine letzten Bearbeitung. – Ajax1234

+0

Danke! Dies funktioniert für das gegebene Szenario.Auch wenn ich mich fragte, warum deine 'cls' funktionierten und' argc', habe ich auch meinen Fehler herausgefunden. Ich habe 'args' und 'kwargs' in 'return function (args, kwargs)' übergeben, wenn ich '* args' und' ** kwargs' als 'return function (* args, ** kwargs)' – Anshul

1

Der einzige Weg, ich sehe Sie in der Lage sein zu tun, was Sie wollen, während einschließlich Positions- und Keyword-Argumente, ist die Zuordnung von Positionen zu Namen bieten, etwa so:

class NoneException(Exception): 
    pass 


def check_none(**check): 
    def wrapper(fn): 
     def wrapped(*args, **kwargs): 
      for key, pos in check.items(): 
       try: 
        if kwargs[key] == None: 
         raise NoneException(f'{key} cannot be empty') 
       except KeyError: 
        pass 

       try: 
        if args[pos] == None: 
         raise NoneException(f'{key} cannot be empty') 
       except IndexError: 
        pass 

      return fn(*args, **kwargs) 

     return wrapped 

    return wrapper 


class A: 
    @check_none(name=1) 
    def __init__(self, name): 
     self.name = name 

    @check_none(phrase=1) 
    def test(self, phrase): 
     print(f'My name is {self.name}. {phrase}!') 


a = A('A') 

a.test('Hello world!') 

try: 
    a.test(None) 
except NoneException: 
    print('successfully failed') 
else: 
    print('something went wrong') 

Diese sollte die Fälle abdecken, in denen Sie ein Argument positionell angeben und explizit benannt, jedoch beachten Sie, dass es eine spröde Lösung ist angesichts der Tatsache, dass die Zuordnung von Positionsargument Indizes beibehalten werden müssen.

Wenn es eine Möglichkeit gäbe, das Mapping self=0, name=1, et cetera von Pythons Interna zu erraten, würde ich vorschlagen, das stattdessen zu verwenden.


Alternativ kann eine einfachere Lösung ist ein Funktionsaufruf an der Spitze jeder Funktion, die Sie überprüfen wollen würden.

class NoneException(Exception): 
    pass 

def check_none(*args, **kwargs): 
    for key, value in (*enumerate(args), *kwargs.items()): 
     if value == None: 
      raise NoneException(f'key {key} == None') 

class A: 
    def __init__(self, name): 
     check_none(name) 

     self.name = name 
+0

übergeben hätte Sie haben Recht bezüglich der Positionssprödigkeit des Dekorationsschemas. Dafür kann gesorgt werden. Ich habe diesen Teil später gemacht und festgestellt, dass es bei der Verkettung von Dekorateuren immer noch sehr schlecht wäre. Also, ja, eine Utility-Funktion wäre viel besser bei fast den gleichen Schreibkosten. :-) – Anshul