- E04 - Leadership! Google, Guido van Rossum, PSF

Duncan Booth duncan.booth at invalid.invalid
Tue Jan 3 10:41:42 CET 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