Wprowadzenie
do języka Python
- Wykład 4

Funkcje

Funkcje

def functionname( parameters ):
   "function_docstring"
   function_suite
   return [expression]
def printme(str):
    """Funkcja wyświetlająca string"""
    print(str)
    return


printme("abc")
print(printme.__doc__)
abc
Funkcja wyświetlająca string

Przekazywanie przez referencję

Ale trzeba pamiętać podział typów:

def foo(a):
    a += 1
    print("w", a)


a = 7
print(a)
foo(a)
print(a)
7
w 8
7
def changeme(lista):
    print("Przed zmianą: ", lista)
    lista[2] = 50
    print("Po zmianie: ", lista)
    return


mylist = [10, 20, 30]
changeme(mylist)
print("Poza funkcją: ", mylist)
Przed zmianą:  [10, 20, 30]
Po zmianie:  [10, 20, 50]
Poza funkcją:  [10, 20, 50]
def changeme(lista):
    lista = [2, 3, 4]
    print("Wewnątrz funkcji: ", lista)
    return


lista = [10, 20, 30]
changeme(lista)
print("Poza funkcją: ", lista)
Wewnątrz funkcji:  [2, 3, 4]
Poza funkcją:  [10, 20, 30]
def changeme():
    global lista
    lista = [2, 3, 4]
    print("Wewnątrz funkcji: ", lista)
    return


changeme()
print("Poza funkcją: ", lista)
Wewnątrz funkcji:  [2, 3, 4]
Poza funkcją:  [2, 3, 4]

Obowiązkowy argument (ang. positional argument)

def printme(str):
    print(str)
    return


printme()
## TypeError: printme() missing 1 required 
positional argument: 'str'

Keyword argument

def kwadrat(a):
    return a*a


print(kwadrat(a=4))
16

Domyślny argument

def sumsub(a, b, c=0, d=0):
    return a - b + c - d


print(sumsub(12, 4))
print(sumsub(3, 4, 5, 7))
8
-3
def srednia(first, *values):
    return (first + sum(values)) / (1 + len(values))


print(srednia(2, 3, 4, 6))
print(srednia(45))
3.75
45.0
def f(**kwargs):
    print(kwargs)


f()
f(pl="Polish", en="English")
{}
{'pl': 'Polish', 'en': 'English'}

Inne symbole

  • symbol / oznacza, że wcześniejsze argumenty są pozycyjne
  • symbol * oznacza, że późniejszej argumenty są typu keyword
def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)


f(10, 20, 30, d=40, e=50, f=60)
10 20 30 40 50 60

Funkcje matematyczne

Link do dokumentacji https://docs.python.org/3/library/math.html

import math

a=0
b=math.sin(2*math.pi)
print(b)
print(math.isclose(a,b, rel_tol=1e-09, abs_tol=1e-09))
-2.4492935982947064e-16
True

Wyrażenia lambda

Skąd nazwa? https://pl.wikipedia.org/wiki/Rachunek_lambda

Wyrażenie

def identity(x):
    return x

zapisujemy jako

lambda x: x

Jeśli uruchomimy to w konsoli, to możemy wywołać

_(5)

To tzw. przykład funkcji anonimowej.

By uruchomić to też w skrypcie, to potrzebujemy argumentu:

print((lambda x: x + 1)(2))

lub

add_one = lambda x: x + 1
print(add_one(2))

Możemy też tworzyć złożenia funkcji (funkcje wyższych rzędów):

high_ord_func = lambda x, func: x + func(x)
print(high_ord_func(2, lambda x: x * x))
print(high_ord_func(2, lambda x: x + 3))
6
7

Możemy również skorzystać z różnych opcji argumentów jak dla funkcji:

print((lambda x, y, z: x + y + z)(1, 2, 3))
print((lambda x, y, z=3: x + y + z)(1, 2))
print((lambda x, y, z=3: x + y + z)(1, y=2))
print((lambda *args: sum(args))(1,2,3))
print((lambda **kwargs: sum(kwargs.values()))(one=1, two=2, three=3))
print((lambda x, *, y=0, z=0: x + y + z)(1, y=2, z=3))
6
6
6
6
6
6

Typ

string = 'abc'
print(lambda string: string)
<function <lambda> at 0x000002A113346B90>

Różnice względem zwykłej funkcji:

def cube(y):
    print(f"Finding cube of number:{y}")
    return y * y * y
  
lambda_cube = lambda num: num ** 3
print("invoking function defined with def keyword:")
print(cube(30))
print("invoking lambda function:", lambda_cube(30))
invoking function defined with def keyword:
Finding cube of number:30
27000
invoking lambda function: 27000

Przykłady praktyczne

r = lambda a: a + 15
print(r(10))
r = lambda x, y: x * y
print(r(12, 4))
25
48
subject_marks = [('English', 88), ('Science', 90), ('Maths', 97), ('Social sciences', 82)]
print("Original list of tuples:")
print(subject_marks)
subject_marks.sort(key=lambda x: x[1])
print("\nSorting the List of Tuples:")
print(subject_marks)
Original list of tuples:
[('English', 88), ('Science', 90), ('Maths', 97), ('Social sciences', 82)]

Sorting the List of Tuples:
[('Social sciences', 82), ('English', 88), ('Science', 90), ('Maths', 97)]
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print("Original list of integers:")
print(nums)
print("\nEven numbers from the said list:")
even_nums = list(filter(lambda x: x % 2 == 0, nums))
print(even_nums)
print("\nOdd numbers from the said list:")
odd_nums = list(filter(lambda x: x % 2 != 0, nums))
print(odd_nums)
Original list of integers:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Even numbers from the said list:
[2, 4, 6, 8, 10]

Odd numbers from the said list:
[1, 3, 5, 7, 9]
array_nums1 = [1, 2, 3, 5, 7, 8, 9, 10]
array_nums2 = [1, 2, 4, 8, 9]
print("Original arrays:")
print(array_nums1)
print(array_nums2)
result = list(filter(lambda x: x in array_nums1, array_nums2))
print("\nIntersection of the said arrays: ", result)
Original arrays:
[1, 2, 3, 5, 7, 8, 9, 10]
[1, 2, 4, 8, 9]

Intersection of the said arrays:  [1, 2, 8, 9]

Wywoływalne obiekty tzw. calle

W Pythonie callable to termin odnoszący się do obiektów, które można “wywołać” (ang. call) jako funkcje. W praktyce oznacza to, że obiekt może być użyty z nawiasami okrągłymi i potencjalnie przyjmować argumenty.

Przykład prosty:

def greet(name):
    return f"Hello, {name}!"


print(greet("Tom"))
Hello, Tom!
greet_lambda = lambda name: f"Hello, {name}!"
print(greet_lambda("Anna"))
Hello, Anna!
greet_lambda = lambda name: f"Hello, {name}!"
print(callable(greet_lambda))  # True
print(callable(42))  # False
True
False

Słowo kluczowe yield - leniwe wyrzucanie

Słowo kluczowe yield jest używane w funkcjach generatorów. Generator to specjalny rodzaj iteratora, który pozwala na iterowanie (przechodzenie) przez sekwencję wartości w sposób leniwy, co oznacza, że wartości są generowane na żądanie, a nie na początku. (leniwe wyrzucanie)

Gdy generator napotyka słowo kluczowe yield, zwraca wartość po yield i wstrzymuje wykonywanie, pamiętając swój stan. Wywołanie funkcji next() na iteratorze sprawia, że generator wznowi działanie od miejsca, w którym się zatrzymał, aż napotka kolejne słowo kluczowe yield lub zakończy działanie.

def simple_number_generator(max_number):
    num = 1
    while num <= max_number:
        yield num
        num += 1

# Użycie generatora
for number in simple_number_generator(5):
    print(number)
1
2
3
4
5
def even_numbers_generator(max_number):
    num = 2
    while num <= max_number:
        yield num
        num += 2


for even_number in even_numbers_generator(10):
    print(even_number)
2
4
6
8
10
def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1


gen = infinite_sequence()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
0
1
2
3

import math

def is_prime(num):
    if num <= 1:
        return False
    for i in range(2, int(math.sqrt(num)) + 1):
        if num % i == 0:
            return False
    return True

def prime_number_generator():
    num = 2
    while True:
        if is_prime(num):
            yield num
        num += 1

# Użycie generatora
primes = prime_number_generator()
for _ in range(10):  # Wypiszemy pierwsze 10 liczb pierwszych
    print(next(primes))
2
3
5
7
11
13
17
19
23
29

Funkcja a type hinting

def stringify(num: int) -> str:
    return str(num)


def plus(num1: int, num2: int) -> int:
    return num1 + num2
def show(value: str, excitement: int = 10) -> None:
    print(value + "!" * excitement)

Typowanie tzw. calli

from typing import Callable


def f(a: int, b: float) -> float:
    return a + b


x: Callable[[int, float], float] = f

Typowanie generatorów:

from typing import Iterator


def gen(n: int) -> Iterator[int]:
    i = 0
    while i < n:
        yield i
        i += 1

Kilka różnych typów:

from typing import Union, Optional


def send_email(address: Union[str, list[str]],
               sender: str,
               cc: Optional[list[str]],
               bcc: Optional[list[str]],
               subject: str = '',
               body: Optional[list[str]] = None
               ) -> bool:
    pass
def quux(x: int, /,  *, y: int) -> None:
    pass


quux(3, y=5)  # Ok
# quux(3, 5)  # error: Too many positional arguments for "quux"
# quux(x=3, y=5)  # error: Unexpected keyword argument "x" for "quux"

Bibliografia