Кафедра дискретной математики МФТИ

Курс математической статистики

Никита Волков

На основе http://www.inp.nsk.su/~grozin/python/

Модули

Модуль - это просто файл типа .py, содержащий последовательность операторов питона. Его можно использовать двумя способами: либо запустить как программу, либо импортировать в другой модуль, чтобы сделать доступными определённые там функции и переменные. При импортировании все операторы модуля выполняются от начала до конца, включая определения функций и классов и присваивания переменным. Впрочем, при повторном импортировании модуль не выполняется. Если Вы его изменили и хотите импортировать изменённую версию, нужно приложить специальные усилия.


In [1]:
import math
math, type(math)


Out[1]:
(<module 'math' (built-in)>, module)

Модуль имеет своё пространство имён. Оператор import math вводит объект типа модуль math в текущее пространство имён. Имена, определённые в модуле, при этом в текущем пространстве имён не появляются - их нужно использовать как math.что_то. Функция dir возвращает список имён в модуле (как и в классе или объекте).


In [2]:
dir(math)


Out[2]:
['__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'acos',
 'acosh',
 'asin',
 'asinh',
 'atan',
 'atan2',
 'atanh',
 'ceil',
 'copysign',
 'cos',
 'cosh',
 'degrees',
 'e',
 'erf',
 'erfc',
 'exp',
 'expm1',
 'fabs',
 'factorial',
 'floor',
 'fmod',
 'frexp',
 'fsum',
 'gamma',
 'gcd',
 'hypot',
 'inf',
 'isclose',
 'isfinite',
 'isinf',
 'isnan',
 'ldexp',
 'lgamma',
 'log',
 'log10',
 'log1p',
 'log2',
 'modf',
 'nan',
 'pi',
 'pow',
 'radians',
 'sin',
 'sinh',
 'sqrt',
 'tan',
 'tanh',
 'trunc']

In [3]:
math.__doc__


Out[3]:
'This module is always available.  It provides access to the\nmathematical functions defined by the C standard.'

In [4]:
math.pi, math.exp


Out[4]:
(3.141592653589793, <function math.exp>)

Встроенные функции, классы и т.д. языка питон живут в модуле builtins.


In [5]:
import builtins
dir(builtins)


Out[5]:
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'BlockingIOError',
 'BrokenPipeError',
 'BufferError',
 'BytesWarning',
 'ChildProcessError',
 'ConnectionAbortedError',
 'ConnectionError',
 'ConnectionRefusedError',
 'ConnectionResetError',
 'DeprecationWarning',
 'EOFError',
 'Ellipsis',
 'EnvironmentError',
 'Exception',
 'False',
 'FileExistsError',
 'FileNotFoundError',
 'FloatingPointError',
 'FutureWarning',
 'GeneratorExit',
 'IOError',
 'ImportError',
 'ImportWarning',
 'IndentationError',
 'IndexError',
 'InterruptedError',
 'IsADirectoryError',
 'KeyError',
 'KeyboardInterrupt',
 'LookupError',
 'MemoryError',
 'NameError',
 'None',
 'NotADirectoryError',
 'NotImplemented',
 'NotImplementedError',
 'OSError',
 'OverflowError',
 'PendingDeprecationWarning',
 'PermissionError',
 'ProcessLookupError',
 'RecursionError',
 'ReferenceError',
 'ResourceWarning',
 'RuntimeError',
 'RuntimeWarning',
 'StopAsyncIteration',
 'StopIteration',
 'SyntaxError',
 'SyntaxWarning',
 'SystemError',
 'SystemExit',
 'TabError',
 'TimeoutError',
 'True',
 'TypeError',
 'UnboundLocalError',
 'UnicodeDecodeError',
 'UnicodeEncodeError',
 'UnicodeError',
 'UnicodeTranslateError',
 'UnicodeWarning',
 'UserWarning',
 'ValueError',
 'Warning',
 'ZeroDivisionError',
 '__IPYTHON__',
 '__build_class__',
 '__debug__',
 '__doc__',
 '__import__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'abs',
 'all',
 'any',
 'ascii',
 'bin',
 'bool',
 'bytearray',
 'bytes',
 'callable',
 'chr',
 'classmethod',
 'compile',
 'complex',
 'copyright',
 'credits',
 'delattr',
 'dict',
 'dir',
 'divmod',
 'dreload',
 'enumerate',
 'eval',
 'exec',
 'filter',
 'float',
 'format',
 'frozenset',
 'get_ipython',
 'getattr',
 'globals',
 'hasattr',
 'hash',
 'help',
 'hex',
 'id',
 'input',
 'int',
 'isinstance',
 'issubclass',
 'iter',
 'len',
 'license',
 'list',
 'locals',
 'map',
 'max',
 'memoryview',
 'min',
 'next',
 'object',
 'oct',
 'open',
 'ord',
 'pow',
 'print',
 'property',
 'range',
 'repr',
 'reversed',
 'round',
 'set',
 'setattr',
 'slice',
 'sorted',
 'staticmethod',
 'str',
 'sum',
 'super',
 'tuple',
 'type',
 'vars',
 'zip']

Если Вам лень полностью писать имя модуля перед каждым использованием функции из него, можно использовать as и задать ему краткое имя.


In [6]:
import random as r
r


Out[6]:
<module 'random' from '/usr/lib/python3.5/random.py'>

In [7]:
dir(r)


Out[7]:
['BPF',
 'LOG4',
 'NV_MAGICCONST',
 'RECIP_BPF',
 'Random',
 'SG_MAGICCONST',
 'SystemRandom',
 'TWOPI',
 '_BuiltinMethodType',
 '_MethodType',
 '_Sequence',
 '_Set',
 '__all__',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_acos',
 '_ceil',
 '_cos',
 '_e',
 '_exp',
 '_inst',
 '_log',
 '_pi',
 '_random',
 '_sha512',
 '_sin',
 '_sqrt',
 '_test',
 '_test_generator',
 '_urandom',
 '_warn',
 'betavariate',
 'choice',
 'expovariate',
 'gammavariate',
 'gauss',
 'getrandbits',
 'getstate',
 'lognormvariate',
 'normalvariate',
 'paretovariate',
 'randint',
 'random',
 'randrange',
 'sample',
 'seed',
 'setstate',
 'shuffle',
 'triangular',
 'uniform',
 'vonmisesvariate',
 'weibullvariate']

In [8]:
[r.random() for i in range(10)]


Out[8]:
[0.1066415135640334,
 0.8477670607506278,
 0.6186343345177346,
 0.047312581396509845,
 0.24372538481472616,
 0.2572901767300363,
 0.883241606151692,
 0.580079763056712,
 0.6657674454820419,
 0.518076074541861]

Такая форма оператора import вводит перечисленные имена (функции, переменные, классы) из модуля в текущее пространство имён.


In [9]:
from sys import path

Переменная path - это список имён директорий, в которых оператор import ищет модули. В начале в него входит '' - директория, в которой находится текущая программа (или текущая директория в случае интерактивной сессии); директории, перечисленные в переменной окружения PYTHONPATH (если такая переменная есть); и стандартные директории для данной версии питона. Но это обычный список, его можно менять стандартными языковыми средствами. Например, ревнители безопасности считают, что опасно включать текущую директорию в path - если пользователю в его директорию кто-нибудь подсунет зловредную версию math.py, а программа пользователя выполнит import math, то этот модуль выполнится, и может, скажем, удалить все файлы этого пользователя. Такие ревнители могут сделать path=path[1:].


In [10]:
path


Out[10]:
['',
 '/usr/lib/python3.5',
 '/usr/lib/python3.5/plat-x86_64-linux-gnu',
 '/usr/lib/python3.5/lib-dynload',
 '/usr/local/lib/python3.5/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/usr/local/lib/python3.5/dist-packages/IPython/extensions',
 '/home/nikita/.ipython']

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


In [11]:
from math import factorial as f

In [12]:
f(100)


Out[12]:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Для самых ленивых есть оператор from ... import *, который импортирует в текущее пространство имён все имена, определённые в модуле. Обычно это плохая идея - Вы засоряете текущее пространство имён, и даже не знаете, чем. Такую форму импорта разумно использовать, когда Вы импортируете свой модуль, про который Вы всё знаете. Ну или в интерактивной сессии, когда Вы хотите попробовать всякие функции из какого-нибудь модуля. Но не в программе, которая пишется всерьёз и надолго.

Например, в текущей директории есть файл fac.py. Мы работаем в ipython, который предоставляет всякие удобства для интерактивной работы. Например, можно выполнить shell команду, если в начале строки поставить ! (только не пробуйте этого делать в обычном интерпретаторе питон). Так что легко распечатать этот файл. В нём определена одна функция fac.


In [13]:
!cat fac.py


#!/usr/bin/env python3
'В этом модуле определена функция fac'

def fac(n):
    'calculate factorial of n'
    assert type(n) is int and n >= 0
    r = 1
    for i in range(2, n + 1):
        r *= i
    return r

if __name__ == '__main__':
    from sys import argv, exit
    if len(argv) != 2:
        print('usage: ./fac.py n')
        exit(1)
    print(fac(int(argv[1])))

In [14]:
from fac import *
fac(10)


Out[14]:
3628800

Файл fac.py показывает типичное устройство любого файла на питоне. Первая строка позволяет запустить такой файл, если у него установлен бит, позволяющий исполнять его текущему пользователю. Почему не просто #!/usr/bin/python3 ? Потому что на некоторых машинах питон может быть в /usr/local/bin или ещё где-то; стандартная unix-утилита env (предположительно) всегда живёт в /usr/bin. Она позволяет установить какие-нибудь переменные окружения, а затем, если есть аргумент - имя программы, запускает эту программу в этом модифицированном окружении; если такого аргумента нет, просто печатает это окружение. Так что, вызвав просто env, Вы получите список всех текущих переменных окружения с их значениями. В данном случае вызывается /usr/bin/env python3, то есть никакие изменения окружения не произвадятся, и env вызывает python3, расположенный где угодно в $PATH. Почему python3? python может быть симлинком либо на python2, либо на python3; в свою очередь, python3 может быть симлинком, скажем, на python3.5. Если наша программа предназначена для питона 3, то в первой строке лучше указывать python3, иначе на некоторых машинах могут возникнуть неприятные сюрпризы.

Дальше следует док-строка модуля. Потом определения всех функций, классов и т.д. Заключительная часть файла выполняется, если он запущен как программа, а не импортируется куда-то. В этой части обычно пишут какие-нибудь простые тесты определённых в файле функций. В данном случае используется sys.argv - список строк-аргументов командной строки. argv[0] - это имя программы, нас интересует переданный ей параметр, argv[1].


In [15]:
import fac
fac.__doc__


Out[15]:
'В этом модуле определена функция fac'

Функция dir без аргумента возвращает список имён в текущем пространстве имён. Многие имена в этом списке определены ipython-ом; в сессии с обычным интерпретатором питон их бы не было.


In [16]:
dir()


Out[16]:
['In',
 'Out',
 '_',
 '_1',
 '_10',
 '_12',
 '_14',
 '_15',
 '_2',
 '_3',
 '_4',
 '_5',
 '_6',
 '_7',
 '_8',
 '__',
 '___',
 '__builtin__',
 '__builtins__',
 '__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_dh',
 '_exit_code',
 '_i',
 '_i1',
 '_i10',
 '_i11',
 '_i12',
 '_i13',
 '_i14',
 '_i15',
 '_i16',
 '_i2',
 '_i3',
 '_i4',
 '_i5',
 '_i6',
 '_i7',
 '_i8',
 '_i9',
 '_ih',
 '_ii',
 '_iii',
 '_oh',
 '_sh',
 'builtins',
 'exit',
 'f',
 'fac',
 'get_ipython',
 'math',
 'path',
 'quit',
 'r']

В локальном пространстве имён этой функции два имени.


In [17]:
def f(x):
    y = 0
    print(dir())

In [18]:
f(0)


['x', 'y']

В каждом модуле есть строковая переменная __name__, она содержит имя модуля. Главная программа (или интерактивная сессия) тоже является модулем, его имя __main__. Этим и объясняется вид оператора if, который стоит в конце файла fac.py.


In [19]:
__name__


Out[19]:
'__main__'

In [20]:
r.__name__


Out[20]:
'random'

Модули не обязательно должны размещаться непосредственно в какой-нибудь директории из sys.path; они могут находиться в поддиректории. Например, в текущей директории (включённой в path) есть поддиректория d1, в ней поддиректория d2.


In [23]:
!ls d1


d2  m1.py

In [24]:
!ls d1/d2


m2.py

Мы можем импортировать модули m1 и m2 так.


In [25]:
import d1.m1
d1.m1.f1()


Out[25]:
1

In [26]:
import d1.d2.m2
d1.d2.m2.f2()


Out[26]:
2

Такое поддерево директорий с модулями можно превратить в пакет, который с точки зрения пользователя выглядит как единый модуль. Для этого нужно добавить файл __init__.py. Вот другое поддерево с теми же файлами m1.py и m2.py.


In [27]:
!ls p1


__init__.py  m1.py  p2

In [28]:
!ls p1/p2


m2.py

Только добавлен файл __init__.py.


In [29]:
!cat p1/__init__.py


from p1.m1 import f1
from p1.p2.m2 import f2

Теперь мы можем импортировать этот пакет.


In [30]:
import p1

Питон находит в sys.path директорию p1, содержащую __init__.py, и интерпретирует её как пакет. При импорте выполняется этот файл __init__.py, инициализирующий пакет. Все функции, переменные и т.д., определённые в этом файле (непосредственно или через импорт), становятся символами этого пакета. __init__.py может включать не все функции из модулей этого дерева директорий (и даже не все модули); символы, не определённые в __init__.py, недоступны после импорта пакета (конечно, пользователь всегда может импортировать любой модуль напрямую и получить доступ ко всем его символам).


In [31]:
p1.f1(), p1.f2()


Out[31]:
(1, 2)

Ввод-вывод, файлы, директории

Откроем текстовый файл на чтение (когда второй аргумент не указан, файл открывается именно на чтение).


In [32]:
f = open('text.txt')
f, type(f)


Out[32]:
(<_io.TextIOWrapper name='text.txt' mode='r' encoding='UTF-8'>,
 _io.TextIOWrapper)

Получился объект f одного из файловых типов. Что с ним можно делать? Можно его использовать в for цикле, каждый раз будет возвращаться очередная строка файла (включая '\n' в конце; в конце последней строки текстового файла '\n' может и не быть).


In [33]:
for s in f:
    print(s)


abcd

efgh

ijkl

Теперь файл нужно закрыть.


In [34]:
f.close()

Такой стиль работы с файлом (f=open(...); работа с f; f.close()) на самом деле не рекомендуется. Гораздо правильнее использовать оператор with. Он гарантирует, что файл будет закрыт как в том случае, когда исполнение тела with нормально дошло до конца, так и тогда, когда при этом произошло исключение, и мы покинули тело with аварийно.

В операторе with может использоваться любой объект класса, реализующего методы __enter__ и __exit__. Обычно это объект-файл, возвращаемый функцией open.


In [35]:
with open('text.txt') as f:
    for s in f:
        print(s[:-1])


abcd
efgh
ijkl

Метод f.read(n) читает n символов (когда файл близится к концу и прочитать именно n символов уже невозможно, читает меньше; в самый последний раз он читает 0 символов и возвращает ''). Прочитаем файл по 1 символу.


In [36]:
with open('text.txt') as f:
    while True:
        c = f.read(1)
        if c == '':
            break
        else:
            print(c)


a
b
c
d


e
f
g
h


i
j
k
l


Вызов f.read() без аргумента читает файл целиком (что не очень разумно, если в нём много гигабайт).


In [37]:
with open('text.txt') as f:
    s = f.read()
s


Out[37]:
'abcd\nefgh\nijkl\n'

f.readline() читает очередную строку (хотя проще использовать for s in f:).


In [38]:
with open('text.txt') as f:
    while True:
        s = f.readline()
        if s == '':
            break
        else:
            print(s)


abcd

efgh

ijkl

Метод f.readlines() возвращает список строк (опять же его лучше не применять для очень больших файлов).


In [39]:
with open('text.txt') as f:
    l = f.readlines()
l


Out[39]:
['abcd\n', 'efgh\n', 'ijkl\n']

Теперь посмотрим, чем же оператор with лучше, чем пара open - close.


In [40]:
def a(name):
    global f
    f = open(name)
    s = f.readline()
    n = 1 / 0
    f.close()
    return s

In [41]:
a('text.txt')


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-41-d62372657d26> in <module>()
----> 1 a('text.txt')

<ipython-input-40-43a6f8f26cbd> in a(name)
      3     f = open(name)
      4     s = f.readline()
----> 5     n = 1 / 0
      6     f.close()
      7     return s

ZeroDivisionError: division by zero

In [42]:
f.closed


Out[42]:
False

In [43]:
f.close()

Произошло исключение, мы покинули функцию до строчки close, и файл не закрылся.


In [44]:
def a(name):
    global f
    with open(name) as f:
        s = f.readline()
        n = 1 / 0
    return s

In [45]:
a('text.txt')


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-45-d62372657d26> in <module>()
----> 1 a('text.txt')

<ipython-input-44-1f9da64e36cb> in a(name)
      3     with open(name) as f:
      4         s = f.readline()
----> 5         n = 1 / 0
      6     return s

ZeroDivisionError: division by zero

In [46]:
f.closed


Out[46]:
True

Теперь всё в порядке.

Чтобы открыть файл на запись, нужно включить второй аргумент 'w'.


In [47]:
f = open('newtext.txt', 'w')

In [48]:
f.write('aaa\n')


Out[48]:
4

In [49]:
f.write('bbb\n')


Out[49]:
4

In [50]:
f.write('ccc\n')


Out[50]:
4

In [51]:
f.close()

Метод write возвращает число записанных символов.

Опять же, лучше использовать with.


In [52]:
with open('newtext.txt', 'w') as f:
    f.write('aaa\n')
    f.write('bbb\n')
    f.write('ccc\n')

In [53]:
!cat newtext.txt


aaa
bbb
ccc

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


In [54]:
def copy(old_name, new_name):
    with open(old_name) as old, open(new_name,'w') as new:
        for line in old:
            new.write(line)

In [55]:
copy('text.txt', 'newtext.txt')

In [56]:
!cat newtext.txt


abcd
efgh
ijkl

В интерактивной сессии (или в программе, запущенной с командной строки) можно попросить пользователя что-нибудь ввести. Аргумент функции input - это приглашение для ввода (prompt). Можно использовать просто input(), тогда приглашения не будет. Но это неудобно, т.к. в этом случае трудно заметить, что программа чего-то ждёт.


In [57]:
s = input('Введите целое число ')


Введите целое число 123

In [58]:
s


Out[58]:
'123'

In [59]:
n = int(s)
n


Out[59]:
123

Питон - интерпретатор, поэтому он может во время выполнения программы интерпретировать строки как куски исходного текста на языке питон. Так, функция eval интерпретирует строку как выражение и вычисляет его (в текущем контексте - подставляя текущие значения переменных).


In [61]:
s = input('Введите выражение ')


Введите выражение n+1

In [62]:
s


Out[62]:
'n+1'

In [63]:
eval(s)


Out[63]:
124

А функция exec интерпретирует строку как оператор и выполняет его. Оператор может менять значения переменных в текущем пространстве имён.


In [65]:
s = input('Введите оператор ')


Введите оператор x=0

In [66]:
s


Out[66]:
'x=0'

In [67]:
exec(s)
x


Out[67]:
0

Строка s может быть результатом длинного и сложного вычисления. Но лучше таких фокусов не делать, так как программа фактически становится самомодифицирующейся. Такие программы очень сложно отлаживать.

Для работы с путями к файлам и директориям в стандартной библиотеке существует модуль pathlib. Объект класса Path представляет собой путь к файлу или директории.


In [68]:
from pathlib import Path

Path() возвращает текущую директорию.


In [69]:
p = Path()
p


Out[69]:
PosixPath('.')

Очень полезный метод resolve приводит путь к каноническому виду.


In [ ]:
p.resolve()

Путь может быть записан в совершенно идиотском виде; resolve его исправит.


In [ ]:
p=Path('.././/python')
p=p.resolve()
p

Статический метод cwd возвращает текущую директорию (current working directory).


In [ ]:
Path.cwd()

Если p - путь к директории, то можно посмотреть все файлы в ней.


In [ ]:
for f in p.iterdir():
    print(f)

Если p - путь к директории, то p/'fname' - путь к файлу fname в ней (он, конечно, тоже может быть директорией).


In [ ]:
p2 = p/'python_2.ipynb'
p2

Существует ли такой файл?


In [78]:
p2.exists()


Out[78]:
True

Является ли он симлинком, директорией, файлом?


In [79]:
p2.is_symlink(), p2.is_dir(), p2.is_file()


Out[79]:
(False, False, True)

Части пути p2.


In [ ]:
p2.parts

Родитель - директория, в которой находится этот файл.


In [ ]:
p2.parent, p2.parent.parent

Имя файла, его основа и суффикс.


In [82]:
p2.name, p2.stem, p2.suffix


Out[82]:
('python_2.ipynb', 'python_2', '.ipynb')

Метод stat возвращает всякую ценную информацию о файле.


In [83]:
s = p2.stat()
s


Out[83]:
os.stat_result(st_mode=33204, st_ino=1254838, st_dev=2053, st_nlink=1, st_uid=1000, st_gid=1000, st_size=69779, st_atime=1485528481, st_mtime=1485528481, st_ctime=1485528481)

Например, его размер в байтах.


In [84]:
s.st_size


Out[84]:
69779

В питоне можно работать с переменными окружения как с обычным словарём.


In [85]:
from os import environ

In [86]:
environ['PATH']


Out[86]:
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games'

In [87]:
environ['ABCD']


---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-87-71e016be80d8> in <module>()
----> 1 environ['ABCD']

/usr/lib/python3.5/os.py in __getitem__(self, key)
    723         except KeyError:
    724             # raise KeyError with the original key value
--> 725             raise KeyError(key) from None
    726         return self.decodevalue(value)
    727 

KeyError: 'ABCD'

In [88]:
environ['ABCD'] = 'abcd'

In [89]:
environ['ABCD']


Out[89]:
'abcd'

Мы не просто добавили пару ключ-значение в словарь, а действительно добавили новую переменную к текущему окружению. Если теперь вызвать из питона какую-нибудь внешнюю программу, то она эту переменную увидит. Эта переменная исчезнет, когда закончится выполнение текущей программы на питоне (или интерактивная сессия).