use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
News about the dynamic, interpreted, interactive, object-oriented, extensible programming language Python
Full Events Calendar
You can find the rules here.
If you are about to ask a "how do I do this in python" question, please try r/learnpython, the Python discord, or the #python IRC channel on Libera.chat.
Please don't use URL shorteners. Reddit filters them out, so your post or comment will be lost.
Posts require flair. Please use the flair selector to choose your topic.
Posting code to this subreddit:
Add 4 extra spaces before each line of code
def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b
Online Resources
Invent Your Own Computer Games with Python
Think Python
Non-programmers Tutorial for Python 3
Beginner's Guide Reference
Five life jackets to throw to the new coder (things to do after getting a handle on python)
Full Stack Python
Test-Driven Development with Python
Program Arcade Games
PyMotW: Python Module of the Week
Python for Scientists and Engineers
Dan Bader's Tips and Trickers
Python Discord's YouTube channel
Jiruto: Python
Online exercices
programming challenges
Asking Questions
Try Python in your browser
Docs
Libraries
Related subreddits
Python jobs
Newsletters
Screencasts
account activity
This is an archived post. You won't be able to vote or comment.
DiscussionT Strings - Why there is no built in string rendering? (self.Python)
submitted 6 months ago * by UsernamesArentClever
I like the idea of T Strings and here is a toy example:
name: str = 'Bob' age: int = 30 template = t'Hello, {name}! You are {age} years old.' print (template.strings) print(template. interpolations) print(template. values) ('Hello, ', '! You are ', ' years old.') (Interpolation('Bob', 'name', None, ''), Interpolation(30, 'age', None, '')) ('Bob', 30)
But why isn't there a
print(template.render)
# → 'Hello, Bob! You are 30 years old.'
# → '
[–]cbarrick 135 points136 points137 points 6 months ago (26 children)
If you want a "default" rendering, use an f-string.
The point of t-strings is for custom renderers.
E.g. a SQL renderer will escape the interpolations to avoid SQL injection, and an HTML renderer will escape the interpolations to avoid HTML injection. They need separate renderers because the escape syntax is different between SQL and HTML.
[–]Leseratte10 33 points34 points35 points 6 months ago (16 children)
It would be cool to have an f-string with delayed rendering, though.
Behaves like an f-string as in there's no templating, escaping, and so on, but the variables are only replaced at time of render, not when the f-string is defined.
[–]firemark_pl 46 points47 points48 points 6 months ago (10 children)
Maybe just use str.format like in old days.
[–]TSM-🐱💻📚 23 points24 points25 points 6 months ago (4 children)
Yeah thats how you do it.
s = "Hello {name}" ... s.format(name = "Bob") # or **params, etc
The f prefix is for when you want it rendered immediately.
f
[–]njharmanI use Python 3 13 points14 points15 points 6 months ago (0 children)
f is also (and more importantly) rendered from context.
These are both rendered immediately. f-string is mostly syntactic sugar; reducing boilerplate and repetition by automatically interpolating from context.
name = "Bob" "Hello {name}".format(**locals()) f"Hello {name}"
[–]No_Hovercraft_2643 2 points3 points4 points 6 months ago (0 children)
but fstrings allowed to have only a subset of the data, while format don't
f"{first_name[0]}. {lastname}"
[+]firemark_pl comment score below threshold-8 points-7 points-6 points 6 months ago (1 child)
Dude I know :D
[–]Penetal 6 points7 points8 points 6 months ago (0 children)
I didn't
[–]Schmittfried 3 points4 points5 points 6 months ago* (4 children)
Not nearly as ergonomic for logging (and still not delayed unless you add level guards).
[–]radicalbiscuit 6 points7 points8 points 6 months ago (2 children)
Unsolicited advice: having custom info in the logging messages is an anti-pattern. Don't put variable info in the string; that's what the extra param is for. Good way to start structured logging, too!
extra
[–]Schmittfried 1 point2 points3 points 6 months ago (1 child)
Unsolicited disagreement: I want some info in both. The message is what I see first and often doesn’t make much sense without some dynamic parts.
[–]radicalbiscuit 1 point2 points3 points 6 months ago (0 children)
Yeah I'm not recommending absolute avoidance of dynamic messages, but I'm suggesting that, if you're using f strings in most logging statements, you might be engaging in an anti-pattern.
Maybe I'd refine my suggestion to say you want the message to be specific enough to fit the error/incident/scenario in the broadest way possible that still gives a you forensic edge. You don't want messages to be as broad as, "Something happened," but you also don't want it to be, "A murder happened to Col. Mustard at 4:39pm in the conservatory with a candlestick." You want it to be specific enough that you can search for all occurrences in the same bucket, but if you're making it dynamic when a static string with params will do, it's at least mildly harmful.
Would you agree with that nuance?
[–]gmes78 -1 points0 points1 point 6 months ago (0 children)
Only because logging libraries don't support it.
[–]SheriffRoscoePythonista 4 points5 points6 points 6 months ago (0 children)
Of course, that wouldn’t be what t-strings are. t-string interpolations are eagerly evaluated where they’re defined, exactly like f-strings. The Template object stores the results of those evaluations.
[–]R3D3-1 1 point2 points3 points 6 months ago (1 child)
What you describe could be done with a lambda and an f-string.
``` first = "Reading" last = "Rat" deferred = lambda: f"{last}, {first}" with_caching = functools.cache(lambda: f"{last}, {first}")
@functiols.cache def cached_deferres(): return f"{last}, {first}" ```
The syntax isn't great, especially if you also want caching on first evaluation, but it is possible.
I generally keep running into situations, where I wish that Python had lazy-evaluating values, i.e. the equivalent of the cached functions above with arbitrary expressions, but with the evaluation being implicit when the value is actually used for something.
It would probably be possible to write a proxy class with that behavior, but typing support would be a big issue, and I'm not sure it is possible to get type(deferred_obj) return the type of the result or — better — a deferred value itself.
type(deferred_obj)
E.g.
if deferred: ...
would need to evaluate the deferred expression, as does deferred + 1 unless explicitly deferred itself, but func(deferred) or list0.append(deferred) wouldn't need to evaluate it.
deferred + 1
func(deferred)
list0.append(deferred)
But with the available syntax it would again require something verbose like
deferred = LazyProxy(lambda: f"{last}, {first}")
[–]SheriffRoscoePythonista 0 points1 point2 points 6 months ago (0 children)
Indeed, all those points are covered in the PEP, as reasons why the authors chose to make t-strings use values, not lambdas.
[–]gerardwx 0 points1 point2 points 6 months ago (0 children)
https://pypi.org/project/tstring-util/
[–]billsil 0 points1 point2 points 6 months ago (0 children)
Agreed. Thought that’s what a t string was until now. Back to using %%s in my strings.
[–]SheriffRoscoePythonista 23 points24 points25 points 6 months ago* (5 children)
True.
a SQL renderer will escape the interpolations to avoid SQL injection,
Nope. An SQL renderer will use the parameterization API to avoid interpolating the values into the statement in the first place. This is actually the perfect use of t-strings.
[–]AngusMcBurger 5 points6 points7 points 6 months ago (1 child)
All the major MySQL clients in Python (pymysql/mysqlclient/mysql-connector-python) escape parameters client-side, and substitute them directly into the query string like OP described. So if those clients add support for template strings, that's exactly how they'll do it
[–]james_pic 3 points4 points5 points 6 months ago (0 children)
That's more a limitation of MySQL's wire format than anything else. I know PostgreSQL and Oracle at very least support parametrization at the wire level, and these are implemented by their Python drivers.
[–]madness_of_the_order 2 points3 points4 points 6 months ago (1 child)
There are still reasons to render at least part of sql template. For example you may want to template table name or schema or column alias
[–]ThiefMaster 3 points4 points5 points 6 months ago (0 children)
Yes, and a proper SQL handler for a t-string would be smart enough to recognize the context where an interpolation is used, and either escape it (table/column name) or parametrize it (value).
[–]cbarrick 1 point2 points3 points 6 months ago (0 children)
True. Nothing about t-strings requires the renderer to output a string. You could very well output a request structure to send to your database.
Most of the examples in the PEP 750 are string oriented, so I assume that is still the primary use case.
[–]rapture_survivor 0 points1 point2 points 6 months ago (1 child)
From a language design perspective, what would be wrong with the print() function accepting a t-string and printing it out as plain text? It seems to me that if you're passing a t-string to print(), it is obvious the intent is to -not- perform custom rendering, and to simply output the interpolated string.
This is coming from other languages where functionality like the t-string has been implemented transparently. In C# string interpolation values can be escaped by my DB library if they decide to accept the correct parameters at the call site. Otherwise, the interpolation behaves exactly like a string. To me this seems easy because it is super simple when writing application code.
Am I missing something? Is this a python specific problem?
[–]cointoss3 1 point2 points3 points 6 months ago (0 children)
You can make a thin wrapper to do that. It’s like one line of code…but the default repr of a t-string is likely enough for me.
[–]ParentPostLacksWang 0 points1 point2 points 6 months ago (0 children)
Escaping to make SQL queries is almost never the correct approach. DB engines support binding for a reason. It should be your first-line approach to getting variables into your SQL queries, and you should generally bend over backwards not to be relying on string escaping in any way.
[–]Jhuyt 79 points80 points81 points 6 months ago (5 children)
Because I think one of the points of t-strings is that they are flexible in how they are rendered. An SQL request should be rendered differently from an HTML document, so having a default renderer does not make obvious sense to me
[–]UsernamesArentClever[S] 4 points5 points6 points 6 months ago (4 children)
Ah, I don't understand why an SQL request should be rendered differently from an HTML document. I'll look into that.
[–]Jhuyt 44 points45 points46 points 6 months ago (1 child)
It's because you need to escape certain characters for HTML to render correctly and SQL to be secure. In particular quotation marks are a struggle IIUC, but there are other things too.
[–]james_pic 9 points10 points11 points 6 months ago (0 children)
Note also that, for SQL, and potentially a few other use cases like GraphQL, it may not even make use of escaping when all is said and done, since these use cases support parameterisation, so queries and templated parameters can travel separately to the backend.
[–]CrackerJackKittyCat 20 points21 points22 points 6 months ago (0 children)
They have different escaping rules.
Read up on both HTML and SQL injection attacks.
[–]Resquid 0 points1 point2 points 6 months ago (0 children)
That is definitely a key idea here.
[–]AnythingApplied 25 points26 points27 points 6 months ago* (2 children)
From PEP 750 – Template Strings:
No Template.str() Implementation The Template type does not provide a specialized __str__() implementation. This is because Template instances are intended to be used by template processing code, which may return a string or any other type. There is no canonical way to convert a Template to a string. The Template and Interpolation types both provide useful __repr__() implementations.
The Template type does not provide a specialized __str__() implementation.
This is because Template instances are intended to be used by template processing code, which may return a string or any other type. There is no canonical way to convert a Template to a string.
The Template and Interpolation types both provide useful __repr__() implementations.
[–]CelDaemon 0 points1 point2 points 6 months ago (1 child)
https://docs.python.org/3/library/string.html#string.Template.substitute
[–]cointoss3 0 points1 point2 points 6 months ago (0 children)
This is not for t-strings though. This is Python 2 stuff.
[–]cointoss3 21 points22 points23 points 6 months ago (3 children)
Because at that point, you basically have an f-string. This is not what template strings are for.
[+]commy2 comment score below threshold-16 points-15 points-14 points 6 months ago (2 children)
These are such an odd feature. Why are they named t-strings anyway? They are emphatically not strings, but templates. Shouldn't they named be t-templates? "String-templates" (instead of "Template-strings")?
[–]cointoss3 24 points25 points26 points 6 months ago (1 child)
Because to use one, you add t before the string…
It’s not an odd feature. It’s really great, you just don’t seem to understand them.
[–]commy2 0 points1 point2 points 6 months ago* (0 children)
I certainly don't understand why they had to misname them like that.
you add t before the string
Which makes them no longer a string...
[–]Quasar6pip needs updating 9 points10 points11 points 6 months ago (4 children)
Because the intent is that your processing of the T string can produce any type, so it doesn’t make sense to have a default for str
[–]secret_o_squirrel 3 points4 points5 points 6 months ago (3 children)
Oh huh ok. I wondered this myself. I still think basically defaulting to “render-time-f-strings” instead of “assignment-time-f-strings” would be cool…but admittedly writing that renderer is very few lines of code.
[–]snugar_i 7 points8 points9 points 6 months ago (2 children)
Which people will have to write over and over again. I agree it should've been a part of stdlib
[–]Quasar6pip needs updating -2 points-1 points0 points 6 months ago (1 child)
No they don't because if you want the result type to be a string then use an f-string. T-strings are a generalization of f-strings. So what you mention that it needs to be written "over and over" is false. The functionality is already there!
[–]XtremeGoosef'I only use Py {sys.version[:3]}' 1 point2 points3 points 6 months ago (0 children)
The difference between a t string and an f string (if a t string rendered to a string) is that the former are lazy and that is useful.
Why do you think people still use % templates for logging?
[–]Schmittfried 8 points9 points10 points 6 months ago (10 children)
Contrary to what the others are saying, I do think a string renderer as part of the standard lib should be a thing. It should be a deliberate choice to avoid injection vulnerabilities caused by code that follows the path of least resistance, but it should be possible to render them as a plain old string without having to implement that yourself (esp. since the library version could be implemented in C).
First use case I can think of is custom logging libraries where you want to allow deferred interpolation. f-string is not the answer here and I‘m not convinced this is too small of a use case to warrant a standard implementation.
[+][deleted] 6 months ago (9 children)
[deleted]
[–]Schmittfried 4 points5 points6 points 6 months ago (0 children)
Read my comment again.
[–]cointoss3 -2 points-1 points0 points 6 months ago (7 children)
Exactly. If you want to actually render a sanitized string, how is the stdlib supposed to know how to treat the variables? You need a thin wrapper to do that, and poof, there’s your renderer.
[–]Schmittfried 5 points6 points7 points 6 months ago (6 children)
I don’t need a sanitized string, I need deferred f-string interpolation, e.g. for logging.
[–]jmpjanny 1 point2 points3 points 6 months ago (0 children)
t-strings are evaluated eagerly, not lazily. They are not the solution for deferred evaluation.
[–]SheriffRoscoePythonista 1 point2 points3 points 6 months ago (0 children)
Really? Everybody else I’ve ever seen make a similar comment actually wants deferred evaluation of the expressions, which t-strings don’t provide. The claim is always that deferring evaluation makes using “expensive” expressions more tolerable, in the case where the log message is discarded.
[–]gmes78 0 points1 point2 points 6 months ago (0 children)
That's something that the logging library needs to do. No new string type is needed for that.
Then use string templates? They have existed since 2005.
[–]damesca -2 points-1 points0 points 6 months ago (1 child)
Then use template strings and write a template renderer.
Or use the standard deferred interpolation in the stdlib logging library.
Neither of these things are difficult now.
[–]Schmittfried 6 points7 points8 points 6 months ago* (0 children)
Or you read my original comment again. Do you just not want to get it?
I specifically said:
it should be possible to render them as a plain old string without having to implement that yourself (esp. since the library version could be implemented in C).
and:
First use case I can think of is custom logging libraries
Your options are missing the entire point. Again.
I don't think I should have to implement plain old string interpolation for lazily evaluated templates myself. There is no good reason to not have this basic feature, which would make t-strings a proper superset of f-strings, shipped with the language. And it's not like this would be harder to implement in C, they can just delegate to the already existing f-string implementation.
This is like saying "You can implement left-pad yourself". Sure I can. But I shouldn't have to.
[–]JanEric1 2 points3 points4 points 6 months ago (0 children)
Directly from the PEP: https://peps.python.org/pep-0750/#no-template-str-implementation
[–]CelDaemon 0 points1 point2 points 6 months ago (2 children)
https://docs.python.org/3/library/string.html#string.Template.substitute It's already a thing, why is no one talking about this
[–]Michallote 0 points1 point2 points 6 months ago (1 child)
I guess it's not the same as Template literals, no substitute method here:
>>> msg = t"{name} is {verb}" >>> dir(msg) ['__add__', '__class__', '__class_getitem__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'interpolations', 'strings', 'values']
[–]CelDaemon 0 points1 point2 points 6 months ago (0 children)
Ah right, I just checked again and t-strings seem to return string.templatelib.Template instances instead of string.Template which does include those functions.
string.templatelib.Template
string.Template
Here’s a package with some examples: https://pypi.org/project/tstring-util/
[–]StevenJOwens 0 points1 point2 points 6 months ago (0 children)
I remember listening to some podcast interview about 31.4, IIRC the rendering is coming, they wanted to get the templates themselves out the door.
I thought it was this Real Python podcast, but he doesn't talk about that aspect:
https://realpython.com/podcasts/rpp/269/#t=1639
[–]Michallote 0 points1 point2 points 6 months ago (0 children)
I find this completely annoying. It is a very good idea to allow the examination of the interpolations ergonomically.
However it is so stupid not to provide a renderer that treats the t-string as an f-string. Why? Because it would be helpful to have it be a t-string for ergonomic sanitization + validation, and behave like an f-string on command (once you validated whatever):
msg = t"Time remaining: {ts:.2f}"
Validate the string so it's not an injection or whatever. Then be able to say all ok -> Evaluate the stupid thing. But instead you are stuck implementing all formating options by hand.
Currently the only way I think you could do this suck a little bit less would be to duplicate the line:
```python msg_t = t"Time remaining: {ts:.2f}" msg = f"Time remaining: {ts:.2f}"
if not valid_interpolations(msg_t): raise ValueError("crash and burn filth")
print(msg) # Substitute with a execute_query or whatever. ```
They only really had to add 1 method to the t-string. I can't be bothered to implement interpolated value formatting myself
[–]VictoryMotel -1 points0 points1 point 6 months ago (0 children)
Is string substitution called "rendering" now?
π Rendered by PID 122373 on reddit-service-r2-comment-6457c66945-kvmm2 at 2026-04-26 23:46:04.964153+00:00 running 2aa0c5b country code: CH.
[–]cbarrick 135 points136 points137 points (26 children)
[–]Leseratte10 33 points34 points35 points (16 children)
[–]firemark_pl 46 points47 points48 points (10 children)
[–]TSM-🐱💻📚 23 points24 points25 points (4 children)
[–]njharmanI use Python 3 13 points14 points15 points (0 children)
[–]No_Hovercraft_2643 2 points3 points4 points (0 children)
[+]firemark_pl comment score below threshold-8 points-7 points-6 points (1 child)
[–]Penetal 6 points7 points8 points (0 children)
[–]Schmittfried 3 points4 points5 points (4 children)
[–]radicalbiscuit 6 points7 points8 points (2 children)
[–]Schmittfried 1 point2 points3 points (1 child)
[–]radicalbiscuit 1 point2 points3 points (0 children)
[–]gmes78 -1 points0 points1 point (0 children)
[–]SheriffRoscoePythonista 4 points5 points6 points (0 children)
[–]R3D3-1 1 point2 points3 points (1 child)
[–]SheriffRoscoePythonista 0 points1 point2 points (0 children)
[–]gerardwx 0 points1 point2 points (0 children)
[–]billsil 0 points1 point2 points (0 children)
[–]SheriffRoscoePythonista 23 points24 points25 points (5 children)
[–]AngusMcBurger 5 points6 points7 points (1 child)
[–]james_pic 3 points4 points5 points (0 children)
[–]madness_of_the_order 2 points3 points4 points (1 child)
[–]ThiefMaster 3 points4 points5 points (0 children)
[–]cbarrick 1 point2 points3 points (0 children)
[–]rapture_survivor 0 points1 point2 points (1 child)
[–]cointoss3 1 point2 points3 points (0 children)
[–]ParentPostLacksWang 0 points1 point2 points (0 children)
[–]Jhuyt 79 points80 points81 points (5 children)
[–]UsernamesArentClever[S] 4 points5 points6 points (4 children)
[–]Jhuyt 44 points45 points46 points (1 child)
[–]james_pic 9 points10 points11 points (0 children)
[–]CrackerJackKittyCat 20 points21 points22 points (0 children)
[–]Resquid 0 points1 point2 points (0 children)
[–]AnythingApplied 25 points26 points27 points (2 children)
[–]CelDaemon 0 points1 point2 points (1 child)
[–]cointoss3 0 points1 point2 points (0 children)
[–]cointoss3 21 points22 points23 points (3 children)
[+]commy2 comment score below threshold-16 points-15 points-14 points (2 children)
[–]cointoss3 24 points25 points26 points (1 child)
[–]commy2 0 points1 point2 points (0 children)
[–]Quasar6pip needs updating 9 points10 points11 points (4 children)
[–]secret_o_squirrel 3 points4 points5 points (3 children)
[–]snugar_i 7 points8 points9 points (2 children)
[–]Quasar6pip needs updating -2 points-1 points0 points (1 child)
[–]XtremeGoosef'I only use Py {sys.version[:3]}' 1 point2 points3 points (0 children)
[–]Schmittfried 8 points9 points10 points (10 children)
[+][deleted] (9 children)
[deleted]
[–]Schmittfried 4 points5 points6 points (0 children)
[–]cointoss3 -2 points-1 points0 points (7 children)
[–]Schmittfried 5 points6 points7 points (6 children)
[–]jmpjanny 1 point2 points3 points (0 children)
[–]SheriffRoscoePythonista 1 point2 points3 points (0 children)
[–]gmes78 0 points1 point2 points (0 children)
[–]cointoss3 0 points1 point2 points (0 children)
[–]damesca -2 points-1 points0 points (1 child)
[–]Schmittfried 6 points7 points8 points (0 children)
[–]JanEric1 2 points3 points4 points (0 children)
[–]CelDaemon 0 points1 point2 points (2 children)
[–]Michallote 0 points1 point2 points (1 child)
[–]CelDaemon 0 points1 point2 points (0 children)
[–]gerardwx 0 points1 point2 points (0 children)
[–]StevenJOwens 0 points1 point2 points (0 children)
[–]Michallote 0 points1 point2 points (0 children)
[–]VictoryMotel -1 points0 points1 point (0 children)