
I really appreciate all the feedback and all of the thought put into this idea. I wanted to make a couple of comments on some of the responses and provide my current thoughts on the idea. --- Responses to comments ---
*All* others?
Tuple, frozenset, bytes, bytearray, memoryview, enumerate, range, map, zip, reversed and filter suggest otherwise.
Yes. You are right. My use of "all" was technically incorrect. But I think it is *very* disingenuous to pretend that these types play anywhere near as central a role in python use as list, dict, and set... especially for newbies. Please try to provide contentful comments instead of "gotchas".
If we were re-doing Python from scratch, there's a good chance that we would limit ourselves to a single comprehension syntax, namely generators:
list(expression for x in items if cond) set(expression for x in items if cond) dict((key, value) for x in items if cond)
rather than have dedicated syntax for those three cases.
The proposed syntax doesn't introduce any new concept and would simply make strings more consistent with the rest of the builtins. The argument
This is a very very helpful point. I will address it at the end. that we can already do this with the "".join() idiom is backwards. It's something we have to do _because_ there's no way to write a string comprehensions directly. This is the mindset that I had. I understand there are other ways to do what I am asking. (I provided one in my initial post.) I am saying it relies on what I believe to be a notoriously unintuitive method (str.join) and an even more unintuitive way of calling it ("".join).
Quite literally the ONLY predicate that can be expressed about a single character is it being a member of a subset of all Unicode characters. Yes, you could express that in convoluted ways like it's ord() being in a certain range, but it boils down to subset membership.
I understand if my initial examples led you to think this because I only iterated over a string "old" to construct "new", but consider the following. (I know it is a silly example but I'm just trying to get the point across.)
my_list = ["hotel", "echo", "lima" , "lima", "oscar"] new = c"x[0] for x in my_list" new 'hello'
Rather than toy examples, how about scouring the Python standard library for some real examples?
Here are 73 of them that I found by grepping through Lib. - https://github.com/python/cpython/blob/master/Lib/email/_encoded_words.py#L9... - https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser... - https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser... - https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser... - https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser... - https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser... - https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_import.p... - https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_next.py#... - https://github.com/python/cpython/blob/master/Lib/lib2to3/refactor.py#L235 - https://github.com/python/cpython/blob/master/Lib/msilib/__init__.py#L178 - https://github.com/python/cpython/blob/master/Lib/msilib/__init__.py#L290 - https://github.com/python/cpython/blob/master/Lib/test/_test_multiprocessing... - https://github.com/python/cpython/blob/master/Lib/test/multibytecodec_suppor... - https://github.com/python/cpython/blob/master/Lib/test/test_audioop.py#L6 - https://github.com/python/cpython/blob/master/Lib/test/test_buffer.py#L853 - https://github.com/python/cpython/blob/master/Lib/test/test_code_module.py#L... - https://github.com/python/cpython/blob/master/Lib/test/test_code_module.py#L... - https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.p... - https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.p... - https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.p... - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L149 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1544 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1548 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1552 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1556 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1953 - https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1991 - https://github.com/python/cpython/blob/master/Lib/test/test_decimal.py#L1092 - https://github.com/python/cpython/blob/master/Lib/test/test_decimal.py#L5346 - https://github.com/python/cpython/blob/master/Lib/test/test_email/test_email... - https://github.com/python/cpython/blob/master/Lib/test/test_email/test_email... - https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L91 - https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L92 - https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L93 - https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L94 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L360 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L366 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L372 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L378 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L384 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L391 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L397 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L403 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L409 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L415 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L421 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L427 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L433 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L455 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L457 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L459 - https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L461 - https://github.com/python/cpython/blob/master/Lib/test/test_long.py#L305 - https://github.com/python/cpython/blob/master/Lib/test/test_lzma.py#L1049 - https://github.com/python/cpython/blob/master/Lib/test/test_lzma.py#L1087 - https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L914 - https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L921 - https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L927 - https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L933 - https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L968 - https://github.com/python/cpython/blob/master/Lib/test/test_re.py#L1013 - https://github.com/python/cpython/blob/master/Lib/test/test_strtod.py#L226 - https://github.com/python/cpython/blob/master/Lib/test/test_ucn.py#L192 - https://github.com/python/cpython/blob/master/Lib/test/test_ucn.py#L65 - https://github.com/python/cpython/blob/master/Lib/test/test_unicodedata.py#L... - https://github.com/python/cpython/blob/master/Lib/test/test_zipfile.py#L1833 - https://github.com/python/cpython/blob/master/Lib/tkinter/__init__.py#L268 - https://github.com/python/cpython/blob/master/Lib/unittest/test/test_asserti... - https://github.com/python/cpython/blob/master/Lib/unittest/test/test_case.py... - https://github.com/python/cpython/blob/master/Lib/urllib/parse.py#L907 - https://github.com/python/cpython/blob/master/Lib/xml/etree/ElementTree.py#L...
To me, the chosen syntax is problematic. The idea of introducing structural logic by using “” seems likely to cause confusion. Across all languages I use, quotes are generally and almost always used to introduce constant values. Sometimes, maybe, there are macro related things that may use quoting, but as a developer, if I see quotes, I’m thinking: the runtime will treat this as a constant.
I think this is an over-simplification of the quotations syntax. Python has several prefix characters that you have to look out for when you see quotes, namely the following: r, u, f, fr, rf, b, br, rb. Not only can these change the construction syntax, but they can even construct an object of a completely different type (bytes).
Second, f-strings do not restrict the normal usage of strings for freeform text content (apart from making the curly brace characters special).
Not to nit-pick too much, but the following is a valid string but not a valid f-string.
s = f"This is a valid string but invalid f-string {}" File "<stdin>", line 1 s = f"This is a valid string but invalid f-string {}" ^ SyntaxError: f-string: empty expression not allowed
Your proposal is focusing on strings as iterables and drawing a parallel with other kinds of iterables for which we have comprehensions. But strings aren't like other iterables because they're primarily vessels for freeform text content, not structured data.
I view this as the strongest opposition to the idea in the whole thread, but I think that seal was broken with f-strings and the {}-syntax. The proposed syntax is different from those features only in *degree* (of deviation from strict char-arrays) not in *type*. But I also recognize that the delimiters {} go a long way in helping to mentally compartmentalize chars from python code. --- My current thoughts --- I definitely see the drawbacks of the originally-proposed syntax, but I think it would be beneficial to the conversation for commenters to recognize that *python strings are not nearly as pure as some of the objections make them out to be*. I would be happy to hear the objection that my syntax strays *too* far, but many of the passed-around examples attest to the fact that when users see quotes, they are often already in "code-evaluation" mode (eg. f"[{','.join('0123')}]" ). I think that a comment left by steve was particularly helpful.
If we were re-doing Python from scratch, there's a good chance that we would limit ourselves to a single comprehension syntax, namely generators:
list(expression for x in items if cond) set(expression for x in items if cond) dict((key, value) for x in items if cond)
rather than have dedicated syntax for those three cases.
Would readers see any merit in a syntax like the following?
dirty = "f8sjGe7" clean = str(char for char in dirty if char in string.ascii_letters) clean 'fsjGe'
Or would it stray too far from the behavior of the str() constructor in general? As of now, the behavior is the following.
dirty = "f8sjGe7" clean = str(char for char in dirty if char in string.ascii_letters) clean '<generator object <genexpr> at 0x7f10fc917660>'
*I don't intend to reinvent strings, I only mean to leverage an already existing means of signifying modified string construction syntax (prefixes) to align str construction syntax with the comprehensions available for the other most common builtin iterables, avoid the notoriously unintuitive "".join syntax, and improve readability.* Please continue to send your thoughts! I really appreciate it! DQAL On Sun, May 2, 2021 at 3:51 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Valentin Berlier writes:
f""" Guest list ({len(people)} people): {person.name + '\n' for person in people} """
That's nice! It's already (almost[1]) legal syntax, but it prints the repr of the generator function. This could work, though:
f""" Guest list ({len(people)} people): {person.name + chr(10) for person in people:5.25i} """
with i for "iterate iterable". (The iterable might need to be parenthesized if it's a generator function.) The width spec is intended to be max_elems.per_elem_width. I guess you could also generalize it to something like
f""" Guest list ({len(people)} people): {person.name, '>25s', chr(10), '' for person in people:i} """
where the 2d element of the tuple is a format spec to apply to each element, the 3d is the separator and the 4th the terminator. Or perhaps those parameters belong in the syntax of the 'i' format code.
I saw your later post that suggests making this default. We could tell programmers to use !s or !r if they want to see things like
<generator object <genexpr> at 0x100fde580>
Probably not, though, at least not if you want all iterables treated this way. Possibly this could be restricted to generators, especially if you use the element format as tuple syntax I proposed above rather than embed the element format spec in the overall generator spec.
Steve
Footnotes: [1] Need to substitute 'chr(10)' for '\n' in an f-string.
_______________________________________________ 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/YG4JTK... Code of Conduct: http://python.org/psf/codeofconduct/