[Python-Dev] PEP 231, __findattr__()
Barry A. Warsaw
barry@digicool.com
Mon, 4 Dec 2000 21:54:23 -0500
>>>>> "GvR" == Guido van Rossum <guido@python.org> writes:
GvR> - Do you really think that JimF would do away with
GvR> ExtensionClasses if __findattr__ was intruduced? I kinda
GvR> doubt it. See [*footnote]. It seems that *using*
GvR> __findattr__ is expensive (even if *not* using is cheap :-).
That's not even the real reason why JimF wouldn't stop using
ExtensionClass. He's already got too much code invested in EC.
However EC can be a big pill to swallow for some applications because
it's a C extension (and because it has some surprising non-Pythonic
side effects). In those situations, a pure Python approach, even
though slower, is useful.
GvR> - Why is deletion not supported? What if you want to enforce
GvR> a policy on deletions too?
It could be, without much work.
GvR> - It's ugly to use the same call for get and set. The
GvR> examples indicate that it's not such a great idea: every
GvR> example has *two* tests whether it's get or set. To share a
GvR> policy, the proper thing to do is to write a method that
GvR> either get or set can use.
I don't have strong feelings either way.
GvR> - I think it would be sufficient to *only* use __findattr__
GvR> for getattr -- __setattr__ and __delattr__ already have full
GvR> control. The "one routine to implement the policy" argument
GvR> doesn't really hold, I think.
What about the ability to use "normal" x.name attribute access syntax
inside the hook? Let me guess your answer. :)
GvR> - The PEP says that the "in-findattr" flag is set on the
GvR> instance. We've already determined that this is not
GvR> thread-safe. This is not just a bug in the implementation --
GvR> it's a bug in the specification. I also find it ugly. But
GvR> if we decide to do this, it can go in the thread-state -- if
GvR> we ever add coroutines, we have to decide on what stuff to
GvR> move from the thread state to the coroutine state anyway.
Right. That's where we've ended up in subsequent messages on this thread.
GvR> - It's also easy to conceive situations where recursive
GvR> __findattr__ calls on the same instance in the same
GvR> thread/coroutine are perfectly desirable -- e.g. when
GvR> __findattr__ ends up calling a method that uses a lot of
GvR> internal machinery of the class. You don't want all the
GvR> machinery to have to be aware of the fact that it may be
GvR> called with __findattr__ on the stack and without it.
Hmm, okay, I don't really understand your example. I suppose I'm
envisioning __findattr__ as a way to provide an interface to clients
of the class. Maybe it's a bean interface, maybe it's an acquisition
interface or an access control interface. The internal machinery has
to know something about how that interface is implemented, so whether
__findattr__ is recursive or not doesn't seem to enter into it.
And also, allowing __findattr__ to be recursive will just impose
different constraints on the internal machinery methods, just like
__setattr__ currently does. I.e. you better know that you're in
__setattr__ and not do self.name type things, or you'll recurse
forever.
GvR> So perhaps it may be better to only treat the body of
GvR> __findattr__ itself special, as Moshe suggested.
Maybe I'm being dense, but I'm not sure exactly what this means, or
how you would do this.
GvR> What does Jython do here?
It's not exactly equivalent, because Jython's __findattr__ can't call
back into Python.
GvR> - The code examples require a *lot* of effort to understand.
GvR> These are complicated issues! (I rewrote the Bean example
GvR> using __getattr__ and __setattr__ and found no need for
GvR> __findattr__; the __getattr__ version is simpler and easier
GvR> to understand. I'm still studying the other __findattr__
GvR> examples.)
Is it simpler because you separated out the set and get behavior? If
__findattr__ only did getting, I think it would be a lot similar too
(but I'd still be interested in seeing your __getattr__ only
example). The acquisition examples are complicated because I wanted
to support the same interface that EC's acquisition classes support.
All that detail isn't necessary for example code.
GvR> - The PEP really isn't that long, except for the code
GvR> examples. I recommend reading the patch first -- the patch
GvR> is probably shorter than any specification of the feature can
GvR> be.
Would it be more helpful to remove the examples? If so, where would
you put them? It's certainly useful to have examples someplace I
think.
GvR> There's an easy way (that few people seem to know) to cause
GvR> __getattr__ to be called for virtually all attribute
GvR> accesses: put *all* (user-visible) attributes in a sepate
GvR> dictionary. If you want to prevent access to this dictionary
GvR> too (for Zope security enforcement), make it a global indexed
GvR> by id() -- a destructor(__del__) can take care of deleting
GvR> entries here.
Presumably that'd be a module global, right? Maybe within Zope that
could be protected, but outside of that, that global's always going to
be accessible. So are methods, even if given private names. And I
don't think that such code would be any more readable since instead of
self.name you'd see stuff like
def __getattr__(self, name):
global instdict
mydict = instdict[id(self)]
obj = mydict[name]
...
def __setattr__(self, name, val):
global instdict
mydict = instdict[id(self)]
instdict[name] = val
...
and that /might/ be a problem with Jython currently, because id()'s
may be reused. And relying on __del__ may have unfortunate side
effects when viewed in conjunction with garbage collection.
You're probably still unconvinced <wink>, but are you dead-set against
it? I can try implementing __findattr__() as a pre-__getattr__ hook
only. Then we can live with the current __setattr__() restrictions
and see what the examples look like in that situation.
-Barry