If you can't read please download the document
Upload
ben-james
View
1.405
Download
0
Embed Size (px)
Citation preview
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
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
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
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
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
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
39. (Pause to take in that definition) 40. What's a callable object? 41. Callable objects (quick revision)
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
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
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
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
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
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
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
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
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
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