subscriptable typing
asmodehn opened this issue · 2 comments
Long story short
Subscripting the FrozenList type currently breaks.
I'm guessing a user would expect the FrozenList works the same as standard List as far as typing is concerned...
Example script:
from dataclasses import dataclass
from frozenlist import FrozenList
@dataclass
class MyClass:
lst: FrozenList[int]
dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)
Expected behaviour
I would expect this to work as with List:
from dataclasses import dataclass
from typing import List
@dataclass
class MyClass:
lst: List[int]
dc = MyClass(lst=list([1,2,3]))
print(dc)
MyClass(lst=[1, 2, 3])
Actual behaviour
Traceback (most recent call last):
File "fltest.py", line 5, in <module>
class MyClass:
File "fltest.py", line 7, in MyClass
lst: FrozenList[int]
TypeError: 'type' object is not subscriptable
Your environment
$ python --version
Python 3.8.5
$ pip list | grep frozenlist
frozenlist 1.1.1
While I understand the details and reason of this behaviour, it caught me off-guard, so I thought I mention it.
Maybe the constructor and the type could be different (like list() / List) ?
Maybe also there could be a close match between List/FrozenList and the Set/FrozenSet with set()/frozenset() usage...
FrozenList
works exactly like list
here. You are confusing list
, the concrete type, with typing.List
, the type annotation generic.
The generic types from the typing
module are not the same thing as the list
or set
concrete types, at least not unless you use Python 3.9, and use from __future__ import annotations
.
You can see the difference in your own example:
class MyClass:
lst: List[int]
# ^^^^ Capital L
dc = MyClass(lst=list([1,2,3]))
# ^^^^ lowercase l
FrozenSet
is not a generic type, it is a concrete class. If you tried to do the same with list
(lst: list[int]
) you'd get the same exception!
And while I understand that the naming convention for the generic typing
versions is a source of confusion (using CamelCasing names where the built-in types use lowercase), FrozenSet
should not be using lowercase naming just to avoid confusion here. With PEP 585 the core language is moving towards the concrete types supporting being used as generic type hints, and the confusion is only temporary.
The correct method is to either use from __future__ import annotations
, or put the type in quotes as a forward reference. Both work.
With from __future__ import annotations
:
from __future__ import annotations
from dataclasses import dataclass
from frozenlist import FrozenList
class MyClass:
lst: FrozenList[int]
dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)
or with a forward reference:
from __future__ import annotations
from dataclasses import dataclass
from frozenlist import FrozenList
class MyClass:
lst: "FrozenList[int]"
dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)
Both forms are recognized and supported by mypy as well.
That said, with Python 3.9 now implementing PEP 585, we could look into adding __class_getitem__ = classmethod(types.GenericAlias)
to the class.