end = '...' ключ в print () не безопасен для потоков? - PullRequest
0 голосов
/ 27 мая 2018

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

from concurrent.futures import ThreadPoolExecutor
import sys

items = ['A','B','C','D','E','F',
         'G','H','I','J','K','L',
         'M','N','O','P','Q','R',
         'S','T','U','V','W','X','Y','Z']

def show(name):
    print(name, end=' ')

with ThreadPoolExecutor(10) as executor:
    executor.map(show, items)

# This outputs
# AB  C D E F G H I J KLMNOP      QR STU VW    XY Z 

Но когда я пытаюсь использовать sys.stdout.write(), я не получаю такого поведения.

def show2(name):
    sys.stdout.write(name + ' ')

with ThreadPoolExecutor(10) as executor:
    executor.map(show2, items)

# This gives
# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 

Странно то, что я попробовал это как на ноутбуке Jupyter, так и написав файл .py и запустив его.Но с последним у меня, похоже, нет этой проблемы.Я попытался выполнить поиск, но все, что я получил, это то, что print() в python-3.x является поточно-ориентированным.Если это действительно потокобезопасный, кто-нибудь может объяснить, почему это происходит?

1 Ответ

0 голосов
/ 27 мая 2018

Указание end на самом деле не нужно для этого;даже простое выполнение print(name) иногда приводит к тому, что буквы располагаются рядом друг с другом:

A
B
C
D
EF
G

H
I

Даже flush=True не исправляет это.

Функция печати реализована в CPython здесь , и написано на C. Интересный бит таков:

for (i = 0; i < nargs; i++) {
        if (i > 0) {
            if (sep == NULL)
                err = PyFile_WriteString(" ", file);
            else
                err = PyFile_WriteObject(sep, file,
                                         Py_PRINT_RAW);
            if (err)
                return NULL;
        }
        err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW);
        if (err)
            return NULL;
    }

    if (end == NULL)
        err = PyFile_WriteString("\n", file);
    else
        err = PyFile_WriteObject(end, file, Py_PRINT_RAW);

Вы можете видеть, что он вызывает PyFile_WriteObject один раз для каждого аргумента (и для sep, если указано), а затем еще раз для аргумента end (PyFile_WriteString - это просто оболочка вокруг PyFile_WriteObject, которая принимает const char*, а не PyObject) - я предполагаю, что в конечном итоге появляется возможность для контекстапереключайтесь где-нибудь между этими вызовами.

Каждый вызов PyFile_WriteString по сути аналогичен вызову (в Python) sys.stdout.write, что объясняет, почему вы не видите этого при выполнении sys.stdout.write(name + ' ');если вы вместо этого сделали это:

sys.stdout.write(name)
sys.stdout.write(" ")

, это больше похоже на то, что делает сама функция печати, что также объясняет, почему выполнение print(name + " ", end="") тоже работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...