Python has extremely late type binding. It's compilation pass builds a stream of byte-codes that run on the Python virtual machine, but does nothing at all about type information, tagging variables with specific types, or binding function signatures to names. In fact, variables never have types, only the instances they refer to at run-time.
But at run-time, Python does have an early pass it makes for each module, and this is where the decorators get run, classes are defined, and any other module-level code executed. The decorators modify their functions here, at least for functions defined during this phase. But at any point during the life of the virtual machine, new functions can be created, new modules loaded, and thus decorators applied to new functions.
If I'm a newbie wanting to learn python decorators, I don't want to read several introductory paragraphs about things that aren't what I'm here to learn. Therefore, I would suggest you remove these sections or relate them to after the lesson.
Decorators vs. the Decorator Pattern History of Macros The Goal of Macros
Also, I'd avoid the talk about AOP too. Just get to the how to and why to parts right away.
Thanks for the suggestion. Those parts might be moved to the back of the chapter at some point (I usually have to ruminate on these suggestions, so it probably won't appear right away).
However, this will not be an intro to the language like Thinking in C++ and Thinking in Java -- there are plenty of good intro books on Python. This book will focus on more intermediate and advanced level stuff. Although there is a "quick python for programmers" intro at the beginning we will assume the reader will be able to learn more of the basics elsewhere.
I'm curious -- why do you find using a class to define a decorator easier to understand than the more traditional nested function pattern? Is it because you're not comfortable with lexically scoped nested functions (or because you worry that your audience isn't)?
Indeed, you can use Python decorators to implement the Decorator pattern, but that's an extremely limited use of it.
It sounds like you are artificially assigning a narrow set of use cases to the Decorator pattern. The most important implementation aspect of this pattern, in my humble opinion, is obedience to the Open-Closed Principle.
@Decorators allow you to inject or modify code in functions or classes. Sounds a bit like Aspect-Oriented Programming (AOP) in Java, doesn't it? Except that it's both much simpler and (as a result) much more powerful.
Are you referring to Java annotations, or to a specific implementation of AOP for Java, such as AspectJ?
Aspects allow you to preserve the Open-Closed Principle by reconsidering what is considered a component.
> I'm curious -- why do you find using a class to define a > decorator easier to understand than the more traditional > nested function pattern? Is it because you're not > comfortable with lexically scoped nested functions (or > because you worry that your audience isn't)?
The multiple levels of function definitions were always a stumbling block for me when trying to understand decorators. The two-step process of the class was when I got the "aha" about it. Once you get it, then it was much easier for me to understand the nested functions.
> Well, for a somewhat advanced book, this worry seems silly?
The "somewhat" modifier is very important here. In an intro book you are forced to describe everything in lock step, never mentioning anything before it has been thoroughly introduced. That difficult constraint is removed.
In addition, we can talk about anything in this book, whereas in an intro book you are limited.
That said, people will still be coming to a topic without knowing about it and it will need to be introduced, as much as possible, as if they have never seen it before.
Since this article is intended for "Python 3 Patterns & Idioms", wouldn't it be a good idea to write the code using Python 3 syntax? What shouts Python 2 to me is your use of "print" as a statement; in Python 3 it's "print(x)" (a function) instead of "print x". There may be other cases as well, but I'm not familiar enough with Python 3 yet :-)
My suggestion: write and test your code with Python 3.
> Since this article is intended for "Python 3 Patterns & > Idioms", wouldn't it be a good idea to write the code > using Python 3 syntax? What shouts Python 2 to me is your > use of "print" as a statement; in Python 3 it's "print(x)" > (a function) instead of "print x". There may be other > cases as well, but I'm not familiar enough with Python 3 > yet :-) > > My suggestion: write and test your code with Python 3.
It's evolutionary. Much of the current version of the book is still just roughly cut from a Java version, and I'm still absorbing Python 3 stuff. I expect a lot of changes once I put up the initial draft of the book, when people can start pulling it down using Bazaar and changing it. Right now I'm just trying to get the basic structure in place so most of those questions are answered.
Also, I had hoped for a final version of Python 3 to be out by now but there appears to be some delays. In addition, it looks like more features made it back into Python 2.6 than were originally expected, so a lot of tests can be done with 2.6.
Finally, I think that the majority of people are still using 2.x so the weblog postings here will be more useful to them.
This tactic of using decorators DOES require you to edit code directly. In some cases, this is perfectly fine, and would allow you to nicely define AOP behaviors. But at other times, you want to apply the advice from another place, without editing the code. Spring Python (http://springpython.webfactional.com) has a module that supports this. I have also found decorators very useful, and have built an IoC container using decorators, as well as the ability to mark-up transactions with an @transactional decorator.
I needed to add an LRU cache to a class method, so I started learning about decorators and found these posts useful. But I think I've hit a limitation of using a class and __call__.
When the class is used to wrap a class method, I can't see how to get access to the instance variable, "self": def __call__(self, *args): hashable = [] key = self._hash(args) try: value = self.cache[key] print "Used cache" self.hist.remove(key) except KeyError: print "Baking fresh" value = self.f(*args) self.cache[key] = value if len(hist) == self.n: lru = self.hist.pop(-1) self.cache.pop(lru) self.hist.insert(0, key) return value [code]
The problematic part is where the arguments aren't memoized and I need to "bake fresh" --- I can't pass self back to the method.
I'd prefer to use a class than a wrapped function, because it seems like a more natural way to preserve the cache and history states. Is there anything I can do?