Python Decorators: How Do You Even

8145132ebae0c1f62cdd7b6126d71768?s=47 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.

8145132ebae0c1f62cdd7b6126d71768?s=128

Bill Israel

February 07, 2015
Tweet

Transcript

  1. @decorators pytn · february 7, 2015

  2. None
  3. @epochblue

  4. @epochblue

  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
  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
  7. What this talk is not

  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
  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())
  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())
  11. But first...

  12. Let’s talk about functions

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

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

  15. First-Class Functions

  16. Save functions into variables

  17. def example():
 print("I'm a function!")

  18. def example():
 print("I'm a function!") >>> f = example >>>

    f() I'm a function!
  19. Pass functions as arguments

  20. def example():
 print("I'm a function!")

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

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

    def first_class(func):
 func()
  23. Return functions from functions

  24. def example():
 print("I'm a function!")

  25. def example():
 print("I'm a function!") def first_class2():
 return example

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

    f() I'm a function! def first_class2():
 return example
  27. Nest functions inside of functions

  28. def first_class3():
 msg = "I'm a function!"
 
 def example():


    print(msg)
 
 example()
  29. def first_class3():
 msg = "I'm a function!"
 
 def example():


    print(msg)
 
 example() >>> first_class3() I'm a function!
  30. First-Class Functions

  31. Save functions into variables

  32. Return functions from functions Save functions into variables

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

    from functions
  34. Nest functions inside of functions Save functions into variables Pass

    functions as arguments Return functions from functions
  35. Return functions from functions Pass functions as arguments Nest functions

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

    inside of functions Save functions into variables Decorators
  37. def example():
 print("I'm a function!")

  38. def decorator(func):
 def wrapper():
 print("I'm a wrapper.")
 func()
 
 return

    wrapper def example():
 print("I'm a function!")
  39. def decorator(func):
 def wrapper():
 print("I'm a wrapper.")
 func()
 
 return

    wrapper def example():
 print("I'm a function!")
  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!")
  41. >>> wrapped = decorator(example) >>> wrapped() I'm a wrapper. I'm

    a function!
  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!
  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!
  44. def decorator(f):
 def wrapper():
 print("I'm a wrapper.")
 f()
 
 return

    wrapper
 
 def example():
 print("I'm a function!") example = decorator(example)
  45. def decorator(f):
 def wrapper():
 print("I'm a wrapper.")
 f()
 
 return

    wrapper
 
 def example():
 print("I'm a function!") example = decorator(example)
  46. PEP 318

  47. example = decorator(example)

  48. example = decorator(example) example = decorator(example) @decorator
 def example():
 print("I'm

    a function!")
  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())
  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())
  51. def decorator(func):
 def wrapper():
 print("I'm a wrapper.")
 
 func()
 


    return wrapper
  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
  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
  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
  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
  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
  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
  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
  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
  60. Remember...

  61. @login_required
 def get_user_profile():
 # ... ...is the same as...

  62. get_user_profile = login_required(get_user_profile) @login_required
 def get_user_profile():
 # ... ...is the

    same as...
  63. @role_required('admin')
 def create_user():
 # ...

  64. @role_required('admin')
 def create_user():
 # ... ...is the same as...

  65. create_user = role_required('admin')(create_user) @role_required('admin')
 def create_user():
 # ... ...is the

    same as...
  66. create_user = role_required('admin')(create_user) @role_required('admin')
 def create_user():
 # ... OR ...is

    the same as...
  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...
  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
  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
  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
  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
  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
  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
  74. @login_required
 @role_required('admin')
 def create_user(): # ... ...is the same as...

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

    create_user = login_required( role_required('admin')(create_user) )
  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
  77. None
  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
  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
  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
  81. from functools import wraps

  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
  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
  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():
 # ...
  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__
  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’
  87. Recap

  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
  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
  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
  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
  92. Decorators

  93. Decorators @epochblue Questions? thank you!