питон

Модули

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

In [1]:
import math
math,type(math)
Out[1]:
(<module 'math' from '/usr/lib64/python3.5/lib-dynload/math.cpython-35-x86_64-linux-gnu.so'>,
 module)

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

In [2]:
dir(math)
Out[2]:
['__doc__',
 '__file__',
 '__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 [5]:
math.pi,math.exp
Out[5]:
(3.141592653589793, <function math.exp>)

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

In [6]:
import builtins
dir(builtins)
Out[6]:
['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__',
 '__IPYTHON__active',
 '__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 [7]:
import random as r
r
Out[7]:
<module 'random' from '/usr/lib64/python3.5/random.py'>
In [8]:
dir(r)
Out[8]:
['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 [9]:
[r.random() for i in range(10)]
Out[9]:
[0.37028232039482156,
 0.8294243000248475,
 0.5617213688567851,
 0.8546826053750616,
 0.9695789516365685,
 0.08668547433034068,
 0.3183811918507736,
 0.6457872490180798,
 0.1211475575591282,
 0.21068979402289623]

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

In [11]:
from sys import path

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

In [12]:
path
Out[12]:
['',
 '/usr/lib64/python35.zip',
 '/usr/lib64/python3.5',
 '/usr/lib64/python3.5/plat-linux',
 '/usr/lib64/python3.5/lib-dynload',
 '/usr/lib64/python3.5/site-packages',
 '/usr/lib64/python3.5/site-packages/IPython/extensions',
 '/home/grozin/.ipython']
In [13]:
path.append('/home/grozin/python')
path
Out[13]:
['',
 '/usr/lib64/python35.zip',
 '/usr/lib64/python3.5',
 '/usr/lib64/python3.5/plat-linux',
 '/usr/lib64/python3.5/lib-dynload',
 '/usr/lib64/python3.5/site-packages',
 '/usr/lib64/python3.5/site-packages/IPython/extensions',
 '/home/grozin/.ipython',
 '/home/grozin/python']

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

In [14]:
from math import factorial as f
In [15]:
f(100)
Out[15]:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

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

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

In [16]:
!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 [17]:
from fac import *
fac(10)
Out[17]:
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 [21]:
import fac
fac.__doc__
Out[21]:
'В этом модуле определена функция fac'
In [22]:
help(fac)
Help on module fac:

NAME
    fac - В этом модуле определена функция fac

FUNCTIONS
    fac(n)
        calculate factorial of n

FILE
    /home/grozin/python/fac.py


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

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

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

In [19]:
def f(x):
    y=0
    print(dir())
In [20]:
f(0)
['x', 'y']

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

In [23]:
__name__
Out[23]:
'__main__'
In [24]:
r.__name__
Out[24]:
'random'

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

In [25]:
!ls d1
d2  m1.py
In [26]:
!ls d1/d2
m2.py

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

In [28]:
import d1.m1
d1.m1.f1()
Out[28]:
1
In [29]:
import d1.d2.m2
d1.d2.m2.f2()
Out[29]:
2

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

In [30]:
!ls p1
__init__.py  m1.py  p2
In [31]:
!ls p1/p2
m2.py

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

In [32]:
!cat p1/__init__.py
from p1.m1 import f1
from p1.p2.m2 import f2

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

In [33]:
import p1

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

In [34]:
p1.f1(),p1.f2()
Out[34]:
(1, 2)

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

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

In [37]:
f=open('text.txt')
f,type(f)
Out[37]:
(<_io.TextIOWrapper name='text.txt' mode='r' encoding='UTF-8'>,
 _io.TextIOWrapper)

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

In [38]:
for s in f:
    print(s)
abcd

efgh

ijkl

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

In [39]:
f.close()

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

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

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

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

In [42]:
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 [41]:
with open('text.txt') as f:
    s=f.read()
s
Out[41]:
'abcd\nefgh\nijkl\n'

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

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

efgh

ijkl

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

In [44]:
with open('text.txt') as f:
    l=f.readlines()
l
Out[44]:
['abcd\n', 'efgh\n', 'ijkl\n']

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

In [45]:
def a(name):
    global f
    f=open(name)
    s=f.readline()
    n=1/0
    f.close()
    return s
In [46]:
a('text.txt')
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-46-d62372657d26> in <module>()
----> 1 a('text.txt')

<ipython-input-45-7f445757684d> 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 [47]:
f.closed
Out[47]:
False
In [48]:
f.close()

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

In [49]:
def a(name):
    global f
    with open(name) as f:
        s=f.readline()
        n=1/0
    return s
In [50]:
a('text.txt')
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-50-d62372657d26> in <module>()
----> 1 a('text.txt')

<ipython-input-49-cabd1416e96c> 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 [51]:
f.closed
Out[51]:
True

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

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

In [52]:
f=open('newtext.txt','w')
In [53]:
f.write('aaa\n')
Out[53]:
4
In [54]:
f.write('bbb\n')
Out[54]:
4
In [55]:
f.write('ccc\n')
Out[55]:
4
In [56]:
f.close()

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

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

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

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

In [59]:
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 [60]:
copy('text.txt','newtext.txt')
In [61]:
!cat newtext.txt
abcd
efgh
ijkl

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

In [62]:
s=input('Введите целое число ')
Введите целое число 123
In [63]:
s
Out[63]:
'123'
In [64]:
n=int(s)
n
Out[64]:
123

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

In [65]:
s=input('Введите выражение ')
Введите выражение n+1
In [66]:
s
Out[66]:
'n+1'
In [67]:
eval(s)
Out[67]:
124

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

In [68]:
s=input('Введите оператор ')
Введите оператор x=0
In [69]:
s
Out[69]:
'x=0'
In [70]:
exec(s)
x
Out[70]:
0

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

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

In [71]:
from pathlib import Path

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

In [72]:
p=Path()
p
Out[72]:
PosixPath('.')

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

In [73]:
p.resolve()
Out[73]:
PosixPath('/home/grozin/python')

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

In [74]:
p=Path('.././/python')
p=p.resolve()
p
Out[74]:
PosixPath('/home/grozin/python')

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

In [75]:
Path.cwd()
Out[75]:
PosixPath('/home/grozin/python')

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

In [76]:
for f in p.iterdir():
    print(f)
/home/grozin/python/python1.ipynb
/home/grozin/python/ind.gle
/home/grozin/python/.ipynb_checkpoints
/home/grozin/python/python0.png
/home/grozin/python/python4.ipynb
/home/grozin/python/fac.py
/home/grozin/python/d1
/home/grozin/python/newtext.txt
/home/grozin/python/ind.png
/home/grozin/python/.gle
/home/grozin/python/python2.ipynb
/home/grozin/python/python3.ipynb
/home/grozin/python/text.txt
/home/grozin/python/__pycache__
/home/grozin/python/p1
/home/grozin/python/python.png

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

In [78]:
p2=p/'python2.ipynb'
p2
Out[78]:
PosixPath('/home/grozin/python/python2.ipynb')

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

In [79]:
p2.exists()
Out[79]:
True

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

In [81]:
p2.is_symlink(),p2.is_dir(),p2.is_file()
Out[81]:
(False, False, True)

Части пути p2.

In [82]:
p2.parts
Out[82]:
('/', 'home', 'grozin', 'python', 'python2.ipynb')

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

In [83]:
p2.parent,p2.parent.parent
Out[83]:
(PosixPath('/home/grozin/python'), PosixPath('/home/grozin'))

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

In [84]:
p2.name,p2.stem,p2.suffix
Out[84]:
('python2.ipynb', 'python2', '.ipynb')

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

In [85]:
s=p2.stat()
s
Out[85]:
os.stat_result(st_mode=33188, st_ino=2129722, st_dev=2052, st_nlink=1, st_uid=1000, st_gid=1000, st_size=63539, st_atime=1445700765, st_mtime=1446363303, st_ctime=1446363303)

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

In [86]:
s.st_size
Out[86]:
63539

Я написал полезную утилиту для поиска одинаковых файлов. Ей передаётся произвольное число аргументов - директорий и файлов. Она рекурсивно обходит директории, находит размер всех файлов (симлинки игнорируются) и строит словарь, сопоставляющий каждому размеру список файлов, имеющих такой размер. Это простой этап, не требующий чтения (возможно больших) файлов. После этого файлы из тех списков, длина которых $>1$, сравниваются внешней программой diff (что, конечно, требует их чтения).

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

In [87]:
from os import environ
In [88]:
environ['PATH']
Out[88]:
'/home/grozin/bin:/home/grozin/reduce-3235/bin:/usr/local/bin:/usr/bin:/bin:/opt/bin:/usr/games/bin'
In [89]:
environ['ABCD']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-89-71e016be80d8> in <module>()
----> 1 environ['ABCD']

/usr/lib64/python3.5/os.py in __getitem__(self, key)
    679         except KeyError:
    680             # raise KeyError with the original key value
--> 681             raise KeyError(key) from None
    682         return self.decodevalue(value)
    683 

KeyError: 'ABCD'
In [90]:
environ['ABCD']='abcd'
In [91]:
environ['ABCD']
Out[91]:
'abcd'

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