Разное

Процедуры python: Python функции и процедуры | Вызов, параметры процедуры и функции

Содержание

Python функции и процедуры | Вызов, параметры процедуры и функции

На уроке объясняется синтаксис составления процедуры и функции Питон. Рассматривается вызов функции, параметры функции и процедуры

Процедуры

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

    Пример: Создать процедуру для вывода сообщения об ошибке. Запрашивать у пользователя ввести положительное число, в случае ввода отрицательного числа, вызывать процедуру для вывода сообщения об ошибке.

    ✍ Решение: 

    def Err():  # определение процедуры
      print ( "Ошибка: неверные данные" ) 
    n = int ( input('введите положительное число') ) 
    if n < 0: 
      Err() # вызов процедуры

    def Err(): # определение процедуры
    print ( «Ошибка: неверные данные» )
    n = int ( input(‘введите положительное число’) )
    if n < 0:
    Err() # вызов процедуры

    • Процедура — вспомогательный алгоритм, выполняющий некоторые действия.
    • Это поименованный фрагмент программы, который можно вызвать.
    • Процедура должна быть определена к моменту её вызова. Определение процедуры начинается со служебного слова def.
    • Вызов процедуры осуществляется по ее имени, за которым следуют круглые скобки, например, Err().
    • В одной программе может быть сколько угодно много вызовов одной и той же процедуры.
    • Использование процедур сокращает код и повышает удобочитаемость.

    Процедура с параметрами

    Как используются в Python параметры процедуры, рассмотрим на примере.

    Пример: Написать процедуру, которая печатает 60 раз указанный символ (введенный с клавиатуры), каждый с новой строки.

    ✍ Решение: 

    1
    2
    3
    4
    5
    
    def printChar(s):
       for i in range(60):
          print (s) 
    sim = input('введите символ')
    printChar(sim)

    def printChar(s):
    for i in range(60):
    print (s)
    sim = input(‘введите символ’)
    printChar(sim)

    • Глобальная переменная — если ей присвоено значение в основной программе (вне процедуры).
    • Локальная переменная (внутренняя) известна только на уровне процедуры, обратиться к ней из основной программы и из других процедур нельзя.
    • Параметры процедуры — локальные переменные. В программе s — локальная переменная.

    Задание Python 3_0:
    Создать процедуру, которая вычисляет разность двух вводимых пользователем числа. Выполнить задание двумя способами: 1) процедура без параметров: числа — глобальные переменные, определенные в основной программе; 2) процедура с параметрами: числа — параметры процедуры.

    Локальные и глобальные переменные

    Примеры использования локальных и глобальных переменных:

    1
    2
    3
    4
    
    x = 3 # глобальная переменная
    def pr(): # процедура без параметров
      print (x) # вывод значения глобальной переменной
    pr()

    x = 3 # глобальная переменная
    def pr(): # процедура без параметров
    print (x) # вывод значения глобальной переменной
    pr()

    1
    2
    3
    4
    5
    
    x = 3  # глобальная переменная
    def pr(a): # процедура с параметром
      a = 4 # локальная переменная
      print (a) # 4
    pr(x) # передача параметра глобальной переменной (3)

    x = 3 # глобальная переменная
    def pr(a): # процедура с параметром
    a = 4 # локальная переменная
    print (a) # 4
    pr(x) # передача параметра глобальной переменной (3)

  • Существует возможность изменить значение глобальной переменной (не создавая локальную). В процедуре с помощью слова global:
  • 1
    2
    3
    4
    5
    6
    
    x = 3 # глобальная переменная
    def pr(): # процедура без параметров
      global x
      x = 1
      print (x) # вывод измененного значения глобальной переменной (1)
    pr(x)

    x = 3 # глобальная переменная
    def pr(): # процедура без параметров
    global x
    x = 1
    print (x) # вывод измененного значения глобальной переменной (1)
    pr(x)

    Задание Python 3_1:
    Напишите процедуру, которая выводит на экран в столбик все цифры переданного ей числа, начиная с последней:

    
    число: 4673
    результат:
    3
    7
    6
    4
    

    Задание Python 3_2:
    Напишите процедуру, которая выводит на экран все делители переданного ей числа (в одну строчку).

    Задание Python 3_3:
    Составить программу с процедурой для вычисления степени числа (входные параметры: число и степень).

    Задание Python 3_4:
    Напишите процедуру, которая принимает параметр – натуральное число N – и выводит первые N чисел Фибоначчи.

    Язык Python функции

    Часть функций языка Python являются встроенными функциями, которые обеспечены синтаксисом самого языка. Например, int, input, randint.
    Рассмотрим пример создания пользовательских функций.

    Пример:
    Вычислить сумму цифр числа.

    1
    2
    3
    4
    5
    6
    7
    8
    
    def sumD(n): # определение функции с параметром
      sum = 0 
      while n!= 0: 
         sum += n % 10 
         n = n // 10 
      return sum # возврат значения функции
    # основная программа 
    print (sumD(1075)) # вызов функции с параметром

    def sumD(n): # определение функции с параметром
    sum = 0
    while n!= 0:
    sum += n % 10
    n = n // 10
    return sum # возврат значения функции
    # основная программа
    print (sumD(1075)) # вызов функции с параметром

    • Функция — это поименованный фрагмент программы, который можно вызвать.
    • Как и процедура, функция должна быть определена к моменту её вызова (служебное слово def).
    • Функция в отличии от процедуры возвращает значение.
    • Для возврата значения функции используется оператор return.
    • Вызов функции осуществляется по ее имени и обычно сопровождается выводом значения.

    Задание Python 3_5:
    Напишите функцию, которая вычисляет количество цифр числа.

    Задание Python 3_6:
    Напишите функцию, которая вычисляет факториал натурального числа N.

    Процедуры и функции в python

    Процедуры и функции в Python

    В Python можно задавать собственные процедуры и функции. Процедуры в Python используются, когда часто повторяется один и тот же набор действий. Процедуры позволяют структурировать программу в Питоне, делают ее более наглядной. 

    Задание процедуры в Python
    def название_процедуры(аргумент1, аргумент2,…, аргументN):

        команды, выполняемые процедурой

    Задание функции в Питоне
    def название_функции(аргумент1, аргумент2,…, аргументN):

        команды, выполняемые функцией

        return значение

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

    Чтобы задать процедуру или функцию в Python, нужно указать def, ввести её название и все её аргументы скобках через запятую, после символа двоеточие нужно ввести набор команд процедуры.

    Пример процедуры на Питоне, которая ищет среднее арифметическое двух чисел и выводит его на экран.
    def medium(a, b):

        return (a + b) / 2

    print(medium(a, b))

    Пример процедуры на Python, которая ищет решение уравнения вида ax + b = 0 и выводит его на экран
    def linear(a, b):

        if (a == 0 and b == 0):

            print(«Бесконечное количество решений.»)

        if (a == 0 and b != 0):

            print(«Нет решений.»)   

        if (a != 0):

            sol = -b/a

            print(sol)

    a = int(input())

    b = int(input())

    linear(a, b)

    medium(a, b)

    Чтобы вызвать процедуру в Python, необходимо указать ее имя и ввести значения параметров в круглых скобках.
    linear(1,1)

    В Питоне существуют локальные и глобальные переменные. Локальные переменные используются только в заданной функции. Глобальные переменные могут изменяться и использоваться как  в основной программе, так и в функциях. По умолчанию, Python в заданных процедурах задаёт переменные как локальные. Чтобы сделать переменную глобальной, нужно в начале процедуры написать команду

    global имя_переменной

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

    Вернуться к содержанию Следующая тема Графика в Python

    Полезно почитать примеры использования процедур в python
    Анимация в python
    Пример графики в python
    Решение уравнений с помощью перебора в python 

    Игра камень ножницы бумага на python с процедурами

    Поделиться:

     

    Часть 9. Процессы и потоки

    Программирование на Python

    Сергей Яковлев
    Опубликовано 02.09.2010

    Серия контента:

    Этот контент является частью # из серии # статей: Программирование на Python

    https://www.ibm.com/developerworks/ru/library/?series_title_by=**auto**

    Следите за выходом новых статей этой серии.

    Этот контент является частью серии:Программирование на Python

    Следите за выходом новых статей этой серии.

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

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

    В качестве альтернативы существует другой подход – создание многопоточных программ. Обмен данными между потоками существенно упрощается. Но управление такими программами усложняется, и вся ответственность ложится на программиста.

    Сегодня мы рассмотрим следующие темы.

    • Как работают процессы.
    • Как работают потоки в питоне.
    • Создание потока.
    • Очереди (Queue).
    • Блокировки (Lock).

    1. Как работают процессы

    В питоне есть стандартный модуль subprocess, который упрощает управление другими программами, передавая им опции командной строки и организуя обмен данными через каналы (pipe). Мы рассмотрим пример, в котором пользователь запускает программу из командной строки, которая в свою очередь запустит несколько дочерних программ. В данном примере два скрипта – рarent.py и child.py. Запускается parent.py. Child.py выступает в роли аргумента command, который передается в запускаемый процесс. У этого процесса есть стандартный вход, куда мы передаем два аргумента – поисковое слово и имя файла. Мы запустим два экземпляра программы child.py, каждый экземпляр будет искать слово word в своем файле – это будут файлы исходников самих программ. Запись на стандартный вход осуществляет модуль subprocess. Каждый процесс пишет результат своего поиска в консоль. В главном процессе мы ждем, пока все child не закончат свою работу.

    Код parent.py:

    import os
    import subprocess
    import sys
    
    
    child = os.path.join(os.path.dirname(__file__), "./child.py")
    word  = 'word'
    file = ['./parent.py','./child.py']
    
    pipes = []
    for i in range(0,2):
      command = [sys.executable, child]
      pipe = subprocess.Popen(command, stdin=subprocess.PIPE)
      pipes.append(pipe)
      pipe.stdin.write(word.encode("utf8") + b"\n")
      pipe.stdin.write(file[i].encode("utf8") + b"\n")
      pipe.stdin.close()
    
    while pipes:
        pipe = pipes.pop()
        pipe.wait()

    Код child.py:

    import sys
    
    word = sys.stdin.readline().rstrip()
    filename = sys.stdin.readline().rstrip()
    
    try:
      with open(filename, "rb") as fh:
        while True:
          current = fh.readline()
          if not current:
              break
          if (word in current ):
              print("find: {0} {1}".format(filename,word))
    except :
        pass

    2. Как работают потоки в питоне

    Если нужно, чтобы ваше приложение выполняло несколько задач в одно и то же время, то можете воспользоваться потоками (threads). Потоки позволяют приложениям выполнять в одно и то же время множество задач. Многопоточность (multi-threading) важна во множестве приложений, от примитивных серверов до современных сложных и ресурсоёмких игр.

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

      for x in L

    а второй в этот момент начнет удалять значения из этого списка. Тут может произойти все что угодно: программа может упасть, или мы просто получим неверные данные.

    Решением в этом случае является применение блокировки. При этом доступ к заблокированному списку будет иметь только один поток, второй будет ждать, пока блокировка не будет снята.

    Применение блокировки порождает другую проблему – дедлок (deadlock) – мертвая блокировка. Пример дедлока: имеется два потока и два списка. Первый поток блокирует первый список, второй поток блокирует второй список. Первый поток изнутри первой блокировки пытается получить доступ к уже заблокированному второму списку, второй поток пытается проделать то же самое с первым списком. Получается неопределенная ситуация с бесконечным ожиданием. Эту ситуации легко описать, на практике все гораздо сложнее.

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

    Другая проблема с блокировками – в том, что несколько потоков могут одновременно ждать доступа к уже заблокированному ресурсу и при этом ничего не делать. Каждая питоновская программа всегда имеет главный управляющий поток.

    Питоновская реализация многопоточности ограниченная. Интерпретатор питона использует внутренний глобальный блокировщик (GIL), который позволяет выполняться только одному потоку. Это сводит на нет преимущества многоядерной архитектуры процессоров. Для многопоточных приложений, которые работают в основном на дисковые операции чтения/записи, это не имеет особого значения, а для приложений, которые делят процессорное время между потоками, это является серьезным ограничением.

    3. Создание потока

    Для создания потоков мы будем использовать стандартный модуль threading. Есть два варианта создания потоков:

    вызов функции

      threading.Thread()

    вызов класса

      threading.Thread

    Следующий пример показывает, как к потоку приаттачить функцию через вызов функции:

    import threading
    import time
    def clock(interval):
        while True:
            print("The time is %s" % time.ctime())
            time.sleep(interval)
    t = threading.Thread(target=clock, args=(15,))
    t.daemon = True
    t.start()

    Пример на создание потока через вызов класса: в конструкторе обязательно нужно вызвать конструктор базового класса. Для запуска потока нужно выполнить метод start() объекта-потока, что приведет к выполнению действий в методе run():

    import threading
    import time
    class ClockThread(threading.Thread):
        def __init__(self,interval):
            threading.Thread.__init__(self)
            self.daemon = True
            self.interval = interval
        def run(self):
            while True:
                print("The time is %s" % time.ctime())
                time.sleep(self.interval)
    t = ClockThread(15)
    t.start()

    Для управления потоками существуют методы:

    start() – дает потоку жизнь.

    run() –этот метод представляет действия, которые должны быть выполнены в потоке.

    join([timeout]) – поток, который вызывает этот метод, приостанавливается, ожидая завершения потока, чей метод вызван. Параметр timeout (число с плавающей точкой) позволяет указать время ожидания (в секундах), по истечении которого приостановленный поток продолжает свою работу независимо от завершения потока, чей метод join был вызван. Вызывать join() некоторого потока можно много раз. Поток не может вызвать метод join() самого себя. Также нельзя ожидать завершения еще не запущенного потока.

    getName() – возвращает имя потока.

    setName(name) – присваивает потоку имя name.

    isAlive() – возвращает истину, если поток работает (метод run() уже вызван).

    isDaemon() – возвращает истину, если поток имеет признак демона.

    setDaemon(daemonic) – устанавливает признак daemonic того, что поток является демоном.

    activeCount() – возвращает количество активных в настоящий момент экземпляров класса Thread. Фактически это len(threading.enumerate()).

    currentThread() – возвращает текущий объект-поток, т.е. соответствующий потоку управления, который вызвал эту функцию.

    enumerate() – возвращает список активных потоков.

    4. Очереди (Queue)

    В следующем примере будет решена аналогичная задача, что и в предыдущем примере с процессами: будут запущены три потока, каждый из которых будет работать по принципу утилиты grep. Имеется глобальный ресурс – work_queue – список файлов для поиска, который мы положим в очередь. Для этого будет использован объект Queue, который имеет встроенную блокировку:

    import threading
    import Queue
    
    class Worker(threading.Thread):
    
        def __init__(self, work_queue, word):
            super(Worker,self).__init__()
            self.work_queue = work_queue
            self.word = word
    
        def run(self):
            try:
                filename = self.work_queue.get()
                self.process(filename)
            finally:
                pass
    
        def process(self, filename):
            previous = "
            current=True
            with open(filename, "rb") as fh:
                while current:
                    current = fh.readline()
                    if not current: break
                    current = current.decode("utf8", "ignore")
                    if self.word in current :
                        print("find {0}: {1}".format(self.word,filename))
                    previous = current
    
    word = 'import'
    filelist = ['./file1.py','./file2.py','./file3.py']
    work_queue = Queue.Queue()
    for filename in filelist:
        work_queue.put(filename)
    for i in range(3):
        worker = Worker(work_queue, word)
        worker.start()

    5. Блокировки (Lock)

    В следующем примере будут созданы три потока, каждый из которых будет считывать стартовую страницу по указанному Web-адресу. В примере имеется глобальный ресурс – список урлов – url_list – доступ к которому будет блокироваться с помощью блокировки threading.Lock(). Объект Lock имеет методы:

    acquire([blocking=True]) – делает запрос на запирание замка. Если параметр blocking не указан или является истиной, то поток будет ожидать освобождения замка.

    Если параметр не был задан, метод не возвратит значения.

    Если blocking был задан и истинен, метод возвратит True (после успешного овладения замком).

    Если блокировка не требуется (т.е. задан blocking=False), метод вернет True, если замок не был заперт и им успешно овладел данный поток. В противном случае будет возвращено False.

    release() – запрос на отпирание замка.

    locked() – возвращает текущее состояние замка (True – заперт, False – открыт).

    import threading
    from urllib import urlopen
    
    class WorkerThread(threading.Thread):
      def __init__(self,url_list,url_list_lock):
        super(WorkerThread,self).__init__()
        self.url_list=url_list
        self.url_list_lock=url_list_lock
        
      def run(self):
        while (1):
          nexturl = self.grab_next_url()
          if nexturl==None:break
          self.retrieve_url(nexturl)
            
      def grab_next_url(self):
        self.url_list_lock.acquire(1)
        if len(self.url_list)<1:
          nexturl=None
        else:
          nexturl = self.url_list[0]
          del self.url_list[0]
        self.url_list_lock.release()
        return nexturl  
            
            
      def retrieve_url(self,nexturl):
        text = urlopen(nexturl).read()
        print text
        print '################### %s #######################' % nexturl
        
    url_list=['http://linux.org.ru','http://kernel.org','http://python.org']
    url_list_lock = threading.Lock()
    workerthreadlist=[]
    for x in range(0,3):
      newthread = WorkerThread(url_list,url_list_lock)
      workerthreadlist.append(newthread)
      newthread.start()
    for x in range(0,3):
      workerthreadlist[x].join()

    Заключение

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

    В многопоточной программе усложняется контроль за обменом данных между потоками. Глобальные ресурсы необходимо предохранять от одновременного доступа со стороны нескольких потоков, чтобы не нарушить их целостности. В этой статье были рассмотрены инструменты контроля глобальных данных – блокировки, очереди.

    Приведенные примеры проверялись на версии питона 2.6.

    < Предыдущая статья. Следующая статья >

    Ресурсы для скачивания

    Python. Урок 24. Потоки и процессы в Python. Часть 3. Управление процессами

    Продолжаем изучать параллельное программирование на Python. На этот раз разберемся с тем как создавать процессы, управлять завершением их работы и изучим особенности процессов-демонов.

    Процессы в Python

    Процессы в Python позволяют запустить выполнение нескольких задач в параллельном режиме. По сути, при старте процесса запускает еще одна копия интерпретатора Python, в котором выполняется указанная функция. Таким образом, если мы запустим пять процессов, то будет запущено пять отдельных интерпретаторов, в этом случае уже не будет проблем с GIL. Такой способ  позволяет параллельно запускать задачи активно использующие CPU. Они будут распределяться между несколькими процессами (ядрами), что значительно увеличит производительность вычислений.

    Класс Process

    Классом, который отвечает за создание и управление процессами является Process из пакета multiprocessing. Он совместим по сигнатурам методов и конструктора с threading.Thread, это сделано для более простого перехода от многопотокового приложения к многопроцессному. Помимо одноименных с Thread методов, класс Process дополнительно предоставляет ряд своих. Познакомимся поближе с этим классом, конструктор класса выглядит следующим образом:

    class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

    Параметры конструктора:

    • group
      • Параметр всегда равен None, используется для обратной совместимости с Thread.
    • target
      • Сallable-объект, который будет вызван методом run() при старте процесса.
    • name
    • args
    • kwargs
      • Параметры объекта, переданного через target.
    • daemon
      • Флаг, определяющий является ли данный процесс демоном (True) или нет (False). Если значение не задано, то оно будет равно свойству daemon родительского процесса.

    Помимо методов и свойств, которые совпадают по назначению с аналогичными для класса Thread, класс Process имеет ряд уникальных:

    • pid
      • Возвращает ID процесса. Если процесс ещё не запущен, то вернет None.
    • exitcode
      • Код возврата. Если процесс ещё не завершил свою работу, то вернет None.
    • authkey
      • Ключ аутентификации процесса. При инициализации multiprocessing, с главным процессом связывается специальная строка, которая генерируется с помощью  os.urandom(). Это значение наследуют процессы-потомки, но его можно изменить, задав новое значение, через данное свойство.
    • sentinel
      • Числовой идентификатор, который может использоваться для синхронизации. Подробно об этом будет написано в одном из следующих уроков.
    • close()
      • Освобождает все ресурсы, связанные с процессом. Если процесс еще работает, то вызов метода приведет к выбросу исключения ValueError.

    Создание и ожидание завершения работы процессов

    Как уже было сказано выше, за организацию работы с процессами в Python отвечает класс Process. Для запуска процесса используется метод start(). Разберем простой пример:

    from multiprocessing import Process
    
    
    def func():
       print("Hello from child Process")
    
    
    if __name__ == "__main__":
       print("Hello from main Process")
       proc = Process(target=func)
       proc.start()

    В рамках данного примера мы написали функцию (строка 4), которая будет выполнена в отдельном процессе, в главном процессе мы создали переменную типа Process (строка 10) с указанием функции func, и запустили его (срока 11). В результате в консоли будут выведены два сообщения из родительского и дочернего процессов:

    Hello from main Process
    Hello from child Process

    За ожидание завершения работы процесса(ов) отвечает метод join, со следующей сигнатурой:

    join([timeout])

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

    Если в приведенном выше примере код главного процесса заменить на следующий:

    print("Hello from main Process")
    proc = Process(target=func)
    proc.start()
    print("Goodbye")

    То в консоли сообщения будут выведены в таком порядке:

    Hello from main Process
    Goodbye
    Hello from child Process

    Это происходит потому, что строка print(“Goodbye”) из основного процесса выполняется раньше, чем print(“Hello from child Process”) из дочернего. Для того, чтобы все было выведено в нужном порядке, воспользуемся методом join():

    print("Hello from main Process")
    proc = Process(target=func)
    proc.start()
    proc.join()
    print("Goodbye")

    Для проверки того выполняется процесс сейчас или нет используется метод is_alive(). Дополним наш пример соответствующими проверками:

    print("Hello from main Process")
    
    proc = Process(target=func)
    proc.start()
    
    print(f"Proc is_alive status: {proc.is_alive()}")
    
    proc.join()
    
    print("Goodbye")
    print(f"Proc is_alive status: {proc.is_alive()}")

    Результат выполнения:

    Hello from main Process
    Proc is_alive status: True
    Hello from child Process
    Goodbye
    Proc is_alive status: False

    Создание классов-наследников от Process

    В классе наследнике от Process необходимо переопределить метод run() для того, чтобы он (класс) соответствовал протоколу работы с процессами.  Ниже представлен пример с реализацией этого подхода.

    from multiprocessing import Process
    from time import sleep
    
    
    class CustomProcess(Process):
       def __init__(self, limit):
           Process.__init__(self)
           self._limit = limit
    
       def run(self):
           for i in range(self._limit):
               print(f"From CustomProcess: {i}")
               sleep(0.5)
    
    
    if __name__ == "__main__":
       cpr = CustomProcess(3)
       cpr.start()

    Мы создали класс CustomProcess, который является наследником от Process и переопределили метод run(), так, что он выводит заданное количество сообщений, которое задается при создании объекта, с интервалом в 500 мс. Запуск процесса осуществляется с помощью метода start().

    Принудительное завершение работы процессов

    В отличии от потоков, работу процессов можно принудительно завершить, для этого класс Process предоставляет набор методов:

    • terminate()
      • Принудительно завершает работу процесса. В Unix отправляется команда SIGTERM, в Windows используется функция TerminateProcess().
    • kill()
      • Метод аналогичный terminate() по функционалу, только вместо SIGTERM в Unix будет отправлена команда SIGKILL.
    from multiprocessing import Process
    from time import sleep
    
    
    def func():
       counter = 0
       while True:
           print(f"counter = {counter}")
           counter += 1
           sleep(0.1)
    
    
    if __name__ == "__main__":   
       proc = Process(target=func)
       proc.start()
       sleep(0.7)
       proc.terminate()

    Процессы-демоны

    Процессы демоны по своим свойствам похожи на потоки-демоны, их суть заключается в том, что они завершают свою работу, если завершился родительский процесс. 

    Указание на то, что процесс является демоном должно быть сделано до его запуска (до вызова метода start()). Для демонического процесса запрещено самостоятельно создавать дочерние процессы. Эти процессы не являются демонами (сервисами) в понимании Unix, единственное их свойство – это завершение работы вместе с родительским процессом.

    Указать на то, что процесс является демоном можно при создании экземпляра класса через аргумент daemon, либо после создания через свойство daemon.

    from multiprocessing import Process
    from time import sleep
    
    
    def func(name):
       counter = 0
       while True:
           print(f"proc {name}, counter = {counter}")
           counter += 1
           sleep(0.1)
    
    
    if __name__ == "__main__":   
       # Указание на то, что процесс демон при создании объекта класса Process
       proc1 = Process(target=func, args=("proc1",), daemon=True)
    
       # Указание на то, что процесс демон через свойство daemon
       proc2 = Process(target=func, args=("proc2",))
       proc2.daemon = True
    
       # Запуск процессов
       proc1.start()
       proc2.start()
       sleep(0.3)
    
       # Процессы proc1 и proc2 завершаться вместе с родительским процессом
       # ...
    

    При запуске данной программы на консоли должно появиться следующее:

    proc proc1, counter = 0
    proc proc2, counter = 0
    proc proc1, counter = 1
    proc proc2, counter = 1
    proc proc1, counter = 2
    proc proc2, counter = 2

    P.S.

    Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта. Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.

    Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas.  Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

    Функциональное программирование на Python. Часть 1

    Функциональное программирование является интересной парадигмой, которая поможет по-новому взглянуть на процесс разработки и, несомненно, расширит ваш опыт новыми идеями и подходами.

    Парадигмы программирования

    Для начала обратимся к википедии за определением понятия “парадигма программирования”. Парадигма программирования — это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером.

    Выделяют две крупные парадигмы программирования: императивная и декларативная.

    Императивное программирование предполагает ответ на вопрос “Как?”. В рамках этой парадигмы вы задаете последовательность действий, которые нужно выполнить, для того чтобы получить результат. Результат выполнения сохраняется в ячейках памяти, к которым можно обратиться в последствии. 

    Декларативное программирование предполагает ответ на вопрос “Что?”. Здесь вы описываете задачу, даете спецификацию, говорите, что вы хотите получить в результате выполнения программы, но не определяете, как этот ответ будет получен.

    Каждая из этих парадигм включает в себя более специфические модели. В промышленности наибольшее распространение получили структурное и объектно-ориентированное программирование из группы “императивное программирование” и функциональное программирование из группы “декларативное программирование”.

    В рамках структурного подхода к программированию основное внимание сосредоточено на декомпозиции – разбиении программы/задачи на отдельные блоки / подзадачи. Разработка ведётся пошагово, методом “сверху вниз”. Наиболее распространенным языком, который предполагает использование структурного подхода к программирования является язык C, в нем, основными строительными блоками являются функции.

    В рамках объектно-ориентированного (ООП) подхода программа представляется в виде совокупности объектов, каждый из которых является экземпляром определенного класса, классы образуют иерархию наследования. ООП базируется на следующих принципах: инкапсуляция, наследование, полиморфизм, абстракция. Примерами языков, которые позволяют вести разработку в этой парадигме являются C#, Java.

    В рамках функционального программирования выполнение программы – это процесс вычисления, который трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании). Языки, которые реализуют эту парадигму – это Haskell, Lisp.

    Довольно часто бывает так, что дизайн языка позволяет использовать все перечисленные парадигмы (например Python) или только часть из них (например, C++).

    В чем суть функционального программирования?

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

    • Функции являются объектами первого класса (First Class Object). Это означает, что с функциями вы можете работать, также как и с данными: передавать их в качестве аргументов другим функциям, присваивать переменным и т.п.
    • В функциональных языках не используются переменные (как именованные ячейки памяти), т.к. там нет состояний, а т.к. нет переменных, то и нет операции присваивания, как это понимается в императивном программировании.
    • Рекурсия является основным подходом для управления вычислениями, а не циклы и условные операторы.
    • Используются функции высшего порядка (High Order Functions). Функции высшего порядка – это функций, которые могут в качестве аргументов принимать другие функции.
    • Функции являются “чистыми” (Pure Functions) – т.е. не имеют побочных эффектов (иногда говорят: не имеют сайд-эффектов).
    • Акцент на том, что должно быть вычислено, а не на том, как вычислять.
    • Фокус на работе с контейнерами (хотя это спорное положение).

    Python не является функциональным языком программирования, но его возможностей хватает, чтобы разрабатывать программы в функциональном стиле. 

    Примеры работы в функциональном стиле

    Функции, как объекты первого класса

    Ниже представлен код, который демонстрирует возможность Python, в части того, что функцию можно присвоить переменной и использовать ее:

    >>> def mul5(value):
            return value*5
    
    >>> v1 = 3
    >>> f1 = mul5
    >>> f1(v1)
    15

    Переменной f1, в качестве значения, присваивается функция mul5, после этого появляется возможность вызвать ее как функцию.

    Рекурсия

    Рекурсия – это вызов функции из нее же самой. Для начала приведем пример не рекурсивной функции, которая считает факториал:

    def fact_iter(n):
       if n == 0 or n == 1:
           return 1
       else:
           prod = 1
           for i in range(1, n + 1):
               prod *= i
           return prod
    
    print(fact_iter(5))

    В качестве результата получим число 120.

    Перепишем ее через рекурсию:

    def fact_rec(n):
       if n == 0 or n == 1:
           return 1
       else:
           return n * fact_rec(n - 1)
    
    print(fact_rec(5))

    Результат получим аналогичный первому. Заметим, что такая реализация не является эффективной по памяти, о том почему это так, и как сделать правильно см “О рекурсии и итерации“

    Функции высшего порядка

    Функции высшего порядка принимают в качестве аргументов другие функции. В стандартную библиотеку Python входит достаточно много таких функции, в качестве примера приведем функцию map. Она принимает функцию и список, применяет функцию к каждому элементу списка и возвращает новый модифицированный список.

    >>> fn = lambda x: x**2
    >>> print(list(map(fn, [1, 2, 3, 4, 5])))
    [1, 4, 9, 16, 25]

    Чистые функции

    Чистые функции – это функции, которые не имеют побочных эффектов. В Python это не выполняется. Необходимо самостоятельно следить за тем, чтобы функция была чистой.

    Например, следующая функция модифицирует данные, которые в нее передаются:

    def fun_not_clear(data):
        if len(data) > 0:
            data[0] += 10
    
        return data*2
    
    d1 = [1, 2, 3]
    d2 = fun_not_clear(d1)
    
    >>> print(d1)
    [11, 2, 3]
    
    >>> print(d2)
    [11, 2, 3, 11, 2, 3]

    В функцию fun_not_clear был передан массив данных, над которым был проведен ряд модификаций и сформирован новый массив. При этом исходный массив тоже был изменен. Это пример побочного эффекта, который можно получить в Python. Более корректный вариант будет выглядеть так:

    def fun_clear(data):
        data = data[:]
        if len(data) > 0:
            data[0] += 10
    
        return data*2
    
    d1 = [1, 2, 3]
    d2 = fun_clear(d1)
    
    >>> print(d1)
    [1, 2, 3]
    
    >>> print(d2)
    [11, 2, 3, 11, 2, 3]

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

    P.S.

    Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта. Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.

    Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas.  Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

    Python. Урок 22. Потоки и процессы в Python. Часть 1. Управление потоками

    Этот урок открывает цикл статей, посвященных параллельному программированию в Python. В рамках данного урока будут рассмотрены вопросы терминологии, относящиеся к параллельному программированию, GIL, создание и управление потоками в Python.

    Синхронность и асинхронность. Параллелизм и конкурентность

    Для начала разберемся с терминологией, которую мы будем использовать в рамках данного цикла статей, посвященного параллельному программированию на Python.

    Синхронное выполнение программы подразумевает последовательное выполнение операций. Асинхронное – предполагает возможность независимого выполнения задач.

    Приведем пример из математики, представьте, что у нас есть функция:

    \( f(x)=(x+1)^2 \)

    Для того, чтобы определить, чему равно значение функции при x=4, нам необходимо вначале вычислить выражение (x+1) и только потом, полученное значение возвести в квадрат:

    \( f(4) = (4+1)^2 \)

    \( f(4) = 5^2 \)

    \( f(4) = 25 \)

    Это пример синхронного порядка вычисления: операции были выполнены последовательно и, в данном случае, по-другому быть не могло.

    Теперь посмотрите на такую функцию:

    \( f(x) = x^2 + 2*x \)

    Для вычисления значения функции в точке x=4 мы также можем придерживаться синхронного порядка: вначале выполнить операцию возведения в квадрат, потом вычислим произведение и просуммируем полученные результаты:

    \( f(4) = 4^2 + 2*4 \)

    \( f(4) = 16 + 2*4 \)

    \( f(4) = 16 + 8 \)

    \( f(4) = 24 \)

    Если внимательно посмотреть на эту функцию, то можно заметить, что для того, чтобы вычислить x^2 не нужно знать значение произведения 2*x и наоборот. Операции вычисления квадратного корня и произведения можно выполнять независимо друг от друга.

    \( f(4) = 4^2 + 2*4 \)

    … значения 4^2 и 2*4 вычисляются независимо разными вычислителями…

    \( f(4) = 16 + 8 \)

    \( f(4) = 24 \)

    Более житейский пример будет выглядеть так: синхронность —  это когда вы сначала сварили картошку, а потом помыли кастрюлю, и помыть ее раньше того, как в ней приготовили вы не можете. Асинхронность —  это когда вы варите картошку и одновременно прибираетесь на кухне – эти задачи можно выполнять параллельно.

    Теперь несколько слов о конкурентности и параллелизме. Конкурентность предполагает выполнение нескольких задач одним исполнителем. Из примера с готовкой: один человек варит картошку и прибирается, при этом, в процессе, он может переключаться: немного прибрался, пошел помешал-посмотрел на картошку, и делает он это до тех пор, пока все не будет готово. 

    Параллельность предполагает параллельное выполнение задач разными исполнителями: один человек занимается готовкой, другой приборкой. В примере с математикой операции 4^2 и 2*4 могут выполнять два разных процессора.

    Несколько слов о GIL

    Для того, чтобы двигаться дальше необходимо сказать несколько слов о GIL. GIL —  это аббревиатура от Global Interpreter Lock – глобальная блокировка интерпретатора. Он является элементом эталонной реализации языка Python, которая носит название CPython. Суть GIL заключается в том, что выполнять байт код может только один поток. Это нужно для того, чтобы упростить работу с памятью (на уровне интерпретатора) и сделать комфортной разработку модулей на языке C. Это приводит к некоторым особенностям, о которых необходимо помнить. Условно, все задачи можно разделить на две большие группы: в первую входят те, что преимущественно используют процессор для своего выполнения, например, математические, их ещё называют CPU-bound, во вторую – задачи работающие с вводом выводом (диск, сеть и т.п.), такие задачи называют IO-bound. Если вы запустили в одном интерпретаторе несколько потоков, которые в основном используют процессор, то скорее всего получите общее замедление работы, а не прирост производительности. Пока выполняется одна задача, остальные простаивают (из-за GIL), переключение происходит через определенные промежутки времени. Таким образом, в каждый конкретный момент времени, будет выполняться только один поток, несмотря на то, что у вас может быть многоядерный процессор (или многопроцессорный сервер), плюс ко всему, будет тратиться время на переключение между задачами. Если код в потоках в основном выполняет операции ввода-вывода, то в этом случае ситуация будет в вашу пользу. В CPython все стандартные библиотечные функций, которые выполняют блокирующий ввод-вывод, освобождают GIL, это дает возможность поработать другим потокам, пока ожидается ответ от ОС.

    Потоки в Python

    Потоки позволяют запустить выполнение нескольких задач в конкурентном режиме в рамках одного процесса интерпретатора. При этом, нужно помнить о GIL. Все потоки будут выполняться на одном CPU, даже если задачи могут выполняться параллельно.  Поэтому есть такое правило, если ваши задачи в основном потребляют ресурсы процессора, то используйте процессы, если ввод-вывод, то потоки и другие инструменты асинхронного программирования, которые в Python обладают довольно мощным функционалом.

    Создание и ожидание завершения работы потоков. Класс Thread

    За создание, управление и мониторинг потоков отвечает класс Thread из модуля threading. Поток можно создать на базе функции, либо реализовать свой класс – наследник Thread и переопределить в нем метод run(). Для начала рассмотрим вариант создания потока на базе функции: 

    from threading import Thread
    from time import sleep
    
    def func():
        for i in range(5):
            print(f"from child thread: {i}")
            sleep(0.5)
    
    th = Thread(target=func)
    th.start()
    
    for i in range(5):
        print(f"from main thread: {i}")
        sleep(1)

    В приведенном выше примере мы импортировали нужные модули. После этого объявили функцию func(), которая выводит пять раз сообщение с числовым маркером с задержкой в 500 мс. Далее создали объект класса Thread, в нем, через параметр target, указали, какую функцию запускать как поток и запустили его. В главном потоке  добавили код вывода сообщений с интервалом в 1000 мс.

    В результате запуска этого кода получим следующее:

    from child thread: 0
    from main thread: 0
    from child thread: 1
    from main thread: 1
    from child thread: 2
    from child thread: 3
    from main thread: 2
    from child thread: 4
    from main thread: 3
    from main thread: 4

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

    Если необходимо дождаться завершения работы потока(ов) перед тем как начать выполнять какую-то другую работу, то воспользуйтесь методом join():

    th2 = Thread(target=func)
    th3 = Thread(target=func)
    
    th2.start()
    th3.start()
    
    th2.join()
    th3.join()
    
    print("--> stop")

    У join() есть параметр timeout, через который задается время ожидания завершения работы потоков.

    Для того, чтобы определить выполняет ли поток какую-то работу или завершился используется метод is_alive().

    th = Thread(target=func)
    print(f"thread status: {th.is_alive()}")
    
    th.start()
    print(f"thread status: {th.is_alive()}")
    
    sleep(5)
    print(f"thread status: {th.is_alive()}")

    В результате получим следующее:

    thread status: False
    
    from child thread: 0
    thread status: True
    
    from child thread: 1
    from child thread: 2
    from child thread: 3
    from child thread: 4
    thread status: False

    Для задания потоку имени воспользуйтесь свойством name.

    Создание классов наследников от Thread

    Ещё одни способ создавать и управлять потоками – это реализовать класс наследник от Thread и переопределить у него метод run()

    class CustomThread(Thread):
        def __init__(self, limit):
            Thread.__init__(self)
            self._limit = limit
    
        def run(self):
            for i in range(self._limit):
                print(f"from CustomThread: {i}")
                sleep(0.5)
    
    cth = CustomThread(3)
    cth.start()

    В терминале получим следующее:

    from CustomThread: 0
    from CustomThread: 1
    from CustomThread: 2

    Принудительное завершение работы потока

    В Python у объектов класса Thread нет методов для принудительного завершения работы потока. Один из вариантов решения этой задачи – это создать специальный флаг, через который потоку будет передаваться сигнал остановки. Доступ к такому флагу должен управляться объектом синхронизации.

    from threading import Thread, Lock
    from time import sleep
    
    lock = Lock()
    
    stop_thread = False
    
    def infinit_worker():
        print("Start infinit_worker()")
        while True:
            print("--> thread work")
            lock.acquire()
    
            if stop_thread is True:
               break
    
            lock.release()
    
            sleep(0.1)
    
        print("Stop infinit_worker()")
    
    # Create and start thread
    th = Thread(target=infinit_worker)
    th.start()
    
    sleep(2)
    
    # Stop thread
    lock.acquire()
    stop_thread = True
    lock.release()

    Если мы запустим эту программу, то в консоли увидим следующее:

    Start infinit_worker()
    --> thread work
    --> thread work
    --> thread work
    --> thread work
    --> thread work
    Stop infinit_worker()

    Разберемся с этим кодом более подробно. В строке 4 мы создаем объект класса Lock, он используется для синхронизации доступа к ресурсам из нескольких потоков, про них мы более подробно расскажем в следующей статье. В нашем случае, ресурс — это переменная stop_thread, объявленная в строке 6, которая используется как сигнал для остановки потока. После этого, в строке 8, объявляется функция infinit_worker(), ее мы запустим как поток. В ней выполняется бесконечный цикл, каждый проход которого отмечается выводом в терминал сообщения “–> thread work” и проверкой состояния переменной stop_thread. В главном потоке программы создается и запускается дочерний поток (строки 24, 25), выполняется функция задержки и принудительно завершается поток путем установки переменной stop_thread значения True.

    Потоки-демоны

    Есть такая разновидность потоков, которые называются демоны (терминология взята из мира Unix-подобных систем). Python-приложение не будет закрыто до тех пор, пока в нем работает хотя бы один недемонический поток.

    Пример:

    def func():
        for i in range(5):
            print(f"from child thread: {i}")
            sleep(0.5)
    
    th = Thread(target=func)
    th.start()
    print("App stop")

    Вывод программы:

    from child thread: 0
    App stop
    from child thread: 1
    from child thread: 2
    from child thread: 3
    from child thread: 4

    Как вы можете видеть, приложение продолжает работать, даже после того, как главный поток завершился (сообщение: “App stop”).

    Для того, чтобы потоки не мешали остановке приложения (т.е. чтобы они останавливались вместе с завершением работы программы) необходимо при создании объекта Thread аргументу daemon присвоить значение True, либо после создания потока, перед его запуском присвоить свойству deamon значение True. Изменим процесс создания потока в приведенной выше программе:

    th = Thread(target=func, daemon=True)

    Запустим ее, получим следующий результат:

    from child thread: 0
    App stop

    Поток остановился вместе с остановкой приложения.

    P.S.

    Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта. Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.

    Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas.  Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

    Учимся писать многопоточные и многопроцессные приложения на Python / Хабр

    Эта статья не для матёрых укротителей Python’а, для которых распутать этот клубок змей — детская забава, а скорее поверхностный обзор многопоточных возможностей для недавно подсевших на питон.

    К сожалению по теме многопоточности в Python не так уж много материала на русском языке, а питонеры, которые ничего не слышали, например, про GIL, мне стали попадаться с завидной регулярностью. В этой статье я постараюсь описать самые основные возможности многопоточного питона, расскажу что же такое GIL и как с ним (или без него) жить и многое другое.

    Python — очаровательный язык программирования. В нем прекрасно сочетается множество парадигм программирования. Большинство задач, с которыми может встретиться программист, решаются здесь легко, элегантно и лаконично. Но для всех этих задач зачастую достаточно однопоточного решения, а однопоточные программы обычно предсказуемы и легко поддаются отладке. Чего не скажешь о многопоточных и многопроцессных программах.

    Многопоточные приложения

    В Python есть модуль threading, и в нем есть все, что нужно для многопоточного программирования: тут есть и различного вида локи, и семафор, и механизм событий. Один словом — все, что нужно для подавляющего большинства многопоточных программ. Причем пользоваться всем этим инструментарием достаточно просто. Рассмотрим пример программы, которая запускает 2 потока. Один поток пишет десять “0”, другой — десять “1”, причем строго по-очереди.

    import threading
    
    def writer(x, event_for_wait, event_for_set):
        for i in xrange(10):
            event_for_wait.wait() # wait for event
            event_for_wait.clear() # clean event for future
            print x
            event_for_set.set() # set event for neighbor thread
    
    # init events
    e1 = threading.Event()
    e2 = threading.Event()
    
    # init threads
    t1 = threading.Thread(target=writer, args=(0, e1, e2))
    t2 = threading.Thread(target=writer, args=(1, e2, e1))
    
    # start threads
    t1.start()
    t2.start()
    
    e1.set() # initiate the first event
    
    # join threads to the main thread
    t1.join()
    t2.join()
    

    Никакой магии и voodoo-кода. Код четкий и последовательный. Причем, как можно заметить, мы создали поток из функции. Для небольших задач это очень удобно. Этот код еще и достаточно гибкий. Допустим у нас появился 3-й процесс, который пишет “2”, тогда код будет выглядеть так:

    import threading
    
    def writer(x, event_for_wait, event_for_set):
        for i in xrange(10):
            event_for_wait.wait() # wait for event
            event_for_wait.clear() # clean event for future
            print x
            event_for_set.set() # set event for neighbor thread
    
    # init events
    e1 = threading.Event()
    e2 = threading.Event()
    e3 = threading.Event()
    
    # init threads
    t1 = threading.Thread(target=writer, args=(0, e1, e2))
    t2 = threading.Thread(target=writer, args=(1, e2, e3))
    t3 = threading.Thread(target=writer, args=(2, e3, e1))
    
    # start threads
    t1.start()
    t2.start()
    t3.start()
    
    e1.set() # initiate the first event
    
    # join threads to the main thread
    t1.join()
    t2.join()
    t3.join()
    

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

    Global Interpreter Lock

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

    В первом случае мы сталкиваемся с таким ограничением Python (а точнее основной его реализации CPython), как Global Interpreter Lock (или сокращенно GIL). Концепция GIL заключается в том, что в каждый момент времени только один поток может исполняться процессором. Это сделано для того, чтобы между потоками не было борьбы за отдельные переменные. Исполняемый поток получает доступ по всему окружению. Такая особенность реализации потоков в Python значительно упрощает работу с потоками и дает определенную потокобезопасность (thread safety).

    Но тут есть тонкий момент: может показаться, что многопоточное приложение будет работать ровно столько же времени, сколько и однопоточное, делающее то же самое, или за сумму времени исполнения каждого потока на CPU. Но тут нас поджидает один неприятный эффект. Рассмотрим программу:

    with open('test1.txt', 'w') as fout:
        for i in xrange(1000000):
            print >> fout, 1
    

    Эта программа просто пишет в файл миллион строк “1” и делает это за ~0.35 секунды на моем компьютере.

    Рассмотрим другую программу:

    from threading import Thread
    
    def writer(filename, n):
        with open(filename, 'w') as fout:
            for i in xrange(n):
                print >> fout, 1
    
    t1 = Thread(target=writer, args=('test2.txt', 500000,))
    t2 = Thread(target=writer, args=('test3.txt', 500000,))
    
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    

    Эта программа создает 2 потока. В каждом потоке она пишет в отдельный файлик по пол миллиона строк “1”. По-сути объем работы такой же, как и у предыдущей программы. А вот со временем работы тут получается интересный эффект. Программа может работать от 0.7 секунды до аж 7 секунд. Почему же так происходит?

    Это происходит из-за того, что когда поток не нуждается в ресурсе CPU — он освобождает GIL, а в этот момент его может попытаться получить и он сам, и другой поток, и еще и главный поток. При этом операционная система, зная, что ядер много, может усугубить все попыткой распределить потоки между ядрами.

    UPD: на данный момент в Python 3.2 существует улучшенная реализация GIL, в которой эта проблема частично решается, в частности, за счет того, что каждый поток после потери управления ждет небольшой промежуток времени до того, как сможет опять захватить GIL (на эту тему есть хорошая презентация на английском)

    «Выходит на Python нельзя писать эффективные многопоточные программы?», — спросите вы. Нет, конечно, выход есть и даже несколько.

    Многопроцессные приложения

    Для того, чтобы в некотором смысле решить проблему, описанную в предыдущем параграфе, в Python есть модуль subprocess. Мы можем написать программу, которую хотим исполнять в параллельном потоке (на самом деле уже процессе). И запускать ее в одном или нескольких потоках в другой программе. Такой способ действительно ускорил бы работу нашей программы, потому, что потоки, созданные в запускающей программе GIL не забирают, а только ждут завершения запущенного процесса. Однако, в этом способе есть масса проблем. Основная проблема заключается в том, что передавать данные между процессами становится трудно. Пришлось бы как-то сериализовать объекты, налаживать связь через PIPE или друге инструменты, а ведь все это несет неизбежно накладные расходы и код становится сложным для понимания.

    Здесь нам может помочь другой подход. В Python есть модуль multiprocessing. По функциональности этот модуль напоминает threading. Например, процессы можно создавать точно так же из обычных функций. Методы работы с процессами почти все те же самые, что и для потоков из модуля threading. А вот для синхронизации процессов и обмена данными принято использовать другие инструменты. Речь идет об очередях (Queue) и каналах (Pipe). Впрочем, аналоги локов, событий и семафоров, которые были в threading, здесь тоже есть.

    Кроме того в модуле multiprocessing есть механизм работы с общей памятью. Для этого в модуле есть классы переменной (Value) и массива (Array), которые можно “обобщать” (share) между процессами. Для удобства работы с общими переменными можно использовать классы-менеджеры (Manager). Они более гибкие и удобные в обращении, однако более медленные. Нельзя не отметить приятную возможность делать общими типы из модуля ctypes с помощью модуля multiprocessing.sharedctypes.

    Еще в модуле multiprocessing есть механизм создания пулов процессов. Этот механизм очень удобно использовать для реализации шаблона Master-Worker или для реализации параллельного Map (который в некотором смысле является частным случаем Master-Worker).

    Из основных проблем работы с модулем multiprocessing стоит отметить относительную платформозависимость этого модуля. Поскольку в разных ОС работа с процессами организована по-разному, то на код накладываются некоторые ограничения. Например, в ОС Windows нет механизма fork, поэтому точку разделения процессов надо оборачивать в:

    if __name__ =='__main__':
    

    Впрочем, эта конструкция и так является хорошим тоном.

    Что еще …

    Для написания параллельных приложений на Python существуют и другие библиотеки и подходы. Например, можно использовать Hadoop+Python или различные реализации MPI на Python (pyMPI, mpi4py). Можно даже использовать обертки существующих библиотек на С++ или Fortran. Здесь можно было упомянуть про такие фреймфорки/библиотеки, как Pyro, Twisted, Tornado и многие другие. Но это все уже выходит за пределы этой статьи.

    Если мой стиль вам понравился, то в следующей статье постараюсь рассказать, как писать простые интерпретаторы на PLY и для чего их можно применять.

    Основы определения процедур в Python?

    Переполнение стека

    1. Около
    2. Продукты

    3. Для команд
    1. Переполнение стека
      Общественные вопросы и ответы

    2. Переполнение стека для команд
      Где разработчики и технологи делятся частными знаниями с коллегами

    3. Вакансии
      Программирование и связанные с ним технические возможности карьерного роста

    4. Талант
      Нанимайте технических специалистов и создавайте свой бренд работодателя

    5. Реклама
      Обратитесь к разработчикам и технологам со всего мира

    .

    возвращаемых значений процедуры Python — qaru

    Переполнение стека

    1. Около
    2. Продукты

    3. Для команд
    1. Переполнение стека
      Общественные вопросы и ответы

    2. Переполнение стека для команд
      Где разработчики и технологи делятся частными знаниями с коллегами

    3. Вакансии
      Программирование и связанные с ним технические возможности карьерного роста

    4. Талант
      Нанимайте технических специалистов и создавайте свой бренд работодателя

    5. Реклама
      Обратитесь к разработчикам и технологам со всего мира

    6. О компании

    .

    Python Выполнение хранимой процедуры MySQL [Полное руководство]

    Основная цель этой статьи — показать вам, как выполнять хранимые процедуры MySQL в Python .

    Предварительные требования

    Прежде чем двигаться дальше, убедитесь, что у вас есть следующее.

    • Имя пользователя и пароль, необходимые для подключения к MySQL
    • Имя хранимой процедуры MySQL, которую вы хотите вызвать.

    Для этого урока я создал хранимую процедуру « get_laptop » в базе данных « Electronics ».Эта процедура извлекает сведения о портативном компьютере из таблицы портативных компьютеров на основе идентификатора портативного компьютера, переданного в качестве параметра IN хранимой процедуры.

    Если на вашем сервере MySQL нет таблицы, вы можете обратиться к нашей статье, чтобы создать таблицу MySQL из Python. Также вы можете скачать создание таблицы Laptop с данными в файле MySQL, который вместе с данными содержит SQL-запросы для создания таблицы.

    Если вы уже знаете хранимую процедуру, которую хотите выполнить, то можете пропустить следующий запрос.Откройте консоль MySQL и выполните приведенный ниже запрос, чтобы создать хранимую процедуру MySQL.

      DELIMITER $$
    ИСПОЛЬЗОВАТЬ Электронику $$
    СОЗДАТЬ * с портативного компьютера, где id = d_id;
    КОНЕЦ $$
    DELIMITER;  

    Теперь вы знаете, какую хранимую процедуру нужно выполнить, поэтому перейдем к нашему примеру.

    Примечание : Мы используем модуль MySQL Connector Python для выполнения хранимой процедуры MySQL.

    шагов для выполнения хранимой процедуры MySQL в Python

    Чтобы вызвать хранимую процедуру MySQL из Python, необходимо выполнить следующие действия: —

    • Установите MySQL Connector Python с помощью pip.
    • Создайте соединение с базой данных MySQL.
    • Создайте объект Cursor, используя объект соединения.
    • Выполните хранимую процедуру , используя функцию cursor.callproc () (здесь вы должны знать имя хранимой процедуры и ее параметры IN и OUT).
    • Используйте cursor.stored_results () для получения результатов запроса.
    • Перехватить любые исключения SQL, которые могут возникнуть во время этого процесса.
    • Закройте объект курсора и соединение с базой данных MySQL.

    Выполнить хранимую процедуру MySQL на python

    Теперь рассмотрим пример. В этом примере мы собираемся выполнить хранимую процедуру « get_laptop », используя python.

      импорт mysql.connector
    из ошибки импорта mysql.connector
    
    пытаться:
        соединение = mysql.connector.connect (host = 'localhost',
                                             база данных = 'Электроника',
                                             user = 'pynative',
                                             пароль = 'pynative @ # 29')
        курсор = соединение.курсор()
        cursor.callproc ('get_laptop', [1,])
        # результат печати
        print («Печать деталей ноутбука»)
        для результата в cursor.stored_results ():
            печать (результат.fetchall ())
    
    кроме mysql.connector.Error как ошибки:
        print ("Не удалось выполнить хранимую процедуру: {}". формат (ошибка))
    Ну наконец то:
        если (connection.is_connected ()):
            cursor.close ()
            connection.close ()
            print («MySQL-соединение закрыто»)
      

    Вы должны получить следующий результат.

     Печать деталей ноутбука
    [(1, 'Lenovo ThinkPad P71', 6459.0, datetime.date (2019, 8, 14))]
    MySQL соединение закрыто
     

    Давайте теперь разберемся с приведенным выше примером

    • Строка import mysql.connector импортирует модуль MySQL Connector Python в вашу программу, поэтому вы можете использовать API этого модуля для подключения MySQL.
    • Мы используем mysql.connector и объекты Error из пакета MySQL Connector / Python, который используется, чтобы показать нам ошибку, когда мы не смогли выполнить хранимую процедуру.Пример ER_ACCESS_DENIED_ERROR, когда имя пользователя или пароль неверны.
    • Мы использовали функцию mysql.connector.connect () для подключения к базе данных MySQL.
    • Мы использовали метод connection.cursor для получения объекта курсора из соединения. этот метод создает новый объект MySQLCursor.
    • Затем мы вызвали хранимую процедуру «get_laptop» с помощью функции cursor.callproc ('get_laptop', [1,]) здесь мы передали имя хранимой процедуры, и это значение параметра IN.
    • После успешного выполнения хранимой процедуры мы можем получить результат, используя cursor.stored_results ()
    • В конце концов, мы закрыли объект курсора и соединение с базой данных.

    Cursor callproc () Метод

    Синтаксис callproc ()

      result_args = cursor.callproc (proc_name, args = ())  

    cursor.callproc () метод вызывает хранимую процедуру, указанную в аргументе proc_name .Последовательность параметров args должна содержать по одной записи для каждого аргумента, ожидаемого процедурой. Например, процедура может иметь один или несколько параметров IN и OUT.

    cursor.callproc () возвращает измененную копию входной последовательности. Этот метод не меняет входные параметры. Однако параметры вывода и ввода / вывода могут быть заменены новыми значениями в соответствии с результатом выполнения.

    Хранимая процедура возвращает выходные данные в наборах результатов и автоматически выбирается и сохраняется как экземпляры MySQLCursorBuffered.

    Например, хранимая процедура «сложение» принимает два параметра, складывает значения и возвращает сумму.

      СОЗДАТЬ ПРОЦЕДУРУ add_num (IN num1 INT, IN num2 INT, OUT sum INT)
    НАЧАТЬ
    SET sum: = num1 + num2;
    КОНЕЦ;  

    В следующем примере показано, как выполнить эту процедуру добавления в python.

      args = (5, 6, 0) # 0 - хранить значение суммы параметров OUT
    cursor.callproc ('add_num', аргументы)  

    Следующие шаги:

    Чтобы попрактиковаться в том, что вы узнали из этой статьи, решите проект «Упражнение для базы данных Python», чтобы попрактиковаться и освоить операции с базой данных Python.

    .

    sql server — вызов хранимой процедуры python

    Переполнение стека

    1. Около
    2. Продукты

    3. Для команд
    1. Переполнение стека
      Общественные вопросы и ответы

    2. Переполнение стека для команд
      Где разработчики и технологи делятся частными знаниями с коллегами

    3. Вакансии
      Программирование и связанные с ним технические возможности карьерного роста

    4. Талант
      Нанимайте технических специалистов и создавайте свой бренд работодателя

    5. Реклама
      Обратитесь к разработчикам и технологам со всего мира

    6. О компании

    .

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *