J'essaie de rediriger le STDOUT d'un script python vers un fichier.

Si STDOUT est importé à partir de sys, la sortie du script n'est pas redirigée vers un fichier:

from sys import stdout
stdout = open("text", "w")
print("Hello")

Cependant, si j'importe uniquement sys et utilise sys.stdout, la sortie du script est correctement redirigée:

import sys
sys.stdout = open("text", "w")
print("Hello")

Pourquoi est-ce? Selon cette réponse, la seule différence entre "importer X" et "à partir de X importer Y" est le nom qui est lié. Comment cela parvient-il à affecter la sortie standard?

2
Cat 12 avril 2018 à 04:47

4 réponses

Meilleure réponse

Oui, la seule différence est que le nom Y est lié à X.Y.

Quoi qu'il en soit, la liaison de Y à quelque chose d'autre n'affectera rien dans X.


Si cela facilite les choses, considérez ce parallèle:

>>> y = 2
>>> x = y
>>> x = 3

Vous attendez-vous à ce que cela change y en 3? Bien sûr que non. Mais c'est exactement la même chose que vous faites.


Si ce n'est toujours pas clair, décomposons ce que ces import font réellement.

Lorsque vous import sys, cela équivaut à:

sys.modules['sys'] = __import__('sys')
sys = sys.modules['sys']
sys.stdout = open(text, "w")

Mais avec le from sys import stdout:

sys.modules['sys'] = __import__('sys')
stdout = sys.modules['sys'].stdout
stdout = open(text, "w")
4
abarnert 12 avril 2018 à 01:52

Vous pouvez vous épargner des efforts et gagner des points "Pythonic" avec ce petit truc:

import sys
print('hello', file=sys.stdout)

Bien sûr, print va déjà à sys.stdout par défaut, alors peut-être que je manque quelque chose. Je ne sais pas ce qui se passe avec le open('text', 'w'), mais cela pourrait ne pas être nécessaire si vous le faites de cette façon :)

En réponse à votre question sur l'impact de l'affectation des variables, lorsque vous utilisez l'opérateur = sur une variable, vous l'affectez réellement à la valeur dans le dictionnaire d'étendue (dans ce cas globals).

Ainsi, lorsque vous importez sys, sys est importé dans le dictionnaire globals.

Donc globals ressemble,

{...,
'sys': <module 'sys' (built-in)>}

Vous pouvez considérer le module lui-même comme un dictionnaire. Donc quand vous faites sys.stdout= ... c'est comme faire globals()['sys'].__dict__['stdout'] =...

Lorsque vous venez d'importer stdout, globals ressemble à:

{...,
'stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>}

Ainsi, lorsque vous faites stdout=..., vous remplacez vraiment directement cette clé dans le dictionnaire:

globals()['stdout'] = ...

Espérons que cela aide à ajouter un peu de clarté!

0
snakes_on_a_keyboard 12 avril 2018 à 17:10

La façon dont je le fais est de créer un gestionnaire de contexte.

@contextmanager
def suppress_stdout():
    with open(os.devnull, 'w') as devnull:
        old_stdout = sys.stdout
        old_stderr = sys.stderr
        sys.stdout = devnull
        try:
            yield
        finally:
            sys.stdout = old_stdout
            sys.stderr = old_stderr

Puis quand je veux supprimer la sortie standard d'une certaine commande:

with suppress_stdout():
    # suppressed commands    
1
user9633488user9633488 12 avril 2018 à 02:16

C'est la même chose que:

x = some_object.some_attr
x = open(...)

Vous ne modifiez pas some_object.some_attr dans ce cas. Vous attribuez simplement une valeur locale.

Lorsque vous utilisez sys.stdout = ..., vous mettez à jour la sortie standard.

1
viraptor 12 avril 2018 à 01:52