Class property

Bengt Richter bokr at oz.net
Fri Oct 14 22:31:57 EDT 2005


On Thu, 06 Oct 2005 16:09:22 +0200, Laszlo Zsolt Nagy <gandalf at designaproduct.biz> wrote:

>Peter Otten wrote:
>
>>Laszlo Zsolt Nagy wrote:
>>
>>  
>>
>>>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:
>>  
>>
>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?
>

 >>> 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):
 ...         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...

 >>> class B(A): pass
 ...
 >>> A.ratio
 Getting ratio...
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 11, in ratio
 ZeroDivisionError: float division

Oops ;-)

 >>> A.cnt, A.a_cnt
 (0, 0)
 >>> b=B()
 >>> A.cnt, A.a_cnt
 (1, 0)
 >>> A.ratio
 Getting ratio...
 0.0
 >>> a=A()
 >>> A.ratio
 Getting ratio...
 0.5

 >>> a=A()
 >>> A.ratio
 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...

 >>> b2=B()
 >>> B.ratio
 Getting ratio...
 0.5
 >>> b3=B()
 >>> B.ratio
 Getting ratio...
 0.40000000000000002

Regards,
Bengt Richter



More information about the Python-list mailing list