decorators in python

Download Decorators in Python

If you can't read please download the document

Upload: ben-james

Post on 03-Jun-2015

1.405 views

Category:

Technology


0 download

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 oursketchfunction?

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 spamdecorator modified thesay_somethingfunction, 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 ourspamdecorator 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 definespam :

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 ourcallablewith an instance ofspam 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. Ourspamdecoratorhides 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