Язык программирования Python

Курс IT-Академии Сухорукова

Апрель-июнь 2020

Этот курс закончился

Итоги первой недели

Основы языка Python

Python — современный мультипарадигменный язык программирования высокого уровня. Разработан в 1991 году, автор — голландец Гвидо Ван Россум (Guido van Rossum). Python ориентирован на повышение производительности труда программиста и хорошую читаемость кода.

Арифметика

Документация: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex

Синтаксис записи выражений обычный (скобки, операции, все дела).

Типы: целые — int, вещественные — float, комплексный — complex.

Примеры числовых констант:

  • целочисленные (int): 1, 5, 199999, 0b100101 (двоичные числа), 0o755 (восьмиричные числа), 0x1eff000000 или 0x1EFF000000 (шестнадцатиричные числа)
  • вещественные (float): 1.0, 0.02, 3.1415926, 1.14e2 (то есть 1,14⋅10²)
  • комплексные (complex): 3 + 2j или 3 + 2J (символ j или J — обозначение мнимой части числа)

Операторы

  • x + 2 — сложение
  • x - 2 — вычитание
  • x * 2 — умножение
  • x // 2 — деление (целочисленное, два слэша подряд)
  • x / 2 — деление (настоящее)
  • x ** 2 — возведение в степень (две звёздочки подряд)
  • x % 2 — остаток от деления
  • x | 2 — побитовое ИЛИ
  • x & 2 — побитовое И
  • x ^ 2 — исключающее ИЛИ (xor)
  • ~x — побитовое НЕ
  • x << 2, x >> 2 — побитовый сдвиг влево и вправо
  • Модифицирующие операторы:
    • x += 2
    • x -= 2
    • x *= 2
    • x /= 2
    • x %= 2 и т. д. — как в С-подобных языках, например, x += 2 — это то же самое, что x = x + 2.

Логические операторы

https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
https://docs.python.org/3/library/stdtypes.html#comparisons

Тип для логических выражений — bool.

Константы: True, False (обязательно с большой буквы).

  1. x > 2
    x < 2
    x == 2
    x != 2
    x >= 2
    x <= 2 — как обычно
  2. 2 < x <= 4 — двойное сравнение (эквивалентно 2 < x and x <= 4). В каком ещё языке вы такое видели?
  3. Логические связки:
    a or b — или
    a and b — и
    not a — не

Строки

https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str
https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range

Тип — str.

Способы создания строки:

  • "Hello" — в двойных кавычках
  • 'Hello' — в одинарных кавычках
  • Длинные многострочные строки берутся в тройной разделитель:
s = """This string is
sooo long
and useless
"""
  • Можно вместо трёх двойных кавычек применять три одинарных (три апострофа).

  • Две строковых константы, между которыми нет ничего, кроме пробелов, соединяются в одну: 'Foo' 'bar''Foobar'

Операции над строками:

  • Строки можно соединять оператором +: 'Foo' + 'bar''Foobar'
  • Повторение строк через оператор *: 'Foo' * 3'FooFooFoo'
  • str(x) — строковое представление объекта x (не для любого объекта)
  • int(s), float(s) — преобразование строки в число. Если в s не число, выбрасывет исключение ValueError.
  • repr(x) — внутреннее представление объекта x в виде строки (для отладки)

Форматные строки

https://docs.python.org/3/library/string.html#formatstrings

У строк есть метод format, позволяющий подставлять в строку значения.

'I have {} apples'.format(3 + 2)'I have 5 apples'
'I have {num} apples'.format(num = 3 + 2)'I have 5 apples'

Срезы строк

s = 'Python'

  • len(s) — длина строки
  • s[0] == 'P'
  • s[1] == 'y'
  • s[-1] == 'n' (последний символ)
  • s[-2] == 'o' (предпоследний)
  • s[0:2] == 'Py' — символы с 0 по 2 (исключая 2)
  • s[2:] == 'thon' — символы со 2 и до конца
  • s[:3] == 'Pyt' — символы с начала и до 3 (исключая 3)
  • s[0:5:2] == 'Pto' — символы с 0 по 5 с шагом 2 (то есть каждый второй)

Проверка на наличие символов в строке (или элемента в последовательности)

  • x in sTrue, если подстрока x есть в строке s
  • x not in s — аналогично not (x in s)
  • s.count(x) — количество вхождений подстроки x в строку s

Списки

https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
https://docs.python.org/3/library/stdtypes.html#lists

Список — модифицируемая последовательность (в отличии от строк).

Название типа — list.

Создание списка

  • [] — пустой список
  • [1, 2, 5, 'Low', 'High'] — список из 5 элементов
  • [0] * 5 — список из пяти нулей ([0, 0, 0, 0, 0])
  • list('zorg') — преобразование любой последовательности в список (в данном случае результат ['z', 'o', 'r', 'g'])

Со списками можно делать то же, что и со строками (и любыми последовательностями), но их можно модифицировать, поэтому доступно следующее:

Модификация среза

m = [0, 1, 2, 3, 4, 5]

  • m[0] = 1[1, 1, 2, 3, 4, 5]
  • m[-1] = 10[1, 1, 2, 3, 4, 10]
  • m[1:3] = ['a', 'b', 'c', 'd'][1, 'a', 'b', 'c', 'd', 3, 4, 10] — обратите внимание, что список, вставленный вместо среза, был длиннее. Срез удаляется и заменяется новыми значениями. Да, даже срез с шагом можно использовать, однако в этом случае число элементов в срезе и в замене должно совпадать.
  • del m[0]['a', 'b', 'c', 'd', 3, 4, 10] — удаление элемента.
  • del m[1::2]['a', 'c', 3, 10] — удалили каждый второй элемент, начиная со второго (индекс у него 1) и до конца списка (вторая граница пропущена). Удалить можно любой срез.
  • и так далее, любые комбинации

Методы списка

  • m.append(x) — добавить значение x в конец списка как новый элемент.
  • m.extend(q) — добавить список (или иную последовательность) q к концу списка m. То же самое, что m += q.
  • m.clear() — очистка всего списка, то же, что del m[:].
  • m.copy() — создание нового списка и копирование значений из m в него, новый список — результат этой функции: z = m.copy().
  • m.insert(i, x) — вставка элемента x в список m на позицию i (элементы списка с i-того сдвигаются вправо).
  • m.remove(x) — удаление первого слева элемента списка m, равного x. Чтобы удалить все такие элементы, надо вызвать remove несколько раз (столько, сколько таких элементов).
  • m.pop() — удаляет последнего элемента списка и возвращает его значение.
  • m.reverse() — переворачивает список ([1, 2, 3].reverse()[3, 2, 1).
  • m.sort() — сортирует список по возрастанию.
  • И так далее, смотрите документацию.

Кортежи

https://docs.python.org/3/library/stdtypes.html#tuples

Кортеж — немодифицируемая последовательность. Обычно применяется для гетерогенных данных. Используется вместо пар, троек, векторов. Так же полезны, если из функции нужно вернуть не одно, а одновременно несколько значений (например, функция поиска возвращает и номер найденного объекта, и сам объект)

Название типа — tuple.

Создание кортежа

  • () — пустой кортеж.
  • (1,) — кортеж из одного элемента.
  • (1, 2, 3) — кортеж их трёх элементов.
  • tuple('Hello') — преобразование типа (результат — кортеж ('H', 'e', 'l', 'l', 'o')).
  • Вообще, круглые скобки обязательны для пустого кортежа. Любые значения, перечисленные через запятую, образуют кортеж, если только они не в квадратных или фигурных скобках и не являются параметрами функции. То есть
    k = 1, 2, 3
    — это уже кортеж из трёх чисел. Но:
    k = some_function(1, 2, 3)
    — это вызов функции с передачей ей трёх аргументов, здесь нет кортежа. Чтобы передать в функцию кортеж, его надо взять в круглые скобки:
    k = some_function((1, 2, 3))

Над кортежами можно производить те же операции, что над строками.

Несколько полезных функций

  • zip(s, p, q...) — упаковка нескольких последовательностей. Возвращает итерируемый объект, который возвращает кортежи вида (s[i], p[i], q[i],...). Аргументов может быть 1 или больше. Этот объект можно использовать в цикле for, или создать из него последовательность. Пример:
    x = list(zip([1, 2, 3], ['a', 'b', 'c']))
    x == [(1, 'a'), (2, 'b'), (3 , 'c')]
  • range(x, y, s) — возвращает итерируемый объект, который возвращает числа от x до y с шагом s. Если шаг не указан, он считается равным 1. Если не указано значение y, то числа берутся от 0 до x. Этот объект можно использовать в цикле for или преобразовать в последовательность. Пример:
    s = tuple(range(0, 5, 2))
    s == (0, 2, 4)

Специальные методы строк

https://docs.python.org/3/library/stdtypes.html#string-methods

Строки имеют ряд методов, доступных только для строк. Ниже перечислены некоторые, за полным списком — в документацию.

  • s.capitalize() — Возвращает копию строки s, в которой первое слово начинаются с большой буквы, а остальные становятся маленькими
  • s.center(w, c) — добавляет строке s символов c (или пробелов, если c не указан) слева и справа, чтобы строка-результат была длиной не меньше числа w, при этом содержание s находилось по центру. Пример:
    'Winter'.center(10, '=')'==Winter=='
    'Winter'.center(10)' Winter '
  • s.endswith(r) — проверка, кончается ли строка s подстрокой r. Пример:
    'Python is awesome'.endswith('some')True
  • s.startswith(r) — аналогично, проверка, начинается ли строка s с подстроки r
  • s.find(r, start, end) — поиск подстроки r в строке s, начиная с символа номер start и заканчивая символом номер end. start и end могут отсутствовать, тогда поиск идёт по всей строке. Результат функции — индекс первого символа найденной подстроки, или -1, если подстрока не найдена.
  • Разные тесты: s.isalphanum() — состоит ли строка только из букв и цифр, s.isdigit() — состоит ли строка только из цифр, s.isidentifier() — является ли строка корректным (для Python) именем, и так далее, их довольно много. Все эти методы возвращают True или False.
  • s.join(m) — здесь m — последовательность. В результате получается строка, состоящая из строковых представлений элементов m, разделённых содержимым s. Например:
    '; '.join([1, 2, 3])'1; 2; 3'
  • s.split(sep) — разбивает строку на подстроки по разделителю sep и возвращает их как список. Пример:
    'bob=alice=charlie'.split('=')['bob', 'alice', 'charlie']
  • s.zfill(w) — добивает строку s нулями в начале, пока её длина не станет w. При этом если строка начинается с + или -, сохраняет знак первым символом. Пример:
    '-13'.zfill(5)'-0013'
    '13'.zfill(5)'00013'
  • И так далее. Строки умеют странное.

Словари

https://docs.python.org/3/library/stdtypes.html#mapping-types-dict

Они же map'ы, они же ассоциативные массивы. Содержат пары вида ключ: значение. Ключём может быть любой объект, для которого определён хэш — числа, строки и т. д. Чаще всего используют строки. Значениями могут быть вообще любые объекты.

Словарь — модифицируемый объект.

Тип называется dict.

Создание словаря

  • {} — пустой словарь.
  • {'one': 1, 2: 'two'} — словарь из двух элементов.
  • dict([('one', 1), (2, 'two')]) — тот же словарь, сконструированный из списка кортежей (см. функцию zip выше).

Операции и методы словаря

  • len(d) — количество элементов в словаре d.
  • d[key] — значение, соответствующее ключу key. Если нет такого ключа, выбрасывает исключение KeyError.
  • d[key] = x — присваивает элементу с ключём key значение x, или добавляет элемент, если его ещё не было.
  • del d[key] — удаляет элемент с ключём key.
  • key in d — возвращает True, если в словаре d есть элемент с ключём key.
  • key not in d — то же, что not (key in d).
  • d.clear() — очистка словаря.
  • d.copy() — возвращает копию словаря (новый объект).
  • d.get(key, default) — то же, что d[key], но если элемент не найден, возвращает default (или None, если default не указано) вместо выбрасывания исключения.
  • d.update(k) — добавляет пары ключ-значение из словаря k в словарь d. Если какие-то ключи из k уже есть в d, происходит замена значений.
  • d.keys(), d.values() — возвращают ключи и значения в виде последовательностей, соответственно.
  • И это не все. См. документацию.

Генераторы списков (list comprehension)

https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

Они же списочные выражения. Это возможность генерировать списки по определённой закономерности.

Синтаксис: [выражение for имя in итер if условие]

Причём блоки for...in и if могут неоднократно повторяться.

То же самое справедливо для словарей. Пример:

{x: x ** 2 for x in range(100) if x % 2 == 0}

(это словарь, в котором числа соответствуют своим квадратам, причём только чётные числа).

Простой ввод-вывод

  • print(a, b, c...) — выводит строковое предстваление всех своих аргументов на консоль. Подробное описание: https://docs.python.org/3/library/functions.html#print.
  • input(p) — выводит на консоль приглашение p (строку), и ждёт пользовательского ввода (до конца строки, то есть нажатия Enter). Возвращает введённую строку (без символа '\n' в конце).

Файловый ввод-вывод

Работа с файлами происходит через файловые объекты.

Создание файлового объекта

open(name, mode) — открывает файл с именем name (строка) в режиме mode и возвращает соответствующий ему файловый объект. Если mode не указан, предполагается 'r' — чтение файла. Список режимов тут:
https://docs.python.org/3/library/functions.html#open.

Основные режимы: 'r' — чтение, 'w' — запись, 'a' — добавление в конец. Для двоичных файлов, соответственно, 'rb', 'wb', 'ab'.

Пример: f = open('input.txt', 'r').

Закрытие файла

Закрытие происходит при уничтожении файлового объекта, либо прямым вызовом метода f.close() у файлового объекта f.

Текстовые файлы

https://docs.python.org/3/library/io.html#module-io

Текстовый файл — это итерируемый объект, возвращающий свои строки. По нему можно ходить циклом for, или преобразовать в последовательность.

Методы:

  • f.readline() — читает очередную строку файла и возвращает её (с символом конца строки в конце). Если достигнут конец файла, возвращает пустую строку. Совсем пустую, длины 0.
  • f.readlines() — читает весь файл в список строк. То же самое, что list(f). Не используйте этот метод без необходимости, лучше цикл for.
  • f.read(n) — читает как минимум n байт из файла, или весь файл до конца, если n не указана. Возвращает прочитанное в виде строки.
  • f.tell() — возвращает текущую позицию в файле.
  • f.seek(n) — ставит текущую позицию в файле на позицию n (0 — начало файла, либо используйте результат f.tell()).
  • f.writelines(lines) — пишет строки из списка lines в файл. Не добавляет '\n' в конце строк, так что они уже должны содержать '\n'.
  • f.write(x) — пишет в файл строку x.

Синтаксис Python-программы

Программа записывается в файл (обычно с расширением «.py») построчно. Разделителем инструкций является конец строки файла. Если строка заканчивается на \ (бэкслэш), то она соединяется со следующей, так можно разбивать длинные инструкции на несколько строк файла.

Блоки кода выделяются отступом от начала строки. Отступ может ставиться как пробелами, так и символами табуляции, но предпочтительнее пробелы. Стандартным считается отступ в 4 пробела на каждый уровень вложенности блоков. Если в пределах одного блока отступ изменяется, интерпретатор останавливается с сообщением о синтаксической ошибке (IndentationError).

Управляющие конструкции

https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools

Условие

if условие-1:
    блок-1
elif условие-2:
    блок-2
else:
    блок-3

Блоков elif может быть несколько, а может и ни одного. Блока else тоже может не быть.

Условный оператор как выражение

выражение1 if условие else выражение2

Это аналог триарного оператора ? : из С, Java и подобных языков. Работает так: если условие выполняется (True), то результат выражения — выражение1, а если нет — выражение2.

Пример:

code = inp_code if inp_code is not None else def_code

Это аналогично следующему коду:

if inp_code is not None:
    code = inp_code
else:
    code = def_code

Эквивалент в С: code = (inp_code != NULL) ? inp_code : def_code;

Циклы

while условие:
    блок-1
else:
    блок-2

for переменная in итерируемый-объект:
    блок-1
else:
    блок-2

Циклы могут быть прерваны оператором break. Кроме того, есть оператор continue, который прерывает текущую итерацию и начинает следующую.

Блок кода после else выполняется, если цикл завершился нормально, а не с помощью оператора break. Этого блока может не быть.

Итерируемым объектом для цикла for может быть любая последовательность (строка, список, кортеж, словарь), а так же результат таких функция, как zip(), range(). Файлы и некоторые другие объекты тоже являются итерируемыми.

Функции

https://docs.python.org/3/tutorial/controlflow.html#defining-functions

Определение функции:

def имя-функции(параметры):
    блок-кода

Параметры — имена формальных параметров через запятую.

Для возврата значения из функции служит оператор return:

return значение

Если функция завершилась не вызовом return, она возвращает None.

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

def имя-функции(п1: тип1, п2: тип2...) -> тип-результата:
    блок-кода

Вообще, благодаря утиной типизации проверка типов параметров и не нужна. Просто работайте с пришедшими в функцию объектами, ведь, скажем, итерируемый объект может быть и списком, и результатом функции range, и файлом, и чем угодно ещё. Пусть ваша функция будет универсальной. Если что-то пойдёт не так — просто будет выброшено исключение.

Функции являются объектами первого рода, то есть их можно присваивать перменным, хранить в списках, кортежах и словарях, передавать как параметр в другие функции и так далее.

Сравните:

def f(x):
    return x + 1

z = f    # z содержит саму функцию f
t = f(1) # t содержит результат вызова f при x=1

Параметры функций

Напомним синтаксис объявления функции:

def имя(параметры):
    тело

Параметры — это имена, перечисленные через запятую. Параметрам можно указывать их значения по умолчанию:

def f(x, y, z=0):

В таком случае можно будет вызывать функцию f как от трёх арументов (f(1, 1, 3)), так и от двух (f(1, 1)), во втором случае аргумент z будет равен 0. Очевидно, что после параметров по умолчанию не могут идти параметры без значений по умолчанию.

Функции от перменного числа аргументов

Некоторые функции могут принимать любое число параметров. Например, стандартная функция print(). Можно в своём коде объявить функцию от переменного числа аргуметов:

def f(*args):
    for arg in args:
        print(arg)

Эта функция выводит все свои аргументы на экран, по одному в строке. Параметр со звёздочкой получит в качестве значения все аргументы функции с этой позиции, в виде кортежа.

Функции с keyword-аргументами

Как известно, значения параметров функции при вызове можно задавать в любом порядке, используя их имена (так называемые keyword-аргументы, или kw-аргументы):

sorted(array, reverse=False)

Чтобы создать функцию, принимающую любые kw-аргументы, применяется следующий синтаксис:

def f(**kwargs):
    for k in kwargs:
        print(k, '=', kwargs[k])

Аргумент с двумя звёздочками становится словарём, содержащим все именованные аргументы с этого места. Можно сочетать все способы объявления функции:

def my_function(x, y, z=0, *args, **kwargs):
    ...

Распаковка параметров

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

x = (1, 2, 1, False)
some_function(*x)
some_function(x[0], x[1], x[2], x[3])

Последние две строки примера эквивалентны.

Лямбда-выражения

https://docs.python.org/3/tutorial/controlflow.html?highlight=lambda#lambda-expressions

Лямбда-выражения можно использовать как локально определённые безымянные функции, состоящие из одного выражения. Им можно назначать имена, либо передавать как параметр в другую функцию.

Синтаксис:

func = lambda арг, арг, арг...: выражение

Аргументов может быть сколько угодно.

Пример. Лямбда-функция, возвращающая сумму аргументов:

summator = lambda x, y: x + y
print("Сумма 2 и 3:", summator(2, 3)) # Использование

Исключения

https://docs.python.org/3/tutorial/errors.html

Работают так же, как в большинстве языков. Практически все исключения унаследованы от класса Exception.

Блок обработки исключений:

try:
    код-бросающий-исключение
except Исключение1 as переменная:
    обработка-исключения-1
except Исключение2:
    обработка-исключения-2
else:
    код-если-не-было-исключений
finally:
    код-который-всегда-выполнится

Если мы указали тип as переменная, мы можем обратиться к полю args этой переменной:

  • e.args — кортеж дополнительных аргументов исключения, зависит от его типа.

Пример:

try:
    f = open("some_file.txt")
except OSError as e:
    print("Ошибка:", ", ".join(e.args))

Некоторые классы исключений имеют дополнительные поля и/или методы. Подробнее смотрите в документации.

Типы стандартных исключений

https://docs.python.org/3/library/exceptions.html#bltin-exceptions

  • LookupError — ошибка поиска элемента. Подклассы:
    • IndexError
    • KeyError
  • ArithmeticError — арифметическая ошибка. Подклассы:
    • OverflowError
    • ZeroDivisionError
    • FloatingPointError
    • и так далее
  • AttributeError — обращение к несуществующему аттрибуту
  • TypeError — тип аргумента не подходит. Бросается в ситуациях, когда дейстиве не имеет смысла. Например, нельзя сложить строку и число, или разделить строку на строку
  • ValueError — ошибочное значение параметра. Например, нельзя создать диапазон с шагом 0 (с помощью range())
  • ImportError — ошибка импорта: например, модуль не существует
  • OSError — ошибка ОС: например, недостаточно прав на доступ.
  • RuntimeError — прочие ошибки времени выполнения

NameError, SyntaxError, IndentationError — ошибки в коде программы (неверный идентификатор, ошибка синтаксиса, неправильный отступ в блоке). Строго говоря, ловить их бессмысленно, но формально это тоже исключения, только бросаемые интерпретатором

Выбрасывание исключения

https://docs.python.org/3/tutorial/errors.html#raising-exceptions

Исключение может иметь любой тип, но желательно, чтобы он был унаследован от стандартного класса Exception. В идеале, не следует придумывать свои типы для исключений и пользоваться стандартными, дабы не плодить сущности. Кончено, кроме ситуации, когда ни один стандартный тип не подходит (или неудобен).

Синтаксис выбрасывания исключений:

raise Новый-объект(аргумент, аргумент...)

Аргументы в указанном порядке попадают в поле args исключения (если речь идёт о наследнике класса Exception).

Если вы внутри блока except хотите повторно выбросить пойманное исключение, можно использовать инструкцию raise без параметра:

raise

Пример выбрасывания исключения:

if index >= array_size:
    raise IndexError('Index out of range', index)

Модули

Модуль — это Python-файл, содержащий некие классы, функции и переменные. Модули объединены в пакеты, а те, в свою очередь, в иерархию пакетов. Можно рассматривать их как библиотеки.

Подключение модуля

Простейший случай:

import имя-модуля, имя-модуля...

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

import random
z = random.randint(1, 100)

Можно импортировать имена из модуля в локальное пространство имён:

from модуль import имя, имя, имя...

После этого указанные имена становятся доступны в локальном пространстве имён без префиков. Например:

from random import randint
z = randint(1, 100)

Можно импортировать имена из модуля под другими именами:

from random import randint as random_integer_number
z = random_integer_number(1, 100)
z = randint(1, 100) # Тут будет NameError 

На вызове randint в этом примере возникнет NameError, так как имени randint не существует, ведь мы переименовали функцию randint при импорте.

Ещё можно импортировать весь модуль в локальное пространство имён:

from модуль import *

Никогда так не делайте. Просто поверьте мне: явное лучше неявного.

Пакеты

Пакет — это каталог с файлами модулей. Пакет обязательно содержит файл с именем __init__.py (двойные символы подчёркивания в имени, этот файл может быть пустым). Пакеты могут быть вложенными (это ж каталоги), но каждый пакет обязательно содержит __init__.py.

Вложенные пакеты образуют пространства имён, модули из них импортируются по полному пути, через точку в качестве разделителя:

from django.contrib.auth.models import User

То есть в пакете (то бишь каталоге) django есть пакет (каталог) contrib, в нём — каталог (пакет) auth, в нём модуль (файл) models, а в модуле (в файле) — класс User, который мы импортируем.

Модули интерпретатор ищет в каталоге, в котором находится текущий файл скрипта, в каталогах, указанных в переменной окружения PYTHONPATH и в переменной path модуля sys (sys.path). Последняя является списком строк с именами каталогов. Да, sys.path можно модифицировать, не забудьте про import sys.

Переменная __name__

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

Однако для файла, с которого началось выполнение (который был запущен с помощью интерпретатора напрямую, а не импортирован), __name__ содержит специальное значение — строку '__main__'.

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

if __name__ == '__main__':
    do_run() # Нас запустили как программу
else:
    do_something_else() # Нас импортировали