[issue34497] Remove needless set operator restriction

2018-08-26 Thread Raymond Hettinger


Raymond Hettinger  added the comment:

Sorry Dan, we're going to pass on this one.  The current behavior was an 
intentional design choice by Guido and reflects a careful balance between some 
difficult trade-offs.

An early and permanent mistake in Python's design is that list.__iadd__() and 
list.extend() both accept any input iterable.  For extend(), this proved to be 
useful.  In contrast, __iadd__() was a recurring bug magnet.  People would 
routinely type "s=['abc']; s+='def'" expecting to get ['abc', 'def'] rather 
than ['abc', 'd', 'e', 'f'].   Based on this experience, Guido wisely opined 
that math operators on other concrete collection classes should be restricted 
working with members of their own class.

When abstract base classes were introduced, a seemingly inconsistent decision 
was made.  The Set ABCs allowed the math operators to accept any input iterable 
and did not provide the spelled-out method names (union, intersection, 
difference, etc).

IIRC, there were several reasons for this.  It kept the total number of methods 
to a manageable size (important so as to not unduly burden implementers of 
concrete classes).  Also, having a same type restriction is at odds with some 
of the design goals and use cases for collections ABCs.  Additionally, the code 
for the mixin methods is simpler without the restrictions.

When dict views were implemented, they followed the Set ABCs.  This gave them 
fewer methods than sets but also gave them fewer restrictions.  For the most 
part, these design trade-offs have worked out well in practice.  The existing 
behavior is neither "needless" nor "arbitrary".  It was the result of careful 
consideration by GvR on what works best for most people, most of the time.

--
resolution:  -> rejected
stage: patch review -> resolved
status: open -> closed

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue34497] Remove needless set operator restriction

2018-08-26 Thread Raymond Hettinger


Change by Raymond Hettinger :


--
assignee:  -> rhettinger

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue34497] Remove needless set operator restriction

2018-08-25 Thread Xiang Zhang


Change by Xiang Zhang :


--
nosy: +rhettinger

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue34497] Remove needless set operator restriction

2018-08-25 Thread Dan Snider


Change by Dan Snider :


--
keywords: +patch
pull_requests: +8394
stage:  -> patch review

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue34497] Remove needless set operator restriction

2018-08-25 Thread Dan Snider


New submission from Dan Snider :

I only just now realized that `dict.fromkeys('abc').keys() - 'bc'` returns 
{'a'} instead of raising an error like {*'abc'} - 'bc' would, which is really 
quite handy. 

The __xor__, __and__, and __sub__ methods of dict_keys (and items, assuming no 
there are no unhashable values) work just as set.symmetric_difference, 
set.intersection, and set.difference do, respectively.

>>> a, b, c, d = [*map(dict.keys, map(dict.fromkeys, 'abcd'))]
>>> ((a | 'a') | (b & 'b') | (c ^ 'c')) - d
{'b', 'a'}
>>> a, b, c, d = [*map(dict.items, map(dict.fromkeys, 'abcd'))]
>>> ((a | 'a') | (b & 'b') | (c ^ 'c')) - d
{'c', ('a', None), 'a', ('c', None)}

However, set objects are arbitrarily restricted to taking a set object for the 
second argument on these functions. As for the first example here, there is 
even code specifically there to handle a dictionary as the second argument, but 
it is unreachable when called through the dunder version. 

{, , }
>>> {list, set} | dict.fromkeys((dict, set))
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for |: 'set' and 'dict'

>>> {*'abc'}.difference('cde')
{'b', 'a'}
>>> {*'abc'} - set('cde')
{'b', 'a'}
>>> {*'abc'} - 'cde'
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for -: 'set' and 'str'

>>> {1,2,3}.symmetric_difference(b'\x00')
{0, 1, 2, 3}
>>> {1,2,3} ^ b'\x00'
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for ^: 'set' and 'bytes'

The sources of set_and, set_sub, and set_xor all look like this. All they do is 
add a check that the second argument is a set and then simply call the same 
function their respective non-dunder method uses. They're so identical in fact 
that set_xor actually calls the exact same C function used in the PyMethodDef 
for set.symmetric_update:

static PyObject *
set_xor(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
return set_symmetric_difference(so, other);
}
static PyMethodDef set_methods[] = {
/* ... */
{"symmetric_difference",(PyCFunction)set_symmetric_difference, 
 METH_O, symmetric_difference_doc},
/* ... */
};

All that's needed to fix this is to remove a total of 106 characters from 
setobject.c (4 x " || !PyAnySet_Check(other)").

--
components: Interpreter Core, Library (Lib)
messages: 324060
nosy: bup
priority: normal
severity: normal
status: open
title: Remove needless set operator restriction
type: enhancement
versions: Python 3.7, Python 3.8

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com