1

I am trying to implement a simple logging feature within my app.

class messages(object):

    # Implement decorator here
    def on(self):
        def wrapper():
            # Do something here
        return wrapper

    def off(self):
        def wrapper():
            # Do something here
        return wrapper


class website(object):

    @messages.on #This line can be switched on/off
    def login(self):
        # Do a whole bunch of stuff here
        self.response = "[+] Login Succeeded!"

website = website()
website.login() # prints self.response based on @messages.on/off

But i am not sure what i need to apply in my decorator. I have tried creating instances and adding params but mostly receive TypeError. I am fairly new to decorators. Any help would be appreciated and i'd love to remember this for next time.

Anonymous
  • 15
  • 3
  • You might want to refer to http://stackoverflow.com/questions/1263451/python-decorators-in-classes – Hussain Oct 08 '16 at 10:08
  • @Hussain: that doesn't really apply here; they are not using a function from the same class body here. – Martijn Pieters Oct 08 '16 at 10:10
  • 1
    Your approach doesn't make any sense. For one thing, `self` in the `logger` refers to the `Utilities` instance, which you don't have - did you mean it to be a `@staticmethod`? Why is it in a class at all (this isn't Java)? Secondly, tying it to the `.sound` attribute makes it almost impossible to reuse. Why not have `bark` always return `'woof'`, then make the wrapper print whatever it gets back when it calls the method it wraps? Why have the `print` when you call `Dog.bark`? Finally, don't shadow the class `Dog` with the new instance `Dog`; conventionally, `dog = Dog()`. – jonrsharpe Oct 08 '16 at 10:10
  • @MartijnPieters I meant to show http://stackoverflow.com/a/1263782/1637867 – Hussain Oct 08 '16 at 10:13
  • 2
    @Hussain: yes, and that *doesn't apply here*. – Martijn Pieters Oct 08 '16 at 10:14
  • Thanks for your help @jonrsharpe, i'll take that all on board. – Anonymous Oct 08 '16 at 11:56
  • original code has been updated. – Anonymous Oct 08 '16 at 12:07

2 Answers2

2
  1. If you just want Dog to bark (like in the example), there is no need for enabling a decorator

    class Dog(object):
        def __init__(self):
            self.sound = "Woof!"
    
        def bark(self):
            return self.sound
    
  2. If you want to enable logging for some functions in class, here is a code that does that with explanation in comments

    from functools import wraps
    
    class Utilities(object):
    
        @staticmethod  # no default first argument in logger function
        def logger(f):  # accepts a function
            @wraps(f)  # good practice https://docs.python.org/2/library/functools.html#functools.wraps
            def wrapper(self, *args, **kwargs):  # explicit self, which means this decorator better be used inside classes only
                print("Before:", self.sound)
                result = f(self, *args, **kwargs)
                print("After:", self.sound)
                return result
            return wrapper
    
    
    class Dog(object):
        def __init__(self):
            self.sound = "Woof!"
    
        @Utilities.logger
        def run(self):
            """Perform running movement"""
            print("Running")
    

Example:

>>> dog = Dog()
>>> dog.run()
Before: Woof!
Running
After: Woof!

Though after all there is no need to store decorators functionality in the Utilities class - it is better to have a separate module (file) named utils and put decorator there as a standalone function

jackdaw
  • 564
  • 3
  • 12
  • I'd like to use this code, thank you, except i don't understand how it works so i will do more research on it. Initially, id like to add try except statements after each method in my code but i'd like to use a decorator to almost 'switch' this logging feature on or off. I thought decorators would be a good use case for this. – Anonymous Oct 08 '16 at 11:59
  • i have updated the code. Thanks for your help. Would you recommend the same advice? – Anonymous Oct 08 '16 at 12:08
  • Decorators is a good way to add the same logic for multiple functions, yes. Just insert required code where "before" and "after" are. Glad it works for you, you can mark the answer as accepted if it was helpful :) – jackdaw Oct 08 '16 at 12:13
0

Below is the sample decorator that you may use:

class Utilities:

    @staticmethod
    def add_logger(func):
        def wrapped_func(*args, **kwargs):
            # Sample logic, feel free to update this
            try:
                func_response = func(*args, **kwargs)
            except:
                print 'I am error handled by logger'
                func_response = None
            return func_response
        return wrapped_func

Let's define your class now:

class Dog(object):

    @Utilities.add_logger 
    def bark(self):
        print 'In bark'

    @Utilities.add_logger 
    def my_error_function(self):
        print 'In my_error_function'
        raise Exception  # <--- Raises Exception

Now, let's check how it works:

>>> dog = Dog()
>>> dog.bark()
In bark
>>> dog.my_error_function()
In my_error_function
I am error handled by logger  # Content from logger in case of exception

Note: You shouldn't really be creating a class here to store utility function. Create a separate utility file and write such functions over there.

Without class, your decorator should be like (let's say in utility.py):

def add_logger(func):
    def wrapped_func(*args, **kwargs):
        # Sample logic, feel free to update this
        try:
            func_response = func(*args, **kwargs)
        except:
            print 'I am error handled by logger'
            func_response = None
        return func_response
    return wrapped_func

For using it, simply do:

import utility

class Dog(object):

    @utility.add_logger
    def bark(self):
        print 'In bark'
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126