Класс Thread можно также использовать для запуска простых функций и вызываемых объектов других типов, вообще не создавая подклассы. Метод run класса Thread по умолчанию просто вызывает объект, переданный конструктору в аргументе target, со всеми дополнительными аргументами, переданными в аргументе args (который по умолчанию является пустым списком ()). Это позволяет использовать класс Thread для запуска простых функций, хотя такая форма вызова ненамного проще использования модуля _thread. Например, в следующих фрагментах демонстрируются четыре различных способа запуска одного и того же потока (смотрите сценарии four—threads*.py в дереве примеров; вы можете запустить все четыре потока в одном сценарии, но при этом вам понадобится синхронизировать обращения к функции print, чтобы избежать смешивания выводимых данных):
import threading, _thread def action(i):
print(i ** 32)
# подкласс, хранящий собственную информацию о состоянии class Mythread(threading.Thread): def __init__(self, i): self.i = i
threading.Thread.__init__(self)
def run(self): # переопределить метод run
print(self.i ** 32)
Mythread(2).start() # метод start вызовет метод run()
# передача простой функции
thread = threading.Thread(target=(lambda: action(2))) # run вызовет target thread.start()
# то же самое, но без lambda-функции,
# сохраняющей информацию о состоянии в образуемом ею замыкании
threading.Thread(target=action, args=(2,)).start() # вызываемый объект
# и его аргументы
# с помощью модуля thread
_thread.start_new_thread(action, (2,)) # полностью процедурный интерфейс
Как правило, выбирать реализацию потоков на основе классов имеет смысл, когда потоки должны сохранять информацию о своем состоянии или когда желательно использовать какие-либо из многочисленных преимуществ ООП. Однако классы потоков выполнения необязательно должны наследовать класс Thread. Фактически, как и при использовании модуля _thread, реализация потоков в модуле threading может принимать в аргументе target вызываемые объекты любого типа. При объединении с такими приемами, как связанные методы и вложенные области видимости, различия между приемами программирования становятся еще менее выраженными:
# обычный класс с атрибутами, ООП class Power:
def __init__(self, i): self.i = i
def action(self): print(self.i ** 32)
obj = Power(2)
threading.Thread(target=obj.action).start() # запуск связанного метода
# вложенная область видимости, для сохранения информации о состоянии def action(i):
def power():
print(i ** 32)
return power
threading.Thread(target=action(2)).start() # запуск возвращаемой функции
# запуск обоих вариантов с помощью модуля _thread _thread.start_new_thread(obj.action, ()) # запуск вызываемого объекта
_thread.start_new_thread(action(2), ())
Как видите, интерфейс модуля threading такой же гибкий, как и сам язык Python.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011