Funkce je také jenom objekt¶

Lumír Balhar¶

Red Hat, PyLadies, Pyvo & PyCon CZ Ostrava¶

Z domácích projektů¶

In [1]:
print("Ahoj PyCone!")
print
print("Jak se máte?")
Ahoj PyCone!
Jak se máte?

Jak a kdy má smysl použít jméno funkce „bez závorek“?¶

A co se pod tím jménem vlastně skrývá?¶

Jména a objekty¶

In [2]:
cislo = 10

def moje_nova_funkce(a, b):
    soucet = a + b
    return soucet
In [3]:
print(dir(cislo))
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
In [4]:
print(f"""
Číslo {cislo} má binární reprezentaci {bin(cislo)}.
Počet bitů potřebných k zápisu: {cislo.bit_length()}.
Celkový počet jedniček v binárním zápisu: {cislo.bit_count()}
""")
Číslo 10 má binární reprezentaci 0b1010.
Počet bitů potřebných k zápisu: 4.
Celkový počet jedniček v binárním zápisu: 2

In [5]:
print(dir(moje_nova_funkce))
['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__getstate__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
In [6]:
print(f"""
Funkce: 
- se jmenuje: {moje_nova_funkce.__name__},
- má {moje_nova_funkce.__code__.co_argcount} argumenty,
- pracuje s těmito konstantami: {moje_nova_funkce.__code__.co_consts},
- a lokálními proměnnými: {moje_nova_funkce.__code__.co_varnames}.
""")
Funkce: 
- se jmenuje: moje_nova_funkce,
- má 2 argumenty,
- pracuje s těmito konstantami: (None,),
- a lokálními proměnnými: ('a', 'b', 'soucet').

Funkce a její jméno¶

In [7]:
funkce = print
In [8]:
funkce("Ahoj!")
Ahoj!
In [9]:
seznam = []
seznam.append(funkce)

seznam[0]("Ahoj")
Ahoj
In [10]:
slovnik = {}
slovnik["skryty print"] = funkce

slovnik["skryty print"]("Ahoj")
Ahoj

Funkce vyššího řádu¶

Funkce jako argument jiné funkce¶

In [11]:
def aplikuj(funkce, seznam):
    novy_seznam = []
    for x in seznam:
        novy_seznam.append(funkce(x))
    return novy_seznam


def absolutni_hodnota(a):
    if a < 0:
        return -a
    else:
        return a
In [12]:
aplikuj(absolutni_hodnota, [-1, 3, 4, -5, -8])
Out[12]:
[1, 3, 4, 5, 8]

map¶

In [13]:
map(abs, [-1, 3, 4, -5, -8])
Out[13]:
<map at 0x7f237c147e20>
In [14]:
list(map(abs, [-1, 3, 4, -5, -8]))
Out[14]:
[1, 3, 4, 5, 8]

filter¶

In [15]:
def je_sude(x):
    if x % 2 == 0:
        return True
    else:
        return False
In [16]:
filter(je_sude, [1, 2, 3, 4, 5])
Out[16]:
<filter at 0x7f237c147c70>
In [17]:
list(filter(je_sude, [1, 2, 3, 4, 5]))
Out[17]:
[2, 4]

reduce¶

In [18]:
from functools import reduce

def secti(a, b):
    return a + b
In [19]:
reduce(secti, [1, 2, 3, 4, 5])
Out[19]:
15

Anonymní funkce¶

In [20]:
def obracena_hodnota(x):
    """Vrátí obrácenou hodnotu čísla."""
    return -x

list(
    map(
        obracena_hodnota,
        [-1, 3, 4, -5, -8]
    )
)
Out[20]:
[1, -3, -4, 5, 8]
In [21]:
list(
    map(
        lambda x: -x,
        [-1, 3, 4, -5, -8]
    )
)
Out[21]:
[1, -3, -4, 5, 8]
In [22]:
def secti(a, b):
    return a + b

print(f"Součet je {secti(2, 3)}")

secti = lambda a, b: a + b

print(f"Součet je {secti(2, 3)}")
Součet je 5
Součet je 5

map¶

In [23]:
list(
    map(
        lambda x: x if x >= 0 else -x,  # ← !!!
        [-1, 3, 4, -5, -8]
    )
)
Out[23]:
[1, 3, 4, 5, 8]

filter¶

In [24]:
list(
    filter(
        lambda x: x % 2 == 0,  # ← !!!
        [1, 2, 3, 4, 5]
    )
)
Out[24]:
[2, 4]

reduce¶

In [25]:
reduce(
    lambda x, y: x + y,  # ← !!!
    [1, 2, 3, 4, 5]
)
Out[25]:
15

Funkce jako návratová hodnota jiné funkce¶

In [26]:
def vrat_funkci_pro_nasobeni(x):
    def funkce_pro_nasobeni(y):
        return x * y
    return funkce_pro_nasobeni
In [27]:
vynasob_peti = vrat_funkci_pro_nasobeni(5)

vynasob_peti(3)
Out[27]:
15

Dekorátory¶

aneb jak existující funkci obohatit¶

In [28]:
def loguj(puvodni_funkce):
    def nova_funkce(x, y):
        print(f"Volám funkci {puvodni_funkce.__name__} s argumenty {x} a {y}.")
        vysledek = puvodni_funkce(x, y)
        print(f"Výsledek je {vysledek}.")
        return vysledek
    return nova_funkce
In [29]:
def secti(a, b):
    return a + b
In [30]:
nova_secti = loguj(secti)
In [31]:
nova_secti(3, 4)
Volám funkci secti s argumenty 3 a 4.
Výsledek je 7.
Out[31]:
7
In [32]:
@loguj
def vynasob(a, b):
    return a * b
In [33]:
vynasob(3, 4)
Volám funkci vynasob s argumenty 3 a 4.
Výsledek je 12.
Out[33]:
12

Užitečné dekorátory¶

Standardní knihovna¶

  • @dataclass
  • @functools.lru_cache
  • @contextlib.contextmanager
  • @staticmethod, @classmethod

Externí knihovny¶

  • @pysnooper.snoop
  • @pytest.fixture
  • @flask.route
  • @click.command

Závěrem¶

  • Na začátečnických kurzech vám lžeme, ale z dobrého důvodu 😉
  • Funkce je objekt a má jméno, což umožňuje s nimi pracovat jako s proměnnými.
  • map, filter a reduce jsou super a měly by se používat častěji.
  • Dekorátory jsou skvělý způsob, jak kombinovat váš kód s jiným.

Děkuji za pozornost!¶

Dotazy?¶

Lumír Balhar¶

Red Hat, PyLadies, Pyvo & PyCon CZ Ostrava¶