End = '...' ключ в print () не является потокобезопасным?

С помощью приведенного ниже кода я попытался распечатать кучу вещей параллельно на ноутбуке 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 является. Если это действительно потокобезопасность, может ли кто-нибудь объяснить, почему это происходит?

twitter.com/nedbat/status/194452404794691584?s=19
Daniel Roseman 27.05.2018 13:58

Что происходит, когда вы делаете print("%s "%(name), end ='')? Возможно, вариант end= выводит имя и пространство как операции отчетливый, между которыми может происходить переключение контекста. print из одной строки с уже добавленным пробелом (и пустой end) может облегчить это.

paxdiablo 27.05.2018 14:56

Если вы используете Python 3.3+, вы можете попробовать print(..., flush=True').

user9611000 27.05.2018 15:43
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
3
122
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Указание 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 = "") тоже работает.

Спасибо за этот ответ :) Небольшой уточняющий вопрос: тогда функция печати не является поточно-ориентированной?

Semin Park 27.05.2018 17:50

Честно говоря, не могу сказать; может ответы на этот вопрос помогут? Этот ответ, похоже, предполагает, что в целом файловый ввод-вывод не является потокобезопасным, что означает, что print также не является потокобезопасным.

Ash 27.05.2018 22:40

Другие вопросы по теме