Mateusz Mazurek – programista z pasją

Python, architektura, ciekawostki ze świata IT

Felietony/inne Programowanie

Python 1.0 vs. 3.13: Co się zmieniło?

Cześć.

Dziś luźny artykuł, bo dziś pobawimy się jedną z pierwszy wersji Pythona. Skompilujemy go i zobaczymy co tam w nim wtedy siedziało.

Krótko o historii

Python 1.0 był pierwsza „oficjalną” wersją Pythona. Wcześniej była tylko wersja 0.9 wydana w 1991 roku, która jednak wymagała dopracowania, ale finalnie, 15 lutego 1994 roku, po poprawkach, stała się wersją 1.0.1 (tak, do wersji 1.0 były jeszcze poprawki). Można przyjąć, że od tego momentu język zaczął się naprawdę sprawnie rozwijać. W 1995 roku mieliśmy już wersję 1.2, po drodze była jeszcze 1.6 aż w końcu, w roku 2000, wydana została wersja 2.0. Rok 2006 to już wersja 2.5 a 2009 to pierwsza, naprawdę poważna wersja 2.7, którą spora część z Was może jeszcze pamiętać. Co ciekawe, wsparcie do wersji 2.7 trwało aż do stycznia 2020. To naprawdę długo. Wynikało to z tego, że migracja pomiędzy Pythonem 2 a 3 wymagała zmian w kodzie. Pisząc ten artykuł mamy wersje 3.13.

Zobaczmy jak to się zaczynało.

Kompilacja Pythona 1.0

Na początek muszę wspomnieć, że skompilowanie tak starego kodu nie jest takie proste, bo na nowoczesnych Linuxach biblioteki zależne są znacznie nowsze niż wtedy, kiedy ten kod pisano. Więc trzeba się było trochę nagimnastykować. Żeby cokolwiek uruchomić na tym Pythonie napisałem Dockerfile’a:

1
2
3
4
5
6
7
8
9
10
FROM feverch/debian-legacy:4
RUN apt-get update && apt-get install -y --force-yes wget build-essential gcc make libreadline-dev zlib1g-dev nano

ARG package
ADD $package ./

WORKDIR /python-1.0.1
RUN ./configure && make

ENTRYPOINT [ "/bin/sh" ]

Zauważ, że obraz budowany jest z starego Debiana. Paczka z samym Pythonem jest przekazywana jako argument, bo openssl w tak starym Debianie nie pozwalał ściągnąć pliku poprzez nowoczesne HTTPS. Trochę z tym walczyłem, ale skończyło się na tym, że napisałem dodatkowy kawałek basha:

1
2
3
4
5
6
7
8
9
10
#! /bin/bash
name=$1

echo "Building Python 1.0.1"
if [ ! -f python1.0.1.tar.gz ]; then
    wget https://legacy.python.org/download/releases/src/python1.0.1.tar.gz
fi
docker build . -t python1 --build-arg package=python1.0.1.tar.gz

docker run --rm -it -v "$PWD"/code:/code python1 ./python /code/"$name"

Który ściąga Pythona 1.0.1, buduje obraz Dockera i uruchamia go z nazwą pliku do uruchomienia. Całość buduje się bardzo szybko i sprawnie. Zakładając, że pokazany wyżej plik bash będzie nazywał się „run_py1.sh” to uruchomienie skryptu pod Pythonem 1.0.1 będzie wyglądało tak:

./run_python-1.sh test.py

Gdzie plik „test.py” będzie szukamy w folderze o nazwie „code”. Folder musi znajdować się w zaraz obok tego pliku bash’owego.

Pobawmy się!

Na rozgrzewkę coś łatwego:

1
2
3
import sys

print sys.version

Wynik jest do przewidzenia: 1.0.1 (Feb 12 2025), czyli wersja i data kompilacji. Warto zauważyć, że w Pythonie wcześniejszym niż 3, funkcja print była używana nie jak funkcja a jak słowo kluczowe, tj bez nawiasów, więc, żeby odróżnić ten kod od aktualnych wersji, będę używał printa bez nawiasów. Teraz taki kod by się nie skompilował.

To teraz coś bardziej skomplikowanego:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
f = open("test.csv", "w")

data = [
    ["r1c1", "r1c2"],
    ["r2c1", "r2c2"],
    ["r3c1", "r3c2"]
]

for x in data:
    f.write(x[0] + ", " + x[1] + "\n")

f.close()

f = open("test.csv", "r")
for x in f.readlines():
    print x

Też działa! ALE, zapis tego samego kodu z użyciem context manager już powoduje SyntaxError. Co ciekawe, to rezultat jest dosłownie tylko taki. Czyli tylko „SyntaxError” bez pokazania gdzie dany błąd się znajduje. Plus brak nowej linii na końcu pliku też powoduje SyntaxError. Czasem. A czasem działa ok.

Idźmy dalej!

1
2
3
4
5
6
7
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('www.mmazurek.dev', 80))
request = "GET / HTTP/1.0\r\nHost: www.mmazurek.dev\r\n\r\n"
s.send(request)
data = s.recv(1024)
print data

Sockety też działają!

Natomiast coś chyba średnio skompilowałem, bo

1
import time

rzuca błąd „Segmentation fault (core dumped)”. Jest to dość ogólny błąd pamięci, który wskazuje na próbę uzyskania dostępu do pamięci do której nie ma się dostępu (albo robi to się w niewłaściwy sposób). Ogólnie każdy kto pisał coś z C/C++ na pewno kiedyś ten błąd widział. Przykład programu w C++, którego efekt jest Segmentation fault:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;
    std::cout << "Zaraz wystąpi błąd segmentation fault..." << std::endl;
   
    // Próba dostępu do elementu poza zakresem wektora
    std::cout << vec[1000000] << std::endl;
   
    return 0;
}

Czekaj, stop!

Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:
a w ramach prezentu otrzymasz całkowicie za darmo, dwa dokumenty PDF „6 (nie zawsze oczywistych) błędów popełnianych podczas nauki programowania” który jest jednym z efektów ponad siedmioletniej pracy i obserwacji rozwoju niejednego programisty oraz „Wstęp do testowania w Pythonie”, będący wprowadzeniem do biblioteki PyTest.
Jeśli to Cię interesuje to zapraszam również na swoje social media.

Jak i do ewentualnego postawienia mi kawy :)
Postaw mi kawę na buycoffee.to

A jak wydajność?

Zabawy za nami, więc zobaczmy jak wygląda to pod względem wydajności. Wygenerowałem kawałek kodu w LLMie, który pomoże nam zobaczyć różnice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import math

def prime_numbers(limit):
    primes = []
    for num in range(2, limit):
        is_prime = 1
        for div in range(2, int(math.sqrt(num)) + 1):
            if num % div == 0:
                is_prime = 0
                break
        if is_prime:
            primes.append(num)

    return primes

def cpu_stress_test(iterations, size):
    result = []
    for _ in range(iterations):
        primes = prime_numbers(size)
        result.append(primes)

cpu_stress_test(5000, 1000)

I odpalamy!

Wersja PythonaCzas uruchomienia
1.07.75s
3.64.37s
3.132.22s
3.13 + JIT (link do artykułu, jeśli nie wiesz czym jest JIT)2.01s
PyPy 3.10-7.3.17672.12 ms (!)

Jak widać różnice są ogromne. Testy robiłem systemową funkcją time, nie są one ani trochę rzetelne, nie mniej jednak pokazują różnice.

Zmierzając do końca

Jak widać, Python na swojej drodze do zostania jednym z najpopularniejszych języków programowania, przeszedł wiele zmian, optymalizacji i unowocześnień, coraz lepiej odpowiadając na potrzeby swoich użytkowników. Jestem przekonany, że twórcy nie powiedzieli jeszcze ostatniego słowa.

Dzięki za wizytę,
Mateusz Mazurek

A może wolisz nowości na mail?

ZOSTAW ODPOWIEDŹ

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

This site uses Akismet to reduce spam. Learn how your comment data is processed.