python / cpython

The Python programming language
https://www.python.org
Other
63.7k stars 30.52k forks source link

namedtuple comparison ignores types #75422

Closed d323fbfe-57a0-474e-a901-831c35f742cb closed 7 years ago

d323fbfe-57a0-474e-a901-831c35f742cb commented 7 years ago
BPO 31239
Nosy @bitdancer, @Vlad-Shcherbina

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields: ```python assignee = None closed_at = created_at = labels = ['3.7', 'type-bug', 'library'] title = 'namedtuple comparison ignores types' updated_at = user = 'https://github.com/Vlad-Shcherbina' ``` bugs.python.org fields: ```python activity = actor = 'r.david.murray' assignee = 'none' closed = True closed_date = closer = 'r.david.murray' components = ['Library (Lib)'] creation = creator = 'vlad' dependencies = [] files = [] hgrepos = [] issue_num = 31239 keywords = [] message_count = 3.0 messages = ['300562', '300563', '300571'] nosy_count = 2.0 nosy_names = ['r.david.murray', 'vlad'] pr_nums = [] priority = 'normal' resolution = 'rejected' stage = 'resolved' status = 'closed' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue31239' versions = ['Python 2.7', 'Python 3.3', 'Python 3.4', 'Python 3.5', 'Python 3.6', 'Python 3.7'] ```

d323fbfe-57a0-474e-a901-831c35f742cb commented 7 years ago

Toy example:

>>> from collections import namedtuple
>>> Rectangle = namedtuple('Rectangle', 'width height')
>>> Ellipse = namedtuple('Ellipse', 'x_axis y_axis')
>>> Rectangle(width=1, height=2) == Ellipse(x_axis=1, y_axis=2)
True

I understand this happens because namedtuple inherits comparisons and hash from the regular tuple. However, this seems like confusing and dangerous behavior. It is especially annoying to debug when these tuples are used as dict keys (repr() shows one thing and dict item access shows another).

Why would anyone mix named tuples of different types? Here is a use case: typing.NamedTuple together with typing.Union would be a neat way to express algebraic data types.

Haskell's data Shape = Rectangle Int Int | Ellipse Int Int deriving(Eq, Show)

would become Shape = Union[Rectangle, Ellipse]

except that it does not work as expected because of the flawed comparisons.

d323fbfe-57a0-474e-a901-831c35f742cb commented 7 years ago

While we are at it, namedtuple inherits other operations that make no sense for fixed-length tuples:

>>> Rectangle(width=1, height=2) * 3
(1, 2, 1, 2, 1, 2)
>>> Rectangle(width=1, height=2) + Ellipse(x_axis=3, y_axis=4)
(1, 2, 3, 4)

But those are not so problematic because they are less likely to occur in practice.

bitdancer commented 7 years ago

This is by design: namedtuples are tuples in which you can access the elements by name. If you have a tuple with the same elements, but no name access, they should compare equal, because they are fundamentally tuples. The names are just a convenience.

Even if this were not so, it is not something that could be changed, for backward compatibility reasons.

If you want a different data types, make them :)