[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