property using a classmethod

  • Thread starter Emanuele D'Arrigo
  • Start date
E

Emanuele D'Arrigo

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
 
B

Bruno Desthuilliers

Emanuele D'Arrigo a écrit :
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

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 ?
 
L

Lie Ryan

Emanuele said:
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'ham'
 
B

Bruno Desthuilliers

Lie Ryan a écrit :
Emanuele D'Arrigo wrote: (snip)

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)
 
L

Lie Ryan

Bruno said:
Lie Ryan a écrit :

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. 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
 
B

Bruno Desthuilliers

Lie Ryan a écrit :
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 !-)
 
B

Bruno Desthuilliers

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
 

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

Forum statistics

Threads
473,770
Messages
2,569,586
Members
45,084
Latest member
HansGeorgi

Latest Threads

Top