<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Вопросы по прошлому занятию
Что найдет регулярка "^\d+.\d{1,2}.\d{1,2}\s[^A-Z]?$" ?
Что делает функция filter(lambda s
Step1:  Упражнение
Написать класс RangeCounter, который принимает начальное значение и шаг. У счетчика есть метод step(), который позволяет увеличить значение на размер шага. Давайте запретим менять значение напрямую, а шаг позволим устанавливать через сеттер.
Более подробно о with
Step3:  Что такое потоки?
Системный планировщик отдает процессорное время потокам/процессам, переключая между ними контекст
Процессы/потоки работают "параллельно", в идеале используя несколько ядер процессора
Пути планировщика неисповедимы, нельзя заранее предсказать, какой процесс получит ресурсы в конкретный момент
Потоки надо синхронизировать согласно задачам, чтобы не было проблем с одновременным доступом
Пример - простая версия веб-сервера
Есть CPU-bound задачи и есть I/O-bound задачи - важно понимать разницу
Что такое GIL?
GIL - это глобальный мьютекс (механизм синхронизации) в интерпретаторе Python
GIL запрещает выполнять байткод Python больше чем одному потоку одновременно
Но это касается ТОЛЬКО байткода Python и не распространяется на I/O операции
Потоки Python (в отличие от потоков, скажем, в Ruby) - это полноценные потоки ОС
Step4:  Упражнение
Реализовать "sleepsort". Предположим у нас есть короткий список чисел от 0 до 10. Чтобы их вывести в отсортированном порядке - достаточно каждый поток заставить "спать" количество секунд, равное самому числу, и только потом его выводить. В чем недостаток данного подхода?
Как обойти GIL?
Например, использовать процессы вместо потоков.
Тогда проблема будет с синхронизацией и обменом сообщениями (см. pickle)
И процессы все-таки немного тяжелее потоков. Стартовать по процессу на каждого клиента слишком дорого.
Step5:  Ipyparallel
0MQ + Kernels
Поддержка платформ наподобие EC2
mpi4py
Task DAG
https
Step6:  Примитивы синхронизации - мьютекс
<ASSISTANT_TASK:>
Python Code:
a = 1
b = 3
a + b
a.__add__(b)
type(a)
isinstance(a, int)
class Animal(object):
    
    mammal = True  # class variable
    
    def __init__(self, name, voice, color="black"):
        self.name = name
        self.__voice = voice  # "приватный" или "защищенный" атрибут
        self._color = color  # "типа приватный" атрибут
        
    def make_sound(self):
        print('{0} {1} says "{2}"'.format(self._color, self.name, self.__voice))
    
    @classmethod
    def description(cls):
        print("Some animal")
Animal.mammal
Animal.description()
a = Animal("dog", "bark")
a.mammal
c.__voice
c._color
dir(c)
class Cat(Animal):
    
    def __init__(self, color):
        super().__init__(name="cat", voice="meow", color=color)
c = Cat(color="white")
isinstance(c, Animal)
c1 = Cat(color="white")
c2 = Cat(color="black")
print(c1.mammal)
c1.mammal = False
print(c1.mammal)
print(c2.mammal)
c1 = Cat(color="white")
c2 = Cat(color="black")
print(c1.mammal)
Cat.mammal = False
print(c1.mammal)
print(c2.mammal)
c._color = "green"
c.make_sound()
class Cat(Animal):
    
    def __init__(self, color):
        super().__init__(name="cat", voice="meow", color=color)
        
    @property
    def color(self):
        return self._color
    
    @color.setter
    def color(self, val):
        if val not in ("black", "white", "grey", "mixed"):
            raise Exception("Cat can't be {0}!".format(val))
        self._color = val
c = Cat("white")
c.color
c.color = "green"
c.color
Explanation: Вопросы по прошлому занятию
Что найдет регулярка "^\d+.\d{1,2}.\d{1,2}\s[^A-Z]?$" ?
Что делает функция filter(lambda s: s.startswith("https://"), sys.stdin)?
Объясните своими словами, что такое yield.
В Python 2 была возможность получить очень странную ошибку ValueError: function 'func' accepts at least 2 arguments (2 given). В Python 3 сообщение об ошибке исправили на более информативное, но попытайтесь предположить, что надо было сделать для получения такой ошибки?
Как выбрать случайный элемент из списка?
Классы и магические методы
End of explanation
class A(object):
    def __init__(self):
        self.sandbox = {}
        
    def __enter__(self):
        return self.sandbox
    
    def __exit__(self, exc_type, exc_value, traceback):
        self.sandbox = {}
a = A()
with a as sbox:
    sbox["foo"] = "bar"
    print(sbox)
print(a.sandbox)
from contextlib import contextmanager
@contextmanager
def contextgen():
    print("enter")
    yield 1
    print("exit")
with contextgen() as a:
    print(a)
Explanation: Упражнение
Написать класс RangeCounter, который принимает начальное значение и шаг. У счетчика есть метод step(), который позволяет увеличить значение на размер шага. Давайте запретим менять значение напрямую, а шаг позволим устанавливать через сеттер.
Более подробно о with
End of explanation
import os
import requests
from threading import Thread
 
class DownloadThread(Thread):
    def __init__(self, url, name):
        super().__init__()
        self.url = url
        self.name = name
    
    def run(self):
        res = requests.get(self.url, stream=True)
        res.raise_for_status()        
        fname = os.path.basename(self.url)
    
        with open(fname, "wb") as savefile:
            for chunk in res.iter_content(1024):
                savefile.write(chunk)
        
        print(f"{self.name} закончил загрузку {self.url} !")
def main(urls): 
    for item, url in enumerate(urls):
        thread = DownloadThread(url, f"Поток {item + 1}")
        thread.start()
main([
    "http://www.irs.gov/pub/irs-pdf/f1040.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040a.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040ez.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040es.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040sb.pdf"
])
В данном случае интерпретатор дожидается
завершения всех дочерних потоков.
В других языках может быть иначе!
import queue
class DownloadThread2(Thread):
    def __init__(self, queue, name):
        super().__init__()
        self.queue = queue
        self.name = name
    
    def run(self):
        while True:
            url = self.queue.get()
            fname = os.path.basename(url)
            
            res = requests.get(url, stream=True)
            res.raise_for_status()        
        
            with open(fname, "wb") as savefile:
                for chunk in res.iter_content(1024):
                    savefile.write(chunk)
                    
            self.queue.task_done()
            print(f"{self.name} закончил загрузку {url} !")
def main(urls):
    q = queue.Queue()
    threads = [DownloadThread2(q, f"Поток {i + 1}") for i in range(2)]
    for t in threads:
        # заставляем интерпретатор НЕ ждать завершения дочерних потоков
        t.setDaemon(True)
        t.start()
    
    for url in urls:
        q.put(url)
        
    q.join()  # все обработано - выходим
main([
    "http://www.irs.gov/pub/irs-pdf/f1040.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040a.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040ez.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040es.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040sb.pdf"
])
Explanation: Что такое потоки?
Системный планировщик отдает процессорное время потокам/процессам, переключая между ними контекст
Процессы/потоки работают "параллельно", в идеале используя несколько ядер процессора
Пути планировщика неисповедимы, нельзя заранее предсказать, какой процесс получит ресурсы в конкретный момент
Потоки надо синхронизировать согласно задачам, чтобы не было проблем с одновременным доступом
Пример - простая версия веб-сервера
Есть CPU-bound задачи и есть I/O-bound задачи - важно понимать разницу
Что такое GIL?
GIL - это глобальный мьютекс (механизм синхронизации) в интерпретаторе Python
GIL запрещает выполнять байткод Python больше чем одному потоку одновременно
Но это касается ТОЛЬКО байткода Python и не распространяется на I/O операции
Потоки Python (в отличие от потоков, скажем, в Ruby) - это полноценные потоки ОС
End of explanation
from multiprocessing import Process
from multiprocessing import Queue
Explanation: Упражнение
Реализовать "sleepsort". Предположим у нас есть короткий список чисел от 0 до 10. Чтобы их вывести в отсортированном порядке - достаточно каждый поток заставить "спать" количество секунд, равное самому числу, и только потом его выводить. В чем недостаток данного подхода?
Как обойти GIL?
Например, использовать процессы вместо потоков.
Тогда проблема будет с синхронизацией и обменом сообщениями (см. pickle)
И процессы все-таки немного тяжелее потоков. Стартовать по процессу на каждого клиента слишком дорого.
End of explanation
import time
from concurrent.futures import ThreadPoolExecutor
# аналогично с ProcessPoolExecutor
 
def hold_my_beer_5_sec(beer):
    time.sleep(5)
    return beer
 
pool = ThreadPoolExecutor(3)
 
future = pool.submit(hold_my_beer_5_sec, ("Балтика"))
print(future.done())
time.sleep(5)
print(future.done())
print(future.result())
import concurrent.futures
import requests
 
def load_url(url):
    fname = os.path.basename(url)
    res = requests.get(url, stream=True)
    res.raise_for_status()        
    with open(fname, "wb") as savefile:
        for chunk in res.iter_content(1024):
            savefile.write(chunk)
    return fname
URLS = [
    "http://www.irs.gov/pub/irs-pdf/f1040.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040a.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040ez.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040es.pdf",
    "http://www.irs.gov/pub/irs-pdf/f1040sb.pdf"
]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    future_to_url = {
        executor.submit(load_url, url): url
        for url in URLS
    }
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        print(f"URL '{future_to_url[future]}' is saved to '{future.result()}'")
Explanation: Ipyparallel
0MQ + Kernels
Поддержка платформ наподобие EC2
mpi4py
Task DAG
https://ipyparallel.readthedocs.io/en/latest/
End of explanation
m = threading.Lock()
m.acquire()
m.release()
Explanation: Примитивы синхронизации - мьютекс
End of explanation
<END_TASK>