Class property (was: Class methods)

  • Thread starter Laszlo Zsolt Nagy
  • Start date
L

Laszlo Zsolt Nagy

Is there any way to create a class method? I can create a class
variable like this:
Hmm, seeing this post, I have decided to implement a 'classproperty'
descriptor.
But I could not. This is what I imagined:

class A(object):
_x = 0
@classmethod
def get_x(cls):
print "Getting x..."
return cls._x
@classmethod
def set_x(cls,value):
print "Setting x..."
cls._x = value
x = classproperty(get_x,set_x)

Usage example:
Getting x
0Getting x
8

I was trying for a while, but I could not implement a 'classproperty'
function. Is it possible at all?
Thanks,

Les
 
P

Peter Otten

Laszlo said:
I was trying for a while, but I could not implement a 'classproperty'
function. Is it possible at all?

You could define a "normal" property in the metaclass:
.... class __metaclass__(type):
.... @property
.... def clsprp(cls): return 42
....42

Peter
 
L

Laszlo Zsolt Nagy

Peter said:
Laszlo Zsolt Nagy wrote:




You could define a "normal" property in the metaclass:
The only way I could do this is:

class MyXMetaClass(type):
_x = 0
def get_x(cls):
print "Getting x"
return cls._x
def set_x(cls,value):
cls._x = value
print "Set %s.x to %s" % (cls.__name__,value)
x = property(get_x,set_x)

class A(object):
__metaclass__ = MyXMetaClass

print A.x
A.x = 8


Results in:

Getting x
0
Set A.x to 8

But of course this is bad because the class attribute is not stored in
the class. I feel it should be.
Suppose we want to create a class property, and a class attribute; and
we would like the property get/set methods to use the values of the
class attributes.
A real example would be a class that keeps track of its direct and
subclassed instances:

class A(object):
cnt = 0
a_cnt = 0
def __init__(self):
A.cnt += 1
if self.__class__ is A:
A.a_cnt += 1

class B(A):
pass

print A.cnt,A.a_cnt # 0,0
b = B()
print A.cnt,A.a_cnt # 1,0
a = A()
print A.cnt,A.a_cnt # 2,1

But then, I may want to create read-only class property that returns the
cnt/a_cnt ratio.
This now cannot be implemented with a metaclass, because the metaclass
cannot operate on the class attributes:

class A(object):
cnt = 0
a_cnt = 0
ratio = a_class_property_that_returns_the_cnt_per_a_cnt_ratio() # ????
def __init__(self):
A.cnt += 1
if self.__class__ is A:
A.a_cnt += 1

Any ideas?

Les
 
S

Steven Bethard

Laszlo said:
class A(object):
cnt = 0
a_cnt = 0
def __init__(self):
A.cnt += 1
if self.__class__ is A:
A.a_cnt += 1
class B(A):
pass
print A.cnt,A.a_cnt # 0,0
b = B()
print A.cnt,A.a_cnt # 1,0
a = A()
print A.cnt,A.a_cnt # 2,1

But then, I may want to create read-only class property that returns the
cnt/a_cnt ratio.
This now cannot be implemented with a metaclass, because the metaclass
cannot operate on the class attributes:

Huh? Every function in the metaclass takes the class object as the
first parameter. So they can all operate on the class attributes:

py> class A(object):
.... cnt = 0
.... a_cnt = 0
.... def __init__(self):
.... A.cnt += 1
.... if self.__class__ is A:
.... A.a_cnt += 1
.... class __metaclass__(type):
.... @property
.... def ratio(cls):
.... return cls.a_cnt/float(cls.cnt)
....
py> class B(A):
.... pass
....
py> A.cnt, A.a_cnt
(0, 0)
py> A.ratio
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 11, in ratio
ZeroDivisionError: float division
py> b = B()
py> A.cnt, A.a_cnt, A.ratio
(1, 0, 0.0)
py> a = A()
py> A.cnt, A.a_cnt, A.ratio
(2, 1, 0.5)

STeVe
 
B

Bengt Richter

Hmm, seeing this post, I have decided to implement a 'classproperty'
descriptor.
But I could not. This is what I imagined:

class A(object):
_x = 0
@classmethod
def get_x(cls):
print "Getting x..."
return cls._x
@classmethod
def set_x(cls,value):
print "Setting x..."
cls._x = value
x = classproperty(get_x,set_x)

Usage example:

Getting x
0
Getting x
8

I was trying for a while, but I could not implement a 'classproperty'
function. Is it possible at all?
Thanks,

Les
Using Peter's advice (not tested beyond what you see):
... _x = 0
... class __metaclass__(type):
... def get_x(cls):
... print "Getting x..."
... return cls._x
... def set_x(cls,value):
... print "Setting x..."
... cls._x = value
... x = property(get_x, set_x)
... Getting x...
0 Getting x...
8 [('__module__', '__main__'), ('__metaclass__', <class '__main__.__metaclass__'>), ('_x', 8), ('_
['__module__', '__metaclass__', '_x', '__dict__', '__weakref__', '__doc__']

Regards,
Bengt Richter
 
B

Bengt Richter

The only way I could do this is:

class MyXMetaClass(type):
_x = 0
def get_x(cls):
print "Getting x"
return cls._x
def set_x(cls,value):
cls._x = value
print "Set %s.x to %s" % (cls.__name__,value)
x = property(get_x,set_x)

class A(object):
__metaclass__ = MyXMetaClass

print A.x
A.x = 8


Results in:

Getting x
0
Set A.x to 8

But of course this is bad because the class attribute is not stored in
the class. I feel it should be.
Suppose we want to create a class property, and a class attribute; and
we would like the property get/set methods to use the values of the
class attributes.
A real example would be a class that keeps track of its direct and
subclassed instances:

class A(object):
cnt = 0
a_cnt = 0
def __init__(self):
A.cnt += 1
if self.__class__ is A:
A.a_cnt += 1

class B(A):
pass

print A.cnt,A.a_cnt # 0,0
b = B()
print A.cnt,A.a_cnt # 1,0
a = A()
print A.cnt,A.a_cnt # 2,1

But then, I may want to create read-only class property that returns the
cnt/a_cnt ratio.
This now cannot be implemented with a metaclass, because the metaclass
cannot operate on the class attributes:
But it can install a property that can.
class A(object):
cnt = 0
a_cnt = 0
ratio = a_class_property_that_returns_the_cnt_per_a_cnt_ratio() # ????
def __init__(self):
A.cnt += 1
if self.__class__ is A:
A.a_cnt += 1

Any ideas?
... cnt = 0
... a_cnt = 0
... def __init__(self):
... A.cnt += 1
... if self.__class__ is A:
... A.a_cnt += 1
... class __metaclass__(type):
... def ratio(cls):
... print "Getting ratio..."
... return float(cls.a_cnt)/cls.cnt #
... ratio = property(ratio)
...
I inverted your ratio to lessen the probability if zero division...
Getting ratio...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 11, in ratio
ZeroDivisionError: float division

Oops ;-)
Getting ratio...
0.0 Getting ratio...
0.5
Getting ratio...
0.66666666666666663

The old instance is no longer bound, so should it still be counted as it is?
You might want to check how to use weak references if not...
Getting ratio...
0.5 Getting ratio...
0.40000000000000002

Regards,
Bengt Richter
 

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,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top