Author: Стефан Кънев и Николай Бачийски

# Функциите и техните приятели

### „ Програмиране с Python“, ФМИ

### Стефан Кънев & Николай Бачийски

#### 12.03.2008г.

# Функции

- Дефинират се с ключовата дума `def`
- Връщат стойност с `return`
- Например:

      def factorial(n):
          if n == 0:
          	return 1
          else:
          	return n * factorial(n-1)

      >>> factorial(4)
      24

# Функции (2)

Ако искате една функция да взема повече от един аргумент, просто ги разделяте със запетаи:

    def orderPizza(type, ketchup, mayo):
        order = "I want a " + type + " pizza."
        if ketchup: order += " Please add ketchup."
        if mayo: order += " And please add mayo."

        reply = callAndTell("555-PIZZA", order)
        return reply

    >>> orderPizza("Margaritta", False, True)

# Аргументи по подразбиране

Може да слагате стойности по подразбиране на аргументите:

    def orderPizza(type, ketchup = True, mayo = True):
        order = "I want a " + type + " pizza."
        if ketchup: order += " Please add ketchup."
        if mayo: order += " And please add mayo."

        reply = callAndTell("555-PIZZA", order)
        return reply

    >>> orderPizza("Margaritta")

# Аргументи по подразбиране (2)

Имайте предвид, че стойностите по подразбиране се инициализират само веднъж

    def addBeer(beers = []):
        beers.append("Beer")
        print beers

    >>> addBeer()
    ['Beer']

    >>> addBeer()
    ['Beer', 'Beer']

В този случай е по-добре да сложите `beers` със стойност по подразбиране `None` и създавате нов списък в тялото на функцията, ако потребителя не подаде нещо друго.

# Извикване с наименовани параметри

При извикване можете да подавате параметрите с имената им. По този начин може да ги дадете в различен ред или да използвате само някои, които имат стойност по подразбиране.

    def orderPizza(type, ketchup = True, mayo = True):
        order = "I want a " + type + " pizza."
        if ketchup: order += " Please add ketchup."
        if mayo: order += " And please add mayo."

        reply = callAndTell("555-PIZZA", order)
        return reply

    >>> orderPizza(ketchup = False, mayo = False, type = "Margaritta")
    >>> orderPizza(type = "Margaritta", ketchup = False)
    >>> orderPizza("Margaritta", ketchup = False)

# Имена на функции

Python не прави разлика между имена на функции и обекти. Може да ги третирате по еднакъв начин

    def scare(): print "Ni!"

    >>> sayIt = scare
    >>> sayIt()
    Ni!
    >>> scare()
    Ni!
    >>> del scare
    >>> sayIt()
    Ni!
    >>> scare()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'scare' is not defined

# Имена на функции (2)

Съответно може да предавате функции като аргументи...

    def calculate(operation, n1, n2): return operation(n1, n2)
    def addition(n1, n2): return n1 + n2
    def multiplication(n1, n2): return n1 * n2

    >>> calculate(addition, 6, 7)
    13
    >>> calculate(multiplication, 6, 7)
    42

...или като връщани стойности:

    def makeStamp(owner):
        def stamp(something):
            return something + " is owned by " + owner
        return stamp

    >>> stamper = makeStamp("The Goverment")
    >>> print stamper("This thing there")
    'This thing there is owned by The Goverment'

# Имена на функции (3)

`def` твърденията се пресмятат по време на изпълнение

    def operation(name, n1, n2):
        if name == "addition":
            def oper(x, y):
    	    return x + y
        else:
            def oper(x, y):
    	    return x * y
        return oper(n1, n2)

    >>> operation('addition', 6, 7)
    13
    >>> operation('multiplication', 6, 7)
    42

# Динамични аргументи

Като поставите `*` прeд списък...

    >>> preference = ('Margaritta', True, True)
    >>> orderPizza(*preference)

...или `**` пред речник:

    >>> preference = {'type': 'Margaritta', 'mayo': True}
    >>> orderPizza(**preference)

# Функции с променлив брой аргументи

    def sum(prefix, *things):
        result = 0
        for n in things:
            result += n
        print prefix + str(result)

    >>> sum("The answer:", 1, 2, 3, 5, 7, 11, 13)
    The answer: 42

# Функции с променлив брой аргументи (2)

    def studentInfo(name, **things):
        print "Hello, my name is " + name
        print "I come from " + things['country']
        print "I like " + things['cheese']

    >>> studentInfo('The Black Knight',
              country = 'Assyria', cheese = 'Limburger')
    Hello, my name is The Black Knight
    I come from Assyria
    I like Limburger

# Функции с променлив брой аргументи (3)

- Когато имате и трита вида параметри:

      def func(param, param2, *args, **kwargs)

- Първо трябва да предавате позиционните аргументи
- След тях трябва да предадете именованите аргументи
- Ако предадете позиционен след именован, ще получите грешка

# Област на видимост

В Python има четири области на видимост

- Локален
- Обграждаща функция
- Глобален
- Вграден

# Локален

    x = 11

    def stuff():
        x = 14
        print x

    stuff()
    print x

Резултат:

    14
    11

# Глобален

- Ако искате да променяте глобални променливи, трябва ви ключовата дума `global`

    x = 11

    def stuff():
        global x
        x = 14
        print x

    stuff()
    print x

Резултат:

    14
    14

Глобалните променливи са ЗЛО! Не искайте да ги ползвате.

# Обграждаща функция

    def f1():
        x = 88
        def f2():
            print x
        return f2

    action = f1()
    action()	# Отпечатва 88

# Анонимни функции

- Дефинират се с `lambda`
- Могат да съдържат само един израз, който е връщаната им стойност
- Внасят нотка функционално програмиране в Python

    >>> operation = lambda x, y: x * y
    >>> print operation(6, 7)
    42

# Функции от по-висок ред — map

- Приема: едноаргументна функция и поредица
- Връща: списък образуван от елементите на дадената поредица, върху които е приложена функцията

    >>> map(lambda x: x ** 2, range(1, 10))
    [1, 4, 9, 16, 25, 36, 49, 64, 81]

# Функции от по-висок ред — filter

- Приема: едноаргументна функция и поредица
- Връща: списък образуван от елементите на дадената поредица, за които функцията връща списък

    >>> filter(lambda x: x % 2, range(1, 10))
    [1, 3, 5, 7, 9]

# Функции от по-висок ред — reduce

- Приема: двуаргументна функция и поредица
- Прилага функцията последователно на елементите в поредицата, като започва от първите два, след което го прилага с третия елемент и т.н. Връща крайния резултат
- Може да вземе начална стойност

    >>> reduce(lambda x, y: x + y, [3, 5, 7, 11, 13])
    39
    >>> reduce(lambda x, y: x + y, [3, 5, 7, 11, 13], 3)
    42

# List comprehension

- Python има специален синтаксис за `map`
- `[израз for променлива in поредица]`
- Например:

    >>> [x * x for x in range(0, 10)]
    [1, 4, 9, 16, 25, 36, 49, 64, 81]

# List comprehension (2)

- Можете да добавите функционалността и на `filter`
- `[израз for променлива in поредица if условие]`
- Например:

    >>> [x * x for x in range(0, 10) if x % 2]
    [1, 9, 25, 49, 81]

# List comprehension (3)

Може да вложите list comprehension в друг такъв:

    >>> nums = range(0, 10)
    >>> [(x, y) for x in nums for y in nums if x+y == 13]
    [(4, 9), (5, 8), (6, 7), (7, 6), (8, 5), (9, 4)]