Раздел «Язык Си».PythonModules:

Зачем нужны модули

Программы и модули

Программа - любой файл с расширением .py
Предназначена, чтобы ее запускали.

Модуль - файл с расширением .py
Могут быть модули, написанные на других языках (например, С).
Предназначены для import (или запуска как отдельного модуля)

Как работает import

Напишем программу module_using_sys.py

import sys

print('The command line arguments are:')
for i in sys.argv:
    print(i)

print('\n\nThe PYTHONPATH is', sys.path, '\n')

Запустим ее:

$ python module_using_sys.py we are arguments    # each arg is separated by white space
The command line arguments are:
module_using_sys.py
we
are
arguments


The PYTHONPATH is ['/tmp/py',
# many entries here, not shown here
'/Library/Python/2.7/site-packages',
'/usr/local/lib/python2.7/site-packages']

и другие объекты модуля,

ALERT! не создавайте программы или модули, имена которых совпадают с именами модулей или каталогов верхнего уровня в библиотеке, если только вы не пытаетесь подставить свою собственную реализацию и ваше переопределение преднамеренно.

Чтобы проверить, есть ли уже в системе модуль или пакет с именем, например Music, запустите

python -c "import Music"

Если модуля нет, то будет ошибка ImportError?.

TIP Проверка сейчас не гарантирует, что мы НЕ поставим такой модуль потом.

Байт-код

Чтобы программа выполнялась быстрее, ее можно заранее скомпилировать в байт-код (.pyc) и оптимизировать (.pyo)

Байт-код в питоне создается по необходимости. В других языках (Java), его нужно создавать явно.

Когда интерпретатору нужен байт-код для файла myname.py:

Чтобы получить оптимизированный байт-код запустите питон с ключом -O (буква О, а не ноль!) или установите переменную среды PYTHONOPTIMIZE в O (директория должна иметь права на запись).

Чтобы НЕ сохранять байт-код в файле, запустите питон с ключом -B или установите переменную среды окружения PYTHONDONTWRITEBYTECODE

Формы import

Чтобы использовать функцию из другого файла myfile.py нужно просто написать import myfyle

import пишут вверху файла, по одному пакету на строчку.

Хорошо:

import os
import os.path
import sys

Плохо:

import os, os.path, sys

Порядок импорта:

import имя_модуля as короткое_имя

Импортируем имя модуля и обращаемся к его функциям и переменным по имени модуль.функция

import math
import numpy as np
a = math.sqrt(2)
b = np.sqrt(2)

from модуль import функция1, функция2, функция3

Импортируем только нужные функции и переменные. Обращаемся прямо по их именам.

from math import sin, cos, pi
a = sin(pi) + cos(pi)

from модуль import *

Импортировать все функции и методы из этого модуля.

ALERT! не делайте так. Много лишнего импорта. Возможен конфликт имен функций из разных модулей.

from math import *
a = sin(pi) + cos(pi)

Будут импортированы:

Конфликты имен

import os
print(os.path.basename(filename)) # безопасный доступ по полным
                                  # квалифицированным именам
import os.path as path
print(path.basename(filename))    # есть риск конфликта имен с модулем path

from os import path
print(path.basename(filename))    # есть риск конфликта имен с модулем path

from os.path import basename
print(basename(filename))         # есть риск конфликта имен с модулем basename

from os.path import *
print(basename(filename))         # есть риск множественных конфликтов имен

Пакеты

Пакет – это каталог, содержащий множество модулей и файл с именем __init__.py

Пусть мы написали функции, которые работают с изображениями, например, save и load.

Методы для каждого формата (bmp, png, jpeg) поместили в отдельный файл Bmp.py, Png.py, Jpeg.py

Все эти файлы поместили в директорию Graphics. Туда добавили пустой файл __init__.py

Директория превратилась в пакет Graphics.

Graphics/
   __init__.py
   Bmp.py
   Jpeg.py
   Png.py
   Tiff.py
   Xpm.py

Дальше мы можем работать с этим пакетом, как привыкли:

import Graphics

Переменная __all__

TIP Иногда удобнее загружать все модули пакета одной инструкцией. Для этого в файле __init__.py добавьте в переменную __all__ какие модули нужно загрузить

Файл __init__.py:

__all__ = ["Bmp", "Jpeg", "Png", "Tiff", "Xpm"]

Можно в __init__.py добавлять любой другой код.

Теперь можно импортировать все модули пакета, используя *.

from Graphics import *
image = Xpm.load("sleepy.xpm")

Вложенные пакеты

Пусть в директории Graphics есть директория Vector с модулями Eps.py и Svg.py:

Graphics/
   __init__.py
   Bmp.py
   Jpeg.py
   Png.py
   Tiff.py
   Vector/
      __init__.py
      Eps.py
      Svg.py
   Xpm.py

Чтобы директория Vector стала пакетом, поместим в него файл __init__.py (пустой или с __all__)

import Graphics.Vector.Eps
image = Graphics.Vector.Eps.load("sneezy.eps")

Пример своего модуля

Пример взят из книги Саммерфильда, Глава 5 Модули / Модули и пакеты / Пакеты.

Пример файла TextUtil?.py

#!/usr/bin/env python3
# Copyright (c) 2008 Qtrac Ltd. All rights reserved.
"""
Этот модуль предоставляет несколько функций манипулирования строками.
>>> is_balanced("(Python (is (not (lisp))))")
True
>>> shorten("The Crossing", 10)
'The Cro...'
>>> simplify(" some text with spurious whitespace ")
'some text with spurious whitespace'
"""

import string

def simplify(text, whitespace=string.whitespace, delete=""):
    r"""Возвращает текст, из которого удалены лишние пробелы.
    Параметр whitespace - это строка символов, каждый из которых
    считается символом пробела.Если параметр delete не пустой,
    он должен содержать строку, и тогда все символы, входящие
    в состав строки delete, будут удалены из строки результата.
    >>> simplify(" this and\n that\t too")
    'this and that too'
    >>> simplify(" Washington D.C.\n")
    'Washington D.C.'
    >>> simplify(" Washington D.C.\n", delete=",;:.")
    'Washington DC'
    >>> simplify(" disemvoweled ", delete="aeiou")
    'dsmvwld'
    """
    result = []
    word = ""
    for char in text:
        if char in delete:
            continue
        elif char in whitespace:
            if word:
                result.append(word)
                word = ""
            else:
            word += char
    if word:
        result.append(word)
    return " ".join(result)
    

def shorten(text, length=25, indicator="..."):
    """Возвращает text или усеченную его копию с добавлением indicator в конце
    text – любая строка; 
    length – максимальная длина возвращаемой строки string (включая indicator); 
    indicator – строка, добавляемая в конец результата, чтобы показать, что текст аргумента text был усечен
    >>> shorten("The Road")
    'The Road'
    >>> shorten("No Country for Old Men", 20)
    'No Country for Ol...'
    >>> shorten("Cities of the Plain", 15, "*")
    'Cities of the *'
    """
    if len(text) > length:
        text = text[:length - len(indicator)] + indicator
    return text

def is_balanced(text, brackets="()[]{}<>"):
    """
    Проверяет баланс открывающих и закрывающих скобок в text.
    """
    for left, right in zip(brackets[::2], brackets[1::2]):
        assert left != right, "the bracket characters must differ"
    .... дальше напишете сами, через стек на основе списка, а не через тупой подсчет количества открывающих и закрывающих :)

if __name__ == "__main__":
    import doctest      # import, который нужен только для тестирования, пишем прямо тут
    doctest.testmod()   # выполним примеры (как - расскажем дальше)

Разберем код по строкам.

#!/usr/bin/env python3

Позволяет запускать файл (если есть права доступа) как исполняемый, без указания python3 в командной строке.

./TextUtil.py
Пользователь может не думать, на чем написана ваша программа и чем ее запускать.

Далее в """...""" - документация по модулю. Она доступна в других модулях через TextUtil.__doc__

В модуле может не быть кода вне функций. Если мы запустим такой файл как программу, то ничего не будет выполняться (только определения функций).

Модуль doctest - выполнение тестовых примеров из описаний функций

Разберем подробнее код

if __name__ == "__main__":
    import doctest
    doctest.testmod()

(Вставка из Саммерфильда:)

Функция doctest.testmod() с помощью механизма интроспекции Python выявляет все функции в модуле и их строки документирования, после чего пытается выполнить все фрагменты программного кода, которые приводятся в строках документирования. При запуске модуля таким способом вывод на экране появится только при наличии ошибок. Сначала это может привести в замешательство, так как создается впечатление, будто вообще ничего не происходит; но если интерпретатору передать ключ командной строки –v, на экране может появиться примерно следующее:

Trying:
    is_balanced("(Python (is (not (lisp))))")
Expecting:
    True
ok
...
Trying:
    simplify(" disemvoweled ", delete="aeiou")
Expecting:
    'dsmvwld'
ok
4 items passed all tests:
    3 tests in __main__
    5 tests in __main__.is_balanced
    3 tests in __main__.shorten
    4 tests in __main__.simplify
15 tests in 4 items.
15 passed and 0 failed.
Test passed.
Мы использовали многоточия, чтобы показать, что было опущено множество строк. Если в модуле имеются функции (или классы, или методы), не имеющие тестов, при запуске интерпретатора с ключом –v они будут перечислены. Обратите внимание, что модуль doctest обнаружил тесты как в строке документирования модуля, так и в строках до кументирования функций. Примеры в строках документирования, которые могут выполняться как тесты, называют доктестами (doctests). Обратите внимание, что при написании доктестов мы вызываем функцию simplify(), не используя полное квалифицированное имя (поскольку доктесты находятся непосредственно в самом модуле). За пределами модуля, после выполнения инструкции import TextUtil?, мы должны использовать квалифицированные имена, например, TextUtil?.is_balanced().

Как сделать пакет доступным другим программам?

Несколько вариантов:

Поместите файл TextUtils?.py в директорию

Переменная __name__. Запуск модуля как модуля и как программы.

Если мы импортируем модуль то переменная __name__ этого модуля равна имени модуля (без .py).

Если мы запускаем файл как программу, то переменная __name__ принимает значение "__main__"

Для чего нужно это знание? Мы можем, запуская файл только как отдельную программу, протестировать его функции. Полезно для отладки. Смотри последние строки в

Сохраните этот код в файле t1.py

if __name__ == '__main__':
    print('This program is being run by itself, __name__ = ', __name__)
else:
    print('I am being imported from another module, my name =', __name__)

Запустим файл, как отдельную программу:

$python3 t1.py
This program is being run by itself, __name__ =  __main__

Импортируем этот модуль.

$ python3
Python 3.6.1 (default, Mar 24 2017, 12:50:34) .....
>>> import t1
I am being imported from another module, my __name__ = t1

Функция dir()

(Вставка из A bit of python, глава 11 Модули)

Вы можете использовать встроенную функцию dir, чтобы получить список идентификаторов, которые объект определяет. Так в число идентификаторов модуля входят функции, классы и переменные, определённые в этом модуле.

Когда вы передаёте функции dir() имя модуля, она возвращает список имён, определённых в этом модуле. Если никакого аргумента не передавать, она вернёт список имён, определённых в текущем модуле.

$ python3
>>> import sys # получим список атрибутов модуля 'sys'

>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__s
tderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_compact_freelists',
'_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', '
byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle'
, 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable',
'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getfil
esystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof',
'gettrace', 'getwindowsversion', 'hexversion', 'intern', 'maxsize', 'maxunicode
', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platfor
m', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_in
fo', 'warnoptions', 'winver']

>>> dir() # получим список атрибутов текущего модуля
['__builtins__', '__doc__', '__name__', '__package__', 'sys']

>>> a = 5 # создадим новую переменную 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']

>>> del a # удалим имя 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>>

Как это работает?

Сперва мы видим результат применения dir к импортированному модулю sys. Видим огромный список атрибутов, содержащихся в нём.

Затем мы вызываем функцию dir, не передавая ей параметров. По умолчанию, она возвращает список атрибутов текущего модуля. Обратите внимание, что список импортированных модулей также входит туда.

Чтобы пронаблюдать за действием dir, мы определяем новую переменную a и присваиваем ей значение, а затем снова вызываем dir. Видим, что в полученном списке появилось дополнительное значение. Удалим переменную/атрибут из текущего модуля при помощи оператора del, и изменения вновь отобразятся на выводе функции dir.

Замечание по поводу del: этот оператор используется для удаления переменной/имени, и после его выполнения, в данном случае – del a, к переменной a больше невозможно обратиться – её как будто никогда и не было.

Обратите внимание, что функция dir() работает для любого объекта. Например, выполните dir('print'), чтобы увидеть атрибуты функции print, или dir(str), чтобы увидеть атрибуты класса str.

Относительные пути в import

PEP-328

Есть пакет pkg, состоящий из файлов:

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py
pkg/sub1
pkg/sub1/__init__.py   # каждый уровень вложенности - пишем свой __init__.py
pkg/sub1/hello.py

Как в файле main.py импортировать файл string.py?

Можно написать import string

Но тогда вы можете перепутать имя модуля со стандартным модулем string.

Чтобы явно указать, что хотите именно string.py в той же директории, используйте относительную адресацию. Начинается с точки.

# Import names from pkg.string
from .string import name1, name2

# Import pkg.string
from . import string

Знак .. обозначает "директорией выше".

В модуле A.B.C напишем код:

from . import D                 # Imports A.B.D
from .. import E                # Imports A.E
from ..F import G               # Imports A.F.G

Запуск модуля из командной строки (ключ -m), файл __main__.py

C-модуль

Intermediate Python, C-расширения

Задачи

Задача 0

Напишите свою реализацию функции is_balanced из файла TestUtils?.py, добавляя очередную открывающую скобку в стек (на основе списка) и убирающую верхнюю открывающую скобку из стека, при получении закрывающей скобки.

Задача 1

Файл t1.py должен запускаться как программа python t1.py и как модуль python -m t1

Задача 2

Пакет pkg должен запускаться из командной строки python -m pkg

Задача 3

В модуле pkg/sub1/hello.py использовать функцию из модуля pkg/string.py

Задача 4

Для создания задач в системе ejudge написать:

1) пакет scripts/testgen.py, который по единому файлу тестов в формате

input1
---
output1
===
input2
---
output2
===

Создает директорию tests в директории с задачей и там создает нужные файлы 001.dat, 001.ans, 002.dat, 002.ans

Входные и выходные данные могут быть на нескольких строках.

Дополнительно: выходные данные можно задать как ? и тогда они должны быть созданы по решению. Решение можно класть в виде файла имязадачи_solution.py

2) Взять пример задачи на принадлежность точки области и сделать свою задачу на принадлежность точки области. В задаче должно быть:

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

funk_kr
   +- scripts
      +- testgen.py             - генератор тестов
   +- func_A                    - директория с задачей
      +- func_A.xml             - условие задачи на функции
      +- if_A.xml               - условие задачи на if
      +- func_A_begin.c         - часть файла, которая прикрепляется системой к решению задачи на функцию
      +- func_A_solution.c      - решение задачи на функцию
      +- if_A_solution.c        - решение задачи на if через float (проходит все тесты)
      +- if_A_double_solution.c - решение задачи на if через double (проходит все тесты)
      +- if_A_int_fail.c        - решение задачи на if через int (часть тестов не проходит)
      +- if_A_floatint_fail.c   - решение задачи на if через float и int (часть тестов не проходит)
      +- test.txt               - единый файл тестов для генерации
      +- make_pict.py           - программа на питоне для рисования картинки
      +- tests                  - директория с тестами
         +- 001.dat             - input 1-го теста
         +- 001.ans             - эталонный output 1-го теста
         +- 002.dat
         +- 002.dat
      +- img                    - директория с картинками (создается)
         +- func_A.png          - картинка (создается)

Скопировать файлы примера к себе в текущую директорию

scp -r -P 51940 student@remote.vdi.miptNOSPAM.ru:/home/student/python/ejudge .