On Apr 11, 3:07 pm, Bruno Desthuilliers
<bdesth.quelquech...@free.quelquepart.frwrote:
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