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 Slide

  2. View Slide

  3. @epochblue

    View Slide

  4. @epochblue

    View 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 Slide

  6. @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 Slide

  7. What this talk
    is not

    View Slide

  8. 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 Slide

  9. 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 Slide

  10. 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 Slide

  11. But
    first...

    View Slide

  12. Let’s talk
    about functions

    View Slide

  13. def example():

    print("I'm a function!")

    View Slide

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

    print("I'm a function!")

    View Slide

  15. First-Class
    Functions

    View Slide

  16. Save functions
    into variables

    View Slide

  17. def example():

    print("I'm a function!")

    View Slide

  18. def example():

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

    View Slide

  19. Pass functions
    as arguments

    View Slide

  20. def example():

    print("I'm a function!")

    View Slide

  21. def example():

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

    func()

    View Slide

  22. def example():

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

    func()

    View Slide

  23. Return functions
    from functions

    View Slide

  24. def example():

    print("I'm a function!")

    View Slide

  25. def example():

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

    return example

    View Slide

  26. def example():

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

    return example

    View Slide

  27. Nest functions
    inside of
    functions

    View Slide

  28. def first_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


    example()

    View Slide

  29. def first_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


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

    View Slide

  30. First-Class
    Functions

    View Slide

  31. Save functions
    into variables

    View Slide

  32. Return
    functions from
    functions
    Save functions
    into variables

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. def example():

    print("I'm a function!")

    View Slide

  38. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper
    def example():

    print("I'm a function!")

    View Slide

  39. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper
    def example():

    print("I'm a function!")

    View Slide

  40. >>> 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 Slide

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

    View Slide

  42. >>> 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 Slide

  43. >>> 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 Slide

  44. def decorator(f):

    def wrapper():

    print("I'm a wrapper.")

    f()


    return wrapper


    def example():

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

    View Slide

  45. def decorator(f):

    def wrapper():

    print("I'm a wrapper.")

    f()


    return wrapper


    def example():

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

    View Slide

  46. PEP 318

    View Slide

  47. example = decorator(example)

    View Slide

  48. example = decorator(example)
    example = decorator(example)
    @decorator

    def example():

    print("I'm a function!")

    View Slide

  49. 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 Slide

  50. 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 Slide

  51. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")


    func()


    return wrapper

    View Slide

  52. 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 Slide

  53. 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 Slide

  54. @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 Slide

  55. @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 Slide

  56. 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 Slide

  57. 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 Slide

  58. @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 Slide

  59. @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 Slide

  60. Remember...

    View Slide

  61. @login_required

    def get_user_profile():

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

    View Slide

  62. get_user_profile = login_required(get_user_profile)
    @login_required

    def get_user_profile():

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

    View Slide

  63. @role_required('admin')

    def create_user():

    # ...

    View Slide

  64. @role_required('admin')

    def create_user():

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

    View Slide

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

    def create_user():

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

    View Slide

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

    def create_user():

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

    View Slide

  67. 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 Slide

  68. 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 Slide

  69. 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 Slide

  70. 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 Slide

  71. @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 Slide

  72. @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 Slide

  73. @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 Slide

  74. @login_required

    @role_required('admin')

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

    View Slide

  75. @login_required

    @role_required('admin')

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

    View Slide

  76. @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 Slide

  77. View Slide

  78. @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 Slide

  79. @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 Slide

  80. @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 Slide

  81. from functools import wraps

    View 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

    View Slide

  83. 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 Slide

  84. 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 Slide

  85. 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 Slide

  86. 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 Slide

  87. Recap

    View Slide

  88. 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 Slide

  89. 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 Slide

  90. 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 Slide

  91. @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 Slide

  92. Decorators

    View Slide

  93. Decorators
    @epochblue
    Questions?
    thank you!

    View Slide