Divorce property objects from attribute access dependency?

Bengt Richter bokr at oz.net
Mon Jun 17 22:58:38 EDT 2002


On Mon, 17 Jun 2002 12:49:06 -0500, Gustavo Cordova <gcordova at hebmex.com> wrote:

>> 
>>  >>> class C(object):
>>  ...     def __init__(self,v):
>>  ...         self._v = v
>>  ...     def get_v(self): return self._v
>>  ...     def set_v(self,v): self._v = v
>>  ...     v=property(get_v,set_v)
>>  ...     vlist = [v,v]
>>  ...
>>  >>> c=C('something')
>>  >>> c.v
>>  'something'
>>  >>> c.v='other'
>>  >>> c.v
>>  'other'
>>  >>> c.vlist
>>  [<property object at 0x0082D4A0>, <property object at 0x0082D4A0>]
>>  >>> c.vlist[0]
>>  <property object at 0x0082D4A0>
>> 
>> Why not trigger get_v() in these accesses too, and have 
>> single mechanism to suppress it instead of a single
>> mechanism to effect it?
>>
>
>You mean that you don't recognize the difference between
>evaluating "c.v" and "c.vlist"?
>
No, I don't mean that ;-)
Also notice that I accessed c.vlist[0] to make a more direct analogue to c.v:
(BTW, disev below is just a handy thing to compile and disassemble an expression)

 >>> from miscutil import disev
 >>> c.v
 'something'
 >>> c.vlist[0]
 <property object at 0x00797370>
 >>> disev('c.v')
           0 SET_LINENO               0
           3 LOAD_NAME                0 (c)
           6 LOAD_ATTR                1 (v)
           9 RETURN_VALUE
 >>> disev('cvlist[0]')
           0 SET_LINENO               0
           3 LOAD_NAME                0 (cvlist)
           6 LOAD_CONST               0 (0)
           9 BINARY_SUBSCR
          10 RETURN_VALUE

The byte code itself is apparently not what makes the difference between property or
other attribute access. Apparently LOAD_ATTR checks to see if the attribute-referenced
object is a property or not (and I suspect as far as LOAD_ATTR is concerned, any class
attribute that dereferences to an instance of a class that has a __GET__ method might do).
What I'm saying is that (e.g.) BINARY_SUBSCR etc. could potentially also check on whether
what it was retrieving has a __GET__ method, if there were a different mechanism tying
a property to an object than via the attribute mechanism, so that object's "self" could
be passed to the __GET__ method of the property object.

>Well, for one:
>
>"c.v" is evaluated at runtime, and it's recognized as
>an instance's access to a class' property attribute,
>so all the background magic happens (which is what
>you expected). Just a note: get_v and set_v are
>methods, which take "self", and all that. Ok.
>
>On the other hand, "vlist" is created at compile
>time, and it's a list, not a property attribute;
>it's only a list with two objects. When you're
>evaluating "c.vlist", the items in the list are not
>in the correct context to be evaluated as property
>attributes, so the background magic does not happen.
IMO "correct context" refers to the current state fo affairs, not
a technical limitation. I am trying to suggest "divorcing"
properties from that "correct context" -- for the sake of discussion;-)

The reason vlist items are printed is that they *are* accessed. UIAM, first
as objects, and then normally the '__repr__' attribute is accessed to
get a method to convert the object to a string representation (or __STR__
if exlicit str() or '%s'% is involved).

However, in the case of a property-jiggered class attribute access,
something apparently discovers that the object is a property object,
and instead of looking for a __repr__ attribute immediately, attribute
access is coded to evaluate the property's __GET__ first (with the
attribute-search's associated object 'self' as argument), and *then*
with the result of that - not the property object any more -- proceed to look
for __repr__ to get the string to print. Note that it's a matter of
attribute access per se looking at an object, not looking for a particular
name. If we bind an alternate attribute name to C.v's property object
as well as using a list, the new name will trigger the property action:

 >>> class C(object):
 ...     def __init__(self,v):
 ...         self._v = v
 ...     def get_v(self): return self._v
 ...     def set_v(self,v): self._v = v
 ...     v=property(get_v,set_v)
 ...     vlist = [v,v]
 ...     a=v
 ...
 >>> c=C('something')
 >>> c.v
 'something'
 >>> c.a
 'something'
 >>> c.vlist[0]
 <property object at 0x007A9EC0>

>For one, there is no "self" to pass to the methods
There are two "selves" in the list. They would work fine
if those property selves had closure-style bindings to
the object they were a property of. Note that we can transfer
the "property" to another class and have its instances see it:

 >>> class X(object):
 ...     def __init__(self,v):
 ...         self._v = v
 ...
 >>> x=X('initial x')
 >>> x._v
 'initial x'
 >>> X.z = c.vlist[1]
 >>> x.z
 'initial x'

because the jiggered access passes the 'self' (as you point out)
of the instance involved in the attribute access.

>get_v and set_v; another, they're "property attributes",
>not "magical list items". :-)
No, they are not "property attributes," they are property
_objects_ in a list. That is my point. They don't _have_ to have anything
to do with getting accessed via a class attribute name. That's just
one conventional and handy use that provides an opportunity to pass
the associated self, which could as well be part of a property closure.

 >>> c.vlist
 [<property object at 0x00797370>, <property object at 0x00797370>]

Property object, not property attribute.
> 
>> ISTM kind of a hack to rig access via instance attribute as a 
>> mechanism for controlling referenced-object behavior (this is
>> my impression, I haven't checked code ;-). IMO default behavior
>> of an object in lhs or rhs context should not depend on how a
>> reference to it was acquired and dereferenced.
>> 
>> (Obviously, if property objects always acted as they now do 
>> through instance attribute access, "vlist=[v,v]" in this
>> example would have to have some kind of access override like
>> "vlist = [obj(v),obj(v)]", or else it would generate 
>> ['something','something'] right there. Using obj(p) a property
>> object could be passed around without evaluation and the
>> potential side effects thereof).
Sorry, that's not quite right. The vlist assignment would not find
a _v attribute from the get_v function, if the property was bound
to the class itself. I was/am thinking of the property
assignment happening in the __init__ function like
self.v=property(get_v,set_v), followed by self.vlist=[v,v].
Currently you can do that, but not with live property effect.

My generalized property factory function would have to be passed an object
to attach to, e.g., self.v=attach_property(self, self.get_v, self.set_v)
inside an __init__.

Doing it for class variables would also be valid, but of course
if you used the class itself as the attachee of the property,
you would have to have get/set methods that operated validly
in that environment.
>
>A property object *can* be passed around without side effects,
>it's only when accesed as an attribute of an instance of a
>class which defines them, that the expected behaviour
>triggers.
I know that. My post suggests a divorce, alternate ways of getting
hitched, and an alternate implementation of triggering, and
I'm talking about object-property realtionships ;-)

>
>For example, try this:
>
>>>> class klass(object): pass
>...
>>>> ob = klass()
>>>> ob._blah = 10
>>>> def _get_blah(self): return self._blah
>>>> def _set_blah(self, b): self._blah = b
>>>> ob.blah = property(_get_blah, _set_blah)
>
>What behaviour do you expect from this? Give it a try. :-)
I expect it to work as currently implemented. I am trying to
suggest an alternative implementation of property objects.

>
>> BTW, it would be nice to be able to define a property at 
>> global module scope also, and perhaps this would tie in nicely.
>> 
>> Just musing ;-) Thoughts? Am I missing a big gotcha?
>> One thing is that it will effectively make possible a ()-less 
>> function call, which will upset some people ;-)
>
>You can do that without properties:
I know interactive use triggers __repr__ and __str__ in various useful
ways. I don't want to (ab)use that. I'd like a generalized property object
that you can attach to any object and store anywhere. The normal use would
be to attach to class instances, and do that by storing as attributes of
the same instances during __init__. (Not as class attributes, note.
The class would be the normal place to define the get/set/del methods).

>
>>>> class Caller:
>...    def __init__(self, func, *args, **kw):
>...       self.func, self.args, self.kw = func, args, kw
>...    def __str__(self): return self.func(*args, **kw)
>...    __repr__ = __str__
>...
That depends on access via __str__ or __repr__, which is not
what I want. That's a worse context dependency than depending
on access via class attribute name.

>>>> def PrintTen(): print 10
>...
>>>> ten = Caller(PrintTen)
>>>> ten
>10
>
>Another thing, you *can* create "magical list items"
>with this class; since __repr__ is also defined,
>then when the list which contains them is rendered,
>the __repr__ method of the items is called. Neat. :-)
>
Neat for what it's neat for, but that's the kind of "magic"
I'm trying totalk about ;-) All those __repr__/__str__-based
ways are beside the point. I'm not looking for help with that,
thanks ;-)

>I Know, it's a simple-minded example, but you know
>what I mean. You can use this Caller class to execute
>a function when you type the name of the object that's
>related to the function.
>
>Good luck :-)
>
Thanks. I was hoping for comments on the idea.
I probably didn't make it sufficiently clear, but
maybe the above helps?

I just wanted to explore the idea of property objects
as related to what they function as properties of, potentially
associated by means other than being stored as attributes
of the instances' classes, and triggered by any access (except a
special one designed to inhibit it), not just class attribute access.

Regards,
Bengt Richter



More information about the Python-list mailing list