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

property using a classmethod

8 views
Skip to first unread message

Emanuele D'Arrigo

unread,
Jul 9, 2009, 6:59:45 AM7/9/09
to
Greetings,

today I did something like this:

class MyClass(object):

@classmethod
def myClassMethod(self):
print "ham"

myProperty = property(myClassMethod, None, None)

As many of you know this doesn't work and returns a TypeError: the
object passed to the property is not a callable function but a
classmethod object, which isn't callable at all. So, how do I do this?
Ultimately all I want is a non-callable class-level attribute
MyClass.myProperty that gives the result of MyClass.myClassMethod().

Can it be done?

Manu

Bruno Desthuilliers

unread,
Jul 9, 2009, 7:59:44 AM7/9/09
to
Emanuele D'Arrigo a �crit :

> Greetings,
>
> today I did something like this:
>
> class MyClass(object):
>
> @classmethod
> def myClassMethod(self):

<ot> Usually, the first argument of classmethods is named 'cls' </ot>

> print "ham"
>
> myProperty = property(myClassMethod, None, None)
>
> As many of you know this doesn't work and returns a TypeError: the
> object passed to the property is not a callable function but a
> classmethod object, which isn't callable at all. So, how do I do this?
> Ultimately all I want is a non-callable class-level attribute
> MyClass.myProperty

properties *are* class attributes.

> that gives the result of MyClass.myClassMethod().
>
> Can it be done?

You could write your own custom descriptor. Or just use an additional
level of indirection, ie:

myProperty = property(lambda self: self.myClassMethod())


but since you already have myClassMethod available, I don't see the
point. What problem are you trying to solve exactly ?

Lie Ryan

unread,
Jul 9, 2009, 8:37:30 AM7/9/09
to
Emanuele D'Arrigo wrote:
> Greetings,
>
> today I did something like this:
>
> class MyClass(object):
>
> @classmethod
> def myClassMethod(self):
> print "ham"
>
> myProperty = property(myClassMethod, None, None)
>
> As many of you know this doesn't work and returns a TypeError: the
> object passed to the property is not a callable function but a
> classmethod object, which isn't callable at all.

That code runs fine for me. Although I doubt the behavior is what you
wanted to do.

> So, how do I do this?
> Ultimately all I want is a non-callable class-level attribute
> MyClass.myProperty that gives the result of MyClass.myClassMethod().

This works like what you seem to want (it's ugly):

class MyClass(object):
class _MyClass(object):
@classmethod
def myClassMethod(cls):
return 'ham'
@property
def myProperty(self):
return MyClass._MyClass.myClassMethod()
@classmethod
def myClassMethod(cls):
return MyClass._MyClass.myClassMethod()
@property
def myProperty(self):
return MyClass._MyClass.myClassMethod()

def __call__(self, *args, **kargs):
# this is __init__
return MyClass._MyClass(*args, **kargs)
# note this is NOT a real MyClass instantiation
MyClass = MyClass()

$ python -i ./strangeclass.py
>>> MyClass.myClassMethod()
'ham'
>>> MyClass.myProperty
'ham'
>>> mc = MyClass()
>>> mc.myProperty
'ham'
>>> mc.myClassMethod()
'ham'

Bruno Desthuilliers

unread,
Jul 9, 2009, 8:56:18 AM7/9/09
to
Lie Ryan a écrit :
> Emanuele D'Arrigo wrote:
(snip)

>> Ultimately all I want is a non-callable class-level attribute
>> MyClass.myProperty that gives the result of MyClass.myClassMethod().
>
> This works like what you seem to want (it's ugly):

Ugly, indeed. And an extreme case of arbitrary overcomplexification too :-/

(snip rube goldberg code)

Scott David Daniels

unread,
Jul 9, 2009, 1:40:58 PM7/9/09
to
Emanuele D'Arrigo wrote:
> class MyClass(object):
> @classmethod
> def myClassMethod(self):
> print "ham"
> myProperty = property(myClassMethod, None, None)
>
> ... doesn't work and returns a TypeError: .... So, how do I do this?

> Ultimately all I want is a non-callable class-level attribute
> MyClass.myProperty that gives the result of MyClass.myClassMethod().

properties affect instances, and classes are instances of types.
What you want is a new metaclass:

class MyType(type):
@property
def demo(class_):
return class_.a + 3

class MyClass(object):
__metaclass__ = MyType
a = 5

print MyClass.a, MyClass.demo

--Scott David Daniels
Scott....@Acm.Org

Lie Ryan

unread,
Jul 9, 2009, 1:35:19 PM7/9/09
to

Can't think of anything simpler than that without meddling with
descriptor. I'm not even sure descriptor can help here as it seems
descriptor needs an instance? (I've just skimmed it, so I may be wrong)

The ugliness of the code hints to two possible reasons:
- there should be a better way
- if there isn't an easier way, then something is wrong the class' design

Bruno Desthuilliers

unread,
Jul 10, 2009, 6:14:26 AM7/10/09
to
Lie Ryan a écrit :

> Bruno Desthuilliers wrote:
>> Lie Ryan a écrit :
>>> Emanuele D'Arrigo wrote:
>> (snip)
>>>> Ultimately all I want is a non-callable class-level attribute
>>>> MyClass.myProperty that gives the result of MyClass.myClassMethod().
>>> This works like what you seem to want (it's ugly):
>> Ugly, indeed. And an extreme case of arbitrary overcomplexification too :-/
>>
>> (snip rube goldberg code)
>>
>
> Can't think of anything simpler than that without meddling with
> descriptor.

Hmmm... Rereading the OP's spec, I guess you understood it better than I
did - seems the OP wants to be able to call the "property" on the class
object itself - which won't work with the builtin property type. So my
own proposed solution won't do :-/

But still, "meddling with descriptor" is *way* simpler than your
proposed solution. Here's a simple non-binding descriptor that do the job:

class ClsProperty(object):
def __init__(self, fget):
if not isinstance(fget, (classmethod, staticmethod)):
raise ValueError(
"fget must be a classmethod or staticmethod"
)
self.fget = fget

def __get__(self, obj, cls=None):
if cls is None:
assert obj is not None
cls = type(obj)
return self.fget.__get__(obj, cls)()


# example use
class Foo(object):
@classmethod
def bar(cls):
return "%s.bar" % cls.__name__

quux = ClsProperty(bar)


> I'm not even sure descriptor can help here as it seems
> descriptor needs an instance?

wrt/ descriptors, no, they don't "need" an instance, at least for
non-binding descriptors. Else, MyClass.MyMethod would return the
MyClass.__dict__['MyMethod'] function, not an unbound method object !-)


Bruno Desthuilliers

unread,
Jul 10, 2009, 6:38:49 AM7/10/09
to
Bruno Desthuilliers a �crit :
(snip)

> You could write your own custom descriptor. Or just use an additional
> level of indirection, ie:
>
> myProperty = property(lambda self: self.myClassMethod())
>

Sorry, looks like I didn't read carefully enough. The above code won't
work if you intend to lookup the property directly on the class object,
ie "MyClass.myProperty". If that was your intention, you'll need a
custom descriptor. The following code should do the job, or at least get
you started:

# python 2.5.x

# the custom (non binding) descriptor


class ClsProperty(object):
def __init__(self, fget):
if not isinstance(fget, (classmethod, staticmethod)):

# XXX better error message


raise ValueError(
"fget must be a classmethod or staticmethod"
)
self.fget = fget

def __get__(self, obj, cls=None):
if cls is None:
assert obj is not None
cls = type(obj)
return self.fget.__get__(obj, cls)()

# helper -> a simple decorator
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClsProperty(func)


# example use
class Foo(object):

# the hard way


@classmethod
def bar(cls):
return "%s.bar" % cls.__name__

quux = ClsProperty(bar)

# the simple way
@classproperty
def baaz(cls):
return "%s.baaz" % cls


Given your example, this should be enough. If you need a binding
descriptor (one with a setter), you'll have to implement the __set__
method (and possibly __del__). Google for "python descriptor" to find
more doc about the descriptor protocol.

HTH

0 new messages