F-stringi to nowoczesny i bardzo wygodny sposób na osadzanie wartości zmiennych i wyrażeń bezpośrednio w tekście ciągu znaków. Aby utworzyć f-string, wystarczy umieścić literę f przed otwierającym cudzysłowem. Wewnątrz ciągu znaków można wstawiać zmienne, umieszczając je w nawiasach klamrowych {}, a Python automatycznie zastąpi {zmienna} jej wartością.
<em># Tworzymy dwie zmienne z wartościami liczbowymi.</em>
jeden = 1
dwa = 2
<em># Wstawiamy wartości zmiennych 'jeden' i 'dwa' do ciągu znaków.</em>
f"{jeden}, {dwa}"
<em># Wynik: '1, 2'</em>
Siła f-stringów leży w ich zdolności do zawierania nie tylko zmiennych, ale niemal każdego poprawnego wyrażenia Pythona wewnątrz nawiasów klamrowych. Pozwala to na wykonywanie obliczeń w locie, bez tworzenia niepotrzebnych zmiennych pośrednich.
<em># Wykonujemy dodawanie bezpośrednio wewnątrz f-stringa.</em>
f"{jeden} + {dwa} = {jeden + dwa}"
<em># Wynik: '1 + 2 = 3'</em>
Można łatwo uzyskać dostęp do elementów struktur danych, takich jak słowniki (po kluczu) i listy (po indeksie), z poziomu f-stringa. Dotyczy to również dostępu do atrybutów obiektów (za pomocą notacji kropkowej), a nawet wywoływania funkcji.
<em># Dostęp do wartości słownika po kluczu.</em>
kolory = {"czerwony": "#ff0000", "zielony": "#00ff00"}
f"Kolor czerwony to {kolory['czerwony']}"
<em># Wynik: 'Kolor czerwony to #ff0000'</em>
<em># Dostęp do elementów listy po indeksie.</em>
dane = [4, 8, 15, 16, 23, 42]
f"Najlepsze liczby: {dane[4]} i {dane[5]}"
<em># Wynik: 'Najlepsze liczby: 23 i 42'</em>
<em># Dostęp do atrybutów obiektu i wywoływanie funkcji.</em>
from dataclasses import dataclass
@dataclass
class Punkt:
x: int
y: int
poz = Punkt(23, 42)
f"Pozycja: {poz.x}, {poz.y}. Większa wartość to {max(poz.x, poz.y)}."
<em># Wynik: 'Pozycja: 23, 42. Większa wartość to 42.'</em>
Używanie f-stringów do debugowania
Od wersji Pythona 3.8, f-stringi zawierają potężne narzędzie do szybkiego debugowania. Umieszczając znak równości (=) po zmiennej lub wyrażeniu w nawiasach klamrowych, instruujesz Pythona, aby wyświetlił nie tylko wartość, ale także samo wyrażenie. Jest to niezwykle przydatne do inspekcji kodu.
from datetime import date, timedelta
uzytkownik = "eric_idle"
czlonek_od = date(1975, 7, 31)
delta = date(2022, 4, 11) - czlonek_od
<em># Użyj f"{uzytkownik=}", aby automatycznie uzyskać "uzytkownik='eric_idle'".</em>
f"{uzytkownik=} {czlonek_od=}"
<em># Wynik: "uzytkownik='eric_idle' czlonek_od=datetime.date(1975, 7, 31)"</em>
Ten specyfikator debugowania można łączyć ze standardowymi specyfikatorami formatowania. Można go również używać na całych wyrażeniach, aby wyraźnie pokazać zarówno obliczenia, jak i ich wynik.
<em># = pokazuje nazwę zmiennej, : rozpoczyna formatowanie, ,d formatuje jako liczbę całkowitą z separatorem tysięcy.</em>
f"{delta.days=:,d}"
<em># Wynik: 'delta.days=17,056'</em>
<em># Pokazuje zarówno dane wejściowe, jak i wynik obliczeń.</em>
from math import cos, radians
theta = 30
f"{theta=} {cos(radians(theta))=:.3f}"
<em># Wynik: 'theta=30 cos(radians(theta))=0.866'</em>
Drukowanie reprezentacji debugowania (repr)
Obiekty w Pythonie mają dwie reprezentacje tekstowe: str() (dla czytelnego dla użytkownika wyniku) i repr() (dla jednoznacznego, przyjaznego dla programisty wyniku). Domyślnie f-stringi używają str(). Aby uzyskać wynik z repr(), można użyć flagi konwersji !r, która jest wygodnym skrótem do ręcznego wywołania repr(). Flaga !s jawnie wywołuje str().
class Dane:
def __repr__(self): return '<Dane ...>' <em># Reprezentacja do debugowania</em>
def __str__(self): return 'reprezentacja tekstowa' <em># Reprezentacja dla użytkownika</em>
dane = Dane()
<em># Domyślne zachowanie wywołuje str(dane)</em>
f"dane: {dane}"
<em># Wynik: 'dane: reprezentacja tekstowa'</em>
<em># Użycie flagi !r wywołuje repr(dane)</em>
f"dane: {dane!r}"
<em># Wynik: 'dane: <Dane ...>'</em>
Dopełnianie, wyrównywanie i przycinanie
F-stringi zapewniają pełną kontrolę nad formatowaniem wyniku, w tym szerokością pola, wyrównaniem i znakami dopełniającymi. Specyfikator formatu umieszcza się po dwukropku (:).
>wyrównuje tekst do prawej.<wyrównuje tekst do lewej.^centruje tekst.- Liczba po znaku wyrównania określa całkowitą szerokość pola.
- Znak przed znakiem wyrównania określa znak dopełniający.
val = "test"
<em># Wyrównaj do prawej w polu o szerokości 10 znaków</em>
f"{val:>10}"
<em># Wynik: ' test'</em>
<em># Wyrównaj do lewej i wypełnij puste miejsce znakiem '_'</em>
f"{val:_<10}"
<em># Wynik: 'test______'</em>
<em># Wyśrodkuj tekst</em>
f"{val:^10}"
<em># Wynik: ' test '</em>
Aby przyciąć ciąg znaków do maksymalnej długości, użyj składni :.N, gdzie N to liczba znaków.
instrument = "xylophone"
<em># Przytnij ciąg do 5 znaków.</em>
f"{instrument:.5}"
<em># Wynik: 'xylop'</em>
Reprezentacje liczbowe
Dla liczb dostępnych jest wiele opcji formatowania. Można łatwo konwertować liczby na system binarny (b), ósemkowy (o) lub szesnastkowy (x/X). Użycie # dodaje odpowiedni prefiks (0b, 0o, 0x).
odpowiedz = 42
<em># Reprezentacja szesnastkowa</em>
f"{odpowiedz:x}, {odpowiedz:X}"
<em># Wynik: '2a, 2A'</em>
<em># Z prefiksem</em>
f"{odpowiedz:#x}"
<em># Wynik: '0x2a'</em>
<em># Notacja naukowa</em>
f"{odpowiedz ** 8:e}"
<em># Wynik: '9.682652e+12'</em>
Dopełnianie liczb zerami wiodącymi odbywa się za pomocą 0 jako znaku dopełniającego. W przypadku liczb zmiennoprzecinkowych można kontrolować zarówno całkowitą szerokość, jak i liczbę miejsc po przecinku. Można również kontrolować wyświetlanie znaku i dodawać separatory tysięcy.
<em># Dopełnij do 4 cyfr zerami wiodącymi</em>
f"{odpowiedz:04d}"
<em># Wynik: '0042'</em>
<em># Dla Pi: całkowita szerokość 6, 2 miejsca po przecinku, dopełnij zerami</em>
import math
f"{math.pi:06.2f}"
<em># Wynik: '003.14'</em>
<em># Zawsze pokazuj znak (+ dla dodatnich, - dla ujemnych)</em>
f"{odpowiedz:+d}"
<em># Wynik: '+42'</em>
<em># Użyj przecinka jako separatora tysięcy</em>
num = 1234567890
f"{num:,d}"
<em># Wynik: '1,234,567,890'</em>
Dodatkowe tematy i najlepsze praktyki
Wiele wbudowanych obiektów, takich jak datetime, ma swój własny mini-język formatowania, który działa bezpośrednio w f-stringach.
from datetime import datetime
dt = datetime(2022, 4, 11, 13, 37)
<em># Użyj standardowych kodów formatowania daty/czasu</em>
f"{dt:%Y-%m-%d %H:%M}"
<em># Wynik: '2022-04-11 13:37'</em>
Same specyfikatory formatu nie muszą być statyczne! Można je ustawiać dynamicznie za pomocą zmiennych z zagnieżdżonymi nawiasami klamrowymi.
<em># Szerokość i wyrównanie są ustawiane przez zmienne</em>
wartosc = "test"
wyrownanie = "^"
szerokosc = 10
<em># f'{wartosc:{wyrownanie}{szerokosc}}' najpierw staje się f'{wartosc:^10}'</em>
f'{wartosc:{wyrownanie}{szerokosc}}'
<em># Wynik: ' test '</em>
Jeśli musisz umieścić w ciągu znaków dosłowny nawias klamrowy { lub }, po prostu go podwój.
f"Dosłowne nawiasy klamrowe: {{wartosc}}"
<em># Wynik: 'Dosłowne nawiasy klamrowe: {wartosc}'</em>
Praktyczne porady i pułapki
Czytelność kontra zwięzłość
F-stringi pozwalają na osadzanie bardzo złożonych wyrażeń, ale nie zawsze jest to dobry pomysł. Podstawową zasadą w Pythonie jest czytelność kodu.
Zła praktyka (trudne do odczytania):
<em># Technicznie działa, ale co tu się dzieje?</em>
print(f"Wynik: {round([user.height for user in users if user.active][0] / 2.54, 2)} cali.")
Dobra praktyka (oddzielenie logiki od prezentacji):
<em># 1. Najpierw przygotowujemy dane</em>
wzrost_aktywnych_uzytkownikow = [user.height for user in users if user.active]
wzrost_pierwszego_uzytkownika_cm = wzrost_aktywnych_uzytkownikow[0]
wzrost_w_calach = round(wzrost_pierwszego_uzytkownika_cm / 2.54, 2)
<em># 2. Następnie formatujemy wynik</em>
print(f"Wynik: {wzrost_w_calach} cali.")
Kod jest dłuższy, ale stał się zrozumiały, testowalny i łatwy do modyfikacji. Ogólna zasada: jeśli wyrażenie wewnątrz {} jest dłuższe niż 20-30 znaków lub zawiera złożoną logikę, lepiej wyodrębnić je do osobnej zmiennej.
Wieloliniowe f-stringi
W przypadku długich ciągów znaków lub generowania tekstu wieloliniowego (np. zapytań SQL, szablonów HTML), użyj potrójnych cudzysłowów. Wyrażenia wewnątrz również mogą być wieloliniowe.
uzytkownik = "hypo69"
uprawnienia = ['read', 'write', 'execute']
<em># Wyrażenie w f-stringu jest rozbite na wiele linii dla czytelności</em>
raport = f"""
Raport użytkownika: {uzytkownik.upper()}
----------------------------------
Uprawnienia: {', '.join(p for p in uprawnienia
if p != 'execute')}
"""
print(raport)
Kwestie bezpieczeństwa (Bardzo ważne!)
Same f-stringi są bezpieczne. Wyrażenie takie jak f"Witaj, {imie}" nie stanowi luki w zabezpieczeniach; Python po prostu pobiera reprezentację tekstową imie.
Niebezpieczeństwo pojawia się, gdy łączysz f-stringi z funkcjami wykonującymi kod, takimi jak eval().
NIEZWYKLE NIEBEZPIECZNY KOD — NIGDY TEGO NIE RÓB:
import os
szablon_uzytkownika = input("Wprowadź szablon: ") <em># Użytkownik wprowadza: '{os.system("reboot")}'</em>
eval(f'f"{szablon_uzytkownika}"')
Jeśli użytkownik wprowadzi {os.system("reboot")}, twój serwer zostanie ponownie uruchomiony. Jest to luka typu Remote Code Execution (RCE). Nigdy nie przekazuj ciągów znaków dostarczonych przez użytkownika do funkcji takich jak eval(), nawet za pośrednictwem f-stringów. Do szablonów definiowanych przez użytkownika używaj bezpiecznych silników szablonów, takich jak Jinja2.
Wydajność: dlaczego f-stringi są tak szybkie?
F-string nie jest wywołaniem funkcji; jest częścią składni Pythona. Kompilator analizuje f-string i dzieli go na części dosłowne oraz wyrażenia do obliczenia. Jest to kompilowane do wysoce wydajnego kodu bajtowego, który buduje końcowy ciąg znaków w jednej szybkiej operacji. Jest to znacznie szybsze niż metoda .format(), która musi analizować ciąg formatujący w czasie wykonywania.
Narzędzia do migracji
Narzędzia takie jak flynt i pyupgrade mogą automatycznie konwertować stary kod używający .format() i % na nowoczesne f-stringi, co jest świetnym sposobem na szybką modernizację starszej bazy kodu.