Location>code7788 >text

Python 3.14 t-string is coming, how is it different from f-string?

Popularity:188 ℃/2025-04-28 20:26:28

Python recently made a big news: the PEP-750 t-string syntax has been officially adopted!

This means that Python will introduce a new string prefix in version 3.14 released in October this yeart, called Template Strings, i.e. t-string.

This is a major upgrade in string processing capabilities following f-string, aiming to provide a safer and more flexible string interpolation processing mechanism.

The basic syntax of t-string is very similar to that of f-string, but the results are very different:

name = "World"

 # f-string syntax
 formatted = f"Hello {name}!"
 print(type(formatted)) # Output: <class 'str'>
 print(formatted) # Output: Hello World!

 # t-string syntax
 templated = t"Hello {name}!"
 print(type(templated)) # Output: <class ''>
 print() # Output: ('Hello ', '')
 print([0].value) # Output: World
 print("".join(
         item if isinstance(item, str) else str()
         for item in templated
     )) # Output: Hello World!

As mentioned above, t-string is different from f-string, it does not immediately get a normal string, but returns a new typeTemplate(From Python 3.14 new standard library module)。

This type cannot be output directly as a normal string, but it provides structured access to the string and its internal interpolation expressions, allowing developers to add interception and conversion before string combination insertion values.

In a word, the core feature of t-string isDelay rendering

Why design t-string?

f-string is popular for its simplicity and ease of use, but it also has some limitations that cannot be ignored:

  1. Safety hazards: f-string can cause injection attacks when directly embedding user input into SQL queries, HTML content, or system commands
  2. Lack of conversion ability:f-string does not provide a mechanism for intercepting and converting insert values ​​before string combination
  3. Insufficient flexibility: For complex string processing tasks, f-string has limited capabilities

Improve the security of string processing

Improper use of f-string may lead to security vulnerabilities:

# Unsafe example using f-string (SQL injection risk)
 sql_query = f"SELECT * FROM users WHERE name = '{user_input}'"

 # Unsafe examples using f-string (XSS risk)
 html_content = f"<div>{user_input}</div>"

t-string allows developers to properly handle interpolation before string combination:

# Security example using t-string
 evil = "<script>alert('evil')</script>"
 template = t"<p>{evil}</p>"
 # You can define processing functions to escape content
 assert html(template) == "<p>&lt;script&gt;alert('evil')&lt;/script&gt;</p>"

Enhanced string processing flexibility

The biggest advantage of t-string is that it provides a unified string processing mechanism, allowing developers to implement various custom rendering logic according to actual needs. This design avoids the complexity of creating a specialized syntax for each scenario while maintaining a simple and unified style of Python.

The following example shows how to output different formats using different renderers based on the same t-string template:

from import Template, Interpolation

 data = {"name": "Python Cat", "age": 18}
 template = t"The age of user {data['name']} is {data['age']}"

 def standard_renderer(template: Template) -> str:
     """Standard text rendering"""
     return "".join(
         item if isinstance(item, str) else str()
         for item in template
     )

 def json_renderer(template: Template) -> str:
     """JSON format rendering"""
     import json, re
     values ​​= {}
     for item in template:
         if not isinstance(item, str):
             # Use regular expression to extract key names from expressions
             # Match name in data['name'] or data["name"] mode
             match = (r"\['([^']+)'\]|\[\"([^\"]+)\"\]", )
             If match:
                 # Get the first matched group
                 key = (1) if (1) else (2)
                 values[key] =
     return (values, ensure_ascii=False)
    
 def xml_renderer(template: Template) -> str:
     """XML format rendering"""
     parts = ["<data>"]
     for item in template:
         if not isinstance(item, str) and hasattr(item, "expression"):
             name = ("'")[1] if "'" in else
             (f" <{name}>{}</{name}>")
     ("</data>")
     return "\n".join(parts)

 # Same template, different output formats
 print(standard_renderer(template)) # Output: The age of the user Python cat is 18
 print(json_renderer(template)) # Output: {"name": "Python Cat", "age": 18}
 print(xml_renderer(template)) # Output: <data>\n <name>Python Cat</name>\n <age>18</age>\n</data>

This flexibility is not available to f-string and is very valuable for building various DSLs (domain-specific languages), template engines, or formatting systems.

The structure of the Template class

t-string after evaluationTemplateA class has the following main properties and methods:

class Template:
     strings: tuple[str, ...]
     """
     A non-empty tuple of the string part in the template.
     Contains N+1 elements, where N is the number of interpolated expressions in the template.
     """

     interpolations: tuple[Interpolation, ...]
     """
     Tuples of the interpolated portions in the template.
     If there is no interpolation expression, this will be an empty tuple.
     """

     def __new__(cls, *args: str | Interpolation):
         """
         Create a new Template instance.
         Parameters can be provided in any order.
         """
         ...

     @property
     def values(self) -> tuple[object, ...]:
         """
         Returns a tuple composed of the `value` attribute of each Interpolation in the template.
         If there is no interpolation expression, this will be an empty tuple.
         """
         ...

     def __iter__(self) -> Iterator[str | Interpolation]:
         """
         Iterate over string parts and interpolation expressions in the template.
         These may appear in any order.  No empty string is included.
         """
         ...

This structure enables developers to:

  • Access the original string fragment (strings

  • Access interpolation expression and its calculation results (interpolations

  • Get all interpolated values ​​directly (values

  • Iterate all components of the template in order

Note:__iter__Function comments say that the order of occurrence is not fixed, but its specific implementation in the PEP document is in sequence. I think the comments are incorrect.

Similarities and differences between t-string and f-string

Similarities

  1. Basic syntax: Both use curly braces{}As a separator for interpolation expression
  2. Expression evaluation: Both support placing any Python expression in curly braces
  3. Format specifier: Both support format specifiers (such as.2f) and conversion specifiers (e.g.!r
  4. Quotation mark support: All valid quote marks are supported ('"'''""")
  5. Case-insensitive prefixtandTAll are valid, just likefandF

The difference

  1. Return type: f-string returns directlystrtype, and t-string returnsTemplatetype
  2. Evaluation timing: f-string is evaluated immediately when defined, t-string provides delayed evaluation capability
  3. Structural access:t-string allows access to the structure of the original template (string part and interpolation part)
  4. Processing Model: f-string is the "instant completion" model, t-string is the "preprocessing + conversion" model

Application scenarios of t-string

1. Secure HTML templates

Use t-string to create HTML templates that automatically escape user input:

def html(template: Template) -> str:
     parts = []
     for item in template:
         if isinstance(item, str):
             (item)
         else: #Interpolation
             (html_escape())
     return "".join(parts)

 user_input = "<script>alert('XSS')</script>"
 safe_html = html(t"<div>{user_input}</div>")
 # Output: <div>&lt;script&gt;alert('XSS')&lt;/script&gt;</div>

2. Secure SQL query construction

t-string can build anti-injection SQL queries:

def safe_sql(template: Template) -> str:
    parts = []
    params = []
    
    for item in template:
        if isinstance(item, str):
            (item)
        else:
            ("?")
            ()
    
    return "".join(parts), params

user_id = "user' OR 1=1--"
query, params = safe_sql(t"SELECT * FROM users WHERE id = {user_id}")
# query: "SELECT * FROM users WHERE id = ?"
# params: ["user' OR 1=1--"]

3. Structured logs

Use t-string to achieve elegant structured logging:

import json
 import logging
 from import Template, Interpolation

 class TemplateMessage:
     def __init__(self, template: Template) -> None:
          = template
    
     @property
     def message(self) -> str:
         # Format as readable message
         return f() # Use custom f() function
    
     @property
     def values(self) -> dict:
         # Extract structured data
         return {
             :
             for item in
         }
    
     def __str__(self) -> str:
         return f"{} >>> {()}"

 action, amount, item = "traded", 42, "shrubs"
 (TemplateMessage(t"User {action}: {amount:.2f} {item}"))
 # Output: User traded: 42.00 shrubs >>> {"action": "traded", "amount": 42, "item": "shrubs"}

4. Safe child process calls

PEP-787 has been specifically extended to the scenario of t-string in child process calls, so thatsubprocessThe module can natively support t-string:

# Unsafe f-string usage
 (f"echo {message_from_user}", shell=True) # Command injection risk

 # Safe t-string usage
 (t"echo {message_from_user}") # Automatically perform appropriate command escape

This method not only preserves the readability of string commands, but also avoids security risks.

Advanced usage of t-string

1. Custom multi-function template renderer

The real power of t-string is that it can customize renderer templates:

from import Template, Interpolation
 import html

 def smart_renderer(template: Template, context="text") -> str:
     """Context-aware renderer
     Automatically decide how to handle each interpolation according to the context parameter:
     - "text": Normal text mode, directly converted to string
     - "html": HTML pattern, automatically escape HTML special characters, prevent XSS
     - "sql": SQL mode, automatically escapes SQL special characters to prevent injection
     """
     parts = []
    
     for item in template:
         if isinstance(item, str):
             (item)
         else: #Interpolation
             value =
             expression =
            
             # Intelligent processing based on value type and context
             if context == "html":
                 # HTML pattern: Automatically escape HTML special characters
                 ((str(value))))
             elif context == "sql":
                 # SQL Mode: Prevent SQL Injection
                 if isinstance(value, str):
                     # Escape 1 single quote into 2
                     escaped_value = ("'", "''")
                     (f"'{escaped_value}'")
                 elif value is None:
                     ("NULL")
                 else:
                     (str(value))
             else:
                 (str(value))
    
     return "".join(parts)

 # Automatic adaptation rendering of the same template in different contexts
 user_input = "<script>alert('evil')</script>"
 template = t"User input: {user_input}"
 print(smart_renderer(template, context="html")) # Output: User input: &lt;script&gt;alert(&#x27;evil&#x27;)&lt;/script&gt;

 # SQL injection protection example
 user_id = "1'; DROP TABLE users; --"
 sql_template = t"SELECT * FROM users WHERE id = {user_id}"
 print(smart_renderer(sql_template, context="sql")) # Output: SELECT * FROM users WHERE id = '1''; DROP TABLE users; --'

 # f-string For SQL injection, the value must be processed first and then f-string is put into
 escaped_id = user_id.replace("'", "''")
 sql_safe_id = f"'{escaped_id}'"
 print(f"SQL query(f-string): SELECT * FROM users WHERE id = {sql_safe_id}")

2. Structured nested template processing

There are essential differences between t-string and f-string when used in nesting:

#Nesting of f-string: internal expressions are evaluated immediately, information is lost
 value = "world"
 inner_f = f"inner {value}"
 outer_f = f"outer {inner_f}"
 print(outer_f) # Output: outer inner world
 print(type(outer_f)) # <class 'str'> - Just a normal string

 # t-string nesting: retain complete structural information
 inner_t = t"inner {value}"
 outer_t = t"outer {inner_t}"
 print(type(outer_t)) # <class ''>
 print(type(outer_t.interpolations[0].value)) # is also a Template object!

 # Can access and process nested structures at any depth
 user = {"name": "Alice", "age": 30}
 message = t"User{user['name']} information: {t'Age:{user['age']}'}"
 inner_template = [1].value
 print(inner_template.strings) # Output: ('Age:', '')
 print(inner_template.interpolations[0].value) # Output: 30

This structured processing power makes t-string especially suitable for building complex template systems that can delay or customize all parts of the rendering process on demand.

3. Delay evaluation and asynchronous processing

The structural nature of t-string makes it support delayed evaluation and asynchronous processing. Here is an example of asynchronous template rendering:

import asyncio

 # Simulate asynchronous data acquisition
 async def fetch_data(key: str) -> str:
     await (0.1) # simulate network latency
     return f "get {key} data"

 async def render_async(template):
     tasks = {}
     # Start all asynchronous queries in parallel
     for item in :
         tasks[] = asyncio.create_task(
             fetch_data()
         )
    
     # Wait for all queries to complete
     for expr, task in ():
         tasks[expr] = await task
    
     # Assembly results
     result = []
     for item in template:
         if isinstance(item, str):
             (item)
         else:
             (tasks[])
    
     return "".join(result)

 async def main():
     template = t"User: {user}, Age: {age}"
     result = await render_async(template)
     print(result)

 # (main())

Key advantages of this model:

  • Structural retention: You can obtain complete expression information
  • Parallel acquisition: Handle multiple asynchronous tasks simultaneously
  • Delay combination: Serve all data before splicing

Summarize

Python's t-string syntax is an important extension of string processing capabilities. It provides a more flexible and safer string interpolation processing mechanism while maintaining similarity to f-string syntax. By structuring the string template intoTemplateObjects, developers can intercept and convert interpolation before string combinations, thus avoiding common security issues and supporting more advanced use cases.

It's like a separation pattern of data and views, f-string is a directly rendered view, while t-string retains the data model, allowing you to perform various transformation rules and validations before final rendering.

The design philosophy of t-string reflects the balance of functionality and security. Although it is more complex than f-string, this complexity brings more advanced composability and greater security.

It follows Python's "explicit over implicit" principle, making every step of string processing clear by explicitly separating the template structure and rendering process.

t-string is not a syntax to replace f-string, and the simplicity and ease of use still have important value.

So, after Python 3.14, how to choose two string interpolation methods?

In a word, use f-string is more direct and efficient when simply formatting a string; and when processing untrusted input, custom rendering logic, building complex template systems, or performing asynchronous processing, you should choose a more powerful t-string.

References

  1. PEP 750 – Template Strings
  2. PEP 787 – Safer subprocess usage using t-strings
  3. Template strings accepted for Python 3.14

Python Trend Weekly Season 3 Summary, with e-book download:/posts/2025-04-20-sweekly

Python Trendy Weekly Season 2 ends (31~60):/posts/2025-04-20-iweekly

Python Trend Weekly Season 2 is over, and I share a few summary:/posts/2024-07-14-iweekly

Python Trendy Weekly Season 2 (31~60) - Pure Link Edition:/posts/2025-04-19-sweekly

Python Fashion Weekly Season 1 Highlights Collection (1~30):/posts/2023-12-11-weekly

Ten Thousand Words Condensation Edition, 800 Links to Python Trend Weekly Season 1! :/post/78c3d645-86fa-4bd8-8eac-46fb192a339e

Follow Python Cat on WeChat/python_cat.jpg