diff --git a/questions.md b/questions.md index 4a4fe2e..71c1045 100644 --- a/questions.md +++ b/questions.md @@ -622,7 +622,7 @@ class NextClass(FirstClass): class D: attr = 3 # D:3 E:2 class B(D) pass # | | class E: attr = 2 # B C:1 -class C(E): attr = 1 # / / +class C(E): attr = 1 # \ / class A(B, C): pass # A X = A() # | print(X.attr) # X @@ -635,10 +635,10 @@ print(X.attr) # X **Пример наследования в ромбовидных иерархаических деревьях:** ```python -class D: attr = 3 # D:3 D:3 -class B(D) pass # | | +class D: attr = 3 # D:3 +class B(D) pass # / \ class C(D): attr = 1 # B C:1 -class A(B, C): pass # / / +class A(B, C): pass # \ / X = A() # A print(X.attr) # | # X @@ -1327,9 +1327,11 @@ greet_hello('Ivan') ```python greet_curried('Hi')('Roma') +``` А дальше можно сделать это с любым количеством аргументов: +```python def greet_deeply_curried(greeting): def w_separator(separator): def w_emphasis(emphasis): @@ -1350,13 +1352,13 @@ greet('Ivan') Функция `filter` оставляет лишь те элементы последовательности, для которых заданная функция истинна. В Python 2 возвращает список, в Python 3 – объект-итератор. -Функция `reduce` (в Python 2 встроенная, в Python 3 находится в модуле functools) принимает функцию от двух аргументов, последовательность и опциональное начальное значение и вычисляет свёртку (fold) последовательности как результат последовательного применения данной функции к текущему значению (так называемому аккумулятору) и следующему элементу последовательности. +Функция `reduce` (в Python 2 встроенная, в Python 3 находится в модуле functools) принимает функцию от двух аргументов, последовательность, и опциональное начальное значение, и вычисляет свёртку (fold) последовательности как результат последовательного применения данной функции к текущему значению (так называемому аккумулятору) и следующему элементу последовательности. ### Какие еще вы знаете функции из модуля functools Модуль functools содержит большое количество стандартных функций высшего порядка. Среди них особенно полезны: -- lru_cache – декоратор, который кеширует значения функций, которые не меняют свой результат при неизменных аргументах; полезен для кеширования данных, мемоизации (сохранения результатов для возврата без вычисления функции) значений рекурсивных функций (например, такого типа, как функция вычисления n-го числа Фибоначчи) и т.д.; +- lru_cache – декоратор, который кеширует значения функций, которые не меняют свой результат при неизменных аргументах; полезен для кеширования данных, мемоизации (сохранения результатов для возврата без повторного вычисления функции) значений рекурсивных функций (например, такого типа, как функция вычисления n-го числа Фибоначчи) и т.д.; - partial – частичное применение функции (вызов функции с меньшим количеством аргументов, чем она ожидает, и получение функции, которая принимает оставшиеся параметры). ### Какие вы функции знаете из модуля itertools @@ -1374,7 +1376,8 @@ greet('Ivan') ### Для чего нужен модуль operator -Модуль operator содержит функции, которые соответствуют стандартным операторам. Таким образом, вместо lambda x, y: x + y можно использовать уже готовую функцию operator.add и т.д. +Модуль operator предоставляет доступ к внутренним функциям Python, которые соответствуют стандартным операторам. Например, вместо `lambda x, y: x + y` можно использовать уже готовую функцию `operator.add(x, y)`, а для получения значений двух вложенных атрибутов использовать `operator.attrgetter('name.first', 'name.last')`, и т.д. Такие вызовы выполняются быстрее аналогичных лямбд и именованных фукций. + ## GIL, потоки, процессы @@ -1398,6 +1401,8 @@ greet('Ivan') По сути, GIL в питоне делает бесполезной идею применять потоки для параллелизма в вычислительных задачах. Они будут работать последовательно даже на многопроцессорной системе. На CPU Bound задачах программа не ускорится, а только замедлится, так как теперь потокам придется делить пополам процессорное время. При этом I/O операции GIL не замедлит, так как перед системным вызовом поток отпускает GIL. +Следует отметить, что GIL решает ту же задачу по управлению ресурсами, что и планировщик потоков в операционой системе. В случае использования логических потоков они могут даже мешать друг другу у величивая время ожидания доступа к ресурсу, что замедит исполнение программы. Но при физическом разделении (вычисление на разных физических ядрах или физически многопоточных ядрах) будет ожидаемый прирост производительности. + ### Работали ли Вы с asyncio. В чём его особенность - [Асинхронное программирование в Python](https://webstudio-uwk.ru/asinhronnoe-programmirovanie-v-python/) @@ -1412,7 +1417,7 @@ greet('Ivan') Переключение контекста — вообще дорогая для процессора операция, которая требует сброса регистров, кэша и таблицы отображения страниц памяти. Чем больше потоков запущено, тем больше процессор совершает холостых переключений на потоки, заблокированные GIL, прежде чем дойдет до того самого, который этот GIL удерживает. Не очень-то эффективно. -Есть старые добрые сопрограммы — то, что сейчас предлагает AsyncIO и Tornado. Их еще называют корутинами или просто потоками на уровне пользователя. Модная нынче штука, но, далеко не новая, а использовалась еще во времена, когда в ходу были ОС без поддержки многозадачности. +Есть старые добрые сопрограммы — то, что сейчас предлагает AsyncIO и Tornado. Их еще называют корутинами или просто потоками на уровне пользователя. Модная нынче штука, но, далеко не новая, а использовалась еще во времена, когда в ходу были ОС без поддержки многопоточности. В отличи от потоков, сопрограммы выполняют только полезную работу, а переключение между ними происходит только в тот момент, когда сопрограмма ожидает завершения какой-то внешней операции. @@ -1445,7 +1450,7 @@ loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(futures)) ``` -Программа состоит из метода async. Во время выполнения он возвращает сопрограмму, которая затем находится в ожидании. +Сопрограмма описана как асинхронная функция. Во время выполнения она вызывает I/O подпрограммы отправки get запроса и чтения текстовой части ответа, во время ожидания которых управление передается исполнению других сопрограмм (переключение сопрограмм в моменты вызова await происходит даже если фактическое время ожидания незначительно). Таким образом, все сопрограммы (опрос трех серверов по url) исполняются параллельно и асинхронно. `async/await` нужен для того, чтобы не блокировать поток выполнения на время ожидания какого-нибудь асинхронного события. Конструкция `async/await` превращает по сути процедуру в корутину (сопрограмму): она прекращает своё выполнение на время `await`, дожидается асинхронного события, и возобновляет работу. @@ -1457,7 +1462,7 @@ loop.run_until_complete(asyncio.wait(futures)) ### В чем отличие тредов от мультипроцессинга -Главное отличие в разделении памяти. Процессы независимы друг от друга, имеют раздельные адресные пространства, идентификаторы, ресурсы. Треды исполняются в совместном адресном порстранстве, имеют общий доступ к памяти, переменным, загруженным модулям. +Главное отличие в разделении памяти. Процессы независимы друг от друга, имеют раздельные адресные пространства, идентификаторы, ресурсы. Треды исполняются в совместном адресном пространстве, имеют общий доступ к памяти, переменным, загруженным модулям. ### Какие задачи хорошо параллелятся, какие плохо