How to substitute variable in string directly? [duplicate] - python

I'm trying out Python 3.6. Going through new code, I stumbled upon this new syntax:
f"My formatting string!"
It seems we can do things like this:
>>> name = "George"
>>> print(f"My cool string is called {name}.")
My cool string is called George.
Can anyone shed some light on the inner workings of this? In particular what is the scope of the variables that an f-prefixed string can take?

See PEP 498 Literal String Interpolation:
The expressions that are extracted from the string are evaluated in the context where the f-string appeared. This means the expression has full access to local and global variables. Any valid Python expression can be used, including function and method calls.
So the expressions are evaluated as if they appear in the same scope; locals, closures, and globals all work the same as in other code in the same context.
You'll find more details in the reference documentation:
Expressions in formatted string literals are treated like regular Python expressions surrounded by parentheses, with a few exceptions. An empty expression is not allowed, and a lambda expression must be surrounded by explicit parentheses. Replacement expressions can contain line breaks (e.g. in triple-quoted strings), but they cannot contain comments. Each expression is evaluated in the context where the formatted string literal appears, in order from left to right.
Since you are trying out a 3.6 alpha build, please do read the What's New In Python 3.6 documentation. It summarises all changes, including links to the relevant documentation and PEPs.
And just to be clear: 3.6 isn't released yet; the first alpha is not expected to be released until May 2016. See the 3.6 release schedule.

f-strings also support any Python expressions inside the curly braces.
print(f"My cool string is called {name.upper()}.")

It might also be worth noting that this PEP498 has a backport to Python <3.6
pip install fstring
from fstring import fstring
x = 1
y = 2.0
plus_result = "3.0"
print fstring("{x}+{y}={plus_result}")
# Prints: 1+2.0=3.0

letter f for "format" as in f"hello {somevar}. This little f before the "(double-quote) and the {} characters tell python 3, "hey, this string needs to be formatted. So put these variable in there and format it.".
hope this is clear.

Related

F-string error in Python - What does the equals sign mean? [duplicate]

The usage of {} in Python f-strings is well known to execute pieces of code and give the result in string format (some tutorials here). However, what does the '=' at the end of the expression mean?
log_file = open("log_aug_19.txt", "w")
console_error = '...stuff...' # the real code generates it with regex
log_file.write(f'{console_error=}')
This is actually a brand-new feature as of Python 3.8.
Added an = specifier to f-strings. An f-string such as f'{expr=}'
will expand to the text of the expression, an equal sign, then the
representation of the evaluated expression.
Essentially, it facilitates the frequent use-case of print-debugging, so, whereas we would normally have to write:
f"some_var={some_var}"
we can now write:
f"{some_var=}"
So, as a demonstration, using a shiny-new Python 3.8.0 REPL:
>>> print(f"{foo=}")
foo=42
>>>
From Python 3.8, f-strings support "self-documenting expressions", mostly for print de-bugging. From the docs:
Added an = specifier to f-strings. An f-string such as f'{expr=}' will
expand to the text of the expression, an equal sign, then the
representation of the evaluated expression. For example:
user = 'eric_idle'
member_since = date(1975, 7, 31)
f'{user=} {member_since=}'
"user='eric_idle' member_since=datetime.date(1975, 7, 31)"
The usual f-string format specifiers allow more control over how the
result of the expression is displayed:
>>> delta = date.today() - member_since
>>> f'{user=!s} {delta.days=:,d}'
'user=eric_idle delta.days=16,075'
The = specifier will display the whole expression so that calculations
can be shown:
>>> print(f'{theta=} {cos(radians(theta))=:.3f}')
theta=30 cos(radians(theta))=0.866
This was introduced in python 3.8. It helps reduce a lot of f'expr = {expr} while writing codes. You can check the docs at What's new in Python 3.8.
A nice example was shown by Raymond Hettinger in his tweet:
>>> from math import radians, sin
>>> for angle in range(360):
print(f'{angle=}\N{degree sign} {(theta:=radians(angle))=:.3f}')
angle=0° (theta:=radians(angle))=0.000
angle=1° (theta:=radians(angle))=0.017
angle=2° (theta:=radians(angle))=0.035
angle=3° (theta:=radians(angle))=0.052
angle=4° (theta:=radians(angle))=0.070
angle=5° (theta:=radians(angle))=0.087
angle=6° (theta:=radians(angle))=0.105
angle=7° (theta:=radians(angle))=0.122
angle=8° (theta:=radians(angle))=0.140
angle=9° (theta:=radians(angle))=0.157
angle=10° (theta:=radians(angle))=0.175
...
You can also check out this to get the underlying idea on why this was proposed in the first place.
As mention here:
Equals signs are now allowed inside f-strings starting with Python 3.8. This lets you quickly evaluate an expression while outputting the expression that was evaluated. It's very handy for debugging.:
It mean it will run the execution of the code in the f-string braces, and add the result at the end with the equals sign.
So it virtually means:
"something={executed something}"
f'{a_string=}' is not exactly the same as f'a_string={a_string}'
The former escapes special characters while the latter does not.
e.g:
a_string = 'word 1 tab \t double quote \\" last words'
print(f'a_string={a_string}')
print(f'{a_string=}')
gets:
a_string=word 1 tab double quote \" last words
a_string='word 1 tab \t double quote \\" last words
I just realised that the difference is that the latter is printing the repr while the former is just printing the value. So, it would be more accurate to say:
f'{a_string=}' is the same as f'a_string={a_string!r}'
and allows formatting specifications.

How to use repr() instead of backquotes in Python 3 [duplicate]

This question already has answers here:
Having both single and double quotation in a Python string
(9 answers)
Closed 3 years ago.
I'm looking for a way to declare a string that contains double quotes and single quote.
In javascript I would do
let str = `"," '`
But when I try the same syntax in python, my IDE shows this error
Python version 3.7 does not support backquotes, use repr() instead
How can I use repr() to achieve this result?
The reason the error message says what it does is because backquotes have never been used in Python to do what you want. Instead, they used to be a shortcut for using the repr function, that is no longer supported.
According to documentation it take an object
Everything is an object in Python, so there is no issue there. But there is an issue in that the repr function does not do what you want.
We need to go back to the original question instead:
I'm looking for a way to declare a string that contains double quotes and single quote.
In Python, you may either escape whichever quote is the one you used for the string, for example:
"\",\" '" # using double quotes
'"," \'' # using single quotes
Or you may use so-called triple quotes:
""""," '""" # like so
But beware that this does not work if you have the same kind of quote at the end of the string:
# '''"," '''' -- does not work!
'''"," \'''' # works, but it defeats the purpose
In each case, '"," \'' is the form that Python will use to report the string back to you.
The message in the IDE is referring to using backticks around a variable name or other expression. In Python 2, `someVar` was a shortcut for repr(someVar).
But this isn't really what you're trying to do. The message is simply hard-coded for any use of backticks.
You just have to escape the quotes that are the same as the string delimiter.
s = '"," \''
I figured that out
So literally all I had to do was this
text = repr(",'") # returns this string ",'"
The part that confused me was I wasn't sure how to pass the argument to the function since according to documentation I should have passed an object, not a string or a list of string. Until I realized that a string is an object too
A few examples that helped me to understand it in details
>>> print("123")
123
>>> print(repr("123"))
'123'
>>> print(repr(",'"))
",'"

Why don't f-strings play nicely with dictionaries?

f-strings don't behave nicely when used with dictionaries, as mentioned here.
Here is an example of the not-so-nice behavior:
d = {'foo': 'bar'}
# Both work as expected
d["foo"]
d['foo']
# This only works when different quotations are used in the inner and outer strings
f'{d["foo"]}'
f"{d['foo']}"
# This doesn't work
f'{d['foo']}'
f"{d["foo"]}"
# The .format() method doesn't care
'{}'.format(d['foo'])
The last two f-strings listed result in a SyntaxError: invalid syntax, which happens because the string '{d['foo']}' is evaluated as '{d['foo']}'.
What is the underlying reason everything inside the curly brackets of f-strings doesn't get evaluated separately, as when using the old .format() method, and what could possibly be the reason for implementing f-strings in this way?
I love f-strings, but this seems like a point in favor of the old method.
F-strings are literal strings. Including unescaped quotes within quotes (of the same type) is invalid syntax. This makes sense, since the result is ambiguous: the interpreter will not know when the string should end. One traditional way of including quotes within quotes is to use a backslash. But PEP498 forbids backslashes in expressions within f-strings:
Backslashes may not appear inside the expression portions of
f-strings...You can use a different type of quote inside the expression...
Therefore, the only way left to access a dictionary value given a key in an f-string expression is to use a different type quote. Using single quotes, or double quotes, everywhere is ambiguous and gives SyntaxError.
str.format is a regular method, and as such works differently: d['foo'] is evaluated before the string is constructed. Just like when you feed arguments to a function, the arguments are evaluated before the function does anything.
This has nothing to do with f-strings. f strings are common strings once evaluated. What you are trying would be a problem with standard strings too
The problem is that
'a "b" c'
is declares the literal a "b" c
while
'a 'b' c'
the quotes close and reopen. So, it is equivalent to string a, followed by variable b, followed by string c.
That's the whole reason python supports both types of quotation marks
According to PEP 498, f-strings rely on the implementation of regular string literals, and as such are subject to the same constraints. In addition to those, f-strings have their own constraints, such as the exclusion of backslashes anywhere within expressions.
A rationale for the choice of implementation has not been published, but ease of implementation, backwards compatibility, syntax highlighting, and consistency with existing interpolation syntax all featured in discussions among core developers on the publicly accessible mailing lists.1
A central point of contention was whether the proposed concept constitutes a string with special properties or a string interjected with code.2 The former view was favored. PEP 536, embodying the dissenting view and additionally seeking to lift several syntactic constraints, was subsequently filed.
Based on this discussion, a tentative compromise of prohibiting backslashes within expressions was agreed upon, leaving string delimiter inversion as the only remaining option for indexing dictionaries within f-string expressions.
Select discussions predating f-string introduction (Python 3.6):
[Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
[Python-ideas] String interpolation for all literal strings
[Python-Dev] PEP-498: Literal String Formatting
[Python-Dev] PEP 498 f-string: is it a preprocessor?
[Python-Dev] PEP 498 href="interpolated f-string" tweak
[Python-Dev] Parsing f-strings from PEP 498 -- Literal String Interpolation
[Python-ideas] Let’s make escaping in f-literals impossible

How do I convert a string into a formatted string literal at run-time?

I am working my way through PEP 498. Maybe I do not quite get the concept, but how could I possibly convert a regular Python string into a formatted string literal and evaluate it at run-time?
Imagine you have a string:
some_string = 'Name: {name}'
If it was a formatted string literal, the following would work:
name = 'Demo'
some_string_literal = f'Name: {name}'
some_string_literal == 'Name: Demo' # returns True
Ignoring the question of whether this makes sense at all, how could I e.g. read the contents of some_string from a file at run-time, turn it into some_string_literal and evaluate it? My understanding of the underlying CPython implementation is that string literals are interpreted at compile-time (translation into byte code).
I am aware of the more "explicit" option of just using ...
some_string = 'Name: {name}'
some_string.format(name = 'Demo') == 'Name: Demo' # returns True
... but this is not what I am looking for.
EDIT: It was noted in the comments that the "explicit" option is what I should be looking for. I'd agree that what I am asking here is definitely insecure in a number of ways. Nevertheless, I am interested in whether there is a way to do this or not.
PEP 498 says
Because the compiler must be involved in evaluating the expressions contained in the interpolated strings, there must be some way to denote to the compiler which strings should be evaluated.
In other words, the f has an effect on the way the string literal is parsed, just as r does. So you have to invoke the parser for the interpolation to work.
You want to read data into some_string and turn it into a string literal that can be parsed as an f-string. That is not very different from wanting to read data into some_lambda_expression and turning it into a lambda expression. You can do it, of course, using eval, because eval is a way to turn the contents of a string variable into code, by invoking the parser. I know that is not what you wanted. But you can't parse some_string as an f-string without invoking the parser, so you can't have what you want.

Why does python use unconventional triple-quotation marks for comments?

Why didn't python just use the traditional style of comments like C/C++/Java uses:
/**
* Comment lines
* More comment lines
*/
// line comments
// line comments
//
Is there a specific reason for this or is it just arbitrary?
Python doesn't use triple quotation marks for comments. Comments use the hash (a.k.a. pound) character:
# this is a comment
The triple quote thing is a doc string, and, unlike a comment, is actually available as a real string to the program:
>>> def bla():
... """Print the answer"""
... print 42
...
>>> bla.__doc__
'Print the answer'
>>> help(bla)
Help on function bla in module __main__:
bla()
Print the answer
It's not strictly required to use triple quotes, as long as it's a string. Using """ is just a convention (and has the advantage of being multiline).
A number of the answers got many of the points, but don't give the complete view of how things work. To summarize...
# comment is how Python does actual comments (similar to bash, and some other languages). Python only has "to the end of the line" comments, it has no explicit multi-line comment wrapper (as opposed to javascript's /* .. */). Most Python IDEs let you select-and-comment a block at a time, this is how many people handle that situation.
Then there are normal single-line python strings: They can use ' or " quotation marks (eg 'foo' "bar"). The main limitation with these is that they don't wrap across multiple lines. That's what multiline-strings are for: These are strings surrounded by triple single or double quotes (''' or """) and are terminated only when a matching unescaped terminator is found. They can go on for as many lines as needed, and include all intervening whitespace.
Either of these two string types define a completely normal string object. They can be assigned a variable name, have operators applied to them, etc. Once parsed, there are no differences between any of the formats. However, there are two special cases based on where the string is and how it's used...
First, if a string just written down, with no additional operations applied, and not assigned to a variable, what happens to it? When the code executes, the bare string is basically discarded. So people have found it convenient to comment out large bits of python code using multi-line strings (providing you escape any internal multi-line strings). This isn't that common, or semantically correct, but it is allowed.
The second use is that any such bare strings which follow immediately after a def Foo(), class Foo(), or the start of a module, are treated as string containing documentation for that object, and stored in the __doc__ attribute of the object. This is the most common case where strings can seem like they are a "comment". The difference is that they are performing an active role as part of the parsed code, being stored in __doc__... and unlike a comment, they can be read at runtime.
Triple-quotes aren't comments. They're string literals that span multiple lines and include those line breaks in the resulting string. This allows you to use
somestr = """This is a rather long string containing
several lines of text just as you would do in C.
Note that whitespace at the beginning of the line is\
significant."""
instead of
somestr = "This is a rather long string containing\n\
several lines of text just as you would do in C.\n\
Note that whitespace at the beginning of the line is\
significant."
Most scripting languages use # as a comment marker so to skip automatically the shebang (#!) which specifies to the program loader the interpreter to run (like in #!/bin/bash). Alternatively, the interpreter could be instructed to automatically skip the first line, but it's way more convenient just to define # as comment marker and that's it, so it's skipped as a consequence.
Guido - the creator of Python, actually weighs in on the topic here:
https://twitter.com/gvanrossum/status/112670605505077248?lang=en
In summary - for multiline comments, just use triple quotes. For academic purposes - yes it technically is a string, but it gets ignored because it is never used or assigned to a variable.

Categories

Resources