<div dir="ltr">OK, so I think there isn't anything we can or should do here. Yes, it's possible that type(x).__add__ succeeds but x.__add__ fails. That's how you spell descriptor. :-) You could also use a random number generator  in __getattribube__...<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Apr 19, 2015 at 6:36 PM, Eric Snow <span dir="ltr"><<a href="mailto:ericsnowcurrently@gmail.com" target="_blank">ericsnowcurrently@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Mon, Apr 20, 2015 at 2:20 AM, Guido van Rossum <<a href="mailto:guido@python.org">guido@python.org</a>> wrote:<br>
> (I suppose this new thread is a result of some research you did regarding<br>
> the thread complaining about callable()?)<br>
<br>
</span>Yep. :)<br>
<span class=""><br>
> On Sun, Apr 19, 2015 at 4:03 PM, Eric Snow <<a href="mailto:ericsnowcurrently@gmail.com">ericsnowcurrently@gmail.com</a>><br>
> wrote:<br>
>><br>
>> _PyObject_LookupSpecial is used in place of obj.__getattribute__ for<br>
>> looking up special methods.  (As far as I recall it is not exposed in<br>
>> the stdlib, e.g. inspect.getattr_special.)  Correct me if I'm wrong<br>
>> (please!), but there are two key reasons:<br>
>><br>
>>  * access to special methods in spite of obj.__getattribute__<br>
>>  * speed<br>
><br>
> Good question! I don't have an easy pointer to the original discussion, but<br>
> I do recall that this was introduced in response to some issues with the<br>
> original behavior, which looked up dunder methods on the instance and relied<br>
> on the general mechanism for binding it to the instance. I don't think the<br>
> reason was to circumvent __getattribute__, but your second bullet rings<br>
> true: for every +, -, * etc. there would be a (usually failing) lookup in<br>
> the instance dict before searching the class dict and then the base classes<br>
> etc. There may also have been some confusion where people would e.g. assign<br>
> a function of two arguments to x.__add__ and would be disappointed to find<br>
> out it was called with only one argument. I think there were some folks who<br>
> wanted to fix this by somehow "binding" such calls to the instance (since<br>
> there's no easy way otherwise to get the first argument) but I thought the<br>
> use case was sufficiently odd that it was better to avoid it altogether.<br>
><br>
> In any case, it's not just an optimization -- it's an intentional (though<br>
> obscure) feature.<br>
<br>
</span>Thanks for explaining.<br>
<span class=""><br>
>> While _PyObject_LookupSpecial does not do lookup on obj.__dict__ or<br>
>> call obj.__getattr__, it does resolve descriptors.  This is important<br>
>> particularly since special methods will nearly always be some kind of<br>
>> descriptor.  However, one consequence of this is that instances can<br>
>> influence whether or not some capability, as relates to the special<br>
>> method, is available.  This is accomplished by the descriptor's<br>
>> __get__ raising AttributeError.<br>
><br>
> Well, it's not really the instance that raises AttributeError -- it's the<br>
> descriptor, which is a separate class (usually but not always a builtin<br>
> class, such as property or classmethod). And the descriptor is "owned" by<br>
> the class.<br>
<br>
</span>Sure.  That's what I meant. :)  The instance can influence what the<br>
descriptor returns.<br>
<span class=""><br>
>> My question is: was this intentional?  Considering the obscure bugs<br>
>> that can result (e.g. where did the AttributeError come from?), it<br>
>> seems more likely that it is an oversight of an obscure corner case.<br>
><br>
> I'm not sure what you would do to avoid this. You can't very well declare<br>
> that a descriptor's __get__ method must not raise AttributeError. It could<br>
> be implemented in Python and it could just hit a bug or something.<br>
<br>
</span>Right.  And such a bug will be misinterpreted and obscured and hard to<br>
unravel.  I ran into this a while back with pickle (which still does<br>
lookup for special methods on the instance).  Ultimately it's the same<br>
old problem of not knowing how to interpret an exception that may have<br>
bubbled up from some other layer.<br>
<br>
Like I said, I don't think there's anything to be done about it either<br>
way.  I just got the feeling that in the case of special methods, the<br>
descriptor part of lookup should not expect AttributeError to come out<br>
of the getter.  So I wanted to see if my intuition was correct even if<br>
the point is essentially irrelevant. :)  At this point, though, I<br>
think my intuition wasn't quite right, though I still don't think a<br>
descriptor's getter is the right place to raise AttributeError.<br>
<span class=""><br>
> But<br>
> perhaps I'm misunderstanding the situation you're describing?<br>
><br>
>><br>
>> If that is the case then it would be nice if we could fix<br>
>> _PyObject_LookupSpecial to chain any AttributeError coming from<br>
>> descr.__get__ into a RuntimeError.  However, I doubt we could get away<br>
>> with that at this point.<br>
><br>
> Yeah, I think that ship has sailed. It also seems to be hardly worth trying<br>
> to control "double fault" situations like this. (It's not really a double<br>
> fault, but it reeks like it.)<br>
><br>
> I wonder if maybe you're feeling inspired by PEP 479? But that's really a<br>
> much more special case, and I don't really want to start down a whole<br>
> cascade of trying to "fix" all cases where an AttributeError could be raised<br>
> due to a problem in the user's lookup code.<br>
<br>
</span>Nah.  It isn't about fixing all the cases nor directly related to PEP<br>
479.  Instead it is in response to one obscure corner case (the<br>
behavior of callable).<br>
<span class=""><br>
>> Also, while it may be appropriate in general to allow instances to<br>
>> dictate the availability of attributes/methods (e.g. through<br>
>> __getattribute__, __getattr__, or descriptors), I'm not convinced it<br>
>> makes sense for special methods.  We are already explicitly<br>
>> disconnecting special methods from instances in<br>
>> _PyObject_LookupSpecial (except in the case of descriptors...).<br>
><br>
> I'm still a little bit confused why you consider an error from the<br>
> descriptor as "dictated by the instance".<br>
<br>
</span>Sorry, I should have been more clear.  The descriptor part of attr<br>
lookup involves a way for the instance to influence the outcome of the<br>
lookup (at the discretion of the descriptor).  I didn't mean to imply<br>
that the instance has a direct role in special method lookup.<br>
<span class=""><br>
> I think what you're trying to<br>
> describe is that there is a method on the class but trying to bind it to the<br>
> instance fails. Well, all sorts of things may fails. (In fact very few<br>
> things cannot raise an exception in Python.)<br>
><br>
>><br>
>> -eric<br>
>><br>
>> p.s. I also find it a bit strange that instances have any say at all<br>
>> in which methods (i.e. behavior) are *available*.  Certainly instances<br>
>> influence behavior, but I always find their impact on method<br>
>> availability to be surprising.  Conceptually for me instances are all<br>
>> about state and classes about behavior (driven by state).  However, it<br>
>> is very rarely that I run into code that takes advantage of the<br>
>> opportunity. :)<br>
><br>
><br>
> If I understand what you're trying to say, what you're describing is due to<br>
> Python's unification of instance variables and methods into attributes. It's<br>
> pretty powerful that if x.foo(args) is a method call, you can also write<br>
> this as (x.foo)(args), and you can separate the attribute access even<br>
> further from the call and pass x.foo to some other function that is<br>
> eventually going to call it. Languages that don't do this have to use a<br>
> lambda at that point. Like every design choice each choice has its pluses<br>
> and minuses, but this is how it's done in Python, and it's not going to<br>
> change.<br>
<br>
</span>Again I wasn't very clear.  Rather than the unification of attributes,<br>
I'm referring to how descriptors can raise AttributeError in __get__<br>
and  it gets interpreted as "attribute missing" in<br>
object.__getattribute__ (and similar lookup methods).  However, just<br>
in the context of descriptors it makes a little more sense, even if<br>
still consider it too clever.  I think I was just focusing too much on<br>
the influence of instances on descriptors. :)<br>
<span class="HOEnZb"><font color="#888888"><br>
-eric<br>
</font></span></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>