Literal type alternative syntax
Hello all, Why can’t we use the literals directly as types? For example, x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John" Become …. x: 1 | 2 | 3 = 3 name: “John” | None = “John" def open(file: Path | str, mode: “w” | “a” = “w”): … Best Regards, Abdulla
I was trying to write a click option argument with type=click.Choice((“North”, “South”, “West”, “East”)) and I also like to annotate the function itself for documentation (even though I call it without passing arguments). Something like the the following.. @click.command() @click.option(“--direction”, type=click.Choice((“North”, “South”, “West”, “East”)), default=“North”) def main(direction: “North” | “South” | “West” | “East” = “North”) -> None: print(direction)
On 5 Feb 2022, at 11:21 PM, Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
Hello all,
Why can’t we use the literals directly as types? For example,
x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John"
Become ….
x: 1 | 2 | 3 = 3 name: “John” | None = “John"
def open(file: Path | str, mode: “w” | “a” = “w”): …
Best Regards,
Abdulla
To do this EVERY object that you might list as alternatives would have to support `__or__()` and `__ror__()`. Moreover, for many types, `|` is defined with conflicting meaning. E.g. `3 | 5 | 11 == 15`. But of course 3, 5, 11 aren't the unique collection of numbers that bitwise or to 15. At runtime, we'd have no idea what the alternatives so expressed were. On Sat, Feb 5, 2022, 2:24 PM Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
Hello all,
Why can’t we use the literals directly as types? For example,
x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John"
Become ….
x: 1 | 2 | 3 = 3 name: “John” | None = “John"
def open(file: Path | str, mode: “w” | “a” = “w”): …
Best Regards,
Abdulla _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ... Code of Conduct: http://python.org/psf/codeofconduct/
The “|” operator is another way of doing Union[Literal[“John], None] which is already supported in the new versions of Python. Correct me if I am wrong please. Maybe the types already use __or__ and __ror__ to sugar coat Union? Maybe if integers or strings or any other literal occurs after “:”, we could treat them slightly differently than what their actual __or__ and __ror__ would normally do?
On 5 Feb 2022, at 11:42 PM, David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
To do this EVERY object that you might list as alternatives would have to support `__or__()` and `__ror__()`.
Moreover, for many types, `|` is defined with conflicting meaning. E.g. `3 | 5 | 11 == 15`. But of course 3, 5, 11 aren't the unique collection of numbers that bitwise or to 15. At runtime, we'd have no idea what the alternatives so expressed were.
On Sat, Feb 5, 2022, 2:24 PM Abdulla Al Kathiri <alkathiri.abdulla@gmail.com <mailto:alkathiri.abdulla@gmail.com>> wrote: Hello all,
Why can’t we use the literals directly as types? For example,
x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John"
Become ….
x: 1 | 2 | 3 = 3 name: “John” | None = “John"
def open(file: Path | str, mode: “w” | “a” = “w”): …
Best Regards,
Abdulla _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ... <https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ...> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
On Sun, Feb 06, 2022 at 02:13:59AM +0400, Abdulla Al Kathiri wrote:
The “|” operator is another way of doing Union[Literal[“John], None] which is already supported in the new versions of Python. Correct me if I am wrong please. Maybe the types already use __or__ and __ror__ to sugar coat Union?
Type objects are objects, and their behaviour is controlled by their metaclass, just as instances are objects and their behaviour is controlled by their class. That means we can do this:
T = int | float type(T) <class 'types.UnionType'>
What we can't do is have instances behave differently according to context. There is no way to have the `|` operator behave differently in different contexts: value = 1 | 8 # return 9 T = 1 | 8 # return Literal[1, 8] L = [0, 1 | 8, 2] # what should | do inside a list? arr[:1 | 8] # what about after a colon in a slice?
Maybe if integers or strings or any other literal occurs after “:”, we could treat them slightly differently than what their actual __or__ and __ror__ would normally do?
Fortunately the Steering Council has said that syntax in annotations and syntax outside of annotations will remain the same. So any expression that you can write in an annotation will have the same meaning if you take it out and assign it to a named type. In other words, we can always refactor type hints by moving them out of the annotation: x: expression = value T = expression x: T = value And that is excellent. -- Steve
Thank you Steven for the explanation. I like that the behaviors in annotation or outside it are the same. It makes sense and it’s easier to teach and learn. Maybe it’s not a good idea after all even though visually it makes sense to me as a normal python user. You guys see things differently because you are familiar with the CPython internals.
On 6 Feb 2022, at 2:32 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Feb 06, 2022 at 02:13:59AM +0400, Abdulla Al Kathiri wrote:
The “|” operator is another way of doing Union[Literal[“John], None] which is already supported in the new versions of Python. Correct me if I am wrong please. Maybe the types already use __or__ and __ror__ to sugar coat Union?
Type objects are objects, and their behaviour is controlled by their metaclass, just as instances are objects and their behaviour is controlled by their class. That means we can do this:
T = int | float type(T) <class 'types.UnionType'>
What we can't do is have instances behave differently according to context. There is no way to have the `|` operator behave differently in different contexts:
value = 1 | 8 # return 9 T = 1 | 8 # return Literal[1, 8] L = [0, 1 | 8, 2] # what should | do inside a list? arr[:1 | 8] # what about after a colon in a slice?
Maybe if integers or strings or any other literal occurs after “:”, we could treat them slightly differently than what their actual __or__ and __ror__ would normally do?
Fortunately the Steering Council has said that syntax in annotations and syntax outside of annotations will remain the same. So any expression that you can write in an annotation will have the same meaning if you take it out and assign it to a named type.
In other words, we can always refactor type hints by moving them out of the annotation:
x: expression = value
T = expression x: T = value
And that is excellent.
-- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/HF5FNO... Code of Conduct: http://python.org/psf/codeofconduct/
I like the idea, quite a bit. Unfortunately, string annotations are currently reserved for type annotations (particularly for forward references), so`x: str` and `x: "str"` are currently equivalent. This would rule-out using string literals in the manner you suggest. On Sat, 2022-02-05 at 23:21 +0400, Abdulla Al Kathiri wrote:
Hello all,
Why can’t we use the literals directly as types? For example,
x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John"
Become ….
x: 1 | 2 | 3 = 3 name: “John” | None = “John"
def open(file: Path | str, mode: “w” | “a” = “w”): …
Best Regards,
Abdulla _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ... Code of Conduct: http://python.org/psf/codeofconduct/
I thought since we now can forward reference types in the new parser, there is no need to stringify forward types but as you and the rest mentioned this might introduce compatibility issues with the older versions.
On 6 Feb 2022, at 1:18 AM, Paul Bryan <pbryan@anode.ca> wrote:
I like the idea, quite a bit. Unfortunately, string annotations are currently reserved for type annotations (particularly for forward references), so`x: str` and `x: "str"` are currently equivalent. This would rule-out using string literals in the manner you suggest.
On Sat, 2022-02-05 at 23:21 +0400, Abdulla Al Kathiri wrote:
Hello all,
Why can’t we use the literals directly as types? For example,
x: Literal[1, 2, 3] = 3 name: Literal[“John”] | None = “John"
Become ….
x: 1 | 2 | 3 = 3 name: “John” | None = “John"
def open(file: Path | str, mode: “w” | “a” = “w”): …
Best Regards,
Abdulla _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ... <https://mail.python.org/archives/list/python-ideas@python.org/message/IJ74AQ...> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
On Sat, Feb 05, 2022 at 11:21:48PM +0400, Abdulla Al Kathiri wrote:
Hello all,
Why can’t we use the literals directly as types? For example,
Before suggesting a new feature, you should try it to see what it currently does.
x: 1 | 2 | 3 = 4 __annotations__ {'x': 3}
`1 | 2 | 3` already has a meaning as the bitwise-or of integers 1, 2, 3 and so it evaluates to `3`. So backwards compatibility rules out changing the | operator meaning. Other types don't support bitwise-or, and we don't want them to. E.g. bitwise-or of a string and None, or two strings, or a string and an int, should remain errors and not suddenly return a type object. The bottom line here is that we cannot use literals as types because they aren't types, they are values, and the semantics of operations on types and operations on values are different. -- Steve
Why do we have Literal type in the typing module? I sure can use them as types to indicate to the user "these are your only options”?
On 6 Feb 2022, at 2:18 AM, Steven D'Aprano <steve@pearwood.info> wrote:
he bottom line here is that we cannot use literals as types because they aren't types, they are values, and the semantics of operations on types and operations on values are different.
participants (4)
-
Abdulla Al Kathiri
-
David Mertz, Ph.D.
-
Paul Bryan
-
Steven D'Aprano