Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Python Decorators: How Do You Even

Bill Israel
February 07, 2015

Python Decorators: How Do You Even

All the hip frameworks have them, you see them scattered around your codebase, but what IS a decorator? In this talk, Bill Israel will cover the features of Python that make decorators possible, how they work under the hood, what they're good for, and how you can start writing your own.

Bill Israel

February 07, 2015
Tweet

More Decks by Bill Israel

Other Decks in Programming

Transcript

  1. @decorators
    pytn · february 7, 2015

    View full-size slide

  2. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())



    def create_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  3. @login_required

    def get_user_profile():

    user = users.get(session.get('user_id')

    return jsonify(user=user.to_json())



    @login_required

    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  4. What this talk
    is not

    View full-size slide

  5. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())



    def create_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  6. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())

    View full-size slide

  7. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())

    View full-size slide

  8. Let’s talk
    about functions

    View full-size slide

  9. def example():

    print("I'm a function!")

    View full-size slide

  10. >>> example()
    I'm a function!
    def example():

    print("I'm a function!")

    View full-size slide

  11. First-Class
    Functions

    View full-size slide

  12. Save functions
    into variables

    View full-size slide

  13. def example():

    print("I'm a function!")

    View full-size slide

  14. def example():

    print("I'm a function!")
    >>> f = example
    >>> f()
    I'm a function!

    View full-size slide

  15. Pass functions
    as arguments

    View full-size slide

  16. def example():

    print("I'm a function!")

    View full-size slide

  17. def example():

    print("I'm a function!")
    def first_class(func):

    func()

    View full-size slide

  18. def example():

    print("I'm a function!")
    >>> first_class(example)
    I'm a function!
    def first_class(func):

    func()

    View full-size slide

  19. Return functions
    from functions

    View full-size slide

  20. def example():

    print("I'm a function!")

    View full-size slide

  21. def example():

    print("I'm a function!")
    def first_class2():

    return example

    View full-size slide

  22. def example():

    print("I'm a function!")
    >>> f = first_class2()
    >>> f()
    I'm a function!
    def first_class2():

    return example

    View full-size slide

  23. Nest functions
    inside of
    functions

    View full-size slide

  24. def first_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


    example()

    View full-size slide

  25. def first_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


    example()
    >>> first_class3()
    I'm a function!

    View full-size slide

  26. First-Class
    Functions

    View full-size slide

  27. Save functions
    into variables

    View full-size slide

  28. Return
    functions from
    functions
    Save functions
    into variables

    View full-size slide

  29. Pass functions
    as arguments
    Save functions
    into variables
    Return functions
    from functions

    View full-size slide

  30. Nest functions
    inside of
    functions
    Save functions
    into variables
    Pass functions
    as arguments
    Return functions
    from functions

    View full-size slide

  31. Return functions
    from functions
    Pass functions
    as arguments
    Nest functions
    inside of functions
    Save functions
    into variables

    View full-size slide

  32. Return functions
    from functions
    Pass functions
    as arguments
    Nest functions
    inside of functions
    Save functions
    into variables
    Decorators

    View full-size slide

  33. def example():

    print("I'm a function!")

    View full-size slide

  34. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper
    def example():

    print("I'm a function!")

    View full-size slide

  35. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper
    def example():

    print("I'm a function!")

    View full-size slide

  36. >>> wrapped = decorator(example)
    >>> wrapped()
    I'm a wrapper.
    I'm a function!
    def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper
    def example():

    print("I'm a function!")

    View full-size slide

  37. >>> wrapped = decorator(example)
    >>> wrapped()
    I'm a wrapper.
    I'm a function!

    View full-size slide

  38. >>> wrapped = decorator(example)
    >>> wrapped()
    I'm a wrapper.
    I'm a function!
    >>> wrapped = decorator(example)
    >>> wrapped()
    I'm a wrapper.
    I'm a function!

    View full-size slide

  39. >>> example = decorator(example)
    >>> example()
    I'm a wrapper.
    I'm a function!
    >>> wrapped = decorator(example)
    >>> wrapped()
    I'm a wrapper.
    I'm a function!

    View full-size slide

  40. def decorator(f):

    def wrapper():

    print("I'm a wrapper.")

    f()


    return wrapper


    def example():

    print("I'm a function!")
    example = decorator(example)

    View full-size slide

  41. def decorator(f):

    def wrapper():

    print("I'm a wrapper.")

    f()


    return wrapper


    def example():

    print("I'm a function!")
    example = decorator(example)

    View full-size slide

  42. example = decorator(example)

    View full-size slide

  43. example = decorator(example)
    example = decorator(example)
    @decorator

    def example():

    print("I'm a function!")

    View full-size slide

  44. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())

    View full-size slide

  45. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())

    View full-size slide

  46. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")


    func()


    return wrapper

    View full-size slide

  47. def decorator(func):

    def wrapper():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    func()


    return wrapper

    View full-size slide

  48. def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  49. @login_required

    def get_user_profile():

    user = users.get(session.get('user_id'))

    return jsonify(user=user.to_json())
    def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  50. @login_required

    def get_user_profile():

    user = users.get(session.get('user_id'))

    return jsonify(user=user.to_json())
    def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  51. def create_new_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    newbie = users.create(**data)

    return jsonify(user=newbie.to_json()), 201

    View full-size slide

  52. def create_new_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    newbie = users.create(**data)

    return jsonify(user=newbie.to_json()), 201

    View full-size slide

  53. @login_required
    def create_new_user():
    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    newbie = users.create(**data)

    return jsonify(user=newbie.to_json()), 201

    View full-size slide

  54. @login_required

    def create_new_user():

    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    newbie = users.create(**data)

    return jsonify(user=newbie.to_json()), 201

    View full-size slide

  55. @login_required

    def get_user_profile():

    # ...
    ...is the same as...

    View full-size slide

  56. get_user_profile = login_required(get_user_profile)
    @login_required

    def get_user_profile():

    # ...
    ...is the same as...

    View full-size slide

  57. @role_required('admin')

    def create_user():

    # ...

    View full-size slide

  58. @role_required('admin')

    def create_user():

    # ...
    ...is the same as...

    View full-size slide

  59. create_user = role_required('admin')(create_user)
    @role_required('admin')

    def create_user():

    # ...
    ...is the same as...

    View full-size slide

  60. create_user = role_required('admin')(create_user)
    @role_required('admin')

    def create_user():

    # ...
    OR
    ...is the same as...

    View full-size slide

  61. create_user = role_required('admin')(create_user)
    @role_required('admin')

    def create_user():

    # ...
    OR
    decorator = role_required('admin')

    create_user = decorator(create_user)
    ...is the same as...

    View full-size slide

  62. def admin_required(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)

    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  63. def admin_required():

    def decorator(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)
    return func(*args, **kwargs)

    return wrapper

    return decorator

    View full-size slide

  64. def role_required(role):

    def decorator(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role(role):

    abort(403)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View full-size slide

  65. @login_required
    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201
    def role_required(role):

    def decorator(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role(role):

    abort(403)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View full-size slide

  66. @login_required
    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201
    def role_required(role):

    def decorator(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role(role):

    abort(403)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View full-size slide

  67. @login_required

    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  68. @login_required

    @role_required('admin')

    def create_user():
    # ...
    ...is the same as...

    View full-size slide

  69. @login_required

    @role_required('admin')

    def create_user():
    # ...
    ...is the same as...
    create_user = login_required(
    role_required('admin')(create_user)
    )

    View full-size slide

  70. @login_required

    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  71. @login_required

    def get_user_profile():

    # ...
    def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  72. @login_required

    def get_user_profile():

    # ...
    >>> get_user_profile.__name__
    def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  73. @login_required

    def get_user_profile():

    # ...
    >>> get_user_profile.__name__
    'wrapper'
    def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  74. from functools import wraps

    View full-size slide

  75. def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper

    View full-size slide

  76. from functools import wraps


    def login_required(func):

    @wraps(func)

    def wrapper(*args, **kwargs):

    user_id = session.get(user_id)

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)

    return wrapper

    View full-size slide

  77. from functools import wraps


    def login_required(func):

    @wraps(func)

    def wrapper(*args, **kwargs):

    user_id = session.get(user_id)

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)

    return wrapper
    @login_required

    def get_user_profile():

    # ...

    View full-size slide

  78. from functools import wraps


    def login_required(func):

    @wraps(func)

    def wrapper(*args, **kwargs):

    user_id = session.get(user_id)

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)

    return wrapper
    @login_required

    def get_user_profile():

    # ...
    >>> get_user_profile.__name__

    View full-size slide

  79. from functools import wraps


    def login_required(func):

    @wraps(func)

    def wrapper(*args, **kwargs):

    user_id = session.get(user_id)

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)

    return wrapper
    @login_required

    def get_user_profile():

    # ...
    >>> get_user_profile.__name__
    ‘get_user_profile’

    View full-size slide

  80. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())



    def create_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(session.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  81. def get_user_profile():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(user_id)

    return jsonify(user=user.to_json())



    def create_user():

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    user = users.get(g.get('user_id'))

    if not user.has_role('admin'):

    abort(403)


    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  82. def login_required(func):

    def wrapper(*args, **kwargs):

    user_id = session.get('user_id')

    if user_id is None:

    return redirect('/login', code=302)


    return func(*args, **kwargs)


    return wrapper
    def role_required(role):

    def decorator(func): 

    def wrapper(*args, **kwargs):

    user = users.get(session.get('user_id'))

    if not user.has_role(role):

    abort(403)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View full-size slide

  83. @login_required

    def get_user_profile():

    user = users.get(session.get(‘user_id'))

    return jsonify(user=user.to_json())



    @login_required

    @role_required('admin')

    def create_user():

    data = request.get_json()

    new_user = users.create(**data)

    return jsonify(user=new_user.to_json()), 201

    View full-size slide

  84. Decorators
    @epochblue
    Questions?
    thank you!

    View full-size slide