Alex Martelli wrote: > It IS true that in Python you cannot set arbitrary attributes on > arbitrary objects. The workaround is to use a dict, indexed by the id > of the object you want to "set arbitrary attributes on"; this has the > helpful consequence that separate namespaces are used, so your arbitrary > setting of metadata cannot interfere with the `true' attributes of the > object in question. > That's a horrible suggestion (using id's, not the bit about separate namespaces). If you use the id then attributes will persist beyond the lifetime of the object and may suddenly reappear on other unrelated objects later.
A better suggestion here would be to use weak references. Unfortunately, not every Python object can be the target of a weak reference, so there is a limitation here preventing a useful implementation for many builtin types. I can't actually think of a use case for what Ilias wants, and if there isn't a use case it isn't a big problem, but if anyone can come up with a usecase please say. BTW, I don't know Ruby enough to understand the example at http://lazaridis.com/case/lang/ruby/base.html: class Object def meta # adds variable "meta" to all objects in the system end Talker.meta = "Class meta information" john.meta = "Instance meta information" 1234.meta = 'any Instance meta information" puts Talker.meta puts john.meta puts 1234.meta # an integer object With the above code what would 'puts someexpressionresultingin1234.meta' output? i.e. is the attribute being set on all integers with the value 1234, or just on a specific instance of an integer. I don't know if the question even makes sense for Ruby, but it obviously needs to be answered before similar code could be implemented for Python. Anyway, subject to the restriction that it doesn't work for int, list, tuple, etc. here is some code which lets you assign attributes the way I think Ilias wants. Unlike the Ruby code it doesn't just dump them all in the same namespace as other attributes, instead you have to create one or more meta namespaces which then don't interfere at all with other attributes on the objects, but which in other ways work just like attributes (e.g. for the purposes of inheritance you can set an attribute or a method on a base class and it works fine in instances of derived classes.) BTW, If anyone does actually want to use this, the attribute lookup code is incomplete: completing it is left as a exercise. ------------- metaspace.py ------------------- from weakref import WeakKeyDictionary class _Metanamespacewrapper(object): def __init__(self, namespace, target): self.__dict__['_namespace'] = namespace self.__dict__['_target'] = target d = namespace.d if target not in d: d[target] = {} self.__dict__['_dict'] = d[target] def __getattribute__(self, name): if name.startswith('_'): return object.__getattribute__(self,name) if name in self._dict: return self._dict[name] t = type(self._target) for klass in (t,)+t.__mro__: try: d = self._namespace.d[klass] v = d[name] except KeyError: continue break else: raise AttributeError, "meta namespace has no attribute '%s' on object '%r'" % (name, self._target) if hasattr(v, '__get__'): return v.__get__(self._target) return v def __setattr__(self, name, value): self._dict[name] = value def __delattr__(self, name): del self._dict[name] class Metanamespace(object): def __init__(self): self.d = WeakKeyDictionary() def __call__(self, target): return _Metanamespacewrapper(self, target) meta = Metanamespace() # Example of use... class Talker(object): def sayHello(self): print "Hello world" john = Talker() # Check simple access to attributes meta(Talker).name = 'test' print meta(Talker).name, meta(john).name meta(john).name = 'a name' john.name = 'real attribute' # Does not interfere with meta namespace print meta(john).name, john.name meta(object).arg = 5 print "arg=", meta(john).arg meta(john).arg = 2 print "arg=", meta(john).arg del meta(john).arg print "arg=", meta(john).arg def fn1(self, arg): print "fn1", self, arg def fn2(self, arg): print "fn2", self, arg # Check that methods work properly meta(object).fn = fn1 meta(john).fn(1) meta(Talker).fn = fn2 meta(john).fn(2) ------------------------------------------------- The output is: test test a name real attribute arg= 5 arg= 2 arg= 5 fn1 <__main__.Talker object at 0x009D9670> 1 fn2 <__main__.Talker object at 0x009D9670> 2 -- http://mail.python.org/mailman/listinfo/python-list