W Pythonie mamy takie coś, jak funkcja len. Możemy tego używać na napisach, listach i podaje nam to ilość elementów. Oto przykłady:

msg = "Hello world"
lst = [1,2,3]
print(len(msg)) #11
print(len(lst)) #3

Gdybyśmy jednak zastosowali len na liczbie dostaniemy błąd. Jest to o tyle dziwne, że człowiek spodziewałby się, że len zastosowane na liczbie poda mu ilość cyfr danej liczby.

Wyliczanie cyfr w liczbie to dość powszechna zabawa przy uczeniu programowania. Zrobimy to sobie na kilka sposobów.

Sposób 1 – konwersja na typ string

Liczbę na listę zamienić nie możemy, ale na napis jak najbardziej. Napis w odróżnieniu od liczby posiada swoją długość a zatem funkcja len() będzie tutaj działać:

num = 123
num_str = str(num)
print(num_str)
#123
print(len(num_str))
#3

Działa i podaje nam długość napisu czyli ilość cyfr danej liczby jakby nie patrzeć. Teraz wystarczy tylko napisać odpowiednią funkcję:

def count_digits(num):
    return len(str(num))

print(count_digits(123)) #3
print(count_digits(1234)) #4
print(count_digits(1)) #1

To takie proste.

Sposób 2 – float i int

No a co jeżeli napiszemy coś takiego, używając naszej funkcji?

def count_digits(num):
    return len(str(num))


print(count_digits(123)) #3
print(count_digits(1.23)) #4

Tutaj liczba float, czyli zmiennoprzecinkowa, została podana. Następnie zamieniono ją na tekst i podano, że cyfr jest tam 4. Kropka została policzona jako cyfra. Jak tego uniknąć?

Możemy rozbić sobie nieco naszą funkcję i wywołać replace, które zamieni kropkę (jeżeli taka istnieje) na pusty string „” i ilość znaków będzie odpowiadać ilości cyfr:

def count_digits(num):
    num_str = str(num)
    num_str = num_str.replace(".", "")
    return len(num_str)


print(count_digits(123)) #3
print(count_digits(1.23)) #3

Ale być może liczb zmiennoprzecinkowych nie chcemy traktować tak samo? Może chcemy w przypadku ich wystąpienia zwrócić dwie wartości, jedna to ilość cyfr przed przecinkiem, druga to cyfry po przecinku? Jak to osiągnąć?

Cóż, konwersję liczby do napisu już znamy. Ale napis możemy też konwertować do listy. I nie poprzez „list”, które nam zamieni każdy znak na osobny element, tego nie chcemy. Poprzez split, które przetnie nam ten napis w odpowiednim miejscu. A miejscem tym jest kropka:

num = 123.123
num_str = str(num)
print(num_str)
#123.123
print(num_str.split("."))
#['123', '123']

Jak widać, zamieniliśmy cyfrę zmiennoprzecinkową na napis. Następnie postanawiamy zamienić napis na listę, ale nie znanym nam sposobem list(), który zamieni każdy znak na nowy element, tylko funkcją split, której podajemy gdzie mamy „ciąć”.

„Tniemy” po kropce, rozbijając napis na część przed przecinkiem i po przecinku. Jesteśmy już naprawdę blisko osiągnięcia tego, do czego dążymy, czyli poznania ilości cyfr zarówno przed przecinkiem (kropką) jak i po:

num = 123.123
num_str = str(num)
print(num_str)
#123.123
before_dot, after_dot = num_str.split(".")
print(before_dot, after_dot)
#123 123
print(len(before_dot), len(after_dot))
#3 3

Tutaj zamiast z wyniku „przecięcia” napisu po kropce robić listę, „rozpakowywujemy” go do dwóch zmiennych. Wypisujemy je i ich długość. Wydawać by się mogło, że mamy już wszystko, co potrzebne do napisania naszej funkcji. Ale tak nie jest.

Nadal nie wiemy jak sprawdzić, czy liczba jest typu int czy zmiennoprzecinkowego. Nauczymy się tego szybko, na przykładzie:

def is_float(num):
    return type(num) == float

print(is_float(12.3)) #True
print(is_float(123)) #False

def is_int(num):
    return type(num) == int

print(is_int(12.3)) #False
print(is_int(123)) #True

Używając type możemy sprawdzić, czy nasza liczba jest typu zmiennoprzecinkowego, czy też nie. Oczywiście to nie jedyny sposób, można zastosować inne podejście i np. konwertować liczbę do napisu a następnie testować ją na obecność kropki:

def is_float(num):
    return "." in str(num)

def is_int(num):
    return "." not in str(num)

print(is_float(12.3)) #True
print(is_int(12.3)) #False

Jakie podejście zastosujemy, to już od nas zależy. Tym niemniej, piszemy funkcję, która ma podać ilość cyfr, jeżeli mamy do czynienia z typem int, bądź też krotkę (tuplę) z ilością cyfr przed przecinkiem i po przecinku, jeżeli otrzymaliśmy float, liczbę zmiennoprzecinkową:

def count_digits(num):
    if type(num) == int:
        return len(str(num))
    num_str = str(num)
    before_dot, after_dot = num_str.split(".")
    return len(before_dot), len(after_dot)


print(count_digits(123)) #3
print(count_digits(1.23)) #(1,2)

Tak napisana funkcja sprawdza, czy mamy do czynienia z int, liczbą bez przecinka i wtedy zwraca długość tej liczby zamienionej na tekst.

W innym przypadku zakładamy, że mamy do czynienia z typem float, gdzie zamieniamy na str, tniemy po kropce i pakujemy do dwóch zmiennych, długość jednego i drugiego zwracamy i tym sposobem mamy ilość cyfr przed przecinkiem i po przecinku.

Sposób 3 – podejście matematyczne

Po napisach możemy sobie „przechodzić” czyli iterować przy pomocy pętli for. Taka pętla przechodzi przez każdą literę/znak danego napisu i, na przykład, ją wypisuje:

for letter in "abc":
    print(letter)
    
# a
# b
# c

Cóż, znamy pętlę while. Możemy zrobić coś takiego:

num = 10
while num:
    print(num)
    num -= 1
# 10
# 9
# 8
# 7
# 6
# 5
# 4
# 3
# 2
# 1

While num oznacza „tak długo, jak num nie jest zerem”. Wewnątrz pętli wypisujemy num i zmniejszamy o 1. Zbliżamy się do naszego celu, jakim jest przejście w pętli tyle razy, ile cyfr ma dana liczba. Ale tutaj przechodzimy po liczbie 10 aż 10 razy, a przecież cyfry są tylko 2.

Można tutaj zastosować podejście matematyczne. I dzielić przez 10. Oto efekt:

num = 123
while num:
    print(num)
    num  = num // 10
# 123
# 12
# 1

Trzy cyfry – trzy obroty pętli. Teraz tylko wypada dodać jakiś licznik, który ma zero, ale przy każdym obrocie zostaje zwiększony o 1:

num = 123
cnt = 0
while num:
    print(num)
    num = num // 10
    cnt += 1
print(cnt)
# 123
# 12
# 1
#3

W zasadzie mamy już wszystko, aby napisać naszą funkcję, która weźmie liczbę i nie uciekając się do żadnych „sztuczek” z konwersjami, używając samej matematyki, wyliczy nam ilość cyfr w danej liczbie:

def count_digits(num):
    cnt = 0
    while num:
        cnt += 1
        num = num // 10
    return cnt

print(count_digits(123)) #3
print(count_digits(1)) #1

W zasadzie to jest wszystko, ale warto zwrócić uwagę na to, co się stanie, gdy przekażemy 0 do funkcji. Zmienna cnt będzie ustawiona na 0 zaś warunek while num czyli „tak długo aż num nie jest 0” nie wykona ani jednego obrotu pętli. I dostaniemy 0, choć ewidentnie 0 to 1 cyfra:

def count_digits(num):
    cnt = 0
    while num:
        cnt += 1
        num = num // 10
    return cnt
print(count_digits(0)) #0

Tutaj warto się zabezpieczyć przed takim tzw. edge case i na początku sprawdzić, czy zera ktoś nam nie podał i zwrócić wartość 1, jako że tyle cyfr ma ta liczba:

def count_digits(num):
    if num == 0:
        return 1
    cnt = 0
    while num:
        cnt += 1
        num = num // 10
    return cnt
print(count_digits(0)) #1
print(count_digits(1)) #1
print(count_digits(123)) #3

Tak wygląda podejście matematyczne. Warto je sobie zapamiętać, do różnych zabaw algorytmicznych (takich jak zamiana z systemu dziesiętnego na dowolny inny) może nam się ono kiedyś przydać.