Decorators using instance variables

R

robert2821

Hi,

I'm new; greetings all!

I'm wondering if the following program should work. I think it should
print 'Hello, World', but instead it produces a TypeError. Is this a
bug in decorators, a feature of them, or a mistake or misunderstanding
on my part?

TIA, Bob


def getdec (f):
dec = decorator (f)
return dec. docall

class decorator:

def __init__ (self, f):
self. f = f

def docall (self, *a):
return self. f (*a)

class test:
@ getdec
def doit (self, message):
print message

if __name__ == '__main__':
foo = test ()
foo. doit ('Hello, world')
 
C

castironpi

Hi,

I'm new; greetings all!

I'm wondering if the following program should work.  I think it should
print 'Hello, World', but instead it produces a TypeError.  Is this a
bug in decorators, a feature of them, or a mistake or misunderstanding
on my part?

TIA,  Bob

def    getdec (f):
    dec = decorator (f)
    return dec. docall

class    decorator:

    def __init__ (self, f):
        self. f = f

    def docall (self, *a):
        return self. f (*a)

class    test:
    @ getdec
    def doit (self, message):
        print message

if __name__ == '__main__':
    foo = test ()
    foo. doit ('Hello, world')

 Dec.py
< 1KViewDownload

Have a look at this and fiddle with it:

from types import MethodType
class decorator(object):

def __init__ (self, f):
self. f = f

def docall (self, *a):
print a
return self. f (*a)

def __get__( self, instance, owner ):
print 'in __get__', instance, owner
if instance is not None:
return MethodType( self.docall, instance )
return self.f

class test:
@ decorator
def doit (self, message):
print message

if __name__ == '__main__':
foo = test ()
print test.doit
print foo.doit
foo. doit ('Hello, world')

Output:

in __get__ None __main__.test
<function doit at 0x00A01170>
in __get__ <__main__.test instance at 0x009FEE18> __main__.test
<bound method ?.docall of <__main__.test instance at 0x009FEE18>>
in __get__ <__main__.test instance at 0x009FEE18> __main__.test
(<__main__.test instance at 0x009FEE18>, 'Hello, world')
Hello, world

The reason is that in the first version, the type of test.doit is
InstanceType, and Python only 'binds' objects of type FunctionType to
MethodType. MethodType is the type that contains an implicit first
'self' parameter. If 'doit' has a __get__ attribute, it is called
whenever -class-.doit or -instance-.doit are accessed, and it returns
a bound method, or something else it likes.

A little more investigating reveals:
['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
'__get__', '__ge
tattribute__', '__hash__', '__init__', '__module__', '__name__',
'__new__', '__r
...

functions have a '__get__' attribute to perform this very thing.
 
B

Bruno Desthuilliers

robert2821 a écrit :
Hi,

I'm new; greetings all!

Hello.

Since you're new here, first a couple advises:
1/ the python mailing list is relayed to the comp.lang.python usenet
newsgroup (from where I'm reading your post and answering it), so please
avoid attachments. Either put the full code in your post, or provide an
url to somewhere we can read it.
2/ don't bother reading anything from someone named 'castironpi', it's
one of our currently active resident troll, and he is worse than clueless.
I'm wondering if the following program should work. I think it should
print 'Hello, World', but instead it produces a TypeError. Is this a
bug in decorators, a feature of them, or a mistake or misunderstanding
on my part?

I doubt this is a bug. But it can be both a feature and a mistake or
misunderstanding !-)

<ot topic="your code">
Please read pep08. Coding convention are very strong in Python. And
preferably, use spaces (4 spaces per tab) for indentation.
http://www.python.org/dev/peps/pep-0008/
def getdec(f):
dec = decorator(f)
return dec. docall

class decorator:

<ot>
- pep08 : class names should be CamelCased
- unless you have a compelling reason to stick to a by now antiquated
object model, better to use "new-style" classes. Part of what you'll
read here about Python's OO features apply only to new-style classes,
and most of the remaining apply to both object models.

IOW, make this:

class Decorator(object):
def __init__ (self, f):
self.f = f

def docall (self, *a):
return self.f(*a)

<ot>
You can write your own callable types by implementing the __call__ method.
 
B

Bruno Desthuilliers

Bruno Desthuilliers a écrit :
robert2821 a écrit :

(snip - sorry, hit the 'send' button too soon)

<ot>
Last point : post the whole error message and traceback if possible
</ot>

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/tmp/python-10426amG.py", line 19, in <module>
foo.doit ('Hello, world')
File "/usr/tmp/python-10426amG.py", line 10, in docall
return self. f(*a)
TypeError: doit() takes exactly 2 arguments (1 given)


This has to do with how Python makes methods from functions. Your getdec
function - which is the real decorator - returns a class instance, not
another function. This actually prevents doit() to be wrapped in a
method when looked up on a test instance, so doit doesn't receive the
instance as first param (hence the error message).

Here's a working version of your code using new-style classes:

def getdec(f):
dec = Decorator(f)
return dec

class Decorator(object):
def __init__ (self, f):
self.f = f
self.instance = None

def __get__(self, instance, cls):
self.instance = instance
return self

def __call__(self, *args):
args = (self.instance,) + args
return self.f(*args)

class Test(object):
@ getdec
def doit(self, message):
print message

if __name__ == '__main__':
foo = Test ()

FWIW, note that the getdec function here is useless - you'd have the
same result directly applying the Decorator class as a decorator.

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top