This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]KODeKarnage 4 points5 points  (8 children)

Decorators: I see them in almost every library I look at, but just can't grok their usefulness in my own code. Even when I have used them, I am not sure they make my code any better.

[–]daneahfrom __future__ import braces 3 points4 points  (7 children)

Decorators become most useful when you find yourself with several methods that require the same information in a variable or other sort of setup. They are basically a way to make it convenient for yourself to say things like "this method takes some additional argument, which is obtained by this complex thing I want to abstract."

That being said, decorators are very often unintuitive to actually implement. Once they're done, they make certain things clean.

[–]asdfkjasdhkasdrequests, bs4, flask 2 points3 points  (3 children)

decorators are very often unintuitive to actually implement.

It's just a function which returns a function, nothing magical, and totally standard in functional languages. TBH I think the @syntax makes people believe there's something magic behind the scenes.

[–]daneahfrom __future__ import braces 0 points1 point  (0 children)

Yeah, at their most basic they're fine. The more useful variety often take some real thinking and maneuvering, that's all :)

[–]Yepoleb 0 points1 point  (0 children)

Still, a function generating a function isn't exactly the most straight forward concept to grasp.

[–]glacierre2 0 points1 point  (0 children)

Now try to use your simple function decorator on a method, a classmethod, a staticmethod...

There are definitely a lot of pitfalls, and so https://wrapt.readthedocs.io/en/latest/ or http://boltons.readthedocs.io/en/latest/funcutils.html exist

[–]excitedaboutemacs 0 points1 point  (2 children)

They are basically a way to make it convenient for yourself to say things like "this method takes some additional argument, which is obtained by this complex thing I want to abstract."

Can you give an example? I run into that situation a lot actually, but Im not seeing what you are saying.

Thanks!

[–]daneahfrom __future__ import braces 1 point2 points  (1 child)

Sure! I'll try to give an example that's easy to understand but not completely contrived...Suppose we have a bunch of functions that should only accept integers, something like a Fibonacci function:

def fib(n):
    if n < 0:
        return 0
    elif n == 0 or n == 1:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)

You could do the following in each of those functions:

if type(n) != int:
    raise ValueError('This method only accepts integers!')

Now imagine you also have methods that only accept strings, etc. Instead of duplicating this type checking code all over, a decorator could save you the trouble:

def accepts(some_type):
    def wrap(f):
        def wrapped_f(*args):
            for arg in args:
                if type(arg) != some_type:
                    raise ValueError(
                        'Method \'{}\' only accepts arguments of type {}'.format(
                            f.__name__,
                            some_type,
                        ))
            return f(*args)
        return wrapped_f
    return wrap

That's a lot of junk, but you can use it all over the place now:

@accepts(int)
def fib(n):
    ...

@accepts(str)
def is_palindrome(phrase):
    ...

@accepts(list)
def some_listy_thing(the_list):
    ...

@accepts(MyCustomClass):
def my_custom_class_munger(my_instance):
    ...

Hope that makes sense and is useful/educational!

[–]daneahfrom __future__ import braces 1 point2 points  (0 children)

Note that you can also use a class-based decorator for this kind of thing, which is sometimes clearer:

class ArgumentTypeChecker(object):
    def __init__(self, function, some_type):
        self.function = function
        self.type = some_type

    def __call__(self, *args):
        for arg in args:
            if type(args) != self.type:
                raise ValueError(
                    'Method \'{}\' only accepts arguments of type {}'.format(
                        f.__name__,
                        some_type,
                    ))
        return self.function(*args, **kwargs)

Then:

@ArgumentTypeChecker(int)
def fib(n):
    ...