<jupyter_start><jupyter_text>Funkcjonalny kod Funkcje, programowanie funkcyjne, parametry i argumenty funkcji Wo<NAME> Daft Academy Python4Beginners 19.11.2019  1. Deklarowanie funkcji  Składnia<jupyter_code>def name(attributes):
    """Docstring."""
    statements = None
def foo():
    pass
def foo():
    print('Hello World')
def foo():
    print('Hello World')
    
foo()<jupyter_output>Hello World<jupyter_text>__Nazwa funkcji__:- Może składać się z małych i dużych liter __[a-Z]__ oraz cyfr __[0-9]__ i znaków specjalnych: **_**- Powinna być zapisana małymi literami (pep8)- Zawierając wiele słów powinno oddzielić się je podkreślnikiem- Nie może zaczynać się od cyfry- Nie ma lilitu długości. Ale nazwy powyżej 79 znaków naruszają zasady pep8 w kwestii długości lini- Nie może być taka sama jak zarezerwowane słowa kluczowe python  Argumenty funkcji pozycyjne<jupyter_code>def add(x, y):
    outcome = x + y
    print(outcome)

add(100, 88)<jupyter_output>188<jupyter_text>Argumenty funkcji kluczowe (nazwane)<jupyter_code>def add(x=1, y=1):
    outcome = x + y
    print(outcome)

add(y=-1, x=1)
add(x=100)
add()<jupyter_output>2<jupyter_text>__Argumenty__:- Nie ma limitów na ilość argumentów jakie może przyjmować funkcja- Argumenty są obiektami (mogą być dowolnego typu)- Nazwa argumentu może mieć taką samą nazwę jak każdy inny identyfikator obiektu w python- Argumentu muszą być podane w kolejności: pozycyjne -> nazwane  Funkcje zwracają wartości przez `return`<jupyter_code>def add(x, y):
    outcome = x + y
    return outcome

print(add(100, -12))
def add(x, y):
    return x + y

print(add(100, -12))<jupyter_output>88<jupyter_text>Domyślnie zwracane wartości funkcji<jupyter_code>def foo(x, y):
    pass

print(foo(100, -12))
def bar(x, y):
    return

print(bar(100, -12))
def sic(x, y):
    x + y

print(sic(100, -12))<jupyter_output>None<jupyter_text>Wynik wywołanej funkcji można przypisać do zmiennej<jupyter_code>x = abs(-6)
print(x)<jupyter_output>6<jupyter_text>Funkcja może zawierać więcej niż jedą dyrektywę `return`<jupyter_code>def add(x, y):
    if y > 0:
        return x + y
    else:
        return x - y
add(100, -12)<jupyter_output><empty_output><jupyter_text>Lub logikę w samym wyrażeniu return<jupyter_code>def add(x, y):
    return x + y if y > 0 else x - y 
add(100, -12)<jupyter_output><empty_output><jupyter_text>__Return__:- Funkcja bez `return` zwraca `None`- Linia z `return` rozpoczyna się od słowa kluczowego `return`- `return` może występować w dowolnym miejscu funkcji- Kod funkcji wystepujący po wykonanej dyrektywie `return` nie jest wykonywany- Funkcja może zawierać więcej niż jedeną dyrektywę `return`- `return` zwraca obiekt, jeśli po wystąpieniu dyrektywy `return` występuje działanie, jest ono wykonane przez zwróceniem wyniku z funkcji  2. Funkcje wbudowane  Biblioteka standardowa Pythona posiada pewną liczbę funkcji wbudowanych, które są dostępne w dowolnym momencie wykonania programu.<jupyter_code>print("FooBar")
pow(10, 2)<jupyter_output><empty_output><jupyter_text>Lista funkcji wbudowanych        abs()         delattr()     hash()         memoryview()  set()        all()         dict()        help()         min()         setattr()        any()         dir()         hex()          next()        slice()        ascii()       divmod()      id()           object()      sorted()        bin()         enumerate()   input()        oct()         staticmethod()        bool()        eval()        int()          open()        str()        breakpoint()  exec()        isinstance()   ord()         sum()        bytearray()   filter()      issubclass()   pow()         super()        bytes()       float()       iter()         print()       tuple()        callable()    format()      len()          property()    type()        chr()         frozenset()   list()         range()       vars()        classmethod() getattr()     locals()       repr()        zip()        compile()     globals()     map()          reversed()    __import__()        complex()     hasattr()     max()          round()           - https://docs.python.org/3.7/library/functions.html  Pamiętajmy o nazewnictwie<jupyter_code>max(5, 6)
repr('aaa')
repr = 10
repr('aaa')
repr = 10
repr('aaa')<jupyter_output><empty_output><jupyter_text>__Funkcje wbudowane__:- Funkcji wbudowanych nie trzeba importować- Funkcje wbudowane to obiekty których nazwy nie powinny zostać nadpisane w globalnym kontekście aplikacji !- Z funkcji wbudowanych można skożystać w dowolnym miejscu w kodzie aplikacji  3. Deklarowania funkcji C.D.  W `return` można wywoływać inne funkcje<jupyter_code>def add(x, y):
    return sum([abs(x), abs(y)]) 

add(100, -12)<jupyter_output><empty_output><jupyter_text>Funkcja może zwracać więcej niż 1 wartość<jupyter_code>def multi_multi(x, y):
    a = x * y 
    b = x / y
    return a, b
a, b = multi_multi(100, -12)
print(a)
print(b)<jupyter_output>-1200
-8.333333333333334<jupyter_text>Co pod spodem zwraca funkcja która zwraca więcej niż 1 wartość ? `tuple`<jupyter_code>x = multi_multi(36, 11)
print(x)
print(type(x))
a, b = x
print(a)
print(b)<jupyter_output>396
3.272727272727273<jupyter_text>Mutowalne i niemutowalne nazwane parametry funkcji<jupyter_code>def print_words(words=[]):
    words.append('always_print')
    for word in words:
        print(word)
print_words()
print_words(['printer'])
print_words()
print_words()
print_words()<jupyter_output>always_print
always_print
always_print
always_print<jupyter_text>Poprawne rozwiąznie "problemu" mutowalnych parametrów funkcji<jupyter_code>def print_words(words=None):
    words = words if words else []
    words.append('always_print')
    for word in words:
        print(word)

print_words()
print("*" * 80)
print_words(['printer'])
print("*" * 80)
print_words()<jupyter_output>always_print
********************************************************************************
printer
always_print
********************************************************************************
always_print<jupyter_text>__Mutowalne parametry funkcji__:- Interpreter pythona interpetując kod funkcji wykonuje pracę tylko raz.- Każde kolejne odwołanie do obiektu funkcji wykona operacje na tym samym obiekcie a co za tym idzie parametry funkcji będą już wcześniej zainicjalizowane.  Dodatkowe argumenty pozycyjne<jupyter_code>def add(x, y, *args):
    print(type(args))
    print(args)
    return x + y + sum(args)
print(add(100, -12))
print(add(100, -12, 11, 21, 1))<jupyter_output><class 'tuple'>
(11, 21, 1)
121<jupyter_text>Dodatkowe argumenty kluczowe<jupyter_code>def add(x=1, y=1, **kwargs):
    print(type(kwargs))
    print(kwargs)
    return x + y + sum(kwargs.values())
print(add(100, -12))
print(add(100, -12, a=11, b=21, c=1))<jupyter_output><class 'dict'>
{'a': 11, 'b': 21, 'c': 1}
121<jupyter_text>Przykład mieszania argumentów i akceptowania ich większej ilości<jupyter_code>def add(x, *args, y=1, **kwargs):
    if kwargs.get('absolute', False):
        return abs(x) + abs(y) + sum(map(lambda x: abs(x), args ))
    return x + y + sum(args)
print(add(x=1, y=-1, z= 10))
print(add(100, -12, -100, y=-12, absolute=True))<jupyter_output>224<jupyter_text>__Argumenty CD__:- Argumenty dalej muszą zachować swoją kolejność, najpierw pozycyjne, następnie kluczowe.- Argumenty pozycyjne z którymi zostaje wywołana funkcja w ilości większej niż zadeklarowane zostają przekazane do listy zadeklarowanej z przedrostkiem __*__ w zachowanej kolejności.- Argumenty nazwane z którymi zostaje wywołana funkcja w ilości większej niż zadeklarowane zostają przekazane do słownika zadeklarowanego z przedrostkiem __**__ pod kluczami z jakimi zostały przekazane do funkcji.  Można 'zmusić' funkcję do przyjmowania tylko argumentów nazwanych<jupyter_code>def add(*, x=1, y=1, **kwargs):
    params = [i if isinstance(i, int) else 0 for i in kwargs.values()]
    params.extend((x, y))
    if kwargs.get('absolute', False):
        return sum(map(abs, params))
    return sum(params)
print(add(x=1, y=-1, z=10, kkk=10))
print(add(z=-10, kkk=-1, y=-12, absolute=True))<jupyter_output>25<jupyter_text>'Zmuszenie' funkcji do przyjmowania tylko argumentów nazwanych zaowocuje błędem jeśli wywołamy tę funkcję z podaniem jedynie pozycyjnych parametrów.Może być to bardzo przydatna funkcjonalność jeśli nasze funkcje ewoulują i przyjmują duże ilości parametrów.W czasie developmentu łatwo pomylić się i podać parametry w złej kolejności stąd możliwość wymuszenia na programiście stosowania się do API funkcji.  Funkcje anonimowe<jupyter_code>print(lambda x: x + 1)
print((lambda x: x + 1).__name__)
print((lambda x: x + 1)(10))

y = lambda x: x + 1
print(y(5))<jupyter_output><function <lambda> at 0x7f181436c8c8>
<lambda>
11
6<jupyter_text>Składnia funkcji anonimowej<jupyter_code>def func_name(attribute):
    """docstring"""
    return attribute + 1

print(func_name)
lambda attribute: attribute + 1<jupyter_output><empty_output><jupyter_text>Idealne do jednorazowych operacji wykorzystujących funkcję jako argument<jupyter_code>z = [1, 2, 3, 4, 5]
list(map(lambda x: x + 1, z))
a = [1, 2, 3, 4, 5, 6]
list(filter(lambda x: x % 2 == 0, a))<jupyter_output><empty_output><jupyter_text>4. Zasięg widoczności zmiennych  Zmienne lokalne<jupyter_code>def change_n():
    n = 200 # tworzę lokalną zmienną o nazwie n
    print('local variable n:', n)
change_n()
x = 10

def change_x():
    x = 200
    print('local variable x:', x)
change_x()
print('global variable x:', x)<jupyter_output>local variable x: 200
global variable x: 10<jupyter_text>Zmienne globalne<jupyter_code>n = 100 # tworzę globalną dla całego pliku zmienną o nazwie n
def change_n():
    n = 200 # tworzę lokalną dla funkcji zmienną o nazwie n
change_n()
print(n)
n = 100
def change_n():
    global n # deklaruję, że planuję zmieniać globalne n
    n = 200 # wpisuję wartość do globalnego n
change_n()
print(n) # wypisuję globalne n=200<jupyter_output>200<jupyter_text>5. Funkcja jest obiektem<jupyter_code>def foo(a):
    bar = 20
    return a * bar

foo.a = -1
print(foo(10), foo.a)
print(hasattr(foo, 'bar'), hasattr(foo, 'a'))
print(type(foo))
print(type(type(foo)))<jupyter_output><class 'function'>
<class 'type'><jupyter_text>6. Funkcje rekurencyjne  Funkcja silnia\begin{equation}    n! = \begin{cases} 1 &\text{dla } n \leq 1\\n(n-1)! &\text{dla } n>1 \end{cases}\end{equation}<jupyter_code>def factorial(n):
    if n <= 1:
        return 1
    else:
        return n * factorial(n - 1)
factorial(1)
factorial(2)  # 1*2
factorial(3)  # 1*2*3
factorial(6)  # 1*2*3*4*5*6
def factorial(n):
    if not isinstance(n, int):
        print("ERROR n needs to be an integer")
        return
    if n <= 1:
        return 1
    else:
        return n * factorial(n - 1)
factorial("6")
factorial(6)
def factorial(n):
    if not isinstance(n, int):
        print("ERROR n needs to be an integer")
        return
    
    return 1 if n <= 1 else n*factorial(n - 1)
factorial("6")
factorial(-1)
factorial(6)
def factorial(n):
    if not isinstance(n, int):
        print("ERROR n needs to be an integer")
        return
    if n < 0:
        print("ERROR n needs to be a positive number")
        return
    
    return 1 if n <= 1 else n*factorial(n - 1)
factorial("6")
factorial(-1)
factorial(6)<jupyter_output><empty_output><jupyter_text>7. Funkcje wewnętrzene<jupyter_code>def factorial(n):
    if not isinstance(n, int):
        print("ERROR n needs to be an integer")
        return
    if n < 0:
        print("ERROR n needs to be a positive number")
        return
    return 1 if n <= 1 else n*factorial(n - 1)
factorial("6")
factorial(-1)
factorial(6)
def factorial(n):
    def sub_factorial(n):
        return 1 if n <= 1 else n*sub_factorial(n - 1)
    if not isinstance(n, int):
        print("ERROR n needs to be an integer")
        return
    if n < 0:
        print("ERROR n needs to be a positive number")
        return
    return sub_factorial(n)
factorial("6")
factorial(-1)
factorial(6)<jupyter_output><empty_output><jupyter_text>8. Docstrings<jupyter_code>def foo(a):
    """Function that returns its argument."""
    return a

print(foo.__doc__)
print("*" * 80)
help(foo)<jupyter_output>Function that returns its argument.
********************************************************************************
Help on function foo in module __main__:

foo(a)
    Function that returns its argument.<jupyter_text>9. Import  Importowanie modułu<jupyter_code>import random

print(random)
print(random.randint(0, 10))
print(random._exp(2))<jupyter_output><module 'random' from '/home/wojtek/.virtualenvs/p4b/lib/python3.7/random.py'>
10
7.38905609893065<jupyter_text>Importowanie obiektów z modułu<jupyter_code>from random import *

print(randint)
print(randint(0, 10))
from random import *

print(_exp(2))
from random import *

print(_exp(2))
from random import _exp

print(_exp(2))
from random import randint, sample

a = [randint(0, 100) for _ in range(100)]
print(a)
print("_" * 80)
help(sample)
print("_" * 80)
print(sample(a, 10))<jupyter_output>[80, 54, 37, 34, 74, 78, 76, 10, 2, 45, 84, 9, 4, 30, 40, 89, 20, 88, 6, 14, 21, 89, 62, 96, 43, 19, 6, 76, 56, 18, 90, 3, 97, 78, 5, 46, 37, 88, 31, 33, 58, 18, 18, 78, 93, 9, 61, 80, 65, 45, 58, 42, 5, 3, 54, 35, 78, 17, 95, 56, 81, 46, 36, 30, 5, 100, 41, 74, 41, 74, 55, 5, 5, 74, 2, 80, 47, 22, 34, 86, 82, 21, 94, 39, 54, 15, 36, 78, 45, 11, 44, 49, 3, 48, 66, 61, 23, 48, 20, 94]
________________________________________________________________________________
Help on method sample in module random:

sample(population, k) method of random.Random instance
    Chooses k unique random elements from a population sequence or set.
    
    Returns a new list containing elements from the population while
    leaving the original population unchanged.  The resulting list is
    in selection order so that all sub-slices will also be valid random
    samples.  This allows raffle winners (the sample) to be partitioned
    into grand prize and second place winners (the subslices).
    
    Memb[...]
{'fraction_non_alphanumeric': 0.13120137054750516, 'fraction_numerical': 0.04211578271111428, 'mean_word_length': 4.107546481954065, 'pattern_counts': {'":': 0, '<': 118, '<?xml version=': 0, '>': 113, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '76059', 'license': 'MIT', 'n_tokens_mistral': 5753, 'n_tokens_neox': 5410, 'n_words': 1457, 'path': 'daftacademy-python4beginners-autumn2019/02.Funkcjonalny_Kod/wyklad_2.ipynb', 'repo_name': 'kMatejak/knowledge-hub', 'size': '14021'}