Formatowanie ciągu znaków w Python

Po opublikowaniu poprzedniego artykułu z cyklu „Wtorki z Pythonem” dostałem od jednego z czytelników pytanie, dlaczego tak dziwnie skonstruowałem wywołanie funkcji print(). Chodziło o zapis print("(%s,%s)" % (self.x, self.y)). W dzisiejszym artykule wyjaśnię, dlaczego taka konstrukcja była konieczna, oraz jak można ten sam wynik osiągnąć innymi sposobami. Odpowiednie formatowanie zmiennych by wyświetlić je na ekranie konsoli to jedna z podstawowych umiejętności jakie każdy musi posiadać.

Uważaj na typy danych

Język Python jest bardzo wygodny. Pozwala zapomnieć o tym, że niektóre czynności z punktu widzenia naszego komputera nie są tak proste i oczywiste. Na przykład łączenie dwóch ciągów tekstu w całość. W języku C trzeba było do tego używać dedykowanych funkcji i jeszcze często pamiętać o odpowiedniej alokacji pamięci. W Pythonie po prostu dwie zmienne zawierające tekst łączymy operatorem +

Ułatwienia jakie mamy w Pythonie czasem rozleniwiają i zapominamy, że zmienne mogą być różnego typu. Tylko w określonych przypadkach interpreter wie co ma zrobić. Wróćmy do kodu z poprzedniego artykułu. Za pomocą metody pozycja() wyświetlamy wartości zmiennych x oraz y w naszej klasie, ale nie jako cyfry, ale jako sformatowany tekst:

(2,3)

Na wyświetlany tekst składają się oczywiście dwie cyfry, aktualne wartości zmiennych, lecz wyświetlamy je w ciągu tekstowym. Dlaczego więc nie zadziała poniższy zapis?

print("(" + self.x + "," + self.y + ")")
Traceback (most recent call last):
  File "", line 19, in 
File "", line 11, in pozycja
TypeError: can only concatenate str (not "int") to str

Z komunikatu błędy wyraźnie widzimy, że niedozwolone jest łączenie (konkatenacja) ze sobą zmiennych typu str (ciąg znaków) oraz w naszym przypadku int (liczby).

Łączenie ciągu znaków i liczby i formatowanie wyniku

Pokażę Ci teraz pięć metod na łączenie ze sobą ciągu znaków i liczby w jeden ciąg znaków, aby wypisać go na ekranie. W ten sam sposób możesz wynik połączenia przypisać do innej zmiennej. Spójrz na poniższe przykłady:

str() i __str__()

str() to klasa systemowa służąca do obsługi zmiennych zawierających różne rodzaje tekstu. Dla uproszczenia w wielu publikacjach napisane jest, że to po prostu konwersja prawie dowolnego ciągu czy innej zmiennej na tekst. Można przyjąć takie uproszczenie, ale zapominamy wtedy o wbudowanych w klasę metodach pozwalających na prawie dowolne operacje na tekście. Zresztą sami zobaczcie w dokumentacji języka Python.

        print("(" + str(self.x) + "," + str(self.y) + ")")

Jeżeli nie potrzebujemy przeprowadzać jakiś skomplikowanych operacji na utworzonym ciągu to możemy użyć funkcji __str__(), która jest dostępna dla większości typów obiektów w języku Python. Stosując dziedziczenie możemy ją także nadpisać (overload) w naszej klasie. Jest ona powiązana z klasą str() w ten sposób, że zwraca jej wartość w postaci tekstu. 

        print("(" + self.x.__str__() + "," + self.y.__str__() + ")")

W przypadku prostych, systemowych typów danych możemy obie metody używać naprzemiennie.

Formatowanie ciągu znaków

Poniższy zapis najbardziej przypomina konstrukcje stosowane w języku C. I działa dość podobnie, gdyż jest to formatowanie ciągu znaków za pomocą operatora % (modulo). 

       print("(%s,%s)" % (self.x, self.y))

Technika ta zwana jest interpolacją i znana jest ze starszych wersji języka Python. W Python3 już nie zaleca się jej stosowania. Polega ona na tym, że w ciągu tekstowym umieszczamy informację o typie ciągu (u nas to %s wskazujący na ciąg znakowy).  Następnie po zamknięciu ciągu znaków (znajduje się on pomiędzy "" lub '') po kolejnym znaku % umieszczamy po kolei zmienne, które mają być sformatowane. Operatory formatowania opisane są w dokumentacji.

W wersji 3.6 języka Python została dodana funkcjonalność formatted string literal, w skrócie zwana też jako f-string. Jest to bardzo krótka w zapisie i elastyczna metoda pozwalająca na formatowanie ciągu znaków. Formatowaną zmienną (w naszym wypadku zmienianą na tekst) umieszczamy w nawiasach klamrowych. Wywołanie poprzedzamy literą f lub F na początku.

       print(f'({self.x},{self.y})')

Metoda ta w przeciwieństwie do innych sposobów nie wymusza na programiście wiedzy o tym, ile potencjalnych danych będzie formatowanych. Daje zatem możliwość elastycznego konstruowania ciągów tekstu. Bardziej klasyczną metodą jest wywołanie funkcji format(). Ta wymaga jednak podania jako argumenty dokładnie takiej liczby parametrów, ile pół formatowania wyznaczonych za pomocą {} przewidzieliśmy, na dodatek w odpowiedniej kolejności.

       print("({},{})".format(self.x, self.y))

Subscribe
Powiadom o
guest

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

2 komentarzy
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Piotr
Piotr
29/09/2020 08:17

Cześć
Dla funkcji format można zrobić mały workaround i wykorzystać keywords, wtedy nie trzeba podawać argumentów w kolejności:

In [1]: x = 3


In [2]: y = 4

In [4]: print('{pierwszy} + {drugi} = 7'.format(pierwszy=y, drugi=x))
4 + 3 = 7

In [5]: print('{pierwszy} + {drugi} = 7'.format(drugi=x, pierwszy=y))
4 + 3 = 7

Jest to też bardziej czytelne gdyby takich argumentów było więcej, nie trzeba wtedy liczyć nawiasów klamrowych żeby widzieć gdzie została wstawiona dana zmienna.
Przydaje się w Python2, bo fstring w Python3 są jeszcze lepsze i czytelniejsze.

ZdradziĆ Ci sekretY udanego projektu automatyzacji?

(link otwiera się w nowym oknie)