Labas, Pasauli!

Funkcijos

VP
Vilius P.
2024-09-24
2024-12-15

Sąvoką funkciją turėjote matyti iki šio momento daug kartų. Šį kartą būsime jų kūrėjai ir išsiaiškinsime, kaip jos veikia viduje.

Anksčiau funkciją naudojome, kaip juodą dėžę:

Čia x laikykime, kaip visumą argumentų, kuriuos rašėme į funkciją. Juoda dėžė tai funkcijos kodo eilutės, sakiniai, kurių kviesdami mes nematėme. Išvestis y yra tas rezultatas, kurį gaudavome iškvietę funkciją (kartais grąžinamos reikšmės gali nebūti, pvz. print()). Mūsų užduotis - išmokti kurti tą juodą dėžę.

Dar galima funkciją įsivaizduoti, kaip mašiną, įrenginį gamykloje, kuriai vienoje vietoje duodami kažkokios tai žaliavos, o tos mašinos kitoje pusėje gaunami pabaigtas produktas. Tas grąžinimas yra tarsi galutinio produkto atkeliavimas konvejerio juosta.

Tic-tac-toe perdarymas

Temoje „ciklas cikle“ turėjote padaryti tic-tac-toe žaidimą. Pabandysime keletą vietų perdaryti su funkcijomis. Pasiruoškite analizuotį didelį kiekį kodo.

.py
tic-tac-toe.py
# 3x3 Tic-Tac-Toe board
board = [
    [' ', ' ', ' '],
    [' ', ' ', ' '],
    [' ', ' ', ' ']
]

game_running=True
print("-"*25)
print("Tic-Tac-Toe @ Vilijus")
print("-"*25)
print("Rules:")
print("1. First player is defined as P1 and he places \"X\" on the board;")
print("2. Second player is defined as P2 and he places \"O\" on the board;")
print("3. Enter coordinates in the format \"X,Y\"; for example, \"1,2\" - this represents the first row and the second column.")
print("="*50) 
print("")

print("Game Board:")

print("-"*13)
for row in board:
    for cell in row: 
        print(f"| {cell} ", end="")
    print("|")
    print("-"*13)
print()

moves=0
while true:

    # Game winning condition 
    winners_status = ["", "", "", ""]

    # Player 1 input processing
    P1_input_ok = False
    while not P1_input_ok:
        P1_input = input("Enter P1 (X) move: ")
        P1_input_split = P1_input.split(",")
        if(len(P1_input_split) != 2):
            print("Invalid coordinates entered!")
            continue
        P1_x = int(P1_input_split[0])
        P1_y = int(P1_input_split[1])
        if(not (1 <= P1_x <= 3 and 1 <= P1_y <= 3)):
            print("Coordinates are outside the board!")
            continue   
        if(board[P1_x-1][P1_y-1] != ' '):
            print("Cell is occupied!")
            continue
        board[P1_x-1][P1_y-1] = "X"
        P1_input_ok = True

    moves=moves+1


    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)
    
    if("X" in winners_status):
        print("Player 1 won the game")
        print("Congratulations!!!")
        break

    if(moves >= 9):
        print("No more moves left; the game is ended.")
        break

    # Player 2 input processing
    P2_input_ok = False
    while not P2_input_ok and 4:
        P2_input = input("Enter P2 (0) move: ")
        P2_input_split = P2_input.split(",")
        if(len(P2_input_split) != 2):
            print("Invalid coordinates entered!")
            continue
        P2_x = int(P2_input_split[0])
        P2_y = int(P2_input_split[1])
        if(not (1 <= P2_x <= 3 and 1 <= P2_y <= 3)):
            print("Coordinates are outside the board!")
            continue   
        if(board[P2_x-1][P2_y-1] != ' '):
            print("Cell is occupied!")
            continue
        board[P2_x-1][P2_y-1] = "0"
        P2_input_ok = True

    # Game board drawing
    print("\nGame Board:")
    print("-"*13)
    for row in board:
        for cell in row: 
            print(f"| {cell} ", end="")
        print("|")
        print("-"*13)
    print()

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)

    if("0" in winners_status):
        print("Player 2 won the game")
        print("Congratulations!!!")
        break

Pažymėtos eilutės (21-27 ir 70-78) veikia taip pat. Programavime tokius pat veiksmus mums reikia atlikinėti dažnai. Kad nereiktų perrašinėti kodo, kuris veikia taip pat, programuotojai kuria funkcijas. Kol kas mes funkcijas kvietėme, bet dar nesusitikome jų aprašymų.

Pagrindiniai funkcijų principai:

  • Funkcija gali grąžinti (funkcijos kvietimą galima panaudoti, kaip reikšmę) reikšmę, pvz.: sum(), avg(), len() ;
  • Funkcija gali negrąžinti reikšmės, o tik atlikti veiksmus, pvz.: print();
  • Funkcija turi būti aprašyta anksčiau (aukščiau), negu ji iškviečiama (panaudojama);
  • Funkcija gali turėti parametrus (tai, ką galima įrašyti į skliausteliius) arba gali neturėti;
  • Funkcija turėtų būti sukurta vienai paskirčiai.

Kad galėtume kurti funkcijas dar reikia pasiaiškinti pagrindines sintaksės dalis:

.py
# - "def" is a keyword that is always required to define a function.
# - "my_function" is the function name that will be used to call it.
# It is best to name the function based on its functionality.
# - "parameter1" is the first parameter.
# - "parameter2" is the second parameter.
# Lines that are indented from the function header make up the function body.
# "return 10" means that the function returns the value 10.

def my_function(parameter1, parameter2):
    print(f"My parameters: {parameter1}, {parameter2}")
    return 10

my_number = my_function(1, 2)
print(f"Function 'my_function' returned: {my_number}")

Eilutės nuo 9-11 yra funkcija, kuri iškviečiama 13-14 eilutėse. Funkcija visada prasideda raktažodžiu def, o po jas seka funkcijos pavadinimas ir skliausteliuose parametrai. Toliau funkcijoje (atitraukus nuo kairės) rašomi funkcijos sakiniai. Funkcija baigiasi paskutine atitraukta eilute arba return raktažodžiu.

Grįžkime prie kodo perdarymo. Perkelkime žaidimo lentos paišymo kodą:

.py
tic-tac-toe.py
def draw_board():
    print("\nGame Board:")
    print("-"*13)
    for row in board:
        for cell in row: 
            print(f"| {cell} ", end="")
        print("|")
        print("-"*13)
    print()


# 3x3 Tic-Tac-Toe board
board = [
    [' ', ' ', ' '],
    [' ', ' ', ' '],
    [' ', ' ', ' ']
]

game_running=True
print("-"*25)
print("Tic-Tac-Toe @ Vilijus")
print("-"*25)
print("Rules:")
print("1. First player is defined as P1 and he places \"X\" on the board;")
print("2. Second player is defined as P2 and he places \"O\" on the board;")
print("3. Enter coordinates in the format \"X,Y\"; for example, \"1,2\" - this represents the first row and the second column.")
print("="*50) 

draw_board()

moves=0
while game_running:

    # Game winning condition 
    winners_status = ["", "", "", ""]

    # Player 1 input processing
    P1_input_ok = False
    while not P1_input_ok:
        P1_input = input("Enter P1 (X) move: ")
        P1_input_split = P1_input.split(",")
        if(len(P1_input_split) != 2):
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        P1_x = int(P1_input_split[0])
        P1_y = int(P1_input_split[1])
        if(not (1 <= P1_x <= 3 and 1 <= P1_y <= 3)):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        if(board[P1_x-1][P1_y-1] != ' '):
            print("Cell is occupied! Please choose another cell.")
            continue
        board[P1_x-1][P1_y-1] = "X"
        P1_input_ok = True
    
    moves=moves+1

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)


    if("X" in winners_status):
        print("Player 1 won the game")
        print("Congratulations!!!")
        break

    if(moves >= 9):
        print("No more moves left; the game is ended.")
        break

    # Player 2 input processing
    P2_input_ok = False
    while not P2_input_ok and 4:
        P2_input = input("Enter P2 (0) move: ")
        P2_input_split = P2_input.split(",")
        if(len(P2_input_split) != 2):
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        P2_x = int(P2_input_split[0])
        P2_y = int(P2_input_split[1])
        if(not (1 <= P2_x <= 3 and 1 <= P2_y <= 3)):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        if(board[P2_x-1][P2_y-1] != ' '):
            print("Cell is occupied! Please choose another cell.")
            continue
        board[P2_x-1][P2_y-1] = "0"
        P2_input_ok = True

    moves=moves+1

    # Game board drawing
    draw_board()

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)


    if("0" in winners_status):
        print("Player 2 won the game")
        print("Congratulations!!!")
        break

Nuo 1 iki 9 eilutės sukūrėme funkciją, kurios pavadinimas draw_board(), ji neturi jokių parametrų. 29 ir 73 eilutėje ją iškviesdami panaudojame. Taip išvengėme pakartojimų ir jeigu norėsime pakeisti lentos piešimą, tą reikės padaryti tik vienoje vietoje, anksčiau būtų reikėję pakeisti dvi kodo vietas.

Šiame kode yra dar panašių vietų, kurias galėtume perdaryti su funkcijomis:

.py
tic-tac-toe.py
    # ...
    # Player 1 input processing
    P1_input_ok = False
    while not P1_input_ok:
        P1_input = input("Enter P1 (X) move: ")
        P1_input_split = P1_input.split(",")
        if(len(P1_input_split) != 2):
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        P1_x = int(P1_input_split[0])
        P1_y = int(P1_input_split[1])
        if(not (1 <= P1_x <= 3 and 1 <= P1_y <= 3)):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        if(board[P1_x-1][P1_y-1] != ' '):
            print("Cell is occupied! Please choose another cell.")
            continue
        board[P1_x-1][P1_y-1] = "X"
        P1_input_ok = True

    # Player 2 input processing
    P2_input_ok = False
    while not P2_input_ok and 4:
        P2_input = input("Enter P2 (0) move: ")
        P2_input_split = P2_input.split(",")
        if(len(P2_input_split) != 2):
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        P2_x = int(P2_input_split[0])
        P2_y = int(P2_input_split[1])
        if(not (1 <= P2_x <= 3 and 1 <= P2_y <= 3)):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        if(board[P2_x-1][P2_y-1] != ' '):
            print("Cell is occupied! Please choose another cell.")
            continue
        board[P2_x-1][P2_y-1] = "0"
        P2_input_ok = True
    # ...

Iš esmės šie du blokai atlieka vienodos veiksmus, tik vienu atveju tai padaroma su pirmuoju žaidėju, kitu su antruoju. Galima padaryti bendrą funkcija:

.py
def draw_board():
    print("\nGame Board:")
    print("-"*13)
    for row in board:
        for cell in row: 
            print(f"| {cell} ", end="")
        print("|")
        print("-"*13)
    print()

def process_player_input(player_symbol, board):
    while True:
        player_input = input(f"Enter {player_symbol} move (format: x,y): ")
        input_split = player_input.split(",")
        
        if len(input_split) != 2:
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        
        x = int(input_split[0])
        y = int(input_split[1])

        if not (1 <= x <= 3 and 1 <= y <= 3):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        
        if board[x-1][y-1] != ' ':
            print("Cell is occupied! Please choose another cell.")
            continue
        
        return x,y

# 3x3 Tic-Tac-Toe board
board = [
    [' ', ' ', ' '],
    [' ', ' ', ' '],
    [' ', ' ', ' ']
]

game_running=True
print("-"*25)
print("Tic-Tac-Toe @ Vilijus")
print("-"*25)
print("Rules:")
print("1. First player is defined as P1 and he places \"X\" on the board;")
print("2. Second player is defined as P2 and he places \"O\" on the board;")
print("3. Enter coordinates in the format \"X,Y\"; for example, \"1,2\" - this represents the first row and the second column.")
print("="*50) 

draw_board()

moves=0
while game_running:
    # Game winning condition 
    winners_status = ["", "", "", ""]

    # Player 1 input processing
    x1, y1 = process_player_input("P1", board)
    board[x1-1][y1-1] = "X"
    moves=moves+1

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)

    if("X" in winners_status):
        print("Player 1 won the game")
        print("Congratulations!!!")
        game_running = False
    
    x2, y2 = process_player_input("P2", board)
    board[x2-1][y2-1] = "0"
    moves=moves+1

    # Game board drawing
    draw_board()

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)


    if("0" in winners_status):
        print("Player 2 won the game")
        print("Congratulations!!!")
        game_running = False

Padarėme funkciją, kuri grąžina koordinates, kurios yra teisingos ir tada naudojantis jomis papildome lentą. Vėl išvengiame kodo dublikavimo. Šioje vietoje kodas sumažėjo nuo 113 iki 98. Šioje vietoje reiktų atkreipti į tai, kad galima grąžinti dvi reikšmes, ir į tai, kaip jos panaudojamos.

Kodą dar galima tiesiog iškelti į funkciją, taip kažkuriai kodo daliai uždedant vardą. Dabar žaidimo laimėjimo patikrinimą iškelkime į atskirą funkciją:

.py
def draw_board(board):
    print("\nGame Board:")
    print("-"*13)
    for row in board:
        for cell in row: 
            print(f"| {cell} ", end="")
        print("|")
        print("-"*13)
    print()

def process_player_input(player_symbol, board):
    while True:
        player_input = input(f"Enter {player_symbol} move (format: x,y): ")
        input_split = player_input.split(",")
        
        if len(input_split) != 2:
            print("Invalid coordinates entered! Please enter in the format x,y.")
            continue
        
        x = int(input_split[0])
        y = int(input_split[1])

        if not (1 <= x <= 3 and 1 <= y <= 3):
            print("Coordinates are outside the board! Please enter values between 1 and 3.")
            continue   
        
        if board[x-1][y-1] != ' ':
            print("Cell is occupied! Please choose another cell.")
            continue
        
        return x,y

def check_winner(board):
    # Game winning condition 
    winners_status = ["", "", "", ""]

    # Check rows
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != ' ':
            winners_status[0]=row[0]

    # Check columns
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != ' ':
            winners_status[1]=board[0][col]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != ' ':
        winners_status[2]=board[0][0]  # Return the winning symbol (X or O)
    
    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != ' ':
        winners_status[3]=board[0][2]  # Return the winning symbol (X or O)

    if("X" in winners_status):
        print("Player 1 won the game")
        print("Congratulations!!!")
        return True

    if("0" in winners_status):
        print("Player 2 won the game")
        print("Congratulations!!!")
        return True

    return False


# 3x3 Tic-Tac-Toe board
board = [
    [' ', ' ', ' '],
    [' ', ' ', ' '],
    [' ', ' ', ' ']
]

game_running=True
print("-"*25)
print("Tic-Tac-Toe @ Vilijus")
print("-"*25)
print("Rules:")
print("1. First player is defined as P1 and he places \"X\" on the board;")
print("2. Second player is defined as P2 and he places \"O\" on the board;")
print("3. Enter coordinates in the format \"X,Y\"; for example, \"1,2\" - this represents the first row and the second column.")
print("="*50) 

draw_board()

moves=0
while game_running:

    # Player 1 input processing
    x1, y1 = process_player_input("P1", board)
    board[x1-1][y1-1] = "X"
    
    if check_winner(board):
        break
    
    moves=moves+1
    if(moves >= 9):
        print("No more moves left; the game is ended.")
        break

    x2, y2 = process_player_input("P2", board)
    board[x2-1][y2-1] = "0"

    if check_winner(board):
        break

    # Game board drawing
    draw_board(board)

Dabar pažvelkime į sutvarkytą kodą, pagrindinę jo dalį, nuo 87 eilutės. Šis kodas tapo labiau abstraktesnis, išskirstytas į akivaizdžius procesus:

  1. Apdorojima pirmojo žaidėjo įvestis;
  2. Apdorojama antrojo žaidėjo įvestis;
  3. Atspausdina žaidimų lentą;
  4. Patikrinama, ar yra laimėtojas.

Tokį suskirstymą mums leidžia padaryti funkcijos. Toliau gilinsimės į funkcijų principus ir detales.

Reikšmes grąžinančios funkcijos ir negrąžinančios

Tarkime turime dvi paprastas funkcijas - viena turi return raktažodį, kita ne. Pasižiūrėkime, kas atsitinka, kai jos priskiriamos kintamąjam.

.py
def function_that_returns():
    a = 10 + 10
    return a

def func_that_not_returns():
    a = 10 + 10
    # This function does not return a value

v1 = function_that_returns()
print(f"First function value: {v1}")

v2 = func_that_not_returns()
print(f"Second function value: {v2}")  # This will print "None" because the function does not return anything.

Tokio kodo išvestis:

cmd
First function value: 20
Second function value: None

Matome, kad pirmoji funkcija kintamajam suteikia tokį reikšmę, kuri yra užrašyta po return, kita nesuteikia reikšmės arba konkrečiau suteikia None reikšmę, kuri reiškia reikšmės neegzistavimą.

Taip pat funkcija gali kartu grąžinti ir negrąžinti reikšmės, priklausomai nuo argumentų. Tarkime, norime sukurti funkciją, kuri grąžina mums skaičiaus atvirkštinį skaičių. Atvirkštinis skaičius gaunamas vienetą padalinus iš to skaičiaus: . Tik problema ta, kad matematikoje, kartu ir programavime, negalima dalyba iš nulio. Tokiu atveju, kai gausime argumentą nulį, mes grąžinsime None.

.py
def inverse_number(x):
    if x != 0:
        return 1 / x
    return None

arguments = [10, 0, -15]
for x in arguments:
    y = inverse_number(x)
    if y is None:
        print(f"Number {x} has no inverse number")
    else:
        print(f"{y:.3f} is the inverse for {x}")

Tokio kodo išvestis:

cmd
0.100 is the inverse for 10
Number 0 has no inverse number
-0.067 is the inverse for -15

None grąžinimą galima pakeisti į kitus du ekvivalentiškus - veikimas bus lygiai toks pat. Galima neprirašyti grąžinamos reikšmės:

.py
def inverse_number(x):
    if x != 0:
        return 1 / x
    return

arba galima pabaigti funkciją be return raktažodžio:

.py
def inverse_number(x):
    if x != 0:
        return 1 / x

Visais trimis atvejais funkcija grąžins None reikšmę.

Keletas grąžinamų reikšmių

Python kalba suteikia galimybę su funkcija grąžinti daugiau negu vieną reikšmę, nors dažniausiai funkcijos kuriamos, kad grąžintų vieną reikšmę. Sukurkime funkciją, kuri iš datos (pvz. 2024-09-24) ištrauktų atskiras datos reikšmes: metų, mėnesio ir dienos:

.py
def extract_date_components(date_string):
    year, month, day = date_string.split("-")
    return int(year), int(month), int(day)

date = "2024-09-24"
year, month, day = extract_date_components(date)

print(f"Year: {year}, Month: {month}, Day: {day}")

Terminale turėtumėte matyti tokią išvestį:

cmd
Year: 2023, Month: 9, Day: 23

Kai funkcija grąžina keletą reikšmių, nėra būtina nurodyti tiek pat kintamųjų. Galima nurodyti vieną kintamąjį ir atskiras grąžinamas reikšmes pasiekti su indeksais (kaip sąraše):

.py
def extract_date_components(date_string):
    year, month, day = date_string.split("-")
    return int(year), int(month), int(day)

date = "2024-09-24"
date_components = extract_date_components(date)

print(f"Year: {date_components[0]}, Month: {date_components[1]}, Day: {date_components[2]}")

Reikšmių išpakavimas su *

Kai jau žinome, kad gali būti funkcijų su keleta grąžinamų reikšmių, išmoksime dar vieną gudrybę - jų išpakavimą.

Pirmiausiai, pasižiūrėkime, kad keletas grąžinamų reikšmių veikia, kaip sąrašo reikšmės grąžinimas. Demonstracijai sukurkime funkciją, kuri grąžina skaičiaus kartotinių. Jeigu nurodysime, kad ir , tai programa mums turėtų grąžinti sąrašą su skaičiais .

.py
def multiples(a, n):
    multiples = []
    for i in range(n):
        multiples.append(a * (i + 1))
    return multiples

a = 3
n = 3
print(f"{n} multiples of the number {a}:")
print(multiples(a, n))

Kol kas skaičiuojame mažą kiekį kartotinių:

cmd
3 multiples of the number 3: 
[3, 6, 9]

Kai žinome, kad turėsime tris grąžinamas reikšmes, galima funkcijos rezultatą priskirti trims kintamiesiems:

.py
# ...
a=3
n=3
first_m, second_m, third_m = multiples(a, n)
print(f"Multiples of {a}:")
print(f"First: {first_m}, Second: {second_m}, Third: {third_m}")

Grąžinamas sąrašas veikia (beveik) taip pat, kaip grąžinant tris atskiras reikšmės:

cmd
Multiples of 3: 
First: 3; Second: 6; Third: 9

O kas jeigu nurodytume n didesnį, o mums reiktų išskirti tris pirmas reikšmes, o kitų ne? Kad nereiktų rašyti 10 kintamųjų, Python kalba turi unpacking operatorių *:

.py
# ...
a = 3
n = 10
first_m, second_m, third_m, *rest_multiples = multiples(a, n)
print(f"Multiples of {a}:")
print(f"First: {first_m}, Second: {second_m}, Third: {third_m}")
print(f"Rest of the multiples: {rest_multiples}")
cmd
Multiples of 3:
First: 3, Second: 6, Third: 9
Rest of the multiples: [12, 15, 18, 21, 24, 27, 30]

Rašydami * prieš kintamojo vardą, mes Python sakom, kad jis priskirtų visas likusias, nepriskirtas reikšmes tam kintamajam.

Šį kintamąjį su operatoriumi nebūtina rašyti gale, jis gali būti priekyje ir viduryje:

.py
a=4
n=5
m_1, m_2, *m_middle, m_last = multiples(a, n)
print(f"Multiples of {a}:")
print(f"First: {m_1}; Second: {m_2}; Last: {m_last}")
print(f"Rest of the multiples: {m_middle}")

print("-"*10)

b=2
n=6
m_1, m_2, *m_middle, m_last = multiples(a, n)
print(f"Multiples of {b}:")
print(f"First: {m_1}; Second: {m_2}; Last: {m_last}")
print(f"Rest of the multiples: {m_middle}")
cmd
Multiples of 4:
First: 4; Second: 8; Last: 20
Rest of the multiples: [12, 16]
----------
Multiples of 2:
First: 4; Second: 8; Last: 24
Rest of the multiples: [12, 16, 20]

Šį operatorių nebūtina naudoti su funkcijos kvietimu, galima panaudoti tiesiog su sąrašu:

.py
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
first, *leftovers = numbers
print(f"First number: {first}")
print(f"Rest of the numbers: {leftovers}")

print("*" * 15)

*leftovers, last = numbers
print(f"Last number: {last}")
print(f"Rest of the numbers: {leftovers}")

print("*" * 15)

first, *leftovers, last = numbers
print(f"First number: {first}; Last number: {last}")
print(f"Rest of the numbers: {leftovers}")
cmd
First number: 1
Rest of the numbers: [2, 3, 4, 5, 6, 7, 8, 9]
***************
Last number: 9
Rest of the numbers: [1, 2, 3, 4, 5, 6, 7, 8]
***************
First number: 1; Last number: 9
Rest of the numbers: [2, 3, 4, 5, 6, 7, 8]

Parametrai ir argumentai

Kalbant apie programavimą svarbu naudoti teisingus terminus.Funkcijoje skliausteliuose esančioms reikšmėms nusakyti naudojamos dvi sąvokos: parametras ir argumentas. Ar užrašas skliausteliuose yra argumentas, ar parametras, apibrėžia šių sąvokų apibrėžimai:

  • Parametras - kintamasis funkcijos apraše, kuris nurodo kokią informaciją funkcija gali priimti;
  • Argumentas - reali reikšmė, kuri yra įrašoma į funkciją, kai ji yra kviečiama. Ši reikšmė atitinka funkcijos apraše esantį kintamąjį.

Sąvokų apibrėžimas gali atrodyti šiek tiek sudėtingas be pavyzdžių. Tai pasižiūrėkime į paprastą pavyzdį. Turime funkcija, kuri spausdina pasisveikinimą:

.py
def greet(name):  # 'name' is a parameter
    print(f"Hello, {name}!")

greet("Alice")  # "Alice" is an argument

Funkcijos apraše def greet(name) esantis kintamasis name yra parametras. Šiame apraše jis neturi jokios reikšmės. Kai funkcija iškviečiama greet("Alice"), jai suteikiamas argumentas "Alice" - konkreti reikšmė, kuri perduodama funkcijai.

Argumentai su vardais

Sukurkime funkciją, kuriai reikia įrašyti daug argumentų. Tegul tai būna lentelės spausdinimo funkcija:

.py
def print_table_header():
    header = f"| {'Make':<15} | {'Model':<15} | {'Year':<6} | {'Color':<10} | {'Fuel':<10} | {'Gearbox':<10} | {'Mandatory technical inspection':<30} |"
    print("=" * len(header))
    print(header)
    print("=" * len(header))


def print_car_info_row(make, model, year, color, fuel, is_manual, TI):
    TI_str = "not provided"  # Default value
    if TI is True:
        TI_str = "YES"
    elif TI is False: 
        TI_str = "NO"

    gearbox="Automatic"
    if is_manual:
        gearbox="Manual"

    car_info = f"| {make:15} | {model:15} | {year:6} | {color:10} | {fuel:<10} | {gearbox:<10} | {TI_str:<30} |"
    print(car_info)
    print("-"*len(car_info))

print_table_header()
print_car_info_row("Toyota", "Avensis", "2008", "Black", "Diesel", True, True)
print_car_info_row("Volkswagen", "Beetle", "2003", "White", "Diesel", False, True)
print_car_info_row("Fiat", "Multipla", "1999", "Red", "Gasoline", True, False)
cmd
======================================================================================================================
| Make            | Model           | Year   | Color      | Fuel       | Gearbox    | Mandatory technical inspection |
======================================================================================================================
| Toyota          | Avensis         | 2008   | Black      | Diesel     | Manual     | YES                            |
----------------------------------------------------------------------------------------------------------------------
| Volkswagen      | Beetle          | 2003   | White      | Diesel     | Automatic  | YES                            |
----------------------------------------------------------------------------------------------------------------------
| Fiat            | Multipla        | 1999   | Red        | Gasoline   | Manual     | NO                             |
----------------------------------------------------------------------------------------------------------------------

Kai yra tiek daug argumentų, ypatingai ten, kur gali pasitaikyti vienodos reikšmės (True ir True), galima pasimesti. Dėl kodo skaitomumo Python kalba mums leidžia nurodant argumentus, nurodyti jų vardus:

.py
# ...
print_car_info_row("Toyota", "Avensis", "2008", "Black", "Diesel", is_manual=True, TI=True)
print_car_info_row("Volkswagen", "Beetle", "2003", "White", "Diesel", TI=False, is_manual=True)
print_car_info_row("Fiat", "Multipla", "1999", "Red", "Gasoline", True, TI=False)

Atkreipkite dėmesį į eiliškumą. Nurodant vardus būtina laikytis taisyklių:

  • Nurodant argumentą(-us) su vardu, jų eiliškumas gali nesutapti su funkcijos apraše parametrų eiliškumu (23-24 eilutės);
  • Nurodomas vardų kiekis pasirenkamas savo nuožiūrą;
  • Vardai nurodomi tik nurodžius argumentus be vardų (pavyzdyje visi nurodyti argumentų vardai yra gale);

Svarbus trečiasis principas, nes jo nesilaikius, jūsų kodas tiesiog nepasileis. Žemiau esanti eilutė yra negalima:

.py
# ...
print_car_info_row(make="Fiat", model="Multipla", "1999", "Red", "Gasoline", True, False)

Jeigu norėtume nurodyti markės argumentą su vardu, turėtume nurodyti visus argumentus su vardais:

.py
# ...
print_car_info_row(make="Fiat", model="Multipla", year="1999", color="Red", fuel="Gasoline", is_manual=True, TI=False)

Numatytosios argumentų reikšmės

Funkcijoms galima nustatyti numatytasias reikšmes (angl. default), tai reiškias, jeigu nėra nurodomas argumentas, jo reikšmė tampa tokia, kokia yra numatyta. Pagrindinė taisyklė - apibrėžiant funkciją visi parametrai, turintys numatytąsias reikšmes, turi būti po parametrų be numatytųjų reikšmių.

Parametrui numatyta reikšmė suteikiama parašius ją po parametro:

.py
def function_with_defaults(param1, param2="default", param3="another_default"):
    print(param1, param2, param3)

# Valid calls
function_with_defaults("first")
function_with_defaults("first", "second")
cmd
first default another_default
first second another_default

Jeigu pabandytume suteikti numatytąją reikšmę tik pirmam parametrui, programa tai laikytų klaida, kadangi parametrai su numatytosiomis reikšmėmis turi eiti po įprastai nurodytais parametrais:

.py
def function_with_defaults(param1="default", param2, param3):
    print(param1, param2, param3)

Pakeiskime anksčiau sukurtą lentelės spausdinimo funkciją, suteikime numatytas reikšmės:

  • fuel="Gasoline";
  • is_manual=True;
  • is_manual=True.
.py
def print_table_header():
    header = f"| {'Make':<15} | {'Model':<15} | {'Year':<6} | {'Color':<10} | {'Fuel':<10} | {'Gearbox':<10} | {'Mandatory technical inspection':<30} |"
    print("=" * len(header))
    print(header)
    print("=" * len(header))


def print_car_info_row(make, model, year, color, fuel="Gasoline", is_manual=True, TI=None):
    TI_str = "not provided"  # Default value
    if TI is True:
        TI_str = "YES"
    elif TI is False: 
        TI_str = "NO"

    gearbox="Automatic"
    if is_manual:
        gearbox="Manual"

    car_info = f"| {make:15} | {model:15} | {year:6} | {color:10} | {fuel:<10} | {gearbox:<10} | {TI_str:<30} |"
    print(car_info)
    print("-"*len(car_info))

print_table_header()
print_car_info_row("Toyota", "Avensis", "2008", "Black", "Diesel", TI=True)
print_car_info_row("Volkswagen", "Beetle", "2003", "White", "Diesel", TI=False, is_manual=True)
print_car_info_row(make="Fiat", model="Multipla", year="1999", color="Red")
cmd
======================================================================================================================
| Make            | Model           | Year   | Color      | Fuel       | Gearbox    | Mandatory technical inspection |
======================================================================================================================
| Toyota          | Avensis         | 2008   | Black      | Diesel     | Manual     | YES                            |
----------------------------------------------------------------------------------------------------------------------
| Volkswagen      | Beetle          | 2003   | White      | Diesel     | Manual     | NO                             |
----------------------------------------------------------------------------------------------------------------------
| Fiat            | Multipla        | 1999   | Red        | Gasoline   | Manual     | not provided                   |
----------------------------------------------------------------------------------------------------------------------

Jeigu yra nurodyta numatyta reikšmė ir mums ji tinka, jos galima nenurodyti. Ją galima praleisti nurodanti argumentą su vardu, kaip 24 eilutėje nėra nurodytas argumentas is_manual, nors nurodytas po jo einant TI.

Funkcijos aprašymas

Užėjus ant kai kurių funkcijų, mes galima pamatyti informaciją apie tai, ką jos daro, kokius argumentus priima, kokie jie, kokio tipo, kokias klaidas gali sukelti ta funkcija ir kokiais atvejais. Pavyzdžiui, užėjus ant open() funkcijos:

Šį aprašą galite slinkti žemiau ir pamatysite visą paklodę informacijos. Python viduje - kode, šitas užrašas yra pateikiamas, kaip funkcijos komentaras:

.py
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
         newline=None, closefd=True, opener=None):

    """Open file and return a stream.  Raise OSError upon failure.

    file is either a text or byte string giving the name (and the path
    if the file isn't in the current working directory) of the file to
    be opened or an integer file descriptor of the file to be
    wrapped. (If a file descriptor is given, it is closed when the
    returned I/O object is closed, unless closefd is set to False.)

    mode is an optional string that specifies the mode in which the file is
    opened. It defaults to 'r' which means open for reading in text mode. Other
    common values are 'w' for writing (truncating the file if it already
    exists), 'x' for exclusive creation of a new file, and 'a' for appending
    (which on some Unix systems, means that all writes append to the end of the
    file regardless of the current seek position). In text mode, if encoding is
    not specified the encoding used is platform dependent. (For reading and
    writing raw bytes use binary mode and leave encoding unspecified.) The
    available modes are:

    ========= ===============================================================
    Character Meaning
    --------- ---------------------------------------------------------------
    'r'       open for reading (default)
    'w'       open for writing, truncating the file first
    'x'       create a new file and open it for writing
    'a'       open for writing, appending to the end of the file if it exists
    'b'       binary mode
    't'       text mode (default)
    '+'       open a disk file for updating (reading and writing)
    ========= ===============================================================

    The default mode is 'rt' (open for reading text). For binary random
    access, the mode 'w+b' opens and truncates the file to 0 bytes, while
    'r+b' opens the file without truncation. The 'x' mode implies 'w' and
    raises an `FileExistsError` if the file already exists.

    Python distinguishes between files opened in binary and text modes,
    even when the underlying operating system doesn't. Files opened in
    binary mode (appending 'b' to the mode argument) return contents as
    bytes objects without any decoding. In text mode (the default, or when
    't' is appended to the mode argument), the contents of the file are
    returned as strings, the bytes having been first decoded using a
    platform-dependent encoding or using the specified encoding if given.

    buffering is an optional integer used to set the buffering policy.
    Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
    line buffering (only usable in text mode), and an integer > 1 to indicate
    the size of a fixed-size chunk buffer.  When no buffering argument is
    given, the default buffering policy works as follows:

    * Binary files are buffered in fixed-size chunks; the size of the buffer
      is chosen using a heuristic trying to determine the underlying device's
      "block size" and falling back on `io.DEFAULT_BUFFER_SIZE`.
      On many systems, the buffer will typically be 4096 or 8192 bytes long.

    * "Interactive" text files (files for which isatty() returns True)
      use line buffering.  Other text files use the policy described above
      for binary files.

    encoding is the str name of the encoding used to decode or encode the
    file. This should only be used in text mode. The default encoding is
    platform dependent, but any encoding supported by Python can be
    passed.  See the codecs module for the list of supported encodings.

    errors is an optional string that specifies how encoding errors are to
    be handled---this argument should not be used in binary mode. Pass
    'strict' to raise a ValueError exception if there is an encoding error
    (the default of None has the same effect), or pass 'ignore' to ignore
    errors. (Note that ignoring encoding errors can lead to data loss.)
    See the documentation for codecs.register for a list of the permitted
    encoding error strings.

    newline is a string controlling how universal newlines works (it only
    applies to text mode). It can be None, '', '\n', '\r', and '\r\n'.  It works
    as follows:

    * On input, if newline is None, universal newlines mode is
      enabled. Lines in the input can end in '\n', '\r', or '\r\n', and
      these are translated into '\n' before being returned to the
      caller. If it is '', universal newline mode is enabled, but line
      endings are returned to the caller untranslated. If it has any of
      the other legal values, input lines are only terminated by the given
      string, and the line ending is returned to the caller untranslated.

    * On output, if newline is None, any '\n' characters written are
      translated to the system default line separator, os.linesep. If
      newline is '', no translation takes place. If newline is any of the
      other legal values, any '\n' characters written are translated to
      the given string.

    closedfd is a bool. If closefd is False, the underlying file descriptor will
    be kept open when the file is closed. This does not work when a file name is
    given and must be True in that case.

    The newly created file is non-inheritable.

    A custom opener can be used by passing a callable as *opener*. The
    underlying file descriptor for the file object is then obtained by calling
    *opener* with (*file*, *flags*). *opener* must return an open file
    descriptor (passing os.open as *opener* results in functionality similar to
    passing None).

    open() returns a file object whose type depends on the mode, and
    through which the standard file operations such as reading and writing
    are performed. When open() is used to open a file in a text mode ('w',
    'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open
    a file in a binary mode, the returned class varies: in read binary
    mode, it returns a BufferedReader; in write binary and append binary
    modes, it returns a BufferedWriter, and in read/write mode, it returns
    a BufferedRandom.

    It is also possible to use a string or bytearray as a file for both
    reading and writing. For strings StringIO can be used like a file
    opened in a text mode, and for bytes a BytesIO can be used like a file
    opened in a binary mode.
    """

    # ...
    # Implementation
    # ...

Funkcijų antraštės aprašymas sudaromas (kintamųjų tipai, grąžinama reikšmė) iš to, kas parašyta funkcijos apraše ir jos kode.

Visi komentarai, įskaitant ir funkcijos aprašymą, yra nebūtini. Jie rašomi tam, kad kiti suprastų mūsų kodą, tie kiti gali būti ir mes patys. Kai projektas tampa didelis arba prie jo dirbama retai, nors kodas būna parašytas mūsų pačių, mes, dėl elementarių žmogaus atminties galimybių, negalime atsiminti visų detalių, kaip kodas veikia, ką mes tuo metu galvojome kurdami, kuriose vietose palikome klaidas ir t.t. Tai po ilgo laiko grįžus prie savojo kodo, tikriausiai jį skaitysime, kaip svetimo parašytą.

Sukurkime funkciją, kurią vėliau aprašysime, kuri patikrina, ar duotasis skaičius yra pirminis. Pirminis skaičius yra toks skaičius, kuris turi tik du daliklius: save patį ir vienetą:

  • 1 nėra pirminis, kadangi jis dalijasi tik iš savęs (trūksta dar vieno daliklio);
  • 2 yra pirminis, nes dalijasi iš 2 ir 1;
  • 3 yra piminis, nes dalijasi iš 3 ir 1;
  • 4 nėra pirminis, nes dalijasi iš 1, 2, 4 (3 dalikliai);
  • ir t.t.
.py
is_prime.py
def is_prime(num):
    if num < 0:
        raise ValueError("Input must be a non-negative integer.")

    if num == 1:
        return False

    # Check for factors from 2 to num-1
    for i in range(2, num):
        if num % i == 0:
            return False

    # If no divisors were found, num is prime
    return True

Mūsų funkcijoje negalima pateikti mažesnio skaičiaus negu 0. Tokiu atveju iškelsime klaidą (susipažinsime su šiuo dalyku vėliau) - programa rodys įvykusią klaidą. Toliau tikrinama, ar pateiktas skaičius turi daliklių, jeigu yra, tai skaičius nėra pirminis ir grąžinama reikšmė False.

Funkcijos aprašai rašomi tokie, kad skaitantysis suprastų:

  • Pirmiausia, ką šita funkcija daro;
  • Jeigu funkcija sudėtinga, turėtų būti aprašyta, koks jos veikimo principas;
  • Kokią įtaką daro ar kokia prasmė parametrų;
  • Kokia yra grąžinama reikšmė, kokia jos prasmė;
  • Jeigu gali funkcijoje gali įvykti išimčių (raise ValueError("Input must be a natural number greater than 0.")), tai irgi gali būti aprašyta;

Papildykime funkciją prasmingu komentaru:

.py
is_prime.py
def is_prime(num):
    """
    Determine if a given number is a prime number.

    A prime number is a natural number greater than 1 that has no positive 
    divisors other than 1 and itself. For example, 2, 3, 5, and 7 are 
    prime numbers.

    Args:
        num (int): The number to check.

    Returns:
        bool: True if the number is prime, False otherwise.

    Raises:
        ValueError: If num is less than 1.
    """
    if num < 0:
        raise ValueError("Input must be a non-negative integer.")

    if num == 1:
        return False

    # Check for factors from 2 to num-1
    for i in range(2, num):
        if num % i == 0:
            return False

    # If no divisors were found, num is prime
    return True

Dabar užėję ant funkcijos galite pamatyti, kaip VS code programavimo aplinka leidžia lengvai pasižiūrėti į sukurtos funkcijos informaciją:

Šie aprašai gali būti rašomi bet kaip tarp trigubų kabučių, bet dažniausiai yra laikomosi tam tikrų gairių:

  • Pirmoje eilutėje pateikiama svarbiausia funkcijos informacija - ką ji daro;
  • Toliau detaliau pristatoma funkcija;
  • Galima pateikti skyrelius apie argumentus (Args), grąžinama reikšmę (Returns), galimas išimtis (Raises);

Šis pateiktas aprašymo formatas yra paremtas Google dokumentacijos gairėmis. Galima aprašymus rašyti ir pagal kitokias gaires, kurios taip pat suderinamos su VS code aplinka. Pavyzdžiui naudojantis numpydoc gairėmis ir formatu, mūsų aprašymą galėtume papildyti papildomais skyreliais:

.py
is_prime.py
def is_prime(num):
    """
    Check if a number is prime.

    A prime number[1]_ is a natural number greater than 1 that
    has no positive divisors other than 1 and itself. For example,
    2, 3, 5, and 7 are prime numbers.

    Parameters
    ----------
    num : int
        The number to check.

    Returns
    -------
    bool
        True if num is prime, False otherwise.

    Raises
    ------
    ValueError
        If num is a negative integer.

    Examples
    --------
    Basic usage of the function:

        print(is_prime(5))  # Outputs: True
        print(is_prime(10)) # Outputs: False
        print(is_prime(1))  # Outputs: False

    Notes
    -----
    This function checks for factors from 2 to num-1. It can be inefficient
    for large values of num[2]_.

    References
    ----------
    .. [1] "Prime Number" In Wikipedia. Available at:
        (``https://en.wikipedia.org/wiki/Prime_number``)
    .. [2] Knuth, Donald E. *The Art of Computer Programming, Volume 2:
        Seminumerical Algorithms*. Addison-Wesley, 1998.

    See Also
    --------
    isqrt : A function to compute the integer square root, useful for
        optimizing primality tests by limiting the range of divisors to check.
    Sieve of Eratosthenes : An efficient algorithm for finding all prime
        numbers up to a specified integer, which can be more efficient for
        generating lists of primes than checking individual numbers.
    """
    if num < 0:
        raise ValueError("Input must be a non-negative integer.")

    if num == 1:
        return False

    # Check for factors from 2 to num-1
    for i in range(2, num):
        if num % i == 0:
            return False

    # If no divisors were found, num is prime
    return True

Tokį aprašą VS code atvaizduoja šitaip:

Sužinoję visus šiuos dalykus šiame skyriuje, būtų puiku, jeigu jau pradėtume aprašinėti savo funkcijas nors viena eilute:

.py
is_prime.py
def is_prime(num):
    """
    Check if a number is prime.
    """
    # ....

Parametrų tipų užuominos

Nors Python turi tipus (str, int, float, bool ir kt.), jie nėra matomi, nėra tiesiogiai naudojami. Python palaiko kintamųjų tipų užuominas (angl. type hinting). Mes kintamajam galime suteikti tipą, kuris padės mums programuojant:

Sukurkime funkciją, kuri, pateikus tekstą, grąžina mums unikalius žodžius. Ši funkcija taip pat gali ignoruoti blogus žodžius, kurie aprašomi funkcijoje:

.py
def extract_unique_words(text, filter_offensive):
    """
    Extract unique words from a text.

    If filter_offensive is `True`, the function will remove offensive words.
    If `False`, it will return all words in the text.
    """

    offensive_words = ["badword1", "badword2", "badword3"]
    words = text.split()
    unique_words = []

    for word in words:
        word_lower = word.lower()
        if filter_offensive:
            if word_lower not in offensive_words and word_lower not in unique_words:
                unique_words.append(word_lower)
        else:
            if word_lower not in unique_words:
                unique_words.append(word_lower)

    return unique_words

Jeigu pasižiūrėtumėte į kodo spalvas, metodai split() ir lower() yra nespalvoti. Tai reiškias, kad Python neatpažįsta šių kintamųjų tipų:

Nors funkcija veikia teisingai, programuojant mums VS code aplinka nepateiks užuominų (angl. code suggestions), neveiks kodo automatinis pabaigimas (angl. autocomplete):

Būtų geriau, jeigu būtų taip:

Norint sutvarkyti šią problemą, mes galime kode nurodyti koks turėtų būti kintamojo tipas, Python kalboje tai vadinasi tipų užuominomis (angl. type hints). Tai daroma prie kintamojo parašius dvitaškį ir jo tipą:

.py
def extract_unique_words(text: str, filter_offensive):
    # ...

Dabar jau turėtų nusispalvinti šis kintamasis teisingai ir kodo užbaigimas veikti teisingai. Taip pat tai padeda kodą skaitančiam asmeniui suprasti, kokio tipo kintamąjį pateikti (ypatingai tai gelbėjai, kai nėra parašyta funkcijos apraše). Taip pat šie tipai atsiranda, kai norime pažvelgti į funkcijos informaciją:

Matome, kad dabar funkcijai reikia pateikti kintamąjį text, kurio tipas yra str. Kintamojo filter_offensive Python neatpažįsta. Užrašas po rodyklės -> rodo, koks bus grąžinamos reikšmės tipas, šiuo atveju tai list.

Toliau galime tai patobulinti ir nurodyti tipą kintamajam filter_offensive ir patikslinti grąžinomos reikšmės tipą, kad tai sąrašas iš str tipo reikšmių.

.py
def extract_unique_words(text: str, filter_offensive: bool = True) -> list[str]:
    # ...

Funkcijai taip pat suteikėme numatytąją reikšmę, kad pamatytumėte, kaip tai užrašoma. Dabar rašant kode, mums teisingai siūlomos reikšmės ir pateikiama pilna funkcijos informacija:

Toliau iškvietus funkciją ir dirbant su jos grąžinama reikšme, jeigu nustatyti (teisingi) tipai, programa ir toliau pateikimi kodo užbaigimo pasiūlymai:

Jeigu norite pamatyti efektą be jų, galite pabandyti išsitrinti tipus ir pabandyti rašyti kodą.

Toliau pateikiami tipai, kuriuos jūs jau turėtumėte žinoti ir suprasti. Be to tipai gali būti priskiriami tiesiog kintamajam.

.py
x1: int = 1
x2: float = 1.0
x3: bool = True
x4: str = "test"

x4: list[int] = [1]

def my_func(a: int, b: float, c: bool, d: str, e: list[list[bool]]):
    print("Hello, world!")

Taip pat galima nurodyti, kad kintamasis gali įgyti keletą skirtingų tipų reikšmių, taip galima nurodyti, kad kintamasis gali turėti None reikšmę:

.py
from typing import Optional

x5: list[int | str] = [3, 5, "test", "fun"]  # This list can contain either int or str values.

x6: Optional[str] = "something"  # This line is valid.
x6 = None  # This line is also valid because x6 can be str or None.

# The function below takes g as a list of ints or strings, and h as a string or None.
# It returns either None or a list of floats.
def my_other_func(g: list[int | str], h: Optional[str]) -> Optional[list[float]]:
    print("Hello, world!")

Daugiau apie tipus galite pasiskaityt Type hints cheat sheet - mypy 1.11.2 documentation.

Suprasti kintamųjų tipus padės jums pereinant prie kitų programavimo kalbų, kur sukuriant kintamąjį privaloma iš karto nurodyti jo tipą. Python kalboje kintamųjų tipų rašymas nėra būtinas, bet jis padeda programuojant. Toliau jūs patys renkatės, ar jūs tai darysite, ar ne.

Užduotys

1. Skaičiaus transformacijos

Sukurkite programą, kuri jūsų pateiktą skaičių (iš terminalo), transformuotų:

  1. Padvigubintų kiekvieną skaičiaus skaitmenį.
  2. Suskaičiuotų naujojo skaičiaus skaitmenų sumą;
  3. Pakartotų pirmąjį žingsnį;

Skaičiuje esantis minusas negali būti dvigubinamas arba sumuojamas prie skaitmenų, todėl jis turi išlikti galutiniame skaičiuje.

Jeigu skaičius yra pateiktas slankiojo kablelio (float), jo sveikoji ir trupmeninė dalys yra apdorojamos atskirai, o tada vėl sujungiamas atgal.

Rezultatus pateikite terminale;

Užduočiai padaryti turite būti sukurtos trys funkcijos (atkreipkite dėmesį į argumentų tipus ir grąžinamų reikšmių tipus):

.py
def double_digits(n: int) -> int
def sum_digits(n: int) -> int
def transform_number(n: int|float) -> int|float

Detalesnis paaiškinimas

Tarkime įvedame skaičių 246. Pirmiausia skaičiaus skaitmenys padvigubinami:

  • 2 tampa 4;
  • 4 tampa 8;
  • 6 tampa 12.

Galiausiai rezultatus sujungiame ir gauname 4812.

Tada suskaičiuojame šių skaitmenų sumą: 15.

Gavus sumą pakartojame skaitmenų dvigubinimą ir gauname 15->210.

Įvestys ir tikėtini rezultatas

Įvestistikėtinas rezultatas
12324
-123-24
8932
78942
13518
78.90122.22
-678.9-18.18
0.340.28
00
-5-2
99954
100112
9876543210987654321180
2468024680810
-2468024680-810
-12345.6789-42.48

2. Pasikartojančių elementų pašalinimas

Iš duotų sąrašų ištrinkite pasikartojančius elementus - pakeistame sąraše turi likti tik unikalūs elementai:

Sukurkite funkciją, kuri iš duotojo sąrašo grąžina tik unikalius elementus.

.py
def remove_duplicates(elements: list) -> list

Duomenys:

.csv
data.csv
apple,banana,apple,orange,grape,banana,apple,cherry,peach,plum
5,2,5,7,10,5,2,3,5,15,20,25,2
3.14,2.71,3.14,1.61,3.14,2.71
Berlin,Paris,Berlin,Rome,Madrid,London,Berlin,Paris
Dog,Cat,Bird,Dog,Fish,Cat,Lizard,Dog,Horse,Fish
hello,world,hello,code,python,world,hello,script,programming
Alice,Bob,Alice,Eve,Charlie,Alice,David,Eve,Bob
-10,-20,-10,-30,-40,-50,-10,-60,-70,-10
9.81,3.14,9.81,2.71,1.61,9.81,3.14,1.41,9.81,2.71,1.61
2001,1984,2001,2020,1984,2001,2020,1984,1999
Monday,Friday,Monday,Wednesday,Monday,Tuesday,Monday,Thursday,Saturday,Monday
10,10,10
Green,Green,Blue,Yellow,Green
Alice,alice,ALICE,Alice

1
a,b,a,c,b,d,e,f,e
True,False,True,True,False

Rezultatus pateikite tekstiniame faile:

text
results.txt
Original List 1:            ['apple', 'banana', 'apple', 'orange', 'grape', 'banana', 'apple', 'cherry', 'peach', 'plum']
After Removing Duplicates:  ['apple', 'banana', 'orange', 'grape', 'cherry', 'peach', 'plum']
Number of Removed Elements: 3

Original List 2:            [5, 2, 5, 7, 10, 5, 2, 3, 5, 15, 20, 25, 2]
After Removing Duplicates:  [5, 2, 7, 10, 3, 15, 20, 25]
Number of Removed Elements: 5

Original List 3:            [3.14, 2.71, 3.14, 1.61, 3.14, 2.71]
After Removing Duplicates:  [3.14, 2.71, 1.61]
Number of Removed Elements: 3

Original List 4:            ['Berlin', 'Paris', 'Berlin', 'Rome', 'Madrid', 'London', 'Berlin', 'Paris']
After Removing Duplicates:  ['Berlin', 'Paris', 'Rome', 'Madrid', 'London']
Number of Removed Elements: 3

Original List 5:            ['Dog', 'Cat', 'Bird', 'Dog', 'Fish', 'Cat', 'Lizard', 'Dog', 'Horse', 'Fish']
After Removing Duplicates:  ['Dog', 'Cat', 'Bird', 'Fish', 'Lizard', 'Horse']
Number of Removed Elements: 4

Original List 6:            ['hello', 'world', 'hello', 'code', 'python', 'world', 'hello', 'script', 'programming']
After Removing Duplicates:  ['hello', 'world', 'code', 'python', 'script', 'programming']
Number of Removed Elements: 3

Original List 7:            ['Alice', 'Bob', 'Alice', 'Eve', 'Charlie', 'Alice', 'David', 'Eve', 'Bob']
After Removing Duplicates:  ['Alice', 'Bob', 'Eve', 'Charlie', 'David']
Number of Removed Elements: 4

Original List 8:            [-10, -20, -10, -30, -40, -50, -10, -60, -70, -10]
After Removing Duplicates:  [-10, -20, -30, -40, -50, -60, -70]
Number of Removed Elements: 3

Original List 9:            [9.81, 3.14, 9.81, 2.71, 1.61, 9.81, 3.14, 1.41, 9.81, 2.71, 1.61]
After Removing Duplicates:  [9.81, 3.14, 2.71, 1.61, 1.41]
Number of Removed Elements: 6

Original List 10:           [2001, 1984, 2001, 2020, 1984, 2001, 2020, 1984, 1999]
After Removing Duplicates:  [2001, 1984, 2020, 1999]
Number of Removed Elements: 5

Original List 11:           ['Monday', 'Friday', 'Monday', 'Wednesday', 'Monday', 'Tuesday', 'Monday', 'Thursday', 'Saturday', 'Monday']
After Removing Duplicates:  ['Monday', 'Friday', 'Wednesday', 'Tuesday', 'Thursday', 'Saturday']
Number of Removed Elements: 4

Original List 12:           [10, 10, 10]
After Removing Duplicates:  [10]
Number of Removed Elements: 2

Original List 13:           ['Green', 'Green', 'Blue', 'Yellow', 'Green']
After Removing Duplicates:  ['Green', 'Blue', 'Yellow']
Number of Removed Elements: 2

Original List 14:           ['Alice', 'alice', 'ALICE', 'Alice']
After Removing Duplicates:  ['Alice', 'alice', 'ALICE']
Number of Removed Elements: 1

Original List 15:           []
After Removing Duplicates:  []
Number of Removed Elements: 0

Original List 16:           [1]
After Removing Duplicates:  [1]
Number of Removed Elements: 0

Original List 17:           ['a', 'b', 'a', 'c', 'b', 'd', 'e', 'f', 'e']
After Removing Duplicates:  ['a', 'b', 'c', 'd', 'e', 'f']
Number of Removed Elements: 3

Original List 18:           [True, False, True, True, False]
After Removing Duplicates:  [True, False]
Number of Removed Elements: 3

3. Elementų skaičius sąraše

Parašykite programa, kuri suskaičiuotų nurodyto elementų pasikartojimų skaičių. Elementas, kurio kiekį sąraše skaičiuosime, ir sąrašas nurodytas .csv faile. Eilutėje pirma reikšmė yra tas elementas, kurio kiekį sąraše skaičiuosime, sekantys yra sąrašas, kuriame to elemento ieškosime. Duomenų failas:

.csv
data.csv
apple,banana,apple,orange,grape,banana,apple,cherry,peach,plum
5,2,5,7,10,5,5,3,5,15,20,25,5
3.14,2.71,3.14,1.61,3.14
Berlin,Paris,Berlin,Rome,Madrid,London,Berlin
Dog,Cat,Bird,Dog,Fish,Cat,Lizard,Dog,Horse
hello,world,hello,code,python,world,hello,script,programming
Alice,Bob,Alice,Eve,Charlie,Alice,David,Eve,Bob
-10,-20,-10,-30,-40,-50,-10,-60,-70,-10
9.81,3.14,9.81,2.71,1.61,9.81,3.14,1.41,9.81,2.71,1.61
2001,1984,2001,2020,1984,2001,2020,1984,1999
Monday,Friday,Monday,Wednesday,Monday,Tuesday,Monday,Thursday,Saturday,Monday
10
Green,Green,Green
Alice,alice
Bob,Lorem,ipsum,dolor,sit,amet,consectetur,adipiscing,elit,Etiam,dignissim,nibh

Turite sukurti pagrindę funkciją nenaudodami Python kalboje esančios funkcijos count():

.py
def count_element_occurrence(elements: list, e) -> int:

Rezultatus pateikite terminale, atkreipkite dėmesį, kad reikia atsižvelgti, ar raidės didžiosios, ar mažosios ("Python" == "python" # False):

cmd
In list  1, the element 'apple' occurs 2 times.
In list  2, the element '5' occurs 5 times.
In list  3, the element '3.14' occurs 2 times.
In list  4, the element 'Berlin' occurs 2 times.
In list  5, the element 'Dog' occurs 2 times.
In list  6, the element 'hello' occurs 2 times.
In list  7, the element 'Alice' occurs 2 times.
In list  8, the element '-10' occurs 3 times.
In list  9, the element '9.81' occurs 3 times.
In list 10, the element '2001' occurs 2 times.
In list 11, the element 'Monday' occurs 4 times.
In list 12, the element '10' occurs 0 times.
In list 13, the element 'Green' occurs 2 times.
In list 14, the element 'Alice' occurs 0 times.
In list 15, the element 'Bob' occurs 0 times.

4. Skaitmenų suma

Skaičiai yra sudaryti iš skaitmenų. Skaičius turi skaitmenis , , , , . Jeigu sudėtume šiuos skaičius gautume skaičiaus skaitmenų suma. Tai skaičiaus skaitmenų suma yra lygi .

.py
def sum_of_digits(n: float|int) -> int
.py
numbers = [
    -345,       
    -1234.56,
    0,    
    42,    
    567.89,  
    9821, 
    -0.678,
    12.34,
    123456,
    -98765.4321
]

Rezultatus pateikite terminale, šie galėtų atrodyti šitaip:

cmd
       -345: 3+4+5 = 12
   -1234.56: 1+2+3+4+5+6 = 21
          0: 0 = 0
         42: 4+2 = 6
     567.89: 5+6+7+8+9 = 35
       9821: 9+8+2+1 = 20
     -0.678: 0+6+7+8 = 21
      12.34: 1+2+3+4 = 10
     123456: 1+2+3+4+5+6 = 21
-98765.4321: 9+8+7+6+5+4+3+2+1 = 45

5. Balsių skaičiavimas

Suskaičiuokite tekstuose esančias balses. Rezultatus išspausdinkite terminale:

.py
texts=[
    "Graži tu, mano brangi tėvyne,\nŠalis, kur miega kapuos didvyriai!\nNe veltui bočiai tave taip gynė,Ne veltui dainiai plačiai išgyrė!",
    "Ei, tai vija,\nPinavija, -\nVisą dieną\nGrybais lija.\nVisą dieną\nNuo pat ryto\nGrybai krito,\nKrito, krito...",
    "Everything that happens is either endurable or not. If it’s endurable, then endure it. Stop complaining. If it’s unendurable… then stop complaining. Your destruction will mean its end as well. Just remember: you can endure anything your mind can make endurable, by treating it as in your interest to do so",
    "It followed from the special theory of relativity that mass and energy are both but different manifestations of the same thing - a somewhat unfamiliar conception for the average mind. Furthermore, the equation E is equal to m c-squared, in which energy is put equal to mass, multiplied by the square of the velocity of light, showed that very small amounts of mass may be converted into a very large amount of energy and vice versa. The mass and energy were in fact equivalent, according to the formula mentioned above. This was demonstrated by Cockcroft and Walton in 1932, experimentally.",
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed imperdiet.",
    "Lrm psm dlr st mt, cnscttr dpscng lt. Sd mprdt.",
]
.py
def count_vowels(text: str) -> int

Išvestis galėtų atrodyti šitaip:

cmd
   Text                                               Vowel Count
0  Graži tu, mano brangi tėvyne,\nŠalis, kur mieg...           50
1  Ei, tai vija,\nPinavija, -\nVisą dieną\nGrybai...           36
2  Everything that happens is either endurable or...           94
3  It followed from the special theory of relativ...          182
4  Lorem ipsum dolor sit amet, consectetur adipis...           24
5  Lrm psm dlr st mt, cnscttr dpscng lt. Sd mprdt...            0

6. Temperatūrų konvertavimas

Duotas tekstinis failas su temperatūrų sąrašu. Šios temperatūros pateiktos celsijais arba farenheitais. Sukurkite funkcijas, kuriuos konvertuotų iš matavimo vieneto į kitą. Pradines ir pakeistas temperatūras pateikite rezultatų faile.

Šiai užduočiai padaryti būtina, sukurti dvi funkcijas:

.py
def celsius_to_fahrenheit(celsius: float) -> float
def fahrenheit_to_celsius(fahrenheit: float) -> float

Pradiniai duomenys:

text
data.txt
55.5°F
120.4°F
29.0°C
107.2°F
64.6°F
47.2°F
-11°C
40.4°F
64°F
30.6°C
27.4°C
81°F
-14.8°C
54.3°F
13.5°C
63.2°F
-18.5°C
5°C
35.2°F
84.6°F

Rezultatus pateikite suformatavę skaičius. Temperatūras pateikite 2 skaičių po kablbelio tikslumu. Rezultatų failas turėtų atrodyti šitaip:

text
results.txt
  55.5°F -> 13.06°C
 120.4°F -> 49.11°C
  29.0°C -> 84.2°F
 107.2°F -> 41.78°C
  64.6°F -> 18.11°C
  47.2°F -> 8.44°C
...
...
...
  35.2°F -> 1.78°C
  84.6°F -> 29.22°C

7. Sąrašo rūšiavimas

Duotas tekstinis failas su skaičių sąrašais:

.csv
data.csv
42 17 3 89 23 -10 20 100
2.5 3.1 1.8 4.6 2.2 3.3
7 2.5 3 8.8 4 1.1 5
-10 -3.99 -25 -7.1 -15 -1
3.14 2.71 1.41 1.73 0.57 6.28
-6.4 2.1 -3.3 7.9 -0.2 4 -8.1 9.9
42
5 10
-12.5 -2.0
0 0 0 0 0 0 0 0 0 0

Nuskaitykite šiuos duomenis ir sukūrę skaičių rūšiavimo funkciją pateikite juos išrūšiuotus mmažėjančiai ir didėjančiai. Rūšiavimo funkcija turi būti sukurta rankiniu būdu, nenaudojant Python kalboje jau esančios sort() funkcijos:

.py
def sort_list(numbers: list[int|float], reverse: bool = False) -> list[int|float]

Jūsų rezultatų failas galėtų atrodyti šitaip:

text
results.txt
1. Original List:         [42, 17, 3, 89, 23, -10, 20, 100]
   Sorted List:           [-10, 3, 17, 20, 23, 42, 89, 100]
   Reversed Sorted List:  [100, 89, 42, 23, 20, 17, 3, -10]

2. Original List:         [2.5, 3.1, 1.8, 4.6, 2.2, 3.3]
   Sorted List:           [1.8, 2.2, 2.5, 3.1, 3.3, 4.6]
   Reversed Sorted List:  [4.6, 3.3, 3.1, 2.5, 2.2, 1.8]

3. Original List:         [7, 2.5, 3, 8.8, 4, 1.1, 5]
   Sorted List:           [1.1, 2.5, 3, 4, 5, 7, 8.8]
   Reversed Sorted List:  [8.8, 7, 5, 4, 3, 2.5, 1.1]

4. Original List:         [-10, -3.99, -25, -7.1, -15, -1]
   Sorted List:           [-25, -15, -10, -7.1, -3.99, -1]
   Reversed Sorted List:  [-1, -3.99, -7.1, -10, -15, -25]

5. Original List:         [3.14, 2.71, 1.41, 1.73, 0.57, 6.28]
   Sorted List:           [0.57, 1.41, 1.73, 2.71, 3.14, 6.28]
   Reversed Sorted List:  [6.28, 3.14, 2.71, 1.73, 1.41, 0.57]

6. Original List:         [-6.4, 2.1, -3.3, 7.9, -0.2, 4, -8.1, 9.9]
   Sorted List:           [-8.1, -6.4, -3.3, -0.2, 2.1, 4, 7.9, 9.9]
   Reversed Sorted List:  [9.9, 7.9, 4, 2.1, -0.2, -3.3, -6.4, -8.1]

7. Original List:         [42]
   Sorted List:           [42]
   Reversed Sorted List:  [42]

8. Original List:         [5, 10]
   Sorted List:           [5, 10]
   Reversed Sorted List:  [10, 5]

9. Original List:         [-2.0, -12.5]
   Sorted List:           [-12.5, -2.0]
   Reversed Sorted List:  [-2.0, -12.5]

10. Original List:        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    Sorted List:          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    Reversed Sorted List: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

8. Savaitės pardavimų analizė

Faile sales_data.csv saugomi duomenys apie parduotus produktų kiekius per savaitę. Šį failą galite atsiųsti čia (trys atvejai).

Duomenyse eilutė prasideda produkto pavadinimu, o toliau seka pardavimų skaičius nuo pirmadienio iki sekmadienio.

Rezultatai

Rezultatai turi būti pateikiami tekstiniame faile. Jame turi būti:

  • Kiekvienos dienos geriausiai parduodamas produktas;
  • Kiekvieno produkto pardavimų skaičius per savaitę, iš viso;
  • Produktas su didžiausiu vidutiniu dienos pardavimu;

Rezultatų failo pavyzdys:

sales_summary.txt
Sales Analysis Report
==================================================

Best-Selling Product Each Day:
--------------------------
Day       Product        
--------------------------
Monday    Product C      
Tuesday   Product C      
Wednesday Product C      
Thursday  Product C      
Friday    Product C      
Saturday  Product C      
Sunday    Product A      

Total Weekly Sales Per Product:
--------------------------
Product        Total Sales
--------------------------
Product A      35        
Product B      24        
Product C      49        

Product with the Highest Average Daily Sales: Product C

Reikalavimai

Užduočiai atlikti būtina sukurti tokias funkcijas:

.py
def read_sales_data(file_name: str) -> list[list[int]]
def calculate_daily_best_sellers(data: list[list[int]]) -> list[int]
def calculate_weekly_totals(data: list[list[int]]) -> list[int]
def find_highest_average(data: list[list[int]]) -> int
def write_sales_summary(daily_best_sellers: list[int], weekly_totals: list[int], highest_avg_index: int, product_names: list[str]) -> None

9. Temperatūros

Situacija

Esate metereologistas renkantis 12 (3 mėnesius) savaičių orų pokyčius, kuriuos turite išanalizuoti. Visą šį periodą, jūs renkate temperatūrų duomenis kas valandą. Savaitės temperatūrų duomenys išsaugomi failuose week1.csv, week2.csv, week3.csv, ..., week12.csv. Šiose failuose yra 7 dienų temperatūrų duomenys - eilutėje yra aprašytos 24 valandų temperatūros. Kiekviena eilutė reprezentuoja dieną.

Jūsų užduotis yra išanalizuoti ketvirčio orų duomenis, kad nustatytumėte tendencijas ir temperatūros anomalijas.

Tikslai

  1. Suskaičiuoti vidutinę ketvirčio temperatūrą;
  2. Identifikuoti anomalijas - temperatūras, kurios nuo mėnesio vidurkio skiriasi per 5°C laipsnius;
  3. Pateikite suvestinės failą.

Pradiniai duomenys

Duomenis galite atsisiųsti čia (43 atvejai). Čia rasite dvylikos savaičių failus, kurių formatas vienodas. Failo pavyzdys:

.csv
week1.csv
00:00,01:00,02:00,03:00,04:00,05:00,06:00,07:00,08:00,09:00,10:00,11:00,12:00,13:00,14:00,15:00,16:00,17:00,18:00,19:00,20:00,21:00,22:00,23:00
20.5,21.0,19.8,18.5,17.2,16.0,15.5,15.2,16.5,17.8,19.0,21.0,22.5,23.0,24.0,25.5,27.0,28.0,29.5,30.0,29.0,27.5,25.0,22.0
21.5,22.0,20.8,19.0,18.0,17.5,16.8,16.0,17.2,18.5,20.0,22.5,24.0,25.5,26.0,27.5,29.0,30.0,31.5,32.0,30.0,28.5,26.0,23.5
22.0,23.5,21.0,20.0,19.5,18.5,17.5,17.0,18.0,19.5,21.0,23.0,25.0,26.0,27.0,28.0,29.0,30.5,31.0,32.0,30.0,28.0,25.0,23.0
19.5,20.0,19.0,18.5,18.0,17.0,16.0,15.5,16.0,17.5,19.0,20.0,21.5,22.0,23.0,24.5,26.0,27.0,28.0,29.0,27.5,25.0,23.0,20.0
18.0,18.5,17.5,17.0,16.0,15.0,14.5,14.0,15.0,16.5,18.0,19.5,20.0,21.0,22.0,23.0,24.0,25.0,26.5,27.0,26.0,24.5,22.0,19.0
20.0,21.5,19.5,18.0,17.0,16.5,15.5,15.0,16.0,18.0,19.0,20.5,22.0,23.0,24.0,25.0,26.5,27.5,28.0,27.0,25.0,23.0,21.0,19.5
22.5,23.0,21.5,20.0,19.0,18.5,17.5,16.0,15.5,17.0,18.5,20.0,21.0,22.5,24.0,25.5,26.5,27.0,28.0,29.0,27.5,25.0,23.5,21.0

Rezultatų failas

Failas gali būti pateiktas tekstiniame faile paprastu tekstu arba lentele. Jame turi būti pateikta:

  • vidutinės mėnesių temperatūros;
  • anomalijų skaičius;
  • vidutinis anomalijų temperatūrų nuokrypis nuo vidurkio;
  • anomalijų sąrašas:
text
results.txt
1 month average: 15.2°C
2 month average: 14.2°C
3 month average: 15.1°C
Anomalies Found: 23
Average Anomaly Deviation: +3.0°C
Week,Day,Hour,Temperature,Deviation
2,3,15,21.8,+6.6
5,6,02,9.1,-6.1
...

arba

text
results.txt
1 month average: 15.2°C
2 month average: 14.2°C
3 month average: 15.1°C
Anomalies Found: 23
Average Anomaly Deviation: +3.0°C

===============================================
| Week | Day | Hour | Temperature | Deviation |
===============================================
| 2    | 3   | 15   | 21.8        | +6.6      |
| 5    | 6   | 02   | 9.1         | -6.1      |
|...   |     |      |             |           |
===============================================

Reikalavimai

Darant šią užduotį turite sukurti tokias funkcijas:

.py
# Reads all temperature data from the provided CSV files and
# returns a nested list containing temperature records for each week, day, and hour.
def read_all_data(file_names: list) -> list[list[list[float]]]

# Calculates and returns a list of average temperatures for each month in the quarter.
def calculate_month_average(data: list[list[list[float]]]) -> list[float]

# Identifies temperature anomalies and returns a list containing anomalies with details for each entry
# The return value example: [[2, 3, 15, 21.8, 6.6], [5, 6, 2, 9.1, -6.1]].
# The first number represents week, 2nd - day, 3rd - hour, 4th - temperature of that day, 5 - deviation 
def find_anomalies(list: list[list[list[float]]], month_averages: list[float]) -> list[list[int|float]]

# Calculates and returns the average deviation of all identified anomalies.
def calculate_anomalies_average(anomalies: list[list[int|float]]) -> float

# Writes the summary to a CSV file, including the average temperature of each month, the average deviation of anomalies, and the list of anomalies.
def write_quarter_summary(month_averages: list[float], anomalies_average: float, anomalies: list[list[int|float]]) -> None

Būtina suformatuoti rezultatus:

  • Visos temperatūros turi būti pateikos vieno skaičiaus po kablelio tikslumu;
  • Vidutinės temperatūros turi būti pateikos su celsijaus žymėjimu (°C);
  • Diena turi būti pateikta su nuliu priekyje, jeigu dienos reikšmė sudaryta iš vieno skaitmens;
  • Nuokrypiai (vidutinis ir sąraše esantys) turi pateikti su ženklus (- arba +);

Sukurta
su meile

Kontaktai

2024 — Vilius P.