Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #42036 > unrolled thread
| Started by | "Joseph L. Casale" <jcasale@activenetwerx.com> |
|---|---|
| First post | 2013-03-27 19:49 +0000 |
| Last post | 2013-03-31 00:06 +0000 |
| Articles | 5 — 3 participants |
Back to article view | Back to comp.lang.python
Decorator help "Joseph L. Casale" <jcasale@activenetwerx.com> - 2013-03-27 19:49 +0000
Re: Decorator help Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-03-27 23:29 +0000
Re: Decorator help Jason Swails <jason.swails@gmail.com> - 2013-03-27 22:38 -0400
Re: Decorator help Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-03-28 04:58 +0000
RE: Decorator help "Joseph L. Casale" <jcasale@activenetwerx.com> - 2013-03-31 00:06 +0000
| From | "Joseph L. Casale" <jcasale@activenetwerx.com> |
|---|---|
| Date | 2013-03-27 19:49 +0000 |
| Subject | Decorator help |
| Message-ID | <mailman.3824.1364413867.2939.python-list@python.org> |
I have a class which sets up some class vars, then several methods that are passed in data and do work referencing the class vars. I want to decorate these methods, the decorator needs access to the class vars, so I thought about making the decorator its own class and allowing it to accept args. I was hoping to do all the work on in_data from within the decorator, which requires access to several MyClass vars. Not clear on the syntax/usage with this approach here, any guidance would be greatly appreciated! Class MyDecorator(object): def __init__(self, arg1, arg2): self.arg1 = arg1 self.arg2 = arg2 ... Class MyClass(object): def __init__(self): self.var_a = .... .... @MyDecorator(...) def meth_one(self, in_data): ... Thanks! jlc
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-03-27 23:29 +0000 |
| Message-ID | <515380f4$0$29998$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #42036 |
On Wed, 27 Mar 2013 19:49:54 +0000, Joseph L. Casale wrote:
> I have a class which sets up some class vars, then several methods that
> are passed in data and do work referencing the class vars.
When you say "class vars", do you mean variables which hold classes? Like
"string vars" are variables which hold strings, and "int vars" are
variables that hold ints? Classes (also known as types) are first-class
objects in Python (no pun intended), unlike Java, and so you can store
them in lists, returns them from functions, and bind them in variables.
for aclass in list_of_classes:
do_something_with(aclass)
Consequently, when people talk about "class variables", it is ambiguous,
which is why we prefer to use "class attribute" to refer to attributes on
the class. For example:
class Parrot(object):
breed = "Norwegian Blue" # this is a class attribute
def __init__(self, name="Polly"):
self.name = name # this is an instance attribute
def speak(self):
s = "%s the %s says, 'Hello sailor!'"
print(s % (self.name, self.breed))
Class attributes are attached to the class object itself, and are shared
between all instances. Instance attributes are attached to the instance,
where they over-ride any class attribute of the same name, and are not
shared.
In the following I am going to assume that you actually are talking about
attributes, rather than an actual variable holding a class.
> I want to decorate these methods, the decorator needs access to the
> class vars, so I thought about making the decorator its own class and
> allowing it to accept args.
The one doesn't follow from the other. Writing decorators as classes is
fairly unusual. Normally, they will be regular functions. If your
decorator needs to store so much state that it needs to be a class,
you're probably trying to do too much from a single decorator.
There's more that you need to describe, such as what it is that the
decorator actually does, and whether it does it once, when the decorator
is called, or repeatedly, when the decorated method is called.
The second case is the easiest. Suppose you have a class like this, with
many methods which have code in common. Here's a toy example:
def MyClass(object):
x = "class attribute"
def __init__(self, y):
self.y = y
def spam(self):
do_stuff(self.x)
do_stuff(self.y)
print("spam spam spam spam")
def ham(self):
do_stuff(self.x)
do_stuff(self.y)
return "ham, a growing boy's best friend"
def eggs(self, food="bacon"):
do_stuff(self.x)
do_stuff(self.y)
return "green eggs and %s" % food
A simple decorator function can simplify the common code:
import functools
def decorate(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
# call the common, repeated, code
do_stuff(self.x)
do_stuff(self.y)
# call the function being wrapped
return func(self, *args, **kwargs)
return inner
def MyClass(object):
x = "class attribute"
def __init__(self, y):
self.y = y
@decorate
def spam(self):
print("spam spam spam spam")
@decorate
def ham(self):
return "ham, a growing boy's best friend"
@decorate
def eggs(self, food="bacon"):
return "green eggs and %s" % food
Notice that because the decorator doesn't do any work until the decorated
function is called, there is no difficulty in accessing attributes
regardless of whether they are attached to the class or the instance.
They won't be looked up until self is known.
A more complicated case is where you need to do some pre-processing, and
you *don't* want that calculation repeated every time the method is
called. Decorators are fantastic for that case too, but here you cannot
access instance attributes, since the instance doesn't exist yet. But you
can access *class attributes*, as more-or-less ordinary local variables
*inside* the class definition. Here's a working sketch of the sort of
thing you can do. Copy and paste the following into a Python interactive
session, and then see if you can follow what is being done when.
# === cut ===
import functools
def decorator_factory(a, b):
# This is a factory function that returns a decorator.
# First we do so pre-processing. This gets called only once (per
# usage of the decorator).
value = a*10 + b - 1
print("precalculation of value = %s" % value)
def decorator(func):
print("decorator called on method '%s'" % func.__name__)
@functools.wraps(func)
def inner(self, fe, fi, fo):
return func(self, fe, fi, fo, fum=value)
return inner
# return the decorator
return decorator
class MyClass(object):
spam = 42
ham = 23
@decorator_factory(spam, ham)
def my_method(self, fe, fi, fo, fum):
print(fe, fi, fo, fum)
x = MyClass()
x.my_method(1, 2, 3)
# === end cut ==
Is your mind boggled yet? :-)
Of course, decorators don't *have* to be functions, they can be any
callable object, such as an instance with a __call__ method. But beware
of making your code too clever. Decorators are powerful, but you can over
do it, and make your code unreadable.
> I was hoping to do all the work on in_data from within the decorator,
> which requires access to several MyClass vars. Not clear on the
> syntax/usage with this approach here, any guidance would be greatly
> appreciated!
It's not clear what you actually need to do, so I can't give you any more
guidance apart from the sort of thing that is possible with decorators.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Jason Swails <jason.swails@gmail.com> |
|---|---|
| Date | 2013-03-27 22:38 -0400 |
| Message-ID | <mailman.3841.1364438295.2939.python-list@python.org> |
| In reply to | #42048 |
[Multipart message — attachments visible in raw view] — view raw
On Wed, Mar 27, 2013 at 7:29 PM, Steven D'Aprano < steve+comp.lang.python@pearwood.info> wrote: > > The one doesn't follow from the other. Writing decorators as classes is > fairly unusual. Normally, they will be regular functions. If your > decorator needs to store so much state that it needs to be a class, > you're probably trying to do too much from a single decorator. > > There's more that you need to describe, such as what it is that the > decorator actually does, and whether it does it once, when the decorator > is called, or repeatedly, when the decorated method is called. > > The second case is the easiest. Suppose you have a class like this, with > many methods which have code in common. Here's a toy example: > > > def MyClass(object): > x = "class attribute" > > def __init__(self, y): > self.y = y > In the spirit of nit-picking, I'll point out that Steven meant to use the 'class' keyword instead of 'def' for MyClass. > def MyClass(object): > x = "class attribute" > > def __init__(self, y): > self.y = y > And here as well. It's potentially worth pointing out that this code will actually compile. It will even run, assuming you provide MyClass with a single argument. But it will always return None :). As per usual, the response was thorough and helpful -- I appreciate responses like these and how they've helped improve my command of Python. All the best, Jason
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-03-28 04:58 +0000 |
| Message-ID | <5153cdef$0$29984$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #42068 |
On Wed, 27 Mar 2013 22:38:11 -0400, Jason Swails wrote: >> The second case is the easiest. Suppose you have a class like this, >> with many methods which have code in common. Here's a toy example: >> >> >> def MyClass(object): >> x = "class attribute" >> >> def __init__(self, y): >> self.y = y >> >> > In the spirit of nit-picking, I'll point out that Steven meant to use > the 'class' keyword instead of 'def' for MyClass. /face-palm So I did. Thanks for picking the nit. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | "Joseph L. Casale" <jcasale@activenetwerx.com> |
|---|---|
| Date | 2013-03-31 00:06 +0000 |
| Message-ID | <mailman.4008.1364688371.2939.python-list@python.org> |
| In reply to | #42048 |
> When you say "class vars", do you mean variables which hold classes? You guessed correctly, and thanks for pointing out the ambiguity in my references. > The one doesn't follow from the other. Writing decorators as classes is > fairly unusual. Normally, they will be regular functions. I see, this I didn't know. I'll stick to this guideline now. > A more complicated case is where you need to do some pre-processing, and > you *don't* want that calculation repeated every time the method is > called. Decorators are fantastic for that case too, but here you cannot > access instance attributes, since the instance doesn't exist yet. But you > can access *class attributes*, as more-or-less ordinary local variables > *inside* the class definition. Here's a working sketch of the sort of > thing you can do. Copy and paste the following into a Python interactive > session, and then see if you can follow what is being done when. > Is your mind boggled yet? :-) Steven, That was some of the coolest stuff I have seen a while. I had to wait until I had enough time to actually run this through and utilize it my own work. I haven't enjoyed Python this much since I first started using it. Can't thank you enough for the time and thorough example, that imparted loads of insight. jlc
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web