Jeśli chodzi o pierwszy zamawianych typów, czyli typ liczbowy, to nie jest to jeden typ, ale raczej kategoria obejmująca wiele różnych typów.

W Pythonie występują takie typy liczbowe jak

  • liczby całkowite,
  • liczby zmiennoprzecinkowe,
  • liczby zespolone dziesiętne o ustalonej precyzji,liczby wymierne,
  • zbiory to z operacjami numerycznymi,
  • wartości typu prawda i fałsz, które są interpretowane odpowiednio jako 0 lub 1,
  • Funkcje i moduły takie jak math, random, round,
  • Liczby całkowite o nieograniczonej precyzji,
  • Operacje bitowe,
  • Formaty: binarny, ósemkowy i szesnastkowy.

Najczęściej wykorzystywane są liczby całkowite oraz zmiennoprzecinkowe liczby całkowite mogą być zapisane za pomocą systemu dwójkowego, ósemkowego lub szesnastkowego, a także oczywiście systemu dziesiętnego:

0o176, 0x9ff, 0b010101

>>> 0o176

126

>>> 0x9ff

2559

>>> 0b010101

21

Liczby zmiennoprzecinkowe zapisujemy ze znakiem dziesiętnym w postaci kropki lub ze specjalnym wykładnikiem po literze e lub E. Jeśli zapiszemy liczbę w ten sposób, wówczas Python zrobi z niej obiekt liczby zmiennoprzecinkowej, do której stosowana będzie arytmetyka liczb zmiennoprzecinkowych. W przeciwieństwie do innych języków programowania dla liczb całkowitych Istnieje jedynie jeden typ.

Narzędzia liczbowe

Liczby w Pythonie przekształca się przy użyciu: wyrażeń, funkcji matematycznych lub modułów narzędziowych. Wyrażenie jest to połączenie obiektów (zmiennych) z operatorami, takim jak na przykład znak dodawania, odejmowania czy mnożenia. Poza tym istnieją również takie operatory jak na przykład:

Logiczne and i or

x or y, x and y

Sprawdzanie przynależności w obiektach iterowanych

x in list
x not in my_dict

Potęgowanie

x ** 2

Test identyczności obiektów

 x is y, x is not y

Opery porównania lub równości wartości

 x > y, X <= y, x == y

Indeksowanie

 x[i]

Wycinki

 x[i:j]

Priorytety operatorów

Wyrażenia w Pythonie mogą oczywiście wykorzystywać więcej niż jeden operator. Kolejność wykonywania wyrażeń jest ustalana na podstawie priorytetu operatorów. Jeśli chcemy zmienić kolejność do wykonywania działań musimy umieścić dane wyrażenie w nawiasie. Więcej informacji można znaleźć tuaj: Precedence of Operators.

Co w przypadku operacji na liczbach różnego typu?

Jeśli w danym wyrażeniu pojawią się liczby innego typu, na przykład liczba całkowita i zmienna przecinkowa, to Python automatycznie dokona konwersji argumentów w górę, czyli do typu bardziej skomplikowanego, a następnie wykona obliczenia dla argumentów tego samego typu. Tego typu konwersje Wykonywane są tylko przy łączeniu typów liczbowych. Nie są one przeprowadzane w przypadku typów innego rodzaju, jak na przykład wtedy, gdy chcemy połączyć łańcuch znaków z liczbą.

Jak tworzone są zmienne w Pythonie?

Zmienne są tworzone poprzez przypisanie wartości do nazwy.

a = 3

Teraz, gdy zmienna a zostanie użyta w wyrażeniu, Python podstawi za nią wartość, która jest do niej przypisana.

a + 3
8

Zanim zmienna zostanie użyta w wyrażeniu, należy ją utworzyć. Zmienne nie są deklarowane, czyli nie określamy ich typu, a także zawsze odnoszą się do obiektów.

Porównania liczb: zwykłe i łączone

Liczby można porównywać używając znaku mniejszości lub większości, połączonej ze znakiem równości, a także sprawdzać, czy są one sobie równe lub nierówne. W tego typu przypadkach można stosować mieszane typy liczbowe, a więc na przykład porównywać liczbę całkowitą z liczbą zmiennoprzecinkową. Porównania można łączyć w taką postać jak na przykład X < Y <Z czy co równa się X< Y and Y <Z lub X<Y>Z, co równa się X<Y and Y>Z. Należy uważać na używanie wartości False lub True w porównaniach, jako że mogą dawać one nieintuicyjne wyniki. Na przykład poniższe wyrażenie zwraca wartość False, mimo tego, że intuicyjnie interpretujemy je jako 0 < 3:

1 == 2 < 3
False

Dzielenie: klasyczne, bez reszty i prawdziwe

Sposób dzielenia liczb jest różny w Pythonie w wersji 3.x i 2.x. Nie będziemy tutaj wchodzić w szczegóły tego, jak dzielenie wygląda w wersji 2.x. W Pythonie 3.x dzielenie odbywa się na dwa sposoby:

  • Przy użyciu operandu / dokonywane jest dzielenie prawdziwe, które zachowuje resztę dla wyników zmiennoprzecinkowych, niezależnie od tego, jakiego typu są obiekty w wyrażeniu
  • Przy użyciu  operatora // przeprowadzane jest dzielenie bez reszty, które zawsze odcina część ułamkową w wyniku bez względu na typ obiektu.

W Pythonie 2.x występowały jeszcze dzielenie klasyczne przy użyciu operatora /, które wykonywało dzielenie w zależności od typu liczb użytych w wyrażeniu.

Wyniki różnych typów dzielenia w Pythonie 3.9.13

>>> 8/3
2.6666666666666665
>>> 8 // 3.0
2.0
>>> 8 // 3
2

Typ danych operatora // jest uzależniony od typu operandów. Jeśli który z nich jest liczbą zmiennoprzecinkową, to wynik również będzie liczbą zmiennoprzecinkową. Należy jednak zwrócić uwagę na to że typ zwracanej wartości jest zazwyczaj o wiele mniej istotny niż wartość.

Jeżeli używasz Pythona w wersji 2.x, to do dzielenia liczb całkowitych z odcinaniem reszty powinieneś użyć operatora //. Natomiast jeśli chcesz, aby wyniki były liczbami zmiennoprzecinkowymi z resztą, wówczas powinieneś użyć typu float, aby zagwarantować, że jeden z operatów będzie liczbą zmiennoprzecinkową.

X = Y / float(Z)

Jak tak naprawdę działa operator //? Czy aby na pewno odcina on resztę od wyniku dzielenia? Okazuje się, że ten operator robi jedną, istotną rzecz, istotną zwłaszcza w kontekście liczb ujemnych – zaokrągla wynik w dół. Żeby się o tym przekonać, należy  poeksperymentować z liczbami ujemnymi.

>>> 5//-3
-2
>>> 5//3
1

Ujemny wynik może być zatem nieco mylący, gdy otrzymujemy liczbę inną niż w przypadku wyniku dodatniego. Jeśli zależy nam na odczytaniu reszty zamiast zaokrąglenia w dół, to powinniśmy użyć funkcji math.trunc()z pojedynczym znakiem dzielenia. 

>>> math.trunc(5/-3)
-1

Różne rodzaje notacji: dwójkowa, ósemkowa i szesnastkowa

W Pythonie liczby można zapisać w różnych systemach numerycznych, w tym dwójkowym, ósemkowym oraz szesnastkowym. Ich wartości w pamięci komputera są takie same, są one po prostu różnie zapisywane.

Podstawa 2:

>>> 0b1, 0b1110
(1, 14)

Podstawa 8:

>>> 0o70, 0o07
(56, 7)

Podstawa 16:

>>> 0x1, 0x11
(1, 17)

Domyślnie Python wyświetla liczby oczywiście w systemie dziesiętnym, ma jednak wbudowane funkcje, które pozwalają na konwersję tych liczb na odpowiadające im zapisy w innych podstawach. Są to funkcje: bin(), oct() oraz hex().

Liczby zapisane w innych systemach niż dziesiętny można również szybko rozszyfrować przy użyciu funkcji int(), podając jako pierwszy argumenty liczbę, a następnie podstawę.

>>> int('100', 8)
64
>>> int('100', 2)
4
>>> int('100', 16)
256

Formatowania liczby całkowitych można również dokonać poprzez metody wykorzystujące formatowanie ciągów znaków.

>>> '{0:o}, {1:b}, {2:x}'.format(64, 64, 64)
'100, 1000000, 40'

Operacje na poziomie bitów

Python umożliwia przeprowadzanie operacji na poziomie bitów, gdzie operatory traktują liczby całkowite właśnie jako ciągi bitów. Jest to użyteczne w obsłudze takich zagadnień jak przetwarzanie pakietów sieciowych czy spakowanych danych binarnych.

>>> x = 1
>>> x << 2 #przesunięcie o dwa bity w lewo
4
>>> x | 2 #logiczne or
3
>>> x & 1 #logiczne and
1

Za pomocą operacjach na bitach można również kodować lub wyodrębnić flagi i inne informacje w pojedynczej liczbie całkowitej.

Wbudowane funkcje i moduły bibliotek

Python oferuje wiele wbudowanych funkcji i moduł bibliotek, które służą do przetwarzania danych liczbowych. Należy do nich na przykład funkcja abs() oraz pow(), które służą odpowiednio do obliczania wartości bezwzględnej liczb oraz potęgowania. Osoby chcące skorzystać z dodatkowych moduł bibliotek muszą pamiętać o ich zaimportowaniu. Główną biblioteką służącą do przeprowadzania operacji matematycznych jest moduł math. Inne przydatne funkcje wbudowane to sum(), min() oraz max(), które jako argumenty przyjmują pojedynczą liczbę lub ich sekwencję.

>>> sum((1,2,3,4))
10
>>> max((1,2,3,4))
4
>>> min((1,2,3,4))
1

Liczby można również zaokrąglać lub odcinać resztę, przy użyciu takiej funkcji jak round() oraz trunc().

>>> math.trunc(2.567)
2
>>> round(2.567)
3

Moduł random

Bardzo przydatnym modułem, zwłaszcza do przeprowadzania testów, jest moduł random. Służy on do wybierania losowej liczby, przy czym domyślnie jest to liczba zmiennoprzecinkowa z zakresu 0-1, ale możliwe jest na przykład uzyskanie losowej liczby całkowitej o wartości z określonego przedziału:

>>> random.random()
0.1557803215573932
>>> random.randint
(1,10)7

Warta poznania jest również funkcja choice() oraz shuffle(). Ta pierwsza służy do wybierania losowego elementu z danej sekwencji,  natomiast funkcja shuffle() tasuje elementy w kolekcji (jako argument podaliśmy listę [] będącą typem kolekcji).

>>> random.choice(['a', 'b', 'c'])
'a'
>>> names = ['Ala', 'Jaś', 'Karo']
>>> random.shuffle(names)
>>> print(names)
['Jaś', 'Karo', 'Ala']

Moduł random jest wykorzystywany do wybierania liczby losowych, wyświetlania zdjęć w galerii czy chociażby symulacji gry w karty.

Inne typy liczbowe

Typ Decimal

Jest to specjalny typ liczbowy, dla którego należy zaimportować funkcję Decimal z modulu decimal. Liczby tego typu to liczby zmiennoprzecinkowe o stałej liczbie miejsc dziesiętnych, a więc o stałej precyzji.

Dlaczego powstał typ Decimal?

Ograniczenia miejsca wykorzystywanego do przechowywania wartości sprawiają, że arytmetyka liczb zmiennoprzecinkowych jest niedokładna. Dobrym tego przykładem jest poniższe działanie:

>>> 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17

Wykorzystanie liczb dziesiętnych typu Decimal zapewnia jednak precyzyjny wynik.

>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Decimal('0.0')

Podobnie jak w przypadku zwyczajnych liczb również liczby typu Decimal są konwertowane w górę do liczby z największą liczbą miejsc dziesiętnych:

>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.30')
Decimal('0.00')

Jak globalnie ustawić precyzję liczby dziesiętnych (Decimal)?

Istnieje możliwość ustawienia precyzji wszystkich liczb dziesiętnych, a także trybu zaokrąglania liczby tworzonych w danym module. Ustawienia obejmują wszystkie liczby dziesiętne tworzone w danym wątku wywołującym.

>>> from decimal import Decimal
>>> Decimal('7')/Decimal('0.3')
Decimal('23.33333333333333333333333333')
>>> import decimal
>>> decimal.getcontext().prec = 4
>>> Decimal('7')/Decimal('0.3')Decimal('23.33')

Typ Fraction, czyli ułamek

Od wersji 2.6 Pythona można używać nowego typu liczbowego, który służy do reprezentacji liczb wymiernych. Zachowuje on zarówno licznik, jak i mianownik, aby zapewnić dokładność arytmetyki liczby zmiennoprzecinkowych. Zarówno typ Fraction, jak i Decimal są stosowane w celu zapewnienia większej dokładności liczby zmiennoprzecinkowych. Jak tworzy się liczby typu Fraction? W tym celu należy zaimportować odpowiedni moduł, a następnie użyć konstruktora z licznikiem i mianownikiem.

>>> from fractions import Fraction
>>> x = Fraction(1,3)
>>> y = Fraction(5,6)
>>> x+yFraction(7, 6)

Obiekt typu fraction może być tworzony również z liczby zmiennoprzecinkowych:

>>> Fraction(0.25)
Fraction(1, 4)

Liczby ułamkowe zwiększają dokładność wyniku, a także w znaczny sposób go upraszczają. Porównaj:

>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>> Fraction(1,10) + Fraction(1,10) + Fraction(1,10) - Fraction(3,10)
Fraction(0, 1)

Konwersja liczb zmiennoprzecinkowych na ułamki

Żeby przekonwertować liczbę zmiennoprzecinkową na typ Fraction należy użyć metody (czyli funkcji właściwej dla danego typu danych) from_float obiektu Fraction.

>>> Fraction.from_float(2.75)
Fraction(11, 4)

Stosowanie mieszanych typów jest dozwolone, jednak czasami w celu zachowania precyzji należy dokonać konwersji liczby na typ Fraction:

>>> x + 2
Fraction(7, 3)
>>> x + 3.0
3.3333333333333335
>>> x + Fraction(3, 9)
Fraction(2, 3)

Zbiory

Zbiory w Pythonie to typ liczbowy, pomimo tego, że nie stanowią one pojedynczego elementu, lecz są typem kolekcji. Jest to kolekcja składająca się z elementów:

  • unikalnych, czyli każdy element może pojawić się w zbiorze tylko raz, 
  • niemutowalnych, a więc takich, które nie mogą zostać zmienione w miejscu, zatem zbiory nie mogą zawierać list i słowników, które mogą być mutowalne. Typy elementów, jakie mogą zawierać zbiory to liczby, łańcuchy znaków oraz krotki. Same zbiory są z kolei mutowalne. 

Zbiory są nieuporządkowanym typem obiektów, a więc nie zachowują pierowtnego porządku elementów. Zbiory obsługują działania odpowiadające matematycznej teorii zbiorów. Na zbiorach można dokonywać iteracji, można rozszerzać lub zmniejszać je, a także umieszczać w nich obiekty różnych typów.

Zbiory tworzy się przy wykorzystaniu funkcji set() lub konstruktora {}:

>>> x = set('abcde')
>>> x
{'a', 'b', 'e', 'd', 'c'}
>>> y = set((1,2,3,4))
>>> y
{1, 2, 3, 4}
>>> v={0,9,8,7}
>>> v
{0, 9, 8, 7}
>>> type(v)
<class 'set'>
  • Jak widać w pierwszym przykładzie zbiory nie zachowują pierwotnego porządku.
  • W przykładzie y = set((1,2,3,4)) liczby należy umieścić w krotce, ponieważ zbiór oczekuje argumentu, który jest iterowany, a więc takiego, po którym można po kolei przejść.
  • Konstrukcja przy użyciu nawiasów klamrowych może przypominać sposób tworzenia słowników. Jest to dobre przeczucie, ponieważ zbiory to tak właściwie słowniki bez wartości, jako że ich elementy, podobnie jak klucze słownika, są nieuporządkowane, unikalne oraz niemutowalne.
  • Uwaga! Jeśli jednak chcemy utworzyć pusty zbiór, musimy użyć do tego funkcji set.

Operacje na zbiorach

Na zbiorach można przeprowadzać większość standardowych działań matematycznych wykonywanych na zbiorach, takich jak:

Różnica zbiorów 

>>> x - y

{'a', 'b', 'e', 'd', 'c'}

Suma zbiorów 


>>> x | y

{1, 2, 3, 4, 'a', 'b', 'e', 'd', 'c'}

Iloczyn zbiorów

>>> x | y
{'a', 1, 2, 3, 4, 'b', 'c', 'e', 'd'}

Wyznaczanie podzbiorów:

>>> x > y, x < y

(False, False)

Zbiory obsługują również test przynależności:

>>> 'b' in x

True

>>> 1 in y

True

Przydatne metody zbiorów to dodawanie elementów, a także ich usuwanie.

>>> x.add('z')

>>> x

{'a', 'b', 'e', 'd', 'z', 'c'}

>>> y.remove(3)

>>> y

{1, 2, 4}

Zbiory są iterowane, a więc mogą być wykorzystywane w pętlach for, listach składanych, a także jako argument funkcji len(), która wyznacza długość danej kolekcji. Warto stosować zbiory, ponieważ cechują się one wydajnymi algorytmami oraz technikami implementacyjnymi, dzięki którym wykonywanie standardowych operacji jest szybsze.

Sposobem na to, aby osadzać zbiory w zbiorach jest skorzystanie z konstruktora frozenset, który tworzy zbiory niemutowalne:

>>> test = frozenset('XYZ')

>>> x.add(test)

>>> x

{'a', 'b', frozenset({'X', 'Y', 'Z'}), 'e', 'd', 'z', 'c'}

Jak szybko stworzyć złożony zbiór? Zbiory składane

Od wersji 3.x Pythona możliwa jest tworzenie zbiorów na podstawie tak zwanej konstrukcji składanej. Taką konstrukcję należy zapisać w nawiasach kalmrowych, aby poinformować Pythona, że chce się utworzyć zbiór. Pętla jest umieszczona po prawej stronie, natomiast wyrażenie zbierające po lewej. Zbiory składane są przetwarzane w pętli i zbierają wyniki wyrażeń z każdą literacją. Następnie tworzony jest nowy zbiór.

>>> {x * 3 for x in [1,2,3]}

{9, 3, 6}

 Powyższa instrukcja znaczy mniej więcej tyle co: ”utwórz nowy zbiór, który składa się z x pomnożonych przez 3 dla każdego x z podanej listy”.

Do iteracji można wykorzystać dowolny typ literowalny, taki jak chociażby łańcuch znaków.

>>> {x * 2 for x in 'python'}

{'yy', 'hh', 'oo', 'tt', 'nn', 'pp'}

Do czego mogą przydać się zbiory?

Zbiory to oczywiście najlepsze rozwiązanie dla osób, które szukają typu obiektu przeznaczonego do wykonywania działań na zbiorach. Jednak zbiory mają również wiele innych zastosowań, choćby takich jak odfiltrowywanie duplikatów z kolekcji innego typu, takich jak listy. Należy jednak przy tym pamiętać, że w wyniku tej operacji dochodzi do zmiany kolejności elementów w kolekcji:

>>> L = [1,2,3,4,4,5,5,6,0,2]

>>> set(L)

{0, 1, 2, 3, 4, 5, 6}

Zbiory mogą być również stosowane do wyznaczania różnic w różnych typach kolekcji:

>>> set('Ala ma kota') - set('Ala ma psa')

{'k', 'o', 't'}

Nieco bardziej skomplikowany przykład – różnica atrybutów między dwoma typami:

>>> set(dir(int)) - set (dir(str))

{'denominator', 'conjugate', '__round__', '__abs__', '__rrshift__', '__ceil__', '__rand__', '__float__', '__trunc__', '__pow__', '__rfloordiv__', '__and__', 'to_bytes', 'real', 'imag', 'bit_length', '__int__', '__rshift__', '__xor__', '__pos__', 'from_bytes', '__floor__', 'numerator', '__rlshift__', '__rxor__', '__invert__', '__floordiv__', '__divmod__', '__rsub__', 'as_integer_ratio', '__bool__', '__rdivmod__', '__rpow__', '__neg__', '__ror__', '__lshift__', '__rtruediv__', '__or__', '__truediv__', '__radd__', '__sub__', '__index__'}

Zbiory służą również do testowania równości dwóch obiektów, niezależnie od kolejności ich elementów. Jest to dobre rozwiązanie wtedy, gdy chce się uniknąć na przykład sortowania łańcuchu znaków, które jest operacją zajmującą dość sporo czasu. W poniższym przykładzie używamy dość krótkich łańcuchów znaków, jednak warto sobie wyobrazić sytuację, w której należy porównać tysiące słów z danej listy.

>>> 'Python' == 'thyPon'

False

>>> set('Python') == set('thyPon')

True

Wartości Boolean

Kolejnym typem wartości zaliczanym do typu liczbowych jest wartość Boolean, czyli bool. Jest on uznawany za typ liczbowy, ponieważ jego dwie wartości: True i False są  wyświetlane jako 1 lub 0, i jednak w kodzie programu zachowują się dokładnie tak jak liczby całkowite 1 i 0. Mówiąc konkretniej typ bool tworzy instancję klasy bool, która ma zredefiniowane swoje własne meotdy formatujące sposób wyświetlania danego obiektu, czyli str() i repr().

>>> True - 1

0

>>> True + True

2

>>> True == 1

True

>>> True is 1

False

Choć wartość True równa się 1, to zmienna jedynką nie jest 😉

To by było na razie tyle, jeśli chodzi o typy liczbowe. W kolejnych postach zapoznamy się z łańcuchami znaków Pythonie.