[Python-ideas] Re: Variadic generics PEP draft
On 10/7/20 2:11 PM, Greg Ewing wrote: Generally looks good to me, although the name ListVariadic seems a bit jargony (and worse, appears to be jargon imported from another language). I think something like TypeListVar would be clearer. Agreed. (I was just about to write my own comment saying the same thing.) I was thinking "TypeVarList" originally, but I also like "TypeListVar". -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/G2QFW2DOUWMIUK64VQ55DRX2UKP6YUML/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: async types?
On 11/25/20 2:19 AM, Ben Avrahami wrote: All too often I see the following pattern in asyncio 3rd-party libs, either in their own source code or in the inusage: ``` inst = SomeClass() await inst.initialize() ``` [...] allowing simply for `instance = await SomeClass()`. In classes of `AsyncType`, the __new__ and __init__ methods must be async methods (returning coroutines). I like the idea in principle but I don't currently use any async libraries so my opinion may not count much here. ;) -- David Foster | Seattle, WA, USA ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/UQFOOGBQJOF7EES46NWEO5WV4QZOHREM/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: Alternative to Callable Annotation
On 11/28/20 9:31 AM, Guido van Rossum wrote: > I'm not so keen on the square brackets you propose. How about we write > Callable[[x, y], z] as (x, y) -> z instead? I also like the "(x, y) -> z" syntax a bit better than "[x, y -> z]". (+0.5) On 11/28/20 3:46 PM, Guido van Rossum wrote: > You could parenthesize the return value if you think it's not clear. > (IIRC some functional languages like Haskell use the ... -> ... -> ... > notation to indicate functions of multiple arguments, so it's probably > good to avoid looking like that -- even if it'sconsistent it would > mislead people into thinking that Python uses a similar idiom. To avoid the "... -> ... -> ... " ambiguity, I'd suggest that kind of chaining be recognized and treated as a syntax error. If describing a Callable that itself returned another Callable, it would then be necessary to parenthesize the return value: A function that has two Callables as arguments and a Callable return type would then look like: f: ((int, str) -> int, (…) -> str) -> ((str) -> int) (It's worth noting that I don't think either the original "Callable[[x, y], z]" syntax OR the proposed "(x, y) -> z" syntax is particularly readable when lots of nesting is involved, but I suspect that's just because the type is just complicated. :) ) On 11/28/20 10:02 AM, Abdulla Al Kathiri wrote: > Regardless of how it’s done, the less we depend on the typing module for > annotations, the better I think because it encourages python users to > annotate their functions. Indeed. (+1) -- David Foster | Seattle, WA, USA ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/EHZWQXXO4ANRXF6K7NHWSD3HYD5V7VWM/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: Matching TypedDicts and other values in JSON
On 11/22/20 10:15 AM, Guido van Rossum wrote: > - We intentionally don't support things like `isinstance(x, List[str])` > because that would require checking all the items with `isinstance(item, > str)`, and that seems a speed trap. Reverting this decision would be > hard work. Aye. I imagine many folks would expect isinstance() to be "fast" and altering it to do recursive checks on its argument would lose its current O(1) time. I imagine I *could* implement my own kind of isinstance() that would work on TypedDict values that would still be recognized by typecheckers. Say I write an implementation for the following method: from typing import Optional, Type, TypeVar, TypedDict TD = TypeVar(bound=TypedDict) def try_cast(type: Type[TD], value: object) -> Optional[TD]: """Returns `value` if it can be parsed as a `type`, otherwise None.""" raise NotImplementedError() Then I could use that method in a similar way as my earlier example to parse a value very concisely: if (shape := try_cast(Shape, request.json)) is not None: draw_shape(shape) # is narrowed to Shape else: return HTTPResponse(status=400) # Bad Request Going further, I could extend try_cast() to accept any (non-None) JSON-like value as the top-level object (not just TypedDicts): from typing import Dict, List, Optional, Type, TypeVar, TypedDict, Union TD = TypeVar('TD', bound=TypedDict) JsonValue = Union[ TD, Dict[str, 'OptionalJV'], List['OptionalJV'], Dict, # heterogeneous Dict List, # heterogeneous List float, int, # because json.loads may return an int when parsing a number str, bool, ] JV = TypeVar('JV', bound=JsonValue) OptionalJV = TypeVar('OptionalJV', bound=Union[JsonValue, None]) def try_cast(type: Type[JV], value: object) -> Optional[JV]: """Returns `value` if it can be parsed as a `type`, otherwise None.""" raise NotImplementedError() Now, I'm not sure if mypy can handle that kind of recursive TypedDict definition :), but it *will* work at runtime. I'll see about implementing a function like try_cast() as a separate package. This should be fun. :) -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/QSWAXFCOVRDCJZPIOTI336MJRSU7L45G/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Matching TypedDicts and other values in JSON
ng a PEP to extend isinstance() to cover at least the above cases, would that be welcome? -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/Y2EJEZXYRKCXH7SP5MDF3PT2TYIB7SJS/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: PEP 634-636: Mapping patterns and extra keys
On 11/19/20 10:08 PM, David Foster wrote: I've completed my survey of how other languages use pattern matching to match Mapping-like and dict-like types, especially focusing on whether they ignore (픸) or disallow (픹) extra keys by default. [...] To close the loop on this thread: * Based on (1) the explanation from PEP 622 and Guido RE that "mappings [...] have natural structural sub-typing behavior, i.e., passing a dictionary with extra keys somewhere will likely just work" and (2) the survey results, I'm now personally fine (+0) with keys being ignored by default when matching against mappings. * I do think it might be illustrative to copy the following explanatory sentences from the "Mapping Patterns" section of the older PEP 622 to the same section of PEP 635 (Structural Pattern Matching: Motivation and Rationale): > Extra keys in the subject are ignored even if **rest is not present. This is different from sequence pattern, where extra items will cause a match to fail. But mappings are actually different from sequences: they have natural structural sub-typing behavior, i.e., passing a dictionary with extra keys somewhere will likely just work. Specifically the above might replace the following sentence in PEP 635, which doesn't really give a rationale: > Moreover, the mapping pattern does not check for the presence of additional keys. * I still have an interest in strictly matching dictionaries that are destined to become TypedDicts, but I can take that conversation to a different thread. -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/K5BUKMOCIBUQIITBUQNX6KEHOB4WB5BC/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: PEP 634-636: Mapping patterns and extra keys
On 11/15/20 12:11 AM, Steven D'Aprano wrote: It would be good if the PEP gave a survey of the practical experience of other languages with pattern matching: - are there languages which require an exact match, with no left over keys? what issues, if any, do users have with that choice? - which languages ignore extra keys? do users of those languages consider this feature a bug, a wart, or a feature? If useful I can volunteer to conduct such a survey. I expect to have some cycles this coming weekend. I've completed my survey of how other languages use pattern matching to match Mapping-like and dict-like types, especially focusing on whether they ignore (픸) or disallow (픹) extra keys by default. In general: 1. Dynamically-typed languages that support pattern matching have mostly experimental (†) pattern-matching implementations that all ignore extra keys by default (픸) rather than disallow them (픹). Some popular dynamically-typed languages don't support pattern-matching at all (✖️). - Erlang/Elixir are the only languages in this category with a non-experimental implementation. - Ruby and JavaScript both have experimental implementations only. 2. Statically-typed languages frequently provide pattern matching capabilities but they can't be used for Mapping-like or dict-like types (路♀️). Frequently the pattern matching provided *can* however be used on association lists (ordered lists of key-value tuples) or dataclass-like instances. So it certainly seems at least the popular vote points toward ignoring extra keys by default rather than disallowing them by default, which is consistent with the current wording of PEP 634-636. Below is the full survey summary: Key: * Pattern Match Approach 픸 = Pattern-matches mappings/dicts, ignoring extra keys by default 픹 = Pattern-matches mappings/dicts, disallowing extra keys by default 路♀️ = Has pattern matching, but doesn't apply to a Mapping-like or dict-like types ✖️ = Has no pattern matching for algebraic data types (but might for strings) * Maturity † = Pattern-matching syntax is advertised as "experimental" Dynamically-typed languages: * Erlang 픸 * Elixir (derived from Erlang) 픸 * Ruby (2.7) 픸† * JavaScript (TC39 draft) 픸† * PHP ✖️ * Lua ✖️ * Python (current draft of PEP 634-636) 픸† Statically-typed languages: * OCaml 路♀️ * Scala 路♀️ * Swift 路♀️ * Rust 路♀️ * Haskell 路♀️ * C++ 路♀️ -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/JNQD3CJD4B5LEBT3N4BNMBQ6WDDGLGTZ/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: PEP 634-636: Mapping patterns and extra keys
On 11/15/20 12:11 AM, Steven D'Aprano wrote: It would be good if the PEP gave a survey of the practical experience of other languages with pattern matching: - are there languages which require an exact match, with no left over keys? what issues, if any, do users have with that choice? - which languages ignore extra keys? do users of those languages consider this feature a bug, a wart, or a feature? If useful I can volunteer to conduct such a survey. I expect to have some cycles this coming weekend. -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/WBTOEFRUWB2MQWZ7OTJB6ME2IMSZXTPM/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] Re: PEP 634-636: Mapping patterns and extra keys
On 11/14/20 10:17 PM, Guido van Rossum wrote: It’s a usability issue; mappings are used quite differently than sequences. Compare to class patterns rather than sequence patterns. I just found the following explanation from the superceded PEP 622 as to why extra keys are ignored: > Extra keys in the subject are ignored even if **rest is not present. This is different from > sequence pattern, where extra items will cause a match to fail. But mappings are actually > different from sequences: they have natural structural sub-typing behavior, i.e., passing a > dictionary with extra keys somewhere will likely just work. I suppose this makes sense when using "match" to work with a dictionary used as a lightweight object, which I expect would be relatively common. The examples I originally presented assume use of "match" for parsing, and parsing tends to default to stricter matching. :) -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@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/YGN47RMUHK642TCVA32GWI3OBQIIEKZV/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-ideas] PEP 634-636: Mapping patterns and extra keys
From PEP 636 (Structural Pattern Matching): > Mapping patterns: {"bandwidth": b, "latency": l} captures the "bandwidth" and "latency" values from a dict. Unlike sequence patterns, extra keys are ignored. It surprises me that ignoring extra keys would be the *default* behavior. This seems unsafe. Extra keys I would think would be best treated as suspicious by default. * Ignoring extra keys loses data silently. In the current proposal: point = {'x': 1, 'y': 2, 'z': 3) match point: case {'x': x, 'y': y}: # MATCHES, losing z O_O pass case {'x': x, 'y': y, 'z': z}: # will never match O_O pass * Ignoring extra keys is inconsistent with the handling of sequences: We don't allow extra items when using a destructuring assignment to a sequence: p = [1, 2] [x, y] = p [x, y, z] = p # ERROR: ValueError: not enough values to unpack (expected 3, got 2) :) * Ignoring extra keys in mapping patterns is inconsistent with the current proposal for how sequence patterns match data: point = [1, 2, 3] match point: case [x, y]: # notices extra value and does NOT match :) pass case [x, y, z]: # matches :) pass * Ignoring extra keys is inconsistent with TypedDict's default "total" matching behavior: from typing import TypedDict class Point2D(TypedDict): x: int y: int p1: Point2D = {'x': 1, 'y': 2} p2: Point2D = {'x': 1, 'y': 2, 'z': 3) # ERROR: Extra key 'z' for TypedDict "Point2D" :) * It is *possible* to force an exact key match with a pattern guard but it's clumsy to do so. It should not be clumsy to parse strictly. point = {'x': 1, 'y': 2, 'z': 3) match point: # notices extra value and does NOT match, but requires ugly guard :/ case {'x': x, 'y': y, **rest} if rest == {}: pass case {'x': x, 'y': y, 'z': z, **rest} if rest == {}: pass To avoid the above problems, **I'd advocate for disallowing extra keys in mapping patterns by default**. For cases where extra keys want to be specifically allowed and ignored, I propose allowing a **_ wildcard. Some examples that illustrate behavior when *disallowing* extra keys in mapping patterns: 1. Strict parsing from typing import TypedDict, Union Point2D = TypedDict('Point2D', {'x': int, 'y': int}) Point3D = TypedDict('Point3D', {'x': int, 'y': int, 'z': int}) def parse_point(point_json: dict) -> Union[Point2D, Point3D]: match point_json: case {'x': int(x), 'y': int(y)}: return Point2D({'x': x, 'y': y}) case {'x': int(x), 'y': int(y), 'z': int(z)}: return Point3D({'x': x, 'y': y, 'z': z}) case _: raise ValueError(f'not a valid point: {point_json!r}') 2. Loose parsing, discarding unknown data. Common when reading JSON-like data when it's not necessary to output it again later. from typing import TypedDict TodoItem_ReadOnly = TypedDict('TodoItem_ReadOnly', {'title': str, 'completed': bool}) def parse_todo_item(todo_item_json: Dict) -> TodoItem_ReadOnly: match todo_item_json: case {'title': str(title), 'completed': bool(completed), **_}: return TodoItem_ReadOnly({'title': title, 'completed': completed}) case _: raise ValueError() input = {'title': 'Buy groceries', 'completed': True, 'assigned_to': ['me']} print(parse_todo_item(input)) # prints: {'title': 'Buy groceries', 'completed': True} 3. Loose parsing, preserving unknown data. Common when parsing JSON-like data when it needs to be round-tripped and output again later. from typing import Any, Dict, TypedDict TodoItem_ReadWrite = TypedDict('TodoItem_ReadWrite', {'title': str, 'completed': bool, 'extra': Dict[str, Any]}) def parse_todo_item(todo_item_json: Dict) -> TodoItem_ReadWrite: match todo_item_json: case {'title': str(title), 'completed': bool(completed), **extra}: return TodoItem_ReadWrite({'title': title, 'completed': completed, 'extra': extra}) case _: raise ValueError() def format_todo_item(item: TodoItem_ReadWrite) -> Dict: return {'title': item['title'], 'completed': item['completed'], **item['extra']} input = {'title': 'Buy groceries', 'completed': True, 'assigned_to': ['me']} output = format_todo_item(parse_todo_item(input)) print(output) # prints: {'title': 'Buy groceries', 'completed': True, 'assigned_to': ['me']} Comments? -- David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy ___ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https
Re: [Python-ideas] PEP: Dict addition and subtraction
I have seen a ton of discussion about what dict addition should do, but have seen almost no mention of dict difference. This lack of discussion interest combined with me not recalling having needed the proposed subtraction semantics personally makes me wonder if we should hold off on locking in subtraction semantics just yet. Perhaps we could just scope the proposal to dictionary addition only for now? If I *were* to define dict difference, my intuition suggests supporting a second operand that is any iterable of keys and not just dicts. (Augmented dict subtraction is already proposed to accept such a broader second argument.) David Foster | Seattle, WA, USA On 3/1/19 8:26 AM, Steven D'Aprano wrote: Attached is a draft PEP on adding + and - operators to dict for discussion. This should probably go here: https://github.com/python/peps but due to technical difficulties at my end, I'm very limited in what I can do on Github (at least for now). If there's anyone who would like to co-author and/or help with the process, that will be appreciated. ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/ ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 505: None-aware operators: operators ?= and ?? and OR
RE none-aware operators in general: +1 overall for the latest version of PEP 505 and the utility of ?? in particular. There are several places in my code that could be simplified with ??. find-pep505.py on my current Django-oriented codebase gives: Total None-coalescing `if` blocks: 43 Total [possible] None-coalescing `or`: 4 Total None-coalescing ternaries: 8 Total Safe navigation `and`: 0 Total Safe navigation `if` blocks: 6 Total Safe navigation ternaries: 17 RE OR and ||: * -1 for "OR" as an operator. Insufficiently distinct from "or". Reads the same out loud despite being a different operator. * -1 for "||" with none-aware semantics. This differs from the semantics in every C-derived language I can think of, which will be confusing to several other developers. David Foster | Seattle, WA, USA On 7/19/18 5:30 AM, Jonathan Fine wrote: Hi There is a formatted version of this PEP at https://www.python.org/dev/peps/pep-0505/ I've taken a look at this, and have some comments on the first two examples drawn from standard library code. (And a very grateful +10 for writing a script to find such examples.) I've started a subthread, just to discuss the ?= and ?? operators. And something newish, that I call OR. FIRST EXAMPLE The first example is --- From bisect.py: def insort_right(a, x, lo=0, hi=None): # ... if hi is None: hi = len(a) --- Here, None is a sentinel value. The simpler code --- hi = hi or len(a) --- fails when hi is zero (or any other value that is False in the boolean context). This can be fixed by introducing a new operator OR which is similar to 'or' but has the semantics this example requires. Thus, given OR we can write --- hi = hi OR len(a) --- where (A OR B) returns A if A is not None, otherwise it returns B. (Recall that (A or B) returns A if bool(A), otherwise it returns B.) SECOND EXAMPLE The second example is --- From calendar.py: encoding = options.encoding if encoding is None: encoding = sys.getdefaultencoding() optdict = dict(encoding=encoding, css=options.css) --- Once we have OR we would write this as --- encoding = encoding OR sys.getdefaultencoding() optdict = dict(encoding=encoding, css=options.css) --- And from here we can condense into a single (longish) line: --- optdict = dict(encoding=encoding OR sys.getdefaultencoding(), css=options.css) -- SUMMARY Here, for reference, are the suggestions using ?= and ?? in PEP 505. --- hi ??= len(a) --- optdict = dict(encoding=encoding ?? sys.getdefaultencoding(), css=options.css) --- Comments ?? suggestions. For example, would a None-aware AND operator be useful? ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] The future of Python parallelism. The GIL. Subinterpreters. Actors.
I was not aware of PyParallel. The PyParellel "parallel thread" line-of-execution implementation is pretty interesting. Trent, big kudos to you on that effort. Since you're speaking in the past tense and said "but we're not doing it like that", I infer that the notion of a parallel thread was turned down for integration into CPython, as that appears to have been the original goal. However I am unable to locate a rationale for why that integration was turned down. Was it deemed to be too complex to execute, perhaps in the context of providing C extension compatibility? Was there a desire to see a similar implementation on Linux as well as Windows? Some other reason? Since I presume you were directly involved in the discussions, perhaps you have a link to the relevant thread handy? The last update I see from you RE PyParallel on this list is: https://mail.python.org/pipermail/python-ideas/2015-September/035725.html David Foster | Seattle, WA, USA On 7/9/18 9:17 AM, Trent Nelson wrote: On Sun, Jul 08, 2018 at 11:27:08AM -0700, David Foster wrote: I'd like to solicit some feedback on what might be the most efficient way to make forward progress on efficient parallelization in Python inside the same OS process. The most promising areas appear to be: You might find PyParallel interesting, at least from a "here's what was tried, it worked, but we're not doing it like that" perspective. http://pyparallel.org https://speakerdeck.com/trent/pyparallel-how-we-removed-the-gil-and-exploited-all-cores I still think it was a pretty successful proof-of-concept regarding removing the GIL without having to actually remove it. Performance was pretty good too, as you can see in those graphs. -- David Foster | Seattle, WA, USA Regards, Trent. -- https://trent.me ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 572 version 2: Statement-Local Name Bindings
I mean approximately local to one line of source code. Perhaps the unpopular opinion based on your reaction. :) More specifically, for a simple statement (with no trailing colon), there is one scope enclosing everything in the statement. For a compound statement, composed of multiple clauses, where each clause has a header (ending with a colon) and a suite, there are N non-overlapping scopes, one scope for each of the N clause headers. The scope is limited to the header only and does not include the suite. In considering a 'for' loop, I'd advocate for keeping the scope of the expression_list separate from the target_list, since I can't think of a reasonable case where the target_list would want to reference something from the expression_list. So the following code would have a NameError for magic_index in the target_list: > # NameError: name 'magic index' is not defined > for my_array[magic_index] in list_of_lists[(f(...) as magic_index)]: > ... That's pretty bizarre code, using a fixed index of an array as an iteration variable. The only other type of 'for' loop target that might try to use a named expression from the expression_list is a slice expression, which would be even more bizarre code. Best to make bizarre cases into errors. Cheers, -- David Foster | Seattle, WA, USA On Sat, Mar 17, 2018 at 12:13 AM, Chris Angelico <ros...@gmail.com> wrote: > On Sat, Mar 17, 2018 at 5:49 PM, David Foster <davidf...@gmail.com> wrote: > > (3a) With a header-limited scope (in proposal #1 above), I advocate that > a > > named expression should NOT be able to shadow other variables, giving a > > SyntaxError. I can't think of a reasonable reason why such shadowing > should > > be allowed, and preventing shadowing may eliminate unintentional errors. > > Header-limited scope is hard to define. Do you mean expression-local? > (Also hard to define.) Do you mean local to one line of source code? > Definitely not. And what happens with a 'for' loop - part of its > header gets run after each loop iteration but still kinda references > stuff that was done once-only before the loop started. > > ChrisA > ___ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > -- David Foster ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 572 version 2: Statement-Local Name Bindings
(1) I am concerned with the proposal's ability to introduce variables with a new broader kind of multi-line scope not seen anywhere else in Python. It is difficult to reason about, particularly in constructs like lambdas and inline def functions. Limiting scope to the very same line is great: > stuff = [[(f(x) as y), x/y] for x in range(5)] However I advocate that named expressions should ONLY be usable in the header of statements that enclose other statements. Thus I would expect the following to be an error: > if (re.match(...) as m): > print(m.groups(0)) # NameError: name 'm' is not defined > while (sock.read() as data): > print("Received data:", data) # NameError: name 'data' is not defined Using the named expression in other parts of the statement header is still fine: > if (re.match(...) as m) is not None and m.groups(1) == 'begin': > ... In summary, -1 from me for introducing a new kind of sublocal scope. +0 from me for keeping the scope limited to the header of the statement. Predictable conservative semantics. (2) If there WAS a desire to allow a named expression inside the substatements of a compound statement, I would advocate that the named expression just be treated as a regular (usually-)local variable assignment, with scope that extends to the entire function. This would imply that the variable would appear in locals(). Under this interpretation, the following would be okay: > while (sock.read() as data): > print("Received data:", data) > print("Last received data:", data) # ok; data is a regular local I would be -0 for using a regular local scope for a named expression. Predictable semantics but leaks the expression to the wider scope of the function. (3a) With a header-limited scope (in proposal #1 above), I advocate that a named expression should NOT be able to shadow other variables, giving a SyntaxError. I can't think of a reasonable reason why such shadowing should be allowed, and preventing shadowing may eliminate unintentional errors. (3b) With a local scope (in proposal #2 above), a named expression simply replaces any preexisting local with the same name. (Or it would replace the global/nonlocal if a global/nonlocal statement was in use.) There is no shadowing per-se. Cheers, -- David Foster | Seattle, WA, USA On Fri, Mar 2, 2018 at 3:43 AM, Chris Angelico <ros...@gmail.com> wrote: > After dozens of posts and a wide variety of useful opinions and > concerns being raised, here is the newest version of PEP 572 for your > debating pleasure. > > Formatted version: > > https://www.python.org/dev/peps/pep-0572/ > > There are now several more examples, greater clarity in edge cases, > and improved wording of the actual proposal and specifications. Also, > the reference implementation has been significantly enhanced, for > those who wish to try this themselves. > > ChrisA > > PEP: 572 > Title: Syntax for Statement-Local Name Bindings > Author: Chris Angelico <ros...@gmail.com> > Status: Draft > Type: Standards Track > Content-Type: text/x-rst > Created: 28-Feb-2018 > Python-Version: 3.8 > Post-History: 28-Feb-2018, 02-Mar-2018 > > > Abstract > > > Programming is all about reusing code rather than duplicating it. When > an expression needs to be used twice in quick succession but never again, > it is convenient to assign it to a temporary name with small scope. > By permitting name bindings to exist within a single statement only, we > make this both convenient and safe against name collisions. > > > Rationale > = > > When a subexpression is used multiple times in a list comprehension, there > are currently several ways to spell this, none of which is universally > accepted as ideal. A statement-local name allows any subexpression to be > temporarily captured and then used multiple times. > > Additionally, this syntax can in places be used to remove the need to > write an > infinite loop with a ``break`` in it. Capturing part of a ``while`` loop's > condition can improve the clarity of the loop header while still making the > actual value available within the loop body. > > > Syntax and semantics > > > In any context where arbitrary Python expressions can be used, a named > expression can appear. This must be parenthesized for clarity, and is of > the form ``(expr as NAME)`` where ``expr`` is any valid Python expression, > and ``NAME`` is a simple name. > > The value of such a named expression is the same as the incorporated > expression, with the additional side-effect that NAME is bound to that > value in all retrievals for the remainder of the current statement. > > Just as function-local names shadow global names for the scope of the > funct
Re: [Python-ideas] Coming up with an alternative to PEP 505's None-aware operators
(1) This proposal serves well to eliminate repeated computations by allowing what is an inline assignment to a temporary variable. But it doesn't seem to make the case of None-aware operators any less verbose than they would be otherwise. Proposal: value = ?it.strip()[4:].upper() if (?it=var1) is not None else None Traditional: it = var1 value = it.strip()[4:].upper() if it is not None else None If we wanted to get rid of the "if it is not None else None" boilerplate, we'd need something more concise. For example - completely off the cuff - syntax like: value = var1?.strip()[4:].upper() I'm not really interested in going down a rabbit hole of discussing those kinds of syntax alternatives on this thread. I just want to point out that the current proposal don't seem to make None-aware operations much more concise than they were before except in the case of complex subexpressions being None-tested, which I find uncommon in my own programs. (2) In considering the proposal in the alternative light of specifically trying to eliminate complex subexpressions, I'll also put a +1 in for (expr as it) rather than (?it=) since I find it reads nicer. Also is consistent with existing syntax (with expr as var). Cheers, David -- David Foster | Seattle, WA, USA On 2/15/18 6:06 PM, Nick Coghlan wrote: The recent thread on variable assignment in comprehensions has prompted me to finally share https://gist.github.com/ncoghlan/a1b0482fc1ee3c3a11fc7ae64833a315 with a wider audience (see the comments there for some notes on iterations I've already been through on the idea). == The general idea == The general idea would be to introduce a *single* statement local reference using a new keyword with a symbolic prefix: "?it" * `(?it=expr)` is a new atomic expression for an "it reference binding" (whitespace would be permitted around "?it" and "=", but PEP 8 would recommend against it in general) * subsequent subexpressions (in execution order) can reference the bound subexpression using `?it` (an "it reference") * `?it` is reset between statements, including before entering the suite within a compound statement (if you want a persistent binding, use a named variable) * for conditional expressions, put the reference binding in the conditional, as that gets executed first * to avoid ambiguity, especially in function calls (where it could be confused with keyword argument syntax), the parentheses around reference bindings are always required * unlike regular variables, you can't close over statement local references (the nested scope will get an UnboundLocalError if you try it) The core inspiration here is English pronouns (hence the choice of keyword): we don't generally define arbitrary terms in the middle of sentences, but we *do* use pronouns to refer back to concepts introduced earlier in the sentence. And while it's not an especially common practice, pronouns are sometimes even used in a sentence *before* the concept they refer to ;) If we did pursue this, then PEPs 505, 532, and 535 would all be withdrawn or rejected (with the direction being to use an it-reference instead). == Examples == `None`-aware attribute access: value = ?it.strip()[4:].upper() if (?it=var1) is not None else None `None`-aware subscript access: value = ?it[4:].upper() if (?it=var1) is not None else None `None`-coalescense: value = ?it if (?it=var1) is not None else ?it if (?it=var2) is not None else var3 `NaN`-coalescence: value = ?it if not math.isnan((?it=var1)) else ?it if not math.isnan((?that=var2)) else var3 Conditional function call: value = ?it() if (?it=calculate) is not None else default Avoiding repeated evaluation of a comprehension filter condition: filtered_values = [?it for x in keys if (?it=get_value(x)) is not None] Avoiding repeated evaluation for range and slice bounds: range((?it=calculate_start()), ?it+10) data[(?it=calculate_start()):?it+10] Avoiding repeated evaluation in chained comparisons: value if (?it=lower_bound()) <= value < ?it+tolerance else 0 Avoiding repeated evaluation in an f-string: print(f"{?it=get_value()!r} is printed in pure ASCII as {?it!a} and in Unicode as {?it}" == Possible future extensions == One possible future extension would be to pursue PEP 3150, treating the nested namespace as an it reference binding, giving: sorted_data = sorted(data, key=?it.sort_key) given ?it=: def sort_key(item): return item.attr1, item.attr2 (A potential bonus of that spelling is that it may be possible to make "given ?it=:" the syntactic keyword introducing the suite, allowing "given" itself to continue to be used as a variable name) Another possible extension would be to combine it references with `as` clauses on if statements and while loops: if (?it=pattern.match(data))