property for class objects

Bengt Richter bokr at oz.net
Fri Nov 12 01:24:43 CET 2004


On Wed, 10 Nov 2004 20:02:48 -0500, Steve Menard <foo at bar.com> wrote:
>Bengt Richter wrote:
>> On Wed, 10 Nov 2004 08:28:22 -0500, Steve Menard <foo at bar.com> wrote:
>> [...]
>> 
>>>I wasnt aware of those descriptors (I am just now delving in the "meta" 
>>>side of things), and it looks promising. So thanks for taking the time 
>>>to tlel me about it :)
>>>
>>>Static variables are not just constants however, and the __set__ has to 
>>>work as normal. I did a quick test and it seems by mistake you can even 
>>>erase the descriptor with a new value .. not good.
>>>
>> 
>> Yes, the __set__ only gets called for instance attributes, unless there
>> is something intercepting the class attribute setattr.
>> 
>> 
>>>At least I can use this for the constant static values (which probably 
>>>comprises the bulk) and use the __setattr__ to prevent overwriting them.
>>>
>> 
>> If they're actually constant, why couldn't they be plain old class variables
>> protected by __setattr__?
>> 
>> 
>
>You're right about constants. I could convert them at class-definition 
>time instead of access-time. Thus only the case of non-constant static 
>members do I need to find a solution ...
>
>>>Steve
>>>
>>>Still looking for a more complete solution howeber
>> 
>> Don't know what syntax restrictions you have, but if you are trying to simulate
>> shared static value assignment via an apparent instance attribute assignment, e.g.,
>> 
>>     inst.static = value
>> 
>> why do you need to write
>> 
>>     InstClass.static = something
>> 
>
>Static members are not common in Python. They are however fairly common 
>in Java. So I was hoping to preserve the
>
>	InstClass.value = something
>
>
>
>> at all? You could always make a dummy instance to use when all you
>> wanted to do was access the shared statics, e.g.,
>> 
>>     shared = InstClass()
>>     ...
>>     shared.static = something
>> 
>> or even a dynamically created throwaway instance, as in
>> 
>>     InstClass().static = something
>> 
>This is not an option, as some Java classes that mutable static 
>variables are not instantiatable.
>
>> If you wanted to designate certain names for shared statics to be stored
>> as class variables, you could do something like (only tested as you see here ;-):
>> 
>>  >>> class HasStatics(object):
>>  ...     static_names = 'sa sb sz'.split()
>>  ...     def __setattr__(self, name, value):
>>  ...         if name in self.static_names: setattr(type(self), name, value)
>>  ...         else: object.__setattr__(self, name, value)
>>  ...
>>  >>> hs = HasStatics()
>>  >>> vars(hs)
>>  {}
>>  >>> hs.x = 123
>>  >>> vars(hs)
>>  {'x': 123}
>>  >>> hs.sa = 456
>>  >>> vars(hs)
>>  {'x': 123}
>>  >>> HasStatics.sa
>>  456
>>  >>> hs.sa
>>  456
>>  >>> vars(HasStatics).keys()
>>  ['__module__', '__setattr__', '__dict__', 'sa', '__weakref__', '__doc__', 'static_names']
>> 
>> static_names would be faster as a dict of the names (with ignored values), or possibly now
>> a set.
>> 
>
>This is what I do now. However, this break with the following situation :
>
See if example below fixes that

>class A(object) :
>	# class has a property called foo
>
>class B(A) :
>	...
>
>B.foo
>
>As each class only contain it's static variables and not it's ancestors. 
>I bypass this problem by accumlating all static variables in each class, 
>including the static variables of all bases. It works, but then dir() 
>reports variable that are not really there.
Well, dir() walks the base chain, so you may be seeing stuff legitimately.
To see attributes attached to a particular object, try vars(obj). (See test() below)

>
>thanks for the suggestions,
>
>Steve

You're welcome. Here's another possibility. A base class that monitors setting of attributes
on both instances and classes, and keeps "static" variables as class variables in whatever
class they're declared in using __statics__ = ['list','of','names']. Ordinary attributes
are allowed to work normally.

----< statics.py >----------------------------------------
class StaticBase(object):
    class __metaclass__(type):
        def __setattr__(cls, name, value):
            """intercept class attribute writes"""
            for base in cls.mro():
                bdict = type.__getattribute__(base, '__dict__')
                if '__statics__' in bdict and name in bdict['__statics__']:
                    type.__setattr__(base, name, value)
                    break
            else:
                type.__setattr__(cls, name, value)

    def __setattr__(self, name, value):
        """intercept instance attribute writes"""
        for base in type(self).mro():
            bdict = type.__getattribute__(base, '__dict__')
            if '__statics__' in bdict and name in bdict['__statics__']:
                type.__setattr__(base, name, value)
                break
        else:
            object.__setattr__(self, name, value)

def test():
    class A(StaticBase):
        __statics__ = 'sa1 sa2 sa3'.split()
        cva1 = 'class var a1'
    a = A()
    A.x = 123
    a.x = 456
    a.sa1 = 'a.sa1'
    A.sa2 = 'A.sa2'
    a.sa3 = 'a.sa3'
    A.sa3 = 'A.sa3'
    print '\n-- a -------------'
    for it in vars(a).items(): print '%12s: %r'%it
    print '\n-- A -------------'
    for it in vars(A).items(): print '%12s: %r'%it
       
    class B(A):
        __statics__ = 'sb1 sb2 sb3'.split()
        cva1 = 'class var b1'
    b = B()
    B.x = 789
    b.x = 101112
    b.sb1 = 'b.sb1'
    B.sb2 = 'B.sb2'
    b.sb3 = 'b.sb3'
    B.sb3 = 'B.sb3'
    b.sa1 = 'b.sa1' # should go to A
    B.sa2 = 'B.sa2'
    b.sa3 = 'b.sa3'
    B.sa3 = 'B.sa3'
    print '\n-- b -------------'
    for it in vars(b).items(): print '%12s: %r'%it
    print '\n-- B -------------'
    for it in vars(B).items(): print '%12s: %r'%it
    print '\n-- a -------------'
    for it in vars(a).items(): print '%12s: %r'%it
    print '\n-- A -------------'
    for it in vars(A).items(): print '%12s: %r'%it
    
if __name__ == '__main__':
    test()
----------------------------------------------------------
Result:

[16:19] C:\pywk\cs>statics.py

-- a -------------
           x: 456

-- A -------------
        cva1: 'class var a1'
  __module__: '__main__'
 __statics__: ['sa1', 'sa2', 'sa3']
         sa2: 'A.sa2'
         sa3: 'A.sa3'
           x: 123
         sa1: 'a.sa1'
     __doc__: None

-- b -------------
           x: 101112

-- B -------------
         sb2: 'B.sb2'
        cva1: 'class var b1'
  __module__: '__main__'
         sb1: 'b.sb1'
         sb3: 'B.sb3'
 __statics__: ['sb1', 'sb2', 'sb3']
           x: 789
     __doc__: None

-- a -------------
           x: 456

-- A -------------
        cva1: 'class var a1'
  __module__: '__main__'
 __statics__: ['sa1', 'sa2', 'sa3']
         sa2: 'B.sa2'
         sa3: 'B.sa3'
           x: 123
         sa1: 'b.sa1'
     __doc__: None

Unfortunately this probably slows execution quite a bit, since there is programmed
chasing of base classes for every attribute set. But if most of the accesses are reads
it might be ok, since those aren't monitored. You could memoize name->baseclass
for statics to speed things a little, I suppose, being careful that the caches don't
shadow some dynamic change that might be possible. You could also add write protection.

HTH

Regards,
Bengt Richter



More information about the Python-list mailing list