
This operator will allow to send arguments to function only by certain values. Example: def some_function(a > (1, 2,3), b): # Some code some_function(1, 2) some_function(4, 2) # Error "Value '4' is not allowed for argument 'a'"

Wouldn't it make more sense to write `def some_function(a in (1, 2,3), b):`, meaning `a in (1, 2,3)` must be true? Similarly you could have `def some_function(a > 0):` to mean `a > 0` must be true. On Sat, Jun 13, 2020 at 2:55 PM artem6191 <artem129871@gmail.com> wrote:

On Sat, 13 Jun 2020 at 13:54, artem6191 <artem129871@gmail.com> wrote:
def some_function(a, b): if not a in (1,2,3): raise ValueError(f"Value {a} os not allowed for argument 'a'") # Some code How is your proposal better than this? It needs to add sufficient extra value to justify the cost of adding new syntax, teaching developers (and IDEs) what the new syntax means, etc. Personally, I find the version I wrote, that works in existing versions of Python, much more readable than your version, so you;d have a hard time persuading me that the new syntax is worth it :-) Paul

Paul Moore writes:
I think it's an interesting idea, although I'm with Alex on the color of the bikeshed. Using Alex's 'in' and sets instead of sequences, consider def food() in {2}: return 2 def bar(n in {1, 2, 3}): pass def main(): bar(food()) # Paul doesn't think it tastes so great. :-) That could be checked by MyPy, without global dataflow analysis. Maybe def foo() -> {1, 2, 3}: return 2 is more readable, and probably just as implementable. Steve

On Sun, Jun 14, 2020 at 5:18 PM Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
This definitely looks like a job for type hints, not new syntax. What you're asking for here is that something be one of a specific set of options, and that's really sounding like an enumeration to me. Consider: from enum import IntEnum class Spam(IntEnum): foo = 1 bar = 2 quux = 3 def some_function(a: IntEnum, b): print("Got a:", a) some_function(1, 2) some_function(4, 2) Unfortunately, MyPy (tested with version 0.78) doesn't accept integers as valid parameters when IntEnum is expected, so this can't be done seamlessly. But if you're doing something like this, you probably should be working with the enum for other reasons anyway, so your code would be more like this: some_function(Spam.foo, 2) some_function(..., 2) and right there you can see that there's no way to get a Spam with a value of 4. If you actually need to do it dynamically, you can do a runtime cast: some_function(Spam(1), 2) some_function(Spam(4), 2) And then you get the runtime ValueError like you were wanting. ChrisA

On Sun, 14 Jun 2020 at 08:16, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
That could be checked by MyPy, without global dataflow analysis.
"MyPy can check this" is a good point that I hadn't considered (I've not used mypy much myself, yet). As Chris A says, I'd be inclined to see how far we can get with (extended) type hints before going for new syntax, though.
def foo() -> {1, 2, 3}: return 2
That is, of course, valid syntax right now. I wonder what a type checker could do with it? Similarly,
Paul

On Sun, Jun 14, 2020 at 8:41 AM Paul Moore <p.f.moore@gmail.com> wrote:
Well, the thing is that there is no way to know at compile time what Value is getting passed in -- this is really more a way to catch a ValueError than TypeError, so can't be done with static type checking. Unless you do, as ChrisA suggests, crate a Type (and Enum) that you can then check for. But while I like the idea of Enums, particularly for, say multi-valued flags, They require an extra name and extra typingthat I find annoying (annoying enough that I haven't used one yet in my code. That is, I prefer, so use Chris A's example: some_function(1, 2) ... to: from some_module import Spam some_function(Spam(1), 2) ... That being said, if you want your API to be "safe" and type-chackable, then Enum is the way to go. As for the OP, who was asking for a run-time check, if: def fun(a, b): if a not in {1,2,3}: raise ValueError("a has to be one of these values: 1, 2, 3") is too verbose, you can always write a utility function (or a callable class or closure that stores the options) to make it a simple one-liner: def fun(a, b): value_check(a, options= {1, 2, 3}) -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Such preconditions (checks of input parameters beyond their type at compile-tine) are a feature of DbC: Design by Contract. https://en.wikipedia.org/wiki/Design_by_contract Python is listed under "Languages with third-party support" :
Second, icontract allows inheritance of the contracts and supports weakining of the preconditions as well as strengthening of the
https://www.python.org/dev/peps/pep-0316/ (2003) "Contracts in python -- a report & next steps" (2018) https://mail.python.org/archives/list/python-ideas@python.org/thread/3TDEJI4... ... https://github.com/Parquery/icontract : postconditions and invariants. Notably, weakining and strengthening of the contracts is a feature indispensable for modeling many non-trivial class hierarchies. Please see Section Inheritance. To the best of our knowledge, there is currently no other Python library that supports inheritance of the contracts in a correct way. On Sun, Jun 14, 2020, 12:29 PM Christopher Barker <pythonchb@gmail.com> wrote:

icontract supports preconditions with decorators (that are compatible with type information stored in annotations : ``` @icontract.require(lambda x: x > 3) def some_func(x: int, y: int = 5)->None: pass ``` PyContracts supports a few different syntaxes for specifying preconditions: decorator, annotations, docstrings. Unfortunately, AFAIU, the annotations syntax is not compatible with MyPy/typeshed capitalizations: http://andreacensi.github.io/contracts/ ``` Specifying contracts: Contracts can be specified in three ways: Using the ``@contract`` decorator: @contract(a='int,>0', b='list[N],N>0', returns='list[N]') def my_function(a, b): ... Using annotations (for Python 3): @contract def my_function(a : 'int,>0', b : 'list[N],N>0') -> 'list[N]': # Requires b to be a nonempty list, and the return # value to have the same length. ... Using docstrings, with the :type: and :rtype: tags: @contract def my_function(a, b): """ Function description. :type a: int,>0 :type b: list[N],N>0 :rtype: list[N] """ ... Deployment: In production, all checks can be disabled using the function contracts.disable_all(), so the performance hit is 0. ``` PEP-316 proposed a docstring syntax which also predates py3k annotations. On Sun, Jun 14, 2020, 5:47 PM Wes Turner <wes.turner@gmail.com> wrote:

Wouldn't it make more sense to write `def some_function(a in (1, 2,3), b):`, meaning `a in (1, 2,3)` must be true? Similarly you could have `def some_function(a > 0):` to mean `a > 0` must be true. On Sat, Jun 13, 2020 at 2:55 PM artem6191 <artem129871@gmail.com> wrote:

On Sat, 13 Jun 2020 at 13:54, artem6191 <artem129871@gmail.com> wrote:
def some_function(a, b): if not a in (1,2,3): raise ValueError(f"Value {a} os not allowed for argument 'a'") # Some code How is your proposal better than this? It needs to add sufficient extra value to justify the cost of adding new syntax, teaching developers (and IDEs) what the new syntax means, etc. Personally, I find the version I wrote, that works in existing versions of Python, much more readable than your version, so you;d have a hard time persuading me that the new syntax is worth it :-) Paul

Paul Moore writes:
I think it's an interesting idea, although I'm with Alex on the color of the bikeshed. Using Alex's 'in' and sets instead of sequences, consider def food() in {2}: return 2 def bar(n in {1, 2, 3}): pass def main(): bar(food()) # Paul doesn't think it tastes so great. :-) That could be checked by MyPy, without global dataflow analysis. Maybe def foo() -> {1, 2, 3}: return 2 is more readable, and probably just as implementable. Steve

On Sun, Jun 14, 2020 at 5:18 PM Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
This definitely looks like a job for type hints, not new syntax. What you're asking for here is that something be one of a specific set of options, and that's really sounding like an enumeration to me. Consider: from enum import IntEnum class Spam(IntEnum): foo = 1 bar = 2 quux = 3 def some_function(a: IntEnum, b): print("Got a:", a) some_function(1, 2) some_function(4, 2) Unfortunately, MyPy (tested with version 0.78) doesn't accept integers as valid parameters when IntEnum is expected, so this can't be done seamlessly. But if you're doing something like this, you probably should be working with the enum for other reasons anyway, so your code would be more like this: some_function(Spam.foo, 2) some_function(..., 2) and right there you can see that there's no way to get a Spam with a value of 4. If you actually need to do it dynamically, you can do a runtime cast: some_function(Spam(1), 2) some_function(Spam(4), 2) And then you get the runtime ValueError like you were wanting. ChrisA

On Sun, 14 Jun 2020 at 08:16, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
That could be checked by MyPy, without global dataflow analysis.
"MyPy can check this" is a good point that I hadn't considered (I've not used mypy much myself, yet). As Chris A says, I'd be inclined to see how far we can get with (extended) type hints before going for new syntax, though.
def foo() -> {1, 2, 3}: return 2
That is, of course, valid syntax right now. I wonder what a type checker could do with it? Similarly,
Paul

On Sun, Jun 14, 2020 at 8:41 AM Paul Moore <p.f.moore@gmail.com> wrote:
Well, the thing is that there is no way to know at compile time what Value is getting passed in -- this is really more a way to catch a ValueError than TypeError, so can't be done with static type checking. Unless you do, as ChrisA suggests, crate a Type (and Enum) that you can then check for. But while I like the idea of Enums, particularly for, say multi-valued flags, They require an extra name and extra typingthat I find annoying (annoying enough that I haven't used one yet in my code. That is, I prefer, so use Chris A's example: some_function(1, 2) ... to: from some_module import Spam some_function(Spam(1), 2) ... That being said, if you want your API to be "safe" and type-chackable, then Enum is the way to go. As for the OP, who was asking for a run-time check, if: def fun(a, b): if a not in {1,2,3}: raise ValueError("a has to be one of these values: 1, 2, 3") is too verbose, you can always write a utility function (or a callable class or closure that stores the options) to make it a simple one-liner: def fun(a, b): value_check(a, options= {1, 2, 3}) -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Such preconditions (checks of input parameters beyond their type at compile-tine) are a feature of DbC: Design by Contract. https://en.wikipedia.org/wiki/Design_by_contract Python is listed under "Languages with third-party support" :
Second, icontract allows inheritance of the contracts and supports weakining of the preconditions as well as strengthening of the
https://www.python.org/dev/peps/pep-0316/ (2003) "Contracts in python -- a report & next steps" (2018) https://mail.python.org/archives/list/python-ideas@python.org/thread/3TDEJI4... ... https://github.com/Parquery/icontract : postconditions and invariants. Notably, weakining and strengthening of the contracts is a feature indispensable for modeling many non-trivial class hierarchies. Please see Section Inheritance. To the best of our knowledge, there is currently no other Python library that supports inheritance of the contracts in a correct way. On Sun, Jun 14, 2020, 12:29 PM Christopher Barker <pythonchb@gmail.com> wrote:

icontract supports preconditions with decorators (that are compatible with type information stored in annotations : ``` @icontract.require(lambda x: x > 3) def some_func(x: int, y: int = 5)->None: pass ``` PyContracts supports a few different syntaxes for specifying preconditions: decorator, annotations, docstrings. Unfortunately, AFAIU, the annotations syntax is not compatible with MyPy/typeshed capitalizations: http://andreacensi.github.io/contracts/ ``` Specifying contracts: Contracts can be specified in three ways: Using the ``@contract`` decorator: @contract(a='int,>0', b='list[N],N>0', returns='list[N]') def my_function(a, b): ... Using annotations (for Python 3): @contract def my_function(a : 'int,>0', b : 'list[N],N>0') -> 'list[N]': # Requires b to be a nonempty list, and the return # value to have the same length. ... Using docstrings, with the :type: and :rtype: tags: @contract def my_function(a, b): """ Function description. :type a: int,>0 :type b: list[N],N>0 :rtype: list[N] """ ... Deployment: In production, all checks can be disabled using the function contracts.disable_all(), so the performance hit is 0. ``` PEP-316 proposed a docstring syntax which also predates py3k annotations. On Sun, Jun 14, 2020, 5:47 PM Wes Turner <wes.turner@gmail.com> wrote:
participants (8)
-
Alex Hall
-
artem6191
-
Chris Angelico
-
Christopher Barker
-
Greg Ewing
-
Paul Moore
-
Stephen J. Turnbull
-
Wes Turner