본문 바로가기
컴퓨터이야기/파이썬프로그래밍

파이썬 모듈에 writeln 함수 추가하기

by Saltycrocodile 2025. 2. 16.

 

 

파이썬 공부를 하던 중 

 

간단한 코딩을 하는 데 문제가 발생했다. 아래와 같은 간단한 정수형 연산자로 결과값을 출력하는 프로그램인데

명령 프롬프트에서 python 명령어로는 잘 작동하는데 python3 명령어로는 작동하지 않고 에러메시지가 나왔다.

 

 

정수형 연산자들 결과값 출력
import sys
import stdio

a = int(sys.argv[1])
b = int(sys.argv[2])

total = a + b
diff = a - b
prod = a * b
quot = a // b
rem = a % b
exp = a ** b

stdio.writeln(a + b)
stdio.writeln(a - b)
stdio.writeln(a * b)
stdio.writeln(a // b)
stdio.writeln(a % b)
stdio.writeln(a ** b)

 

이렇게 해서 python 명령문으로 프로그램을 실행시키면 아래 처럼 결과가 잘 나온다. 하지만 python3 명령문으로 프로그램을 실행하면 오류메시지가 나온다.

python intops.py 1234 5
1239
1229
6170
246
4
2861381721051424

 

 

python3명령문 프롬프트 오류
c:\hello\textbook>python3 intops.py 1234 5
Traceback (most recent call last):
  File "c:\hello\textbook\intops.py", line 23, in <module>
    stdio.writeln(a + b)
    ^^^^^^^^^^^^^
AttributeError: module 'stdio' has no attribute 'writeln'. Did you mean: 'write'?

 

AttributeError: python3명령문이 참조하는 영역에 있는 stdio에 있는 모듈에 writeln이라는 함수가 없다는 뜻이고 자동으로 write 함수를 말하는 건지 묻는다. 그래서 우선 python3 명령문이 참조하는 stdio 모듈이 어디에 위치해 있는지 위치부터 찾아보자 아래 같이 코드를 쓰고 실행시키면 위치가 나온다

 

import sys
print(sys.path)

 

이렇게 해서 명령 프롬프트에 실행시켜 보면 아래처럼 나온다. 잘 보면 python 명령문은 Users > paris > AppData > Local 순이고 python3 명령문은 Progam Files > WindowsApps 순으로 나온다. 따라서 

 

 

정리하면 

 

python 명령어가 참조하는 경로 
C:\Users\paris\AppData\Local\Programs\Python\Python312

로컬에 설치된 일반적인 Windows용 Python (기본 설치 경로)

python3 명령어가 참조하는 경로
C:\Program Files\WindowsApps\PythonSoftwareFound.Python.3.12_3.12.2124.0_x64__qbn2kf

 

Microsoft Store에서 설치된 Python

 

 

자 그럼 아까 처음으로 돌아가서 우리는 python3 명령문이 모듈을 사용할 때 참조하는 영역을 알아냈으니 저 위치로 들어가보자 정확하게는 바로 위에 나온 주소를 복사붙여넣기 하는 게 아니라 명령 프롬프트 상 주소다. 위 프롬프트 사진상으로는 마지막 폴더 이름이 'site-pakages'로 되어있는 곳으로 들어가면 된다. 

 

그리고 그곳에 가보면 stdio라는 폴더가 있고 이 폴더에 들어가보면 write.py라는 파일이 있다. 이 파일을 우클릭해서 Edit with IDLE을 선택해서 코드를 확인해보니 write함수는 있지만 writeln은 없다는 것을 알 수 있다. 이래서 오류가 났었던 것이다. 그럼 이곳에 아래처럼 writeln 함수를 작성해서 추가해주면된다. 

 

추가할 함수 코드
def writeln(message=""):  # 추가된 부분
    print(message)
    sys.stdout.flush()

 

 

수정된 write.py 파일
import os
import sys
import platform


def write(message):
    print(message, end='')
    sys.stdout.flush()

def writeln(message=""):  # 추가된 부분
    print(message)
    sys.stdout.flush()    


def skipline(n=1):
    for _ in range(n):
        print()
    sys.stdout.flush()


def deleteline(n=1):
    # Move the cursor up by 'n' lines
    sys.stdout.write(f'\x1b[{n}A')
    # Erase to the end of the screen
    sys.stdout.write('\x1b[J')
    sys.stdout.flush()


def deletechar(n=1):
    sys.stdout.write(f'\x1b[{n}D')
    sys.stdout.write('\x1b[K')
    sys.stdout.flush()


def clear():
    system = platform.system()
    if system == "Windows":
        os.system('cls')
    else:
        os.system('clear')
    sys.stdout.flush()

 

 

이제 다시 명령 프롬프트에서 python3 명령어로 해당 파일을 실행시켜 본다. 

 

 

이제 정상작동함을 볼 수 있다.

 

 

여기서 추가로 알아가면 좋은 지식 write함수와 writeln함수의 차이를 보면 결국 write는 print로 출력된 메시지를 그대로 이어 쓴다는 거고 writeln함수는 메시지 출력 후 한 줄 아래로 내린다는 의민데 이 차이를 가능하게 하는건 end='' 으로 인한 것이다. 

함수 print() 동작 end 인자 설정 결과
write() print(message, end='') end='' (줄바꿈 없음) 출력 후 같은 줄에 유지
writeln() print(message) 기본값 end='\n' (줄바꿈) 출력 후 자동으로 줄바꿈

 

def write(message):
    print(message, end='')
    sys.stdout.flush()

def writeln(message=""):  
    print(message)
    sys.stdout.flush()

 

예를 들어 print함수를 사용할 때 아래처럼 하면 print함수를 사용할 때 줄바꿈을 하지 않을 수 있다.

print("Hello", end='')
print(" World")

 

하지만 아래처럼 하면 

  • print()는 기본적으로 end='\n'이므로, 한 번 실행될 때마다 줄바꿈이 발생!
print("Hello")
print("World")

 

그렇다면 sys.stdout.flush()의 역할을 무엇일까?

 

🛠 sys.stdout.flush()의 역할

print()는 기본적으로 출력을 버퍼(Buffer)에 저장한 후 한꺼번에 출력하는 방식이다.
특히 파일에 출력하거나, 대용량 데이터를 다룰 때 출력이 지연될 수 있다.

 

✔ sys.stdout.flush()를 호출하면 출력 버퍼를 강제로 비워서 즉시 화면에 출력되도록 만듦!

 

flush()가 없을 때 발생할 수 있는 문제

 

import time

print("Hello", end='')  # 줄바꿈 없음
time.sleep(3)  # 3초 대기
print(" World")  # 이어서 출력
Hello (3초 대기) World

 

또는 터미널이나 일부환경에서는 아래처럼 3초 대기 후 나타날 수도 있다. 이는 Hello가 즉시 출력되지 않고 버퍼에 남아 있다가 print(" World") 실행될 때 한꺼번에 출력되는 것.

(3초 대기) Hello World

 

이럴 때 flush()를 사용하면 즉시 출력할 수 있다.

 

import sys
import time

print("Hello", end='')  
sys.stdout.flush()  # 버퍼 비우기 → 즉시 출력
time.sleep(3)  
print(" World")

 

Hello가 즉시 출력되고, 이후 3초 후에 World가 출력됨

Hello (3초 대기) World

 

그렇다면

언제 sys.stdout.flush()가 필요할까?

  • 실시간 진행 상태 표시 (예: 로딩 바)
  • 파일이나 네트워크 로그 출력 시 즉시 반영
  • end=''로 이어서 출력할 때 즉시 반영되도록 하기 위해

 

sys.stdout.flush()를 사용하면 즉시 출력되도록 강제
✔ 터미널에서는 차이를 못 느낄 수도 있지만, 파일 쓰기, 네트워크 출력, 대용량 데이터 처리 시 중요하고

💡 출력을 바로바로 확인하고 싶다면 sys.stdout.flush()를 적극 활용하면 좋다.

 

 

 

 

※ python 명령문이 참조하는 위치에 가보면 stido.py라는 파일이 있는데 이를 열어보면 아래처럼 이미 writeln 코드가 존재했음을 알 수 있다.

 

"""
stdio.py

The stdio module supports reading from standard input and writing to
sys.stdout.

Note:  Usually it's a bad idea to mix these three sets of reading
functions:

-- isEmpty(), readInt(), readFloat(), readBool(), readString()

-- hasNextLine(), readLine()

-- readAll(), readAllInts(), readAllFloats(), readAllBools(),
   readAllStrings(), readAllLines()

Usually it's better to use one set exclusively.
"""
    
import sys
import re

#-----------------------------------------------------------------------

# Change sys.stdin so it provides universal newline support. 

if (sys.hexversion < 0x03000000):
    import os
    sys.stdin = os.fdopen(sys.stdin.fileno(), 'rU', 0)
else:    
    sys.stdin = open(sys.stdin.fileno(), 'r', newline=None)
            
#=======================================================================
# Writing functions
#=======================================================================

def writeln(x=''):
    """
    Write x and an end-of-line mark to standard output.
    """
    if sys.hexversion < 0x03000000:
        x = unicode(x)
        x = x.encode('utf-8')
    else:
        x = str(x)
    sys.stdout.write(x)
    sys.stdout.write('\n')
    sys.stdout.flush()

#-----------------------------------------------------------------------

def write(x=''):
    """
    Write x to standard output.
    """
    if (sys.hexversion < 0x03000000):
        x = unicode(x)
        x = x.encode('utf-8')
    else:
        x = str(x)
    sys.stdout.write(x)
    sys.stdout.flush()

#-----------------------------------------------------------------------

def writef(fmt, *args):
    """
    Write each element of args to standard output.  Use the format
    specified by string fmt.
    """
    x = fmt % args
    if sys.hexversion < 0x03000000:
        x = unicode(x)
        x = x.encode('utf-8')
    sys.stdout.write(x)
    sys.stdout.flush()

#=======================================================================
# Reading functions
#=======================================================================

_buffer = ''

#-----------------------------------------------------------------------

def _readRegExp(regExp):
    """
    Discard leading white space characters from standard input. Then read
    from standard input and return a string matching regular expression
    regExp.  Raise an EOFError if no non-whitespace characters remain
    in standard input.  Raise a ValueError if the next characters to
    be read from standard input do not match 'regExp'.
    """
    global _buffer
    if isEmpty():
        raise EOFError()
    compiledRegExp = re.compile(r'^\s*' + regExp)
    match = compiledRegExp.search(_buffer)
    if match is None:
        raise ValueError()
    s = match.group()
    _buffer = _buffer[match.end():]
    return s.lstrip()

#-----------------------------------------------------------------------

def isEmpty():
    """
    Return True if no non-whitespace characters remain in standard
    input. Otherwise return False.
    """
    global _buffer
    while _buffer.strip() == '':
        line = sys.stdin.readline()
        if sys.hexversion < 0x03000000:
            line = line.decode('utf-8')
        if line == '':
            return True
        _buffer += line
    return False

#-----------------------------------------------------------------------

def readInt():
    """
    Discard leading white space characters from standard input. Then
    read from standard input a sequence of characters comprising an
    integer. Convert the sequence of characters to an integer, and
    return the integer.  Raise an EOFError if no non-whitespace
    characters remain in standard input. Raise a ValueError if the
    next characters to be read from standard input cannot comprise
    an integer.
    """
    s = _readRegExp(r'[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)')
    radix = 10
    strLength = len(s)
    if (strLength >= 1) and (s[0:1] == '0'): radix = 8
    if (strLength >= 2) and (s[0:2] == '-0'): radix = 8
    if (strLength >= 2) and (s[0:2] == '0x'): radix = 16
    if (strLength >= 2) and (s[0:2] == '0X'): radix = 16
    if (strLength >= 3) and (s[0:3] == '-0x'): radix = 16
    if (strLength >= 3) and (s[0:3] == '-0X'): radix = 16
    return int(s, radix)

#-----------------------------------------------------------------------

def readAllInts():
    """
    Read all remaining strings from standard input, convert each to
    an int, and return those ints in an array. Raise a ValueError if
    any of the strings cannot be converted to an int.
    """
    strings = readAllStrings()
    ints = []
    for s in strings:
        i = int(s)
        ints.append(i)
    return ints

#-----------------------------------------------------------------------

def readFloat():
    """
    Discard leading white space characters from standard input. Then
    read from standard input a sequence of characters comprising a
    float. Convert the sequence of characters to a float, and return the
    float.  Raise an EOFError if no non-whitespace characters remain
    in standard input. Raise a ValueError if the next characters to be
    read from standard input cannot comprise a float.
    """
    s = _readRegExp(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?')
    return float(s)

#-----------------------------------------------------------------------

def readAllFloats():
    """
    Read all remaining strings from standard input, convert each to
    a float, and return those floats in an array. Raise a ValueError if
    any of the strings cannot be converted to a float.
    """
    strings = readAllStrings()
    floats = []
    for s in strings:
        f = float(s)
        floats.append(f)
    return floats

#-----------------------------------------------------------------------

def readBool():
    """
    Discard leading white space characters from standard input. Then
    read from standard input a sequence of characters comprising a bool.
    Convert the sequence of characters to a bool, and return the
    bool.  Raise an EOFError if no non-whitespace characters remain
    in standard input. Raise a ValueError if the next characters to be
    read from standard input cannot comprise a bool.

    These character sequences can comprise a bool:
    -- True
    -- False
    -- 1 (means true)
    -- 0 (means false)
    """
    s = _readRegExp(r'(True)|(False)|1|0')
    if (s == 'True') or (s == '1'):
        return True
    return False

#-----------------------------------------------------------------------

def readAllBools():
    """
    Read all remaining strings from standard input, convert each to
    a bool, and return those bools in an array. Raise a ValueError if
    any of the strings cannot be converted to a bool.
    """
    strings = readAllStrings()
    bools = []
    for s in strings:
        b = bool(s)
        bools.append(b)
    return bools

#-----------------------------------------------------------------------

def readString():
    """
    Discard leading white space characters from standard input. Then
    read from standard input a sequence of characters comprising a
    string, and return the string. Raise an EOFError if no
    non-whitespace characters remain in standard input.
    """
    s = _readRegExp(r'\S+')
    return s

#-----------------------------------------------------------------------

def readAllStrings():
    """
    Read all remaining strings from standard input, and return them in
    an array.
    """
    strings = []
    while not isEmpty():
        s = readString()
        strings.append(s)
    return strings

#-----------------------------------------------------------------------

def hasNextLine():
    """
    Return True if standard input has a next line. Otherwise return
    False.
    """
    global _buffer
    if _buffer != '':
        return True
    else:
        _buffer = sys.stdin.readline()
        if sys.hexversion < 0x03000000:
            _buffer = _buffer.decode('utf-8')
        if _buffer == '':
            return False
        return True

#-----------------------------------------------------------------------

def readLine():
    """
    Read and return as a string the next line of standard input.
    Raise an EOFError is there is no next line.
    """
    global _buffer
    if not hasNextLine():
        raise EOFError()
    s = _buffer
    _buffer = ''
    return s.rstrip('\n')

#-----------------------------------------------------------------------

def readAllLines():
    """
    Read all remaining lines from standard input, and return them as
    strings in an array.
    """
    lines = []
    while hasNextLine():
        line = readLine()
        lines.append(line)
    return lines

#-----------------------------------------------------------------------

def readAll():
    """
    Read and return as a string all remaining lines of standard input.
    """
    global _buffer
    s = _buffer
    _buffer = ''
    for line in sys.stdin:
        if sys.hexversion < 0x03000000:
            line = line.decode('utf-8')
        s += line
    return s

#=======================================================================
# For Testing
#=======================================================================

def _testWrite():
    writeln()
    writeln('string')
    writeln(123456)
    writeln(123.456)
    writeln(True)
    write()
    write('string')
    write(123456)
    write(123.456)
    write(True)
    writeln()
    writef('<%s> <%8d> <%14.8f>\n', 'string', 123456, 123.456)
    writef('formatstring\n')

#-----------------------------------------------------------------------

def _main():
    """
    For testing. The command-line argument should be the name of the
    function that should be called.
    """

    map = {
        'readInt':    readInt,    'readAllInts':    readAllInts,
        'readFloat':  readFloat,  'readAllFloats':  readAllFloats,
        'readBool':   readBool,   'readAllBools':   readAllBools,
        'readString': readString, 'readAllStrings': readAllStrings,
        'readLine':   readLine,   'readAllLines' :  readAllLines,
        'readAll':    readAll }

    testId = sys.argv[1]

    if testId == 'write':
        _testWrite()
    else:
        writeln(map[testId]())

if __name__ == '__main__':
    _main()