2016-04-18 6 views
1

Ich möchte einen Dekorateur erstellen einen Logger zu jeder eingerichteten Klasse hinzufügen. Es gelang mir, mit:Decorator nimmt eine Klasse und verschiedene Parameter in der Eingabe

def logged(cls_to_decorate): 
    log_name = cls_to_decorate.__module__ 
    logger = logging.getLogger(log_name) 

    setattr(cls_to_decorate, 'logger', logger) 
    return cls_to_decorate 

Seine Nutzung:

@logged 
class TestFoo(TestCase): 
    def test_foo_function(self): 
     self.logger.debug("ciao!!") 

Nun nehmen wir an, ich diesem Dekorateur ein zusätzlicher Parameter übergeben werden soll, so dass es wie folgt verwenden:

@logged(logging_level=logging.DEBUG) 
class TestFoo(TestCase): 
    pass 

I habe versucht, die Syntax der Decorators für Funktionen/Methoden (mit Wrapped-Funktion) zu verwenden, aber natürlich, da wir über eine Klasse als Parameter sprechen, funktioniert es nicht.

Der Dekorateur sollte wie folgt sein:

... 
def logged(cls_to_decorate, logging_level=None): 
    logging.basicConfig(level=logging_level) 

    log_name = cls_to_decorate.__module__ 
    logger = logging.getLogger(log_name) 

    setattr(cls_to_decorate, 'logger', logger) 
    return cls_to_decorate 
... 

HINWEIS: das Objekt eingerichtet werden soll, eine Klasse und keine Funktion.

Lösung python decorators with parameters gelten soll, aber ich versuchte, die folgenden:

def logged(logging_level=None): 
    def class_decorator(cls_to_decorate): 

     # Do something with arguments 
     logging.basicConfig(level=logging_level) 
     log_name = cls_to_decorate.__module__ 
     logger = logging.getLogger(log_name) 

     setattr(cls_to_decorate, 'logger', logger) 
     return cls_to_decorate 
    return class_decorator 

Aber ich habe einen Fehler jedes Mal, wenn ich takling einige Argument eine Klasse wickeln:

E    TypeError: class_decorator() takes exactly 1 argument (2 given) 

Vielen Dank für Ihre Hilfe!

+0

Nein, da die Antwort über einen Funktionsdekorator ist ... –

+1

gilt genau das gleiche Prinzip! –

Antwort

1

the answer here arbeitet genau die gleiche Art und Weise für eine Klasse als eine Funktion, eine verschachtelte Factory-Funktion erstellen:

def decorator(argument): 
    def real_decorator(class): 
     <DECORATOR CODE HERE> 
    return real_decorator 

so würden Sie tun:

def logged(logging_level): 
    def class_decorator(cls_to_decorate): 
     log_name = cls_to_decorate.__module__ 
     logger = logging.getLogger(log_name) 

     setattr(cls_to_decorate, 'logger', logger) 
     return cls_to_decorate 
    return class_decorator 

Beachten Sie, dass dies bedeutet, dass Sie Muss rufen Sie den Dekorateur, um es als solches zu verwenden, nur mit @logged wird denken, dass die Klasse ist das logging_level Argument, das keinen Sinn macht, müssten Sie mindestens tun @logged() für es funktioniert.


Obwohl seit Sie Keywords verwenden sowieso würde Ich mag eine Alternative mit functools.partial bieten, wenn die Anrufinformationen fehlt:

def logger(cls_to_decorate=None, logging_level=logging.DEBUG): 
    if cls_to_decorate is None: 
     return functools.partial(logger, logging_level=logging_level) 

    ... #other code here 

auf diese Weise, wenn Sie logger(existing_cls) tun es funktioniert richtig und wenn Sie tun @logger(KEYWORDS) funktioniert es richtig und nur @logged funktioniert einwandfrei, die einzige Falle ist, wenn Sie versehentlich die Option anstelle der Klasse angeben.

@logger(logging.INFO) #on no we passed a positional argument!! 
class Test():pass 

Traceback (most recent call last): 
    File "/Users/Tadhg/Documents/codes/test.py", line 25, in <module> 
    @logged(logging.INFO) 
    File "/Users/Tadhg/Documents/codes/test.py", line 19, in logged 
    log_name = cls_to_decorate.__module__ 
AttributeError: 'int' object has no attribute '__module__' 

Dies ist nicht der informativsten Fehlermeldung, obwohl es durch Zugabe eines assert inspect.isclass(cls_to_decorate) an einem gewissen Punkt verbessert werden kann.

+0

'E TypeError: class_decorator() nimmt genau 1 Argument (2 gegeben)' wenn ich versuche, die Zielklasse aufzurufen (natürlich mit einem Parameter) –

+0

Wenn Sie 'logged' ändern, müssen Sie ** jetzt ** anrufen Bei Verwendung als Dekorator wäre '@logged()' ausreichend. –

+0

Sie sind richtig. Danke vielmals. –

Verwandte Themen