Skip to content

namedtuple: add defaults to the Python 3.6 syntax #338

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

Merged
merged 2 commits into from
Jan 18, 2017

Conversation

JelleZijlstra
Copy link
Member

This lets you write:

class Employee(NamedTuple):
    name: str
    id: int
    language: str = 'python'

as suggested by @gvanrossum on python-ideas.

Should I also add this to any relevant documentation? I imagine mypy will also need changes to support this syntax.

This lets you write:

    class Employee(NamedTuple):
        name: str
        id: int
        language: str = 'python'

as suggested by Guido on python-ideas.
@ruxi
Copy link

ruxi commented Dec 2, 2016

How does it work? Would this be valid:

Employee = typing.NamedTuple("Employee", [("name", str), ("id", int)] )

def foo(bar:Employee):
    pass  

@JelleZijlstra
Copy link
Member Author

@ruxi Yes, that syntax already works in Python 3.5. Python 3.6's variable annotation syntax makes it possible to instead create typed NamedTuples using subclassing, like:

class Employee(typing.NamedTuple):
    name: str
    id: int

This pull request extends that syntax to use the values of variables assigned in the class body as default values.

@gvanrossum
Copy link
Member

gvanrossum commented Dec 2, 2016 via email

@gvanrossum
Copy link
Member

(But apart from my desire to delay merging, it's beautiful! Now of course we need support for mypy too...)

@@ -1916,7 +1916,21 @@ def __new__(cls, typename, bases, ns):
raise TypeError("Class syntax for NamedTuple is only supported"
" in Python 3.6+")
types = ns.get('__annotations__', {})
return _make_nmtuple(typename, types.items())
nm_tpl = _make_nmtuple(typename, types.items())
saw_default = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think saw_default is redundant. You could use elif defaults: or elif defaults_dict:, instead of elif saw_default: below.

defaults.append(default_value)
defaults_dict[field_name] = default_value
elif saw_default:
raise TypeError('Non-default namedtuple field cannot follow default field')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be sometimes helpful to mention which fields are conflicting, for example f'Non-default namedtuple field {field_name} cannot follow default fields {list(defaults_dict.keys())}'.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, but I guess we can't use an f-string because this code has to be syntactically valid in older Python 3.

defaults_dict[field_name] = default_value
elif saw_default:
raise TypeError('Non-default namedtuple field cannot follow default field')
nm_tpl.__new__.__defaults__ = tuple(defaults)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably you could use only defaults_dict and then write here = tuple(defaults_dict.values()), since dict is ordered in Python 3.6.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it, dict ordering is an implementation detail of CPython 3.6 and a conforming Python 3.6 implementation could choose to not implement ordering, so this code shouldn't rely on dict ordering (except for class namespaces and **kwargs because of PEP 520 and PEP 468).

@ilevkivskyi
Copy link
Member

@JelleZijlstra Thank you!
I just added few optional comments.

@gvanrossum
Copy link
Member

I'm okay with this idea. @ilevkivskyi Is it good from your POV? We also need a mypy implementation, and docs for Python.

@ilevkivskyi
Copy link
Member

I like the idea. The mypy implementation should not be difficult, but it is not high on my priority list (I will make PRs for several other mypy issues soon).

@ilevkivskyi
Copy link
Member

Although I would prefer this PR to be merged without waiting for mypy support, to avoid possible conflicts with #352.

@JelleZijlstra
Copy link
Member Author

Thanks! I'll work on implementing this in mypy as soon as I finish implementing async generators and will add a doc patch to CPython once this is merged.

@gvanrossum gvanrossum merged commit fed24d2 into python:master Jan 18, 2017
@gvanrossum
Copy link
Member

OK, I'll leave it to @ilevkivskyi to make sure all the parts land.

@gvanrossum
Copy link
Member

Also merged this to CPython.

(Note: we need a Misc/NEWS item for the various typing.py changes.)

@JelleZijlstra
Copy link
Member Author

Thanks! Created http://bugs.python.org/issue29310 to track documenting this in CPython.

@JelleZijlstra JelleZijlstra deleted the namedtupledefault branch January 18, 2017 16:15
JukkaL pushed a commit to python/mypy that referenced this pull request Mar 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants