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

Python Decorators: An Intro

Bill Israel
September 25, 2014

Python Decorators: An Intro

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

September 25, 2014
Tweet

More Decks by Bill Israel

Other Decks in Technology

Transcript

  1. @python.decorators
    pynash · september 25, 2014

    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(401)


    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. A Quick Note

    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())

    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. But first...

    View Slide

  11. Let’s talk
    about functions

    View Slide

  12. def example():

    print("I'm a function!")

    View Slide

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

    print("I'm a function!")

    View Slide

  14. First-Class
    Functions

    View Slide

  15. Save functions
    into variables

    View Slide

  16. def example():

    print("I'm a function!")

    View Slide

  17. def example():

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

    View Slide

  18. Pass functions
    as arguments

    View Slide

  19. def example():

    print("I'm a function!")

    View Slide

  20. def example():

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

    func()

    View Slide

  21. def example():

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

    func()

    View Slide

  22. Return functions
    from functions

    View Slide

  23. def example():

    print("I'm a function!")

    View Slide

  24. def example():

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

    return example

    View Slide

  25. def example():

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

    return example

    View Slide

  26. Nest functions
    inside functions

    View Slide

  27. def fist_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


    example()

    View Slide

  28. def fist_class3():

    msg = "I'm a function!"


    def example():

    print(msg)


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

    View Slide

  29. First-Class
    Functions

    View Slide

  30. Save functions
    into variables

    View Slide

  31. Return functions
    from functions
    Save functions
    into variables

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  36. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper


    def example():

    print("I'm a function!")

    View Slide

  37. def decorator(func):

    def wrapper():

    print("I'm a wrapper.")

    func()


    return wrapper


    def example():

    print("I'm a function!")

    View Slide

  38. >>> 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

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

    View Slide

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

  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 Slide

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

  43. PEP 318

    View Slide

  44. example = decorator(example)

    View Slide

  45. example = decorator(example)
    example = decorator(example)
    @decorator

    def example():

    print("I'm a function!")

    View Slide

  46. 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

  47. 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

  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 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 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 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(401)


    data = request.get_json()

    newbie = users.create(**data)

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

    View 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(401)


    data = request.get_json()

    newbie = users.create(**data)

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

    View Slide

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

    if not user.has_role('admin'):

    abort(401)


    data = request.get_json()

    newbie = users.create(**data)

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

    View Slide

  54. @login_required

    def create_new_user():

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

    if not user.has_role('admin'):

    abort(401)


    data = request.get_json()

    newbie = users.create(**data)

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

    View Slide

  55. Remember...

    View Slide

  56. @login_required

    def get_user_profile():

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

    View Slide

  57. get_user_profile = login_required(get_user_profile)
    @login_required

    def get_user_profile():

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

    View Slide

  58. @role_required('admin')

    def create_user():

    # ...

    View Slide

  59. @role_required('admin')

    def create_user():

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

    View Slide

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

    def create_user():

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

    View Slide

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

    def create_user():

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

    View Slide

  62. 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

  63. def admin_required(func): 

    def wrapper(*args, **kwargs):

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

    if not user.has_role('admin'):

    abort(401)

    return func(*args, **kwargs)


    return wrapper

    View Slide

  64. def admin_required():

    def decorator(func): 

    def wrapper(*args, **kwargs):

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

    if not user.has_role('admin'):

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

    return wrapper

    return decorator

    View Slide

  65. 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(401)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View Slide

  66. @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(401)

    return func(*args, **kwargs)

    return wrapper

    return decorator

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

  68. @login_required

    @role_required('admin')

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

    View Slide

  69. @login_required

    @role_required('admin')

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

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

  71. View Slide

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

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

  74. @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

  75. from functools import wraps

    View Slide

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

  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

    View 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():

    # ...

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

    View Slide

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

  81. Recap

    View Slide

  82. 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(401)


    data = request.get_json()

    new_user = users.create(**data)

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

    View Slide

  83. 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(401)


    data = request.get_json()

    new_user = users.create(**data)

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

    View Slide

  84. 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(401)

    return func(*args, **kwargs)

    return wrapper

    return decorator

    View Slide

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

  86. View Slide

  87. Decorators

    View Slide

  88. Questions?
    @epochblue
    Thank you.

    View Slide