Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _repr to named tuples #129343

Closed
bswck opened this issue Jan 27, 2025 · 5 comments
Closed

Add _repr to named tuples #129343

bswck opened this issue Jan 27, 2025 · 5 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@bswck
Copy link
Contributor

bswck commented Jan 27, 2025

Feature or enhancement

Proposal:

This issue aims to propose a new feature on top of fixing gh-85795.

gh-85795 stems from the necessity of using super() inside methods of typing.NamedTuple subclasses.
The documentation does not explicitly state it as unsupported, nor does it disrecommend the use of it.
Consequently, that behavior should be supported and the converse is considered a bug.

As in the StackOverflow question here, the developer tried their own implementation of __repr__ in the subclass.
And that's how I encountered the issue, too! I think it is very useful to be able to override the default NamedTuple representation and add additional info on top of it/tweak it somehow. For that reason, one would intuitively use super().__repr__(), since we inherit from NamedTuple.

However, that is impossible with the current model of behavior in typed named tuples.
Typed named tuples are constructed from the existing implementation of tuple subclass factory collections.namedtuple that does not insert anything other than tuple above the final class in the MRO. Inheritance from NamedTuple is only possible due to __mro_entries__ hacks on the typing.NamedTuple function. To reference the default representation method of named tuples via super().__repr__(), a middleman class representing the default implementations of named tuple methods between the underlying class and tuple would be necessary. That is currently most likely impossible for backward compatibility reasons, something I learned from #126706 (comment), and does not appeal to me in terms of cost/benefit ratio.

Hence, since __repr__ is the only default method of NamedTuple useful to be referenced in typing.NamedTuple subclasses, and all other come from tuple or object, I propose adding _repr to the named tuple interface. The default __repr__ implementation would be a direct wrapper of it. In typing.NamedTuple, _repr could not be overridden, but __repr__ could, to enable extendability with an optional reuse of _repr.

In NamedTuple subclasses all super().__repr__() calls would resolve to tuple.__repr__().

This introduces no breaking changes, is additive only and retains the stable behavior of default __repr__ of named tuples.
With proper documentation, it can hint advanced named tuple users in how to create their own implementations of __repr__ reusing the existing __repr__ default implementation.

Example

class Import(NamedTuple):
    target: str

    def __repr__(self) -> str:
        # super().__repr__() -> ('target',)
        # self._repr() -> Import(target='target')
        return f'<Token {self._repr()}>'  # <Token Import(target='target')>

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

@serhiy-storchaka
Copy link
Member

There is a number of workarounds:

class Import(NamedTuple):
    target: str

class Import(Import):
    def __repr__(self) -> str:
        return f'<Token {self.__repr__()}>'
class Import(NamedTuple):
    target: str

old_repr = Import.__repr__
def new_repr(self: Import) -> str:
        return f'<Token {old_repr(self)}>'
Import.__repr__ = old_repr

Setting _repr to default __repr__ will break named tuples with the _repr attribute. It will also clutter the help output.

This looks like a pretty niche problem that can be solved by a workaround.

@bswck
Copy link
Contributor Author

bswck commented Jan 27, 2025

I'm pretty sure the first workaround will end up in a recursion limit excess.

Setting _repr to default __repr__ will break named tuples with the _repr attribute.

There can never be a named tuple with a user-defined _repr attribute, so no named tuples would break.

This change is not breaking and offers a clean solution to reusing the default implementation of __repr__ in named tuples, as an alternative to a MRO-available default __repr__ that we can't offer.

I believe this is a fairly small addition and can be helpful once necessary.

@bswck
Copy link
Contributor Author

bswck commented Jan 27, 2025

It will also clutter the help output.

Oh, that's correct. Haven't taken that into account. Will look into this later, thanks!

@bswck
Copy link
Contributor Author

bswck commented Jan 28, 2025

After some consideration, I also think we don't have to do this. We can, and I like it because I brought this idea, but it can also be confusing:

  • Should I use _repr or __repr__ from other methods of NamedTuples, if I hadn't overridden __repr__? Isn't that against There should be one-- and preferably only one --obvious way to do it?
  • Is the addition of _repr in any way useful for traditional namedtuples, or only for typing.NamedTuples?

Instead, we can go with https://discuss.python.org/t/new-function-repr-args-in-pprint-or-reprlib/65193 which maybe does not offer as much reusability as the feature suggested in this issue, but it makes writing NamedTuple-like representations still fairly easy.

@picnixz picnixz added the stdlib Python modules in the Lib dir label Jan 31, 2025
@rhettinger
Copy link
Contributor

ISTM that is too small of a problem to warrant a potentially confusing API change.

[Serhiy]

This looks like a pretty niche problem that can be solved by a workaround.

[Bartosz]

I also think we don't have to do this

@rhettinger rhettinger closed this as not planned Won't fix, can't repro, duplicate, stale Feb 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants