[Python-ideas] Re: Allow modifying message of TypeError from NotImplemented

2021-06-20 Thread Serhiy Storchaka
19.06.21 06:30, wyz2...@163.com пише:
> Python raises TypeError when NotImplemented is returned from __add__, 
> __invert__ etc. and __radd__ etc. aren't available.
> However, this disallows the customization of error messages. For example, in 
> Python 3.8, __float__ etc. were removed from complex to allow methods like 
> __rfloat__. But this makes abs(z) undiscoverable for many users. The original 
> error message is very helpful.
> I suggest to make a way for this usage. Maybe NotImplementedType can accept 
> *args, and NotImplemented can be callable, equal to __init__:
> 
 class A:
> def __add__(self, other):
> return NotImplemented  # Just like before
> def __neg__(self, other):
> return NotImplemented(f'bad operand type for unary -: 
> {type(self).__name__!r}; use ~ to invert')  # <--
> def __invert__(self):
> return ~5
 a = A()
 a + 2
> TypeError: unsupported operand type(s) for +: 'A' and 'int'
 -a
> TypeError: bad operand type for unary -: 'A'; use ~ to invert
 ~a
> -6

NotImplemented is a singleton. And all code which tests for
NotImplemented does it by checking identity. Returning any other object
instead of the NotImplemented would not work.

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/R6P566R4YQBRRB3KMEROK7FGSRIWJXCO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow modifying message of TypeError from NotImplemented

2021-06-19 Thread Jeff Allen

On 19/06/2021 05:38, Steven D'Aprano wrote:

On Sat, Jun 19, 2021 at 03:30:05AM -, wyz2...@163.com wrote:


Python raises TypeError when NotImplemented is returned from __add__,
__invert__ etc. and __radd__ etc. aren't available.

Roughly correct. It's more complex than that.

I just wanted to add that NotImplemented is *only* for binary (and 
binary comparison) operators.


https://docs.python.org/3/library/constants.html?highlight=notimplemented#NotImplemented

Ok, also when __length_hint__ can't help. Generally though, other 
contexts from which the special method might be called do not check for it.


--

Jeff Allen

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/6ERUPVCUFKYKHMT5CUNVRZ4ZRIBMNJSZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow modifying message of TypeError from NotImplemented

2021-06-18 Thread Steven D'Aprano
On Sat, Jun 19, 2021 at 03:30:05AM -, wyz2...@163.com wrote:

> Python raises TypeError when NotImplemented is returned from __add__, 
> __invert__ etc. and __radd__ etc. aren't available.

Roughly correct. It's more complex than that.


> However, this disallows the customization of error messages. 

Given a binary operator like `+`, there are two objects involved. Which 
one do you think should get to customize the error message if neither 
object handles the plus operator?

We can't customize error messages for syntax errors, attribute errors, 
import errors, value errors, etc. I'm not sure that type errors are 
special enough that they need to be customized.


> For example, in Python 3.8, __float__ etc. were removed from complex 
> to allow methods like __rfloat__.

Are you sure about that? This is in 3.9:

>>> complex.__float__


>>> complex.__rfloat__
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: type object 'complex' has no attribute '__rfloat__'


> But this makes abs(z) undiscoverable for many users.

The set of people who know about complex numbers but not about using 
`abs(z)` to get the magnitude of a complex number is surely very small.


> The original error message is very helpful.

We have to go all the way back to Python 2.5 to get a hint to use 
abs(z):

>>> float(1+2j)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: can't convert complex to float; use abs(z)

I don't think Python 3 has ever given such a hint: the oldest version I 
have installed, 3.2, doesn't.

In any case, if we really think this is important, we could special case 
complex numbers. But I don't think it is important.

Remember that it isn't mandatory for error messages to explain 
everything the caller needs to know about a data type. People are 
permitted to read the documentation and even search the internet.


> I suggest to make a way for this usage. Maybe NotImplementedType can 
> accept *args, and NotImplemented can be callable, equal to __init__:

What would the result of calling NotImplemented be?

If it is the immutable NotImplemented singleton then supplying any 
arguments would be a no-op. They would just be ignored.

If it returns something else, then it wouldn't be the NotImplemented 
singleton and that would become the operator's result.

If we made NotImplemented mutable, that would lead to difficulty with 
multi-threaded code. One thread could modify the object, changing the 
error message, before another thread made use of that message. Fixing 
that would require locks, which would have a big performance impact. I 
would expect that it would slow down almost every operator.

If we changed NotImplemented to no longer be a singleton, that would 
break code that checks for it with the `is` operator, which is one of 
the two main uses for `is` (the other is checking for None).

Making this work would be a big change for a very small benefit.

*If* we thought that being able to customize TypeErrors was important, 
it would, I think, be easier to add a new protocol to operators. After 
going through the whole left-and-right operand dance and calling the 
appropriate methods, if neither operand supported the operator, then the 
interpreter could look at each operand in turn for a new dunder, and 
then call that to get the customized error message.

The API for that dunder would be complicated, but here is my quick 
design. There are two cases, for unary functions and operators and for 
binary operators.

The unary operator case: function or operator which calls `__OP__` would 
then look for a new dunder `__OP_error__` containing the error message. 
So:

complex.__float_error__ = 'use abs(z) instead'


The binary operator case: the operator which calls `__OP__` would then 
look for a new *callable* dunder that returns an error message.

For example, matrices don't define division, so we could do:

class Matrix:
def __true_division_error__(self, type_of_other):
if type_of_other is Matrix:
# Matrix / Matrix not permitted
return "matrix division not defined; try multiplying by the 
inverse matrix"

If the `__OP_error__` dunder returns None or NotImplemented, or doesn't 
exist, no additional message is appended to the TypeError.

This is pretty complicated, for not a lot of benefit IMO. I think people 
should just learn to read the docs.


-- 
Steve
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ECC3NPAPC43GYDA46PELOL3UGRNWAYNW/
Code of Conduct: http://python.org/psf/codeofconduct/