-
Notifications
You must be signed in to change notification settings - Fork 242
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
NamedTuple subclassing NamedTuple #427
Comments
I think this feels natural to you because you can do all such things (extending and merging) with
All three options have some downsides. If we go with the second or third option, then I think it is better to do this fast (to minimise the effect of backwards incompatibility). |
Hm, the functional syntax cannot be used to create a subclass. I think it should not make a difference whether you use functional or class syntax for the base class. But the class syntax is new, so this problem is new too. I suppose the backward compatibility problem you're talking about is that in my example, one could also assume that Or did I miss something? If not, now's the time to choose the more useful semantics. |
Also the fact that currently the signature of constructor of a subclass is not changed. One can have some variables added in a subclass and be surprised when the signature of constructor is changed in a new version of Base = NamedTuple('Base', [('x', int), ('y', int)])
class Derived(Base):
some_bookkeeping: List[int] = [] # mypy requires annotation here
Derived(1, 2) # will fail in a new version |
I think we should allow ourselves complete freedom in changing this -- there's only the slightest mention of it in PEP 484 and none in PEP 526. That makes it a provisional feature (like everything in typing.py and in either of those PEPs). Even for a legitimate class deriving from NamedTuple (e.g. A in my example) there seems to be an ambiguity with initial values -- e.g. class A(NamedTuple):
x: int
y: int = 0
a = A(12) passes in mypy but fails at runtime:
|
It fails at runtime with older versions of |
Thanks, no hurry. I actually lost track of the thought that led me to try that. Probably another random issue or PR in the tracker. :-( |
I would propose the following syntax: class C(NamedTuple, get_fields_from=B):
x: int with the following rules:
Similar syntax can be also supported for functional form. Possible rationale for this (at least what I have heard) is producing relatively many similar named tuples. In addition, in future we may switch to structural subtyping between named tuples (it will be a minor extension over current protocols implementation, we just need to take care of fields order), so that: class Point(NamedTuple):
x: int
y: int
class LabeledPoint(NamedTuple):
x: int
y: int
label: str
def fun(p: Point): ...
fun(LabeledPoint(1, 2, 'test')) # OK consequently, this will also work: class Point(NamedTuple):
x: int
y: int
class LabeledPoint(NamedTuple, get_fields_from=Point):
label: str
def fun(p: Point): ...
fun(LabeledPoint(1, 2, 'test')) # OK @gvanrossum @JukkaL what do you think? |
There is basically no experience in the field with the current class-based NamedTuple (it wasn't even mentioned in PEP 526). For the functional API this could be solved much simpler (without new syntax) by teaching mypy about constant expressions so you can write base_fields = [('x', int), ('y', int)]
Point = NamedTuple('Point', base_fields)
LabeledPoint = NamedTuple('LabeledPoint', base_fields + [('label', str)]) (And similar for collections.namedtuple.) For anything fancier in 3.7 we can refer people to dataclasses. |
It is true the class syntax is mostly unknown to people. Which is a bit strange, people seem to be always excited when they encounter it.
It is also true that functional API already supports this, but the class API is more flexible, it supports default values, docstrings, custom
Yes, we don't need anything fancier for named tuples, but It would be nice if class syntax will be as "expressive" as functional API. Although the class keyword will not allow something more complex like: base_fields = [('x', int), ('y', int)]
label = [('label', str)]
LabeledPoint3D = NamedTuple('LabeledPoint3D', base_fields + [('z', int)] + label) it will probably be enough for most use cases. Finally, implementing class keyword will be very simple. On the other hand, maybe it is OK if both class and functional syntax have some strengths. Functional API can be used for more "dynamic" namedtuple creation, while class syntax is more suitable for "static" cases, so just keeping the status quo in |
It seems a lot of extra implementation complexity to occasionally save people a few lines of copied code... |
OK, so I understand that you vote for status quo :-) |
Following the discussion on mypy tracker, I think this issue can be closed as well. |
Would something like this be useful for mimicking a subclass in some cases? Major caveats obviously being that nothing is actually inherited, so you have to enforce correctness yourself.
If methods defined on one of the classes, would need to copy methods defined on the subclass onto the returned instance. This copies methods, but loses
|
The previous syntax is not supported. See python/typing#427.
The previous syntax is not supported. See python/typing#427.
I naively thought that since I can write
I would also be able to subclass this:
That is accepted syntactically and at runtime but no new
__new__
method is generated so callingB(1, 2, 3)
is flagged as an error (too many arguments) by both mypy and runtime.The text was updated successfully, but these errors were encountered: