Simplifying .format() based string interpolation
Hello All, I REALLY like str.format() from PEP3101 since it is simple to use, powerful and extendable. Especially I like the possibility to use named fields for two reasons: readability AND it is less error prone. i.E. compare "{name} is {value} ({value:X} in hex)" to "{} is {} ({1:X} in hex)". In a lot of cases all fields are identical with variables from the local/global name space. This means in the above example one had to write: "{name} is {value} ({value:X} in hex)".format(name=name, value=value) But such statements are violating the DRY principles! How about introducing new string tokens beginning with 'f', which tells the parser to take over that job. In this case one simply had to write: f"{name} is {value} ({value:X} in hex)" and the parser would replace this by "{name} is {value} ({value:X} in hex)".format( **collections.ChainMap(locals(), globals()) ) Would be glad to hear your thoughts, Robert
On Fri, Feb 7, 2014 at 10:41 AM, Robert Hölzl <robert.hoelzl@posteo.de> wrote:
How about introducing new string tokens beginning with 'f', which tells the parser to take over that job. In this case one simply had to write:
f"{name} is {value} ({value:X} in hex)"
and the parser would replace this by
"{name} is {value} ({value:X} in hex)".format( **collections.ChainMap(locals(), globals()) )
PHP programmers would love it. Subsequent maintainers would hate it. It's way too magical for my liking. ChrisA
It already works! from macropy.string_interp import macros, sA = 10B = 5print s["{A} + {B} = {A + B}"]# 10 + 5 = 15 a, b = 1, 2print s["{a} apple and {b} bananas"]# 1 apple and 2 bananas On Thu, Feb 6, 2014 at 4:11 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Feb 7, 2014 at 10:41 AM, Robert Hölzl <robert.hoelzl@posteo.de> wrote:
How about introducing new string tokens beginning with 'f', which tells the parser to take over that job. In this case one simply had to write:
f"{name} is {value} ({value:X} in hex)"
and the parser would replace this by
"{name} is {value} ({value:X} in hex)".format( **collections.ChainMap(locals(), globals()) )
PHP programmers would love it. Subsequent maintainers would hate it.
It's way too magical for my liking.
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/
Haoyi Li <haoyi.sg@...> writes:
It already works!
from macropy.string_interp import macros, s A = 10 B = 5 print s["{A} + {B} = {A + B}"] # 10 + 5 = 15
I checked out macropy and seems to be really funny. It is kind a bringing LISP (which all its power to modify the language by itself) to Python. But nevertheless, this solution is not pythonic (as least as long as the python community did not decide to include macropy and use it as inherent as macros are used in LISP) Furthermore I do NOT want {A+B} to work, since this would be PHP. I would like to use the pythonic .format() Robert
Chris Angelico <rosuav@...> writes:
On Fri, Feb 7, 2014 at 10:41 AM, Robert Hölzl <robert.hoelzl <at> posteo.de> wrote:
How about introducing new string tokens beginning with 'f', which tells the parser to take over that job. In this case one simply had to write:
f"{name} is {value} ({value:X} in hex)"
and the parser would replace this by
"{name} is {value} ({value:X} in hex)".format( **collections.ChainMap(locals(), globals()) )
PHP programmers would love it. Subsequent maintainers would hate it.
In my Optinion the bad thing with PHP is that you can create fields that contain complex expressions (even whole programs). But the nice thing of .format are its restrictions: it allows only getattr() and getitem() operations...
It's way too magical for my liking.
mmh; why magical? - it is explicit (the string is marked by 'f') - it is side effect free - does not change the behaviour of an existing feature
On Fri, Feb 7, 2014 at 12:24 PM, Robert Hölzl <robert.hoelzl@posteo.de> wrote:
mmh; why magical? - it is explicit (the string is marked by 'f') - it is side effect free - does not change the behaviour of an existing feature
It looks like a new form of literal, but it's actually a run-time expression evaluator. All the other types of string literal produce, ultimately, the same thing: a string. (Okay, the u"" and b"" prefixes choose between one of two things, but you still simply get one single literal object.) There's no such thing as a "raw string" or a "multi-line string"; there's just a "raw string literal" and a "multi-line string literal".
r"asdf\qwer" == """asdf\\qwer""" True
But now, what you're suggesting is that f"some string" is a thing that does interpolation. You have something that looks like a literal, but whose value depends majorly on context. No other form of literal is like that, unless you count the way [a,b,c] will depend on the values of a, b, and c - and that's not so much a literal as a piece of syntax that constructs something, as can be seen in dis.dis:
def foo(x): a = "string literal" b = r"raw string literal" c = ["list","literal"] d = [a,b,c] return d
dis.dis(foo) 2 0 LOAD_CONST 1 ('string literal') 3 STORE_FAST 1 (a)
3 6 LOAD_CONST 2 ('raw string literal') 9 STORE_FAST 2 (b) 4 12 LOAD_CONST 3 ('list') 15 LOAD_CONST 4 ('literal') 18 BUILD_LIST 2 21 STORE_FAST 3 (c) 5 24 LOAD_FAST 1 (a) 27 LOAD_FAST 2 (b) 30 LOAD_FAST 3 (c) 33 BUILD_LIST 3 36 STORE_FAST 4 (d) 6 39 LOAD_FAST 4 (d) 42 RETURN_VALUE Technically there's no "list literal" syntax, only a convenient form for constructing a list at run-time. What you're doing here looks like a string literal, but isn't a literal at all. (Note that tuples do kinda have a literal syntax. If you replace the square brackets with round ones, you'll find that the all-literals tuple gets stored as a const, same as the strings are. But that's an optimization that works only when everything's literals, which - kinda by definition - your f'' string won't be.) ChrisA
On 2/6/2014 8:46 PM, Chris Angelico wrote:
Technically there's no "list literal" syntax, only a convenient form for constructing a list at run-time.
There is literally no 'list literal' syntax. Number, string, and byte literals are discussed under Lexical Analysis in http://docs.python.org/3/reference/lexical_analysis.html#literals List, set, and dict displays are discussed under Expressions in http://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-... and tuples in http://docs.python.org/3/reference/expressions.html#expression-lists -- Terry Jan Reedy
On 02/07/2014 02:46 AM, Chris Angelico wrote:
On Fri, Feb 7, 2014 at 12:24 PM, Robert Hölzl <robert.hoelzl@posteo.de> wrote:
mmh; why magical? - it is explicit (the string is marked by 'f') - it is side effect free - does not change the behaviour of an existing feature
It looks like a new form of literal, but it's actually a run-time expression evaluator. All the other types of string literal produce, ultimately, the same thing: a string. (Okay, the u"" and b"" prefixes choose between one of two things, but you still simply get one single literal object.) There's no such thing as a "raw string" or a "multi-line string"; there's just a "raw string literal" and a "multi-line string literal".
r"asdf\qwer" == """asdf\\qwer""" True
But now, what you're suggesting is that f"some string" is a thing that does interpolation. You have something that looks like a literal, but whose value depends majorly on context. No other form of literal is like that, unless you count the way [a,b,c] will depend on the values of a, b, and c - and that's not so much a literal as a piece of syntax that constructs something, as can be seen in dis.dis:
def foo(x): a = "string literal" b = r"raw string literal" c = ["list","literal"] d = [a,b,c] return d
dis.dis(foo) 2 0 LOAD_CONST 1 ('string literal') 3 STORE_FAST 1 (a)
3 6 LOAD_CONST 2 ('raw string literal') 9 STORE_FAST 2 (b)
4 12 LOAD_CONST 3 ('list') 15 LOAD_CONST 4 ('literal') 18 BUILD_LIST 2 21 STORE_FAST 3 (c)
5 24 LOAD_FAST 1 (a) 27 LOAD_FAST 2 (b) 30 LOAD_FAST 3 (c) 33 BUILD_LIST 3 36 STORE_FAST 4 (d)
6 39 LOAD_FAST 4 (d) 42 RETURN_VALUE
Technically there's no "list literal" syntax, only a convenient form for constructing a list at run-time. What you're doing here looks like a string literal, but isn't a literal at all.
(Note that tuples do kinda have a literal syntax. If you replace the square brackets with round ones, you'll find that the all-literals tuple gets stored as a const, same as the strings are. But that's an optimization that works only when everything's literals, which - kinda by definition - your f'' string won't be.)
Well, in fact all your argumentation just supports the proposal, doesn't it? There would be string constants and string variables: "Hello, world!" "Hello, {username}!" just like list constants and list variables: [1,2,3] [a,b,c] So, where's the *actual* problem? It's not as if it were implicit or otherwise hidden. And we would not even need an 'f' prefix if it were not for backward compatibility (or maybe the parser could deal with that?); various languages just use this format (among others, Cobra, highly influenced by Python) and have just abandoned all this complicated variable string syntax noise. Would be very pythonic, in my view (but maybe I don't get what pythonic means for this matter; but surely it does not mean unneeded complication). d
Note that the current spelling of this is just "msg.format_map(locals())". What the proposal is effectively asking for is a new non-string literal type that creates an implicit closure, allowing the names to be referenced without supplying them explicitly. That's a significant new (and hard to understand) feature to replace a fairly simple call. Cheers, Nick.
On 2/7/2014 2:28 AM, spir wrote:
On 02/07/2014 02:46 AM, Chris Angelico wrote:
(Note that tuples do kinda have a literal syntax.
'Constant tuples are no different that other constant expressions for immutable objects. Like tuples, ints are immutable. If the fixed value of an int can be computed at compile time, then an implementation may opt to optimize runtime by doing the computation at compile time. This only really helps if it avoids computing the object multiple times. I suppose one could say that '1 + 1' is a kind of literal syntax for 2, but I am not sure this is helpful in this context. Number and string literals are *lexical* constants, as are keywords. They are all described in the chapter on *lexical* analysis of code. Literals are like the base cases of recursion and starting values of iteration. If you try to treat literals as Python runtime expressions, say 1 = int(1), then you get into infinite regression, as then int(1) = int(int(1)), etcetera.
Well, in fact all your argumentation just supports the proposal, doesn't it?
If the argumentation led you to confuse categories, it is defective. -- Terry Jan Reedy
On 02/06/2014 03:41 PM, Robert Hölzl wrote:
How about introducing new string tokens beginning with 'f', which tells the parser to take over that job.
See https://mail.python.org/pipermail/python-ideas/2013-June/021466.html for a similar discussion. -- ~Ethan~
participants (7)
-
Chris Angelico -
Ethan Furman -
Haoyi Li -
Nick Coghlan -
Robert Hölzl -
spir -
Terry Reedy