Decorators in Python

  • 1,333 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
1,333
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
35
Comments
1
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Decorators in Python
    • First look at a decorator
    • 2. Defining a decorator
    • 3. Callable objects
    • 4. In the wild
    • 5. Possible drawbacks
    • 6. Avoiding the drawbacks
    • 7. Alternatives to decorators
    • 8. Final thoughts
  • 9. First look at a decorator
    • Example of decorator usage:
    • 10. from myapp.decorators import spam
    • 11. @spam
    • 12. def sketch(character, line):
    • 13. print "%s says: %s" % (character, line)
  • 14. First look at a decorator
    • What happens when we call our sketch function?
    • 15. >>> sketch("Wife", "Have you got anything without spam?")
    • 16. Wife says: Have you got anything without spam?
    • 17. Vikings sing: Spam spam spam spam!
  • 18. First look at a decorator
    • What's going on there?
    • 19. Did the decorator add this part?
    • 20. Wife says: Have you got anything without spam?
    • 21. Vikings sing: Spam spam spam spam!
    • 22. The spam decorator modified the say_something function, adding stuff that wasn't in our original definition. Neat!
  • 23. Defining a decorator
    • How does this modification happen?
    • 24. The @ syntax (called "pie" syntax) is syntactic sugar for this:
    • 25. from myapp.decorators import spam
    • 26. def sketch(character, line):
    • 27. print "%s says: %s" % (character, line)
    • 28. sketch = spam(sketch)
  • 29. Defining a decorator
    • The decorator looks like a function there!
    • 30. Guess what? It is. Here's how our spam decorator is defined:
    • 31. # In myapp/decorators.py
    • 32. def spam(callable):
    • 33. def decorated_callable(*args, **kwargs):
    • 34. result = callable(*args, **kwargs)
    • 35. print "Vikings sing: Spam spam spam spam!"
    • 36. return result
    • 37. return decorated_callable
  • 38. Defining a decorator
    • In general, a decorator is a callable object which can be called with another callable object as a parameter, to produce yet another callable object.
    • 39. (Pause to take in that definition)
    • 40. What's a callable object?
  • 41. Callable objects (quick revision)
    • Most obviously, functions and methods are callable. As with everything in Python, they're objects.
    • 42. In Python, classes are also callable; we call them to create instances:
    • 43. spam = Spam()
    • 44. Also, any instance which has a __call__ method is callable
    • 45. We can use pie-decorator syntax above function, method and class definitions
  • 46. Defining a decorator
    • Here's another way we could define spam :
    • 47. class spam(object):
    • 48. def __init__(self, callable):
    • 49. self.c = callable
    • 50. def __call__(self, *args, **kwargs):
    • 51. result = self.c(*args, **kwargs)
    • 52. print "Vikings sing: Spam spam spam spam!"
    • 53. return result
    • 54. Applying the decorator replaces our callable with an instance of spam
  • 55. In the wild
    • Let's look at some useful decorators!
    • 56. We use them to control user access and transaction strategies on Django view functions
    • 57. We use decorators from the Celery library to turn a regular function into an asynchronously executable task
  • 58. In the wild
    • User access control in a Django app:
    • 59. # In views.py
    • 60. from django.contrib.auth.decorators import login_required
    • 61. @login_required
    • 62. def index(request):
    • 63. return render_to_response('index.html', {})
  • 64. In the wild
    • Transaction strategies in a Django app:
    • 65. from django.db import transaction
    • 66. @transaction.commit_on_success
    • 67. def make_breakfast(request):
    • 68. egg = BoiledEgg.objects.create(soft_boiled=True)
    • 69. # Transaction implicitly started
    • 70. if not Toast.objects.count():
    • 71. raise OutOfToastError # Unhandled exception!
    • 72. # Transaction implicitly rolled back
    • 73. return render_to_response('breakfast.html', {'egg': egg})
    • 74. # Transaction implicitly committed
  • 75. In the wild
    • Creating asynchronous tasks with Celery:
    • 76. from celery.decorators import task
    • 77. @task
    • 78. def add(x, y):
    • 79. return x + y
    • 80. >>> result = add. delay (8, 8)
    • 81. >>> result. wait () # Wait for celery daemon to compute result
    • 82. 16
  • 83. Possible drawbacks
    • Decorators are great lightweight way of adding reusable behaviour to your code, but beware!
    • 84. Our spam decorator hides the original function, making it impossible to unit test without the behaviour of the decorator
    • 85. This is bad if your decorator introduces tight coupling to a dependency
    • 86. This could be managed with global behaviour configuration for your decorators. Eww, global state!
  • 87. Avoiding (some of) the drawbacks
    • Add to the callable instead of wrapping it
    • 88. def spam(callable):
    • 89. def sing(*args, **kwargs):
    • 90. result = callable(*args, **kwargs)
    • 91. print "Vikings sing: Spam spam spam spam!"
    • 92. return result
    • 93. callable.sing = sing
    • 94. return callable
    • 95. >>> sketch. sing ("Wife", "Have you got anything that isn't spam?")
    • 96. Wife says: Have you got anything that isn't spam?
    • 97. Vikings sing: Spam spam spam spam!
  • 98. Alternatives to decorators
    • Consider whether inheritance or composition would be a better way to augment your code:
    • 99. class BaseSketch(object):
    • 100. def __init__(self, character, line):
    • 101. print "%s says: %s" % (character, line)
    • 102. class SpamSketch(BaseSketch):
    • 103. def __init__(self, *args):
    • 104. super(SpamSketch, self).__init__(*args)
    • 105. print "Vikings sing: Spam spam spam spam!"
  • 106. Final thoughts
    • Decorators are an awesomely powerful way to configure stuff with useful, defined behaviours
    • 107. But we already have OO constructs to do this... for example MI allows us to use mixin classes
    • 108. Using mixins would mean everything has to be a class... Eek, memories of Java
    • 109. Libraries (e.g. Celery) are tending to provide both class-based and decorator interfaces in their APIs
    • 110. When you have such a choice, you can use decorators to quickly strap the library onto your code
    • 111. Then judge whether you should refactor to use classes