Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

generators as decorators simple issue

58 views
Skip to first unread message

j.m.da...@gmail.com

unread,
Sep 11, 2012, 10:28:10 PM9/11/12
to
I'm trying to call SetName on an object to prevent me from ever having to call it explictly again on that object. Best explained by example.


def setname(cls):
'''this is the proposed generator to call SetName on the object'''
try:
cls.SetName(cls.__name__)
finally:
yield cls


class Trial:
'''class to demonstrate with'''
def SetName(self, name):
print 1, 1

@setname
class Test(Trial):
'''i want SetName to be called by using setname as a decorator'''
def __init__(self):

print 'Yay! or Invalid.'

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


How can i fix this?
This is my exact error: python decors2.py
Traceback (most recent call last):
File "decors2.py", line 23, in <module>
test = Test()
TypeError: 'generator' object is not callable

Ramchandra Apte

unread,
Sep 11, 2012, 10:55:24 PM9/11/12
to
On Wednesday, 12 September 2012 07:58:10 UTC+5:30, pyjoshsys wrote:
> I'm trying to call SetName on an object to prevent me from ever having to call it explictly again on that object. Best explained by example.
>
[snip]
In your decorator, you are using `yield cls` - it should be `return cls` 99.99% of the time.

alex23

unread,
Sep 12, 2012, 12:39:52 AM9/12/12
to
On Sep 12, 12:28 pm, j.m.dagenh...@gmail.com wrote:
> def setname(cls):
>     '''this is the proposed generator to call SetName on the object'''
>     try:
>         cls.SetName(cls.__name__)
>     finally:
>         yield cls

A generator is (basically) a callable that acts like an iterator.
You'd use a generator if you wanted to loop with for or a list
comprehension across the output of the generator: for foo in
setname(Test)

A decorator is a callable that takes another callable as an argument,
either modifying it or returning a wrapped version of it: Test =
setname(Test)

You don't want to iterate over anything, so you should change `yield`
to `return`.

Thomas Rachel

unread,
Sep 12, 2012, 2:08:02 AM9/12/12
to
Am 12.09.2012 04:28 schrieb j.m.da...@gmail.com:
> I'm trying to call SetName on an object to prevent me from ever having to call it explictly again on that object. Best explained by example.
>
>
> def setname(cls):
> '''this is the proposed generator to call SetName on the object'''
> try:
> cls.SetName(cls.__name__)
> finally:
> yield cls
>
>
> class Trial:
> '''class to demonstrate with'''
> def SetName(self, name):
> print 1, 1
>
> @setname
> class Test(Trial):
> '''i want SetName to be called by using setname as a decorator'''
> def __init__(self):
>
> print 'Yay! or Invalid.'
>
> if __name__ == '__main__':
> test = Test()
>
>
> How can i fix this?

I am not sure what exactly you want to achieve, but I see 2 problems here:

1. Your setname operates on a class, but your SetName() is an instance
function.

2. I don't really understand the try...finally yield stuff. As others
already said, you probably just want to return. I don't see what a
generator would be useful for here...

def setname(cls):
'''this is the proposed generator to call SetName on the object'''
try:
cls.SetName(cls.__name__)
finally:
return cls

and

class Trial(object):
'''class to demonstrate with'''
@classmethod
def SetName(cls, name):
print 1, 1

should solve your problems.

pyjoshsys

unread,
Sep 12, 2012, 6:22:31 AM9/12/12
to
The output is still not what I want. Now runtime error free, however the output is not what I desire.



def setname(cls):
'''this is the proposed generator to call SetName on the object'''

try:
cls.SetName(cls.__name__)
except Exception as e:
print e
finally:
return cls

class Trial(object):
'''class to demonstrate with'''
def __init__(self):
object.__init__(self)
self.name = None

@classmethod
def SetName(cls, name):
cls.name = name

@setname
class Test(Trial):
'''i want SetName to be called by using setname as a decorator'''
def __init__(self):
Trial.__init__(self)



if __name__ == '__main__':
test = Test()
print 'instance'
print '', test.name #should be Test
print 'class'
print '', Test.name


The output is: python decors2.py
instance
None
class
Test

I want:
instance
Test
class
Test

Is this possible in this manner?

Oscar Benjamin

unread,
Sep 12, 2012, 6:47:02 AM9/12/12
to pytho...@python.org
On Wed, 12 Sep 2012 03:22:31 -0700 (PDT), pyjoshsys
<j.m.da...@gmail.com> wrote:
> The output is still not what I want. Now runtime error free,
however the output is not what I desire.

> def setname(cls):
> '''this is the proposed generator to call SetName on the
object'''

> try:
> cls.SetName(cls.__name__)
> except Exception as e:
> print e
> finally:
> return cls

I would write the function above in one line:

cls.name = name


> class Trial(object):
> '''class to demonstrate with'''
> def __init__(self):
> object.__init__(self)
> self.name = None

Remove the line above. The instance attribute self.name is hiding the
class attribute cls.name.

Oscar

pyjoshsys

unread,
Sep 12, 2012, 7:15:10 AM9/12/12
to
so decorators only pass the object and not any instance of the object as the implied argument? Is this right?

The idea was to use @setname instead of instance.SetName(instance.__name__).

I thought decorators would do this, but it seems not.







Ian Kelly

unread,
Sep 12, 2012, 11:09:08 AM9/12/12
to Python
On Wed, Sep 12, 2012 at 4:22 AM, pyjoshsys <j.m.da...@gmail.com> wrote:
> The output is still not what I want. Now runtime error free, however the output is not what I desire.

[SNIP]

> class Trial(object):
> '''class to demonstrate with'''
> def __init__(self):
> object.__init__(self)
> self.name = None
>
> @classmethod
> def SetName(cls, name):
> cls.name = name

[SNIP]

> if __name__ == '__main__':
> test = Test()
> print 'instance'
> print '', test.name #should be Test
> print 'class'
> print '', Test.name
>
>
> The output is: python decors2.py
> instance
> None
> class
> Test
>
> I want:
> instance
> Test
> class
> Test
>
> Is this possible in this manner?


The SetName class method sets the name on the *class* dictionary. The
class's __init__ method also sets a name (None) on the *instance*
dictionary. From an instance's perspective, the instance dictionary
will shadow the class dictionary. If you remove the attribute from
the instance dictionary entirely (delete the "self.name = None" line),
and leave the class dictionary as is, then you will get the output you
want (although from your later post I am not certain that this is the
behaviour you want).


On Wed, Sep 12, 2012 at 5:15 AM, pyjoshsys <j.m.da...@gmail.com> wrote:
> so decorators only pass the object and not any instance of the object as the implied argument? Is this right?

Right.

> The idea was to use @setname instead of instance.SetName(instance.__name__).

The appropriate place to do this so that it applies to all instances
of the class rather than to the class would be inside the __init__
method.

Also, instances don't have a __name__ attribute, so it's still unclear
to me what you're looking for. Did you mean the effect to be that of
"instance.SetName(cls.__name__)"? If so, then the decorator approach
(with the line "self.name = None" removed) should be fine for your
purposes -- you'll just have the name stored in the class dict instead
of in each instance dict, but it will still be visible as long as you
haven't shadowed it.
0 new messages