Програмиране с Python: Модули
Author: Стефан Кънев и Николай Бачийски

# Модули

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

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

#### 19.03.2008г.

# Елементарен проблем

world.py:

    """ A module for playing with the world. """
    def destroy():
        """Destroys the world."""
        print "The world was destroyed!"

    def save(kind):
        """Saves a kind."""
        print "All the", kind, "were saved."

Искаме да ползваме горните функции в много програми, а не искаме да копираме кода във всяка от тях.

# Елементарно решение

women.py:

    import world
    world.save('women')
    world.destroy()

animals.py:

    import world
    world.save('animals')
    world.destroy()

Работи без проблеми ако трите файла са в една директория.
Чрез модули можем да използваме един и същи код много пъти (code reuse).

# Атрибути

- Обектите имат свързани с тях имена, наречени атрибути, които се достъпват чрез оператора `.` (точка)
- Нека разгледаме по-подробно `world.destroy()`
- `destroy` е функция и е атрибут на обекта `world`, който пък е модул

-     >>> world
      <module 'world' from '/home/nb/py/world.py'>

-     >>>world.destroy
      <function destroy at 0x00CA46F0>

- с вградената функция `dir` можем да видим всички атрибути на обект:

-     >>> dir(world)
      ['__builtins__', '__doc__', '__file__', '__name__', 'destroy', 'save']
      >>> world.__doc__
      ' A module for playing with the world. '
      >>> world.__name__
      'world'

# Области от имена (1)

- Всяка програма е модул сама по себе си и всички глобални имена, са негови атрибути
- savepoohs.py:

-     import world
      def savePoohs():
          world.save('Poohs')
      insane = True
      print dir()

- Ще отпечата: `[<системни атрибути> 'world', 'savePoohs', 'insane']`

# Области от имена (2)

- В различни модули можем да имаме едни и същи имена
- Всеки модул си има различен набор от имена

-     import pooh
      import piglet
      def breakfast():
          print "Is it already eleven?"
      pooh.breakfast()
      piglet.breakfast()
      breakfast()

Ако използваме модули няма нужда да се притесняваме, че в няколко програми сме използвали едни и същи имена.

# Търсене на атрибути

- `answer = 42`
- в този случай е ясно, че обектът зад името `answer` трябва да се търси по стандартния начин (в разните обхвати)
- `pooh.naivete = 42`
- името `naivete` не трябва да се търси в стандартните обхвати, а трябва да се търси в списъка с атрибути на обекта зад името `pooh`
- `universe.milkyway.earth.europe.bg.beautify()`

# Модули и обхвати

- Глобалните променливи са на практика модулни променливи. Те са глобални само в рамките на модула, в който се намират.
- mod.py:

-     bottles = 1
      def addBottle():
          global bottles
          bottles += 1

- bottler.py:

-     import mod
      bottles = 5
      mod.addBottle()
      print bottles, mod.bottles

# Използване на модули — `import`

Чрез вградената фунцкия `import` ни дава възможност да ползваме код от външни файлове.

Ето какво се случва, ако изпълним `import mod`:

1. Намира се файла, в който се намира модула
2. Кодът се компилира до байт-код (ако е необходимо)
3. Кодът на модула се изпълнява (дефиниции на функции, друг код)
4. На името `mod` се присвоява обекта на изпълнения модул

# Търсене на файла

    import mod

Търси се в:

1. текущата директория
2. променливата на средата `PYTHONPATH`
3. системни директории с модули
4. директории, описани в `.pth` файлове

Всички по реда на претърсване: списъка `sys.path`

# Модул vs. Програма

- И в двата случая кодът ни се изпълнява
- Искаме ако кодът ни е бил изпълнен директно да тестваме модула си
- Искаме ако кодът ни е бил включен с `import` само да дефинираме фукнциите си
- eater.py:

-     def eat(what):
          print what, "was eaten."

      if __name__ == '__main__':
          eat('The world as we know it')

- `$ python eater.py
  The world as we know it was eaten.`
- `>>> import eater`

# Пакети от модули

Представете си, че имаме две отделни системи, в които има модул на име `game`

    system1/
    	game.py
    	<други модули>
    system2/
    	game.py
    	<други модули>

Сега напишете система, която използва модулите `game` и от двете други:

`import game
game.play()`

Тук вече имаме малък проблем.

# Пакети от модули (2)

Би било много удобно ако можехме да разделим двата модула `game` в отделни пакети:

    import system1.game
    import system2.game
    system1.game.play()
    system2.game.play()

Това е възможно при няколко условия:

- директориите system1 и system2 са някъде из пътя за търсене на python
- във всяка от тях има файл с име `__init__.py`

При import на по-сложно име се извършва следното:

- намира се поредната компонента от пътя (разделени с точки)
- ако има съответстващ файл — включва се
- ако е директория — изпълнява се `__init__.py` в нея

# Пакети от модули (3)

С пакети от модули можем с едни замах да import-нем много модули:

forest/pooh.py:

`name = 'Pooh'`

forest/piglet.py:

`name = 'Piglet'`

forest/__init__.py:

    import pooh
    import piglet

user.py:

    import forest
    print forest.pooh.name
    print forest.piglet.name

# import

- `import mod` — намира се модула и обектът му се присвоява на името `mod`

-     sys = '/usr/src/sys'
      import os
      def listByExt(path, ext):
      	return [d for d in os.listdir(path) if d.endswith('.'+ext)]

      # искаме да отпечатаме sys.path
      import sys
      print sys.path

      # сега да видим всички питонски файлове в /usr/src/sys
      print listByExt(sys, 'py')
      # опс:
      TypeError: coercing to Unicode: need string or buffer, module found

# import as

    import sys as sysmod

print sysmod.path Почти същото е като:

    import sys
    sysmod = sys
    del sys
    sysmod.path

само че ще загубим старата стойност на `sys`

# from mod import names

Понякога е досадно да извикваме всеки път цялото име на накой обект, като `universe.milkyway.earth.europe.bg.beautify()`

    from universe.milkyway.earth.europe.bg import beautify
    beautify()

Или даже:

    from universe.milkyway.earth.europe.bg import beautify as b
    b()

# from mod import *

Хубаво ли ви е така:

`from mod import a,b,c,d,e,f,g,h,i,j,k,l`

И на мен. Затова Python ни позволява да внесем в нашия модул всички имена от някой друг: `from mod import *`

- Имената от типа `_име` не се внасят
- Имената от типа `__име__` не се внасят
- Ако в `mod` има списък с име `__all__`, то се внасят само имената съдържащи се в него

# Още разни модулни неща

- Съществува модул `__builtin__`, който съдържа всички вградени функции и други имена:

      import __builtin__
      __builtin__.dir = abs
      print dir(-3)

- модулите се импортират само веднъж:

      >>> import world
      >>> world.save('babas')
      All the babas were saved.
      >>> world.save = 5
      >>> import world
      >>> world.save
      5

  За презареждане трябва да се ползва `reload()`:

      >>> world.save
      5
      >>> reload(world)
      >>> world.save
      <function save at 0x015001F0>

# Още въпроси?

- Страница на курса: http://fmi.py-bg.net/
- Форуми на курса: http://fmi.py-bg.net/forums/