Модуль - это просто файл типа .py
, содержащий последовательность операторов питона. Его можно использовать двумя способами: либо запустить как программу, либо импортировать в другой модуль, чтобы сделать доступными определёённые там функции и переменные. При импортировании все операторы модуля выполняются от начала до конца, включая определения функций и классов и присваивания переменным. Впрочем, при повторном импортировании модуль не выполняется. Если Вы его изменили и хотите импортировать изменённую версию, нужно приложить специальные усилия.
import math
math,type(math)
Модуль имеет своё пространство имён. Оператор import math
вводит объект типа модуль math
в текущее пространство имён. Имена, определённые в модуле, при этом в текущем пространстве имён не появляются - их нужно использовать как math.что_то
. Функция dir
возвращает список имён в модуле (как и в классе или объекте).
dir(math)
math.__doc__
math.pi,math.exp
Встроенные функции, классы и т.д. языка питон живут в модуле builtins
.
import builtins
dir(builtins)
Если Вам лень полностью писать имя модуля перед каждым использованием функции из него, можно использовать as
и задать ему краткое имя.
import random as r
r
dir(r)
[r.random() for i in range(10)]
Такая форма оператора import
вводит перечисленные имена (функции, переменные, классы) из модуля в текущее пространство имён. Мне она нравится - использовать импортированные таким образом объекты удобно, не надо писать перед каждым имя модуля.
from sys import path
Переменная path
- это список имён директорий, в которых оператор import
ищет модули. В начале в него входит ''
- директория, в которой находится текущая программа (или текущая директория в случае интерактивной сессии); директории, перечисленные в переменной окружения PYTHONPATH
(если такая переменная есть); и стандартные директории для данной версии питона. Но это обычный список, его можно менять стандартными языковыми средствами. Например, ревнители безопасности считают, что опасно включать текущую директорию в path
- если пользователю в его директорию кто-нибудь подсунет зловредную версию math.py
, а программа пользователя выполнит import math
, то этот модуль выполнится, и может, скажем, удалить все файлы этого пользователя. Такие ревнители могут сделать path=path[1:]
.
path
path.append('/home/grozin/python')
path
Если Вам лень писать каждый раз длинное имя функции из модуля, можно дать ему короткий псевдоним.
from math import factorial as f
f(100)
Для самых ленивых есть оператор from ... import *
, который импортирует в текущее пространство имён все имена, определённые в модуле. Обычно это плохая идея - Вы засоряете текущее пространство имён, и даже не знаете, чем. Такую форму импорта разумно использовать, когда Вы импортируете свой модуль, про который Вы всё знаете. Ну или в интерактивной сессии, когда Вы хотите попробовать всякие функции из какого-нибудь модуля. Но не в программе, которая пишется всерьёз и надолго.
Например, в текущей директории есть файл fac.py
. Мы работаем в ipython
, который предоставляет всякие удобства для интерактивной работы. Например, можно выполнить shell
команду, если в начале строки поставить !
(только не пробуйте этого делать в обычном интерпретаторе питон). Так что легко распечатать этот файл. В нём определена одна функция fac
.
!cat fac.py
from fac import *
fac(10)
Файл 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]
.
import fac
fac.__doc__
help(fac)
Функция dir
без аргумента возвращает список имён в текущем пространстве имён. Многие имена в этом списке определены ipython
-ом; в сессии с обычным интерпретатором питон их бы не было.
dir()
В локальном пространстве имён этой функции два имени.
def f(x):
y=0
print(dir())
f(0)
В каждом модуле есть строковая переменная __name__
, она содержит имя модуля. Главная программа (или интерактивная сессия) тоже является модулем, его имя __main__
. Этим и объясняется вид оператора if
, который стоит в конце файла fac.py
.
__name__
r.__name__
Модули не обязательно должны размещаться непосредственно в какой-нибудь директории из sys.path
; они могут находиться в поддиректории. Например, в текущей директории (включённой в path
) есть поддиректория d1
, в ней поддиректория d2
.
!ls d1
!ls d1/d2
Мы можем импортировать модули m1
и m2
так.
import d1.m1
d1.m1.f1()
import d1.d2.m2
d1.d2.m2.f2()
Такое поддерево директорий с модулями можно превратить в пакет, который с точки зрения пользователя выглядит как единый модуль. Для этого нужно добавить файл __init__.py
. Вот другое поддерево с теми же файлами m1.py
и m2.py
.
!ls p1
!ls p1/p2
Только добавлен файл __init__.py
.
!cat p1/__init__.py
Теперь мы можем импортировать этот пакет.
import p1
Питон находит в sys.path
директорию p1
, содержащую __init__.py
, и интерпретирует её как пакет. При импорте выполняется этот файл __init__.py
, инициализирующий пакет. Все функции, переменные и т.д., определённые в этом файле (непосредственно или через импорт), становятся символами этого пакета. __init__.py
может включать не все функции из модулей этого дерева директорий (и даже не все модули); символы, не определённые в __init__.py
, недоступны после импорта пакета (конечно, пользователь всегда может импортировать любой модуль напрямую и получить доступ ко всем его символам).
p1.f1(),p1.f2()
Откроем текстовый файл на чтение (когда второй аргумент не указан, файл открывается именно на чтение).
f=open('text.txt')
f,type(f)
Получился объект f
одного из файловых типов. Что с ним можно делать? Можно его использовать в for
цикле, каждый раз будет возвращаться очередная строка файла (включая '\n'
в конце; в конце последней строки текстового файла '\n'
может и не быть).
for s in f:
print(s)
Теперь файл нужно закрыть.
f.close()
Такой стиль работы с файлом (f=open(...)
; работа с f
; f.close()
) на самом деле не рекомендуется. Гораздо правильнее использовать оператор with
. Он гарантирует, что файл будет закрыт как в том случае, когда исполнение тела with
нормально дошло до конца, так и тогда, когда при этом произошло исключение, и мы покинули тело with
аварийно.
В операторе with может использоваться любой объект класса, реализующего методы __enter__
и __exit__
. Обычно это объект-файл, возвращаемый функцией open
.
with open('text.txt') as f:
for s in f:
print(s[:-1])
Метод f.read(n)
читает n
символов (когда файл близится к концу и прочитать именно n
символов уже невозможно, читает меньше; в самый последний раз он читает 0 символов и возвращает ''
). Прочитаем файл по 1 символу.
with open('text.txt') as f:
while True:
c=f.read(1)
if c=='':
break
else:
print(c)
Вызов f.read()
без аргумента читает файл целиком (что не очень разумно, если в нём много гигабайт).
with open('text.txt') as f:
s=f.read()
s
f.readline()
читает очередную строку (хотя проще использовать for s in f:
).
with open('text.txt') as f:
while True:
s=f.readline()
if s=='':
break
else:
print(s)
Метод f.readlines()
возвращает список строк (опять же его лучше не применять для очень больших файлов).
with open('text.txt') as f:
l=f.readlines()
l
Теперь посмотрим, чем же оператор with
лучше, чем пара open
- close
.
def a(name):
global f
f=open(name)
s=f.readline()
n=1/0
f.close()
return s
a('text.txt')
f.closed
f.close()
Произошло исключение, мы покинули функцию до строчки close
, и файл не закрылся.
def a(name):
global f
with open(name) as f:
s=f.readline()
n=1/0
return s
a('text.txt')
f.closed
Теперь всё в порядке.
Чтобы открыть файл на запись, нужно включить второй аргумент 'w'
.
f=open('newtext.txt','w')
f.write('aaa\n')
f.write('bbb\n')
f.write('ccc\n')
f.close()
Метод write
возвращает число записанных символов.
Опять же, лучше использовать with
.
with open('newtext.txt','w') as f:
f.write('aaa\n')
f.write('bbb\n')
f.write('ccc\n')
!cat newtext.txt
Эта функция копирует старый текстовый файл в новый. Если строки нужно как-нибудь обработать, в последней строчке вместо line
будет стоять что-нибудь вроде f(line)
.
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)
copy('text.txt','newtext.txt')
!cat newtext.txt
В интерактивной сессии (или в программе, запущенной с командной строки) можно попросить пользователя что-нибудь ввести. Аргумент функции input
- это приглашение для ввода (prompt). Можно использовать просто input()
, тогда приглашения не будет. Но это неудобно, т.к. в этом случае трудно заметить, что программа чего-то ждёт.
s=input('Введите целое число ')
s
n=int(s)
n
Питон - интерпретатор, поэтому он может во время выполнения программы интерпретировать строки как куски исходного текста на языке питон. Так, функция eval
интерпретирует строку как выражение и вычисляет его (в текущем контексте - подставляя текущие значения переменных).
s=input('Введите выражение ')
s
eval(s)
А функция exec
интерпретирует строку как оператор и выполняет его. Оператор может менять значения переменных в текущем пространстве имён.
s=input('Введите оператор ')
s
exec(s)
x
Строка s
может быть результатом длинного и сложного вычисления. Но лучше таких фокусов не делать, так как программа фактически становится самомодифицирующейся. Такие программы очень сложно отлаживать.
Для работы с путями к файлам и директориям в стандартной библиотеке существует модуль pathlib
. Объект класса Path
представляет собой путь к файлу или директории.
from pathlib import Path
Path()
возвращает текущую директорию.
p=Path()
p
Очень полезный метод resolve
приводит путь к каноническому виду.
p.resolve()
Путь может быть записан в совершенно идиотском виде; resolve
его исправит.
p=Path('.././/python')
p=p.resolve()
p
Статический метод cwd
возвращает текущую директорию (current working directory).
Path.cwd()
Если p
- путь к директории, то можно посмотреть все файлы в ней.
for f in p.iterdir():
print(f)
Если p
- путь к директории, то p/'fname'
- путь к файлу fname
в ней (он, конечно, тоже может быть директорией).
p2=p/'python2.ipynb'
p2
Существует ли такой файл?
p2.exists()
Является ли он симлинком, директорией, файлом?
p2.is_symlink(),p2.is_dir(),p2.is_file()
Части пути p2
.
p2.parts
Родитель - директория, в которой находится этот файл.
p2.parent,p2.parent.parent
Имя файла, его основа и суффикс.
p2.name,p2.stem,p2.suffix
Метод stat
возвращает всякую ценную информацию о файле.
s=p2.stat()
s
Например, его размер в байтах.
s.st_size
Я написал полезную утилиту для поиска одинаковых файлов. Ей передаётся произвольное число аргументов - директорий и файлов. Она рекурсивно обходит директории, находит размер всех файлов (симлинки игнорируются) и строит словарь, сопоставляющий каждому размеру список файлов, имеющих такой размер. Это простой этап, не требующий чтения (возможно больших) файлов. После этого файлы из тех списков, длина которых $>1$, сравниваются внешней программой diff
(что, конечно, требует их чтения).
В питоне можно работать с переменными окружения как с обычным словарём.
from os import environ
environ['PATH']
environ['ABCD']
environ['ABCD']='abcd'
environ['ABCD']
Мы не просто добавили пару ключ-значение в словарь, а действительно добавили новую переменную к текущему окружению. Если теперь вызвать из питона какую-нибудь внешнюю программу, то она эту переменную увидит. Эта переменная исчезнет, когда закончится выполнение текущей программы на питоне (или интерактивная сессия).