- E04 - Leadership! Google, Guido van Rossum, PSF
Duncan Booth
duncan.booth at invalid.invalid
Tue Jan 3 04:41:42 EST 2006
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
More information about the Python-list
mailing list