On Apr 11, 3:07 pm, Bruno Desthuilliers <[EMAIL PROTECTED]> wrote: > Eric Mahurin a écrit : > > > Is there a standard way to get a descriptor object for an arbitrary > > object attribute - independent of whether it uses the descriptor/ > > property protocol or not. I want some kind of handle/reference/ > > pointer to an attribute. > > I'm not sure to understand what you want exactly. A concrete example > would probably help. But from what I understood, you could just use a > thin wrapper like: > > _marker = object() > class Accessor(object): > def __init__(self, obj, name): > self._obj = obj > self._name = name > def __call__(self, new_val=_marker): > if new_val is _marker: > return getattr(self._obj, self._name) > else: > setattr(self._obj, self._name, new_val) > > class Foo(object): > def __init__(self, bar, baak=42): > self.bar = bar > self.baak = baak > > foo = Foo('allo') > bar = Accessor(foo, 'bar') > > assert bar() == foo.bar == 'allo' > bar(42) > assert bar() == foo.bar == 42 > > If that doesn't answer your question, please pardon my stupidity and > provide some more concrete example.
Thanks Bruno, This does answer my question. The protocol you implemented looks to match a weakref except you can also set the value by giving an argument to __call__ (an of course it implements a "hard" ref). I was considering this one already, but decided to use an attribute/ descriptor for getting/setting for a little better efficiency (no if statement). I also made my reference classes forward/proxy attributes from the underlying object. I used the attribute "_" for getting/ setting the underlying object to reduce the chances of colliding with attributes of the underlying object. I still don't like the resulting syntax, but it is workable. I would have to be able to do one of these: my_ref() # get underlying object - possible with __call__ my_ref() = obj # set underlying object - can't: no calling assign my_ref[] # get underlying object - can't: __getitem__ requires a key/index my_ref[] = obj # set underlying object - can't: __setitem__ requires a key/index Below is a stripped down version of what I'm using now. I'm showing the references (sub-classed to links) being used in a singly linked list (I'm really using them with a directed graph where children are implemented with a singly linked list). When implementing these types of structures, the concept of a "reference" (to an object reference) can be quite handy. With a list/dict, you already have a handle to values in a list/dict - an index/key. This isn't quite a reference, but usually it is good enough. For other structures, a reference becomes much more useful since you don't have this. class Ref(object) : ''' Anonymous reference to an object. ''' def __init__(self, object=None) : ''' >>> r = Ref(-1) >>> r._ -1 >>> r._ = 2 >>> r._ 2 ''' self._ = object def __getattr__(self, name) : ''' >>> r = Ref(-1) >>> r.__abs__() 1 ''' return getattr(self._, name) class LookupRef(Ref) : ''' Reference to a value in a lookup (dict, list, tuple, etc). ''' def __init__(self, lookup, key) : self._lookup = lookup self._key = key _ = property( lambda self: self._lookup.__getitem__(self._key), lambda self, value: self._lookup.__setitem__(self._key, value), doc=''' >>> r = LookupRef(('a','b','c'),1) >>> r._ 'b' >>> v = 0 >>> r = LookupRef(locals(),'v') >>> r._ = 1 >>> v 1 ''') class AttrRef(Ref) : ''' Reference to an object attribute. ''' def __init__(self,obj,attr) : self._obj = obj self._attr = attr _ = property( lambda self: getattr(self._obj, self._attr), lambda self, value: setattr(self._obj, self._attr, value), doc=''' >>> class Foo : bar = 123 >>> f = Foo() >>> r = AttrRef(f,'bar') >>> r._ 123 >>> r._ = 'abc' >>> f.bar 'abc' ''') class Node(object) : ''' Node (including head) in a singly linked-list. ''' def __init__(self, data=None, next=None) : self.data = data self.next = next def __add__(self, node) : ''' >>> t = Node('a')+Node('b') >>> t.data 'b' >>> t.next.data 'a' ''' node.next = self return node def __iter__(self) : ''' An iterator through self and all other nodes linked. >>> [node.data for node in Node(1) + Node(2) + Node(3)] [3, 2, 1] ''' node = self while node : yield node node = node.next def __repr__(self) : ''' Gives the coordinates and the children. >>> Node(1)+Node(2) Node(1) + Node(2) ''' return ' + '.join(reversed( ['Node('+repr(node.data)+')' for node in self] )) def push_next(self, node) : ''' >>> l = Node(1)+Node(2) >>> l.push_next(Node(3)) ''' node.next = self.next self.next = node def pop_next(self) : ''' >>> l = Node(1)+Node(2)+Node(3) >>> l.pop_next() Node(2) >>> l Node(1) + Node(3) ''' node = self.next self.next = node.next node.next = None return node class Link(Ref) : ''' A link (type of reference) to a Node. ''' def pop(self) : ''' Pop off the current linked node (returning it) and link to the next node. >>> l = Link(Node(1)+Node(2)+Node(3)) >>> l.pop() Node(3) >>> l._ Node(1) + Node(2) ''' node = self._ self._ = node.next node.next = None return node def push(self, node) : ''' Link to a new node and make the previous linked node the next one. >>> l = Link(Node(1)+Node(2)) >>> l.push(Node(3)) >>> l._ Node(1) + Node(2) + Node(3) ''' node.next = self._ self._ = node def __iter__(self) : ''' Iterator to self and next links. >>> l = Link(Node(1)+Node(2)+Node(3)+Node(4)+Node(5)) >>> for link in l : ... # remove first node with even data ... if not link.data%2 : ... node = link.pop() ... break >>> l._ Node(1) + Node(2) + Node(3) + Node(5) >>> node Node(4) ''' ref = self while ref._ : yield ref ref = AttrLink(ref._,'next') class AttrLink(AttrRef, Link) : pass class LookupLink(LookupRef, Link) : pass -- http://mail.python.org/mailman/listinfo/python-list