티스토리 뷰
Python 초보자들이 먼저 배우고, 정말 많은 곳에서 사용되는 내장 함수를 꼽으라 하면 print를 꼽을 수 있다.
print문은 화면에의 출력 그 이상의 기능을 할 수 있는 함수 중 하나이다. 단순 출력밖에 모른다면, 아직 print문의 잠재력을 십분 활용하지 못한다고 할 수 있다.
이번 포스팅에서는 print에 대하여 자세하게 알아보자. 이번 글을 파이썬 3을 기준으로 다룰 예정이니 파이썬 2에서의 print 구문은 다루지 않을 예정이다.
1. print()
print() 함수는 파이썬 내장함수 중 하나로 특별히 다른 모듈을 활용하지 않아도 사용할 수 있는 함수이다. 실제로 함수 자체를 호출하면 다음과 같은 결과를 얻을 수 있다.
1 2 3 4 5 | # -*- coding: utf-8 -*- print(print) # => <built-in function print> | cs |
출력으로 <built-in function print>을 얻을 수 있고 즉 파이썬 내부에 지어진 함수라는 의미를 지니고 있다.
다르게 해석하자면, print 함수도 엄연한 함수이기에 반환 값이 있지만, 함수 그 자체를 출력하기에 return값을 쓸 일은 거의 없다고 봐도 무방하다.
직접 print함수를 import 하고 싶다면 기본 라이브러리 함수가 모여있는 모듈 builtins을 활용하여 print 함수를 가져올 수 있다.
1 2 3 4 5 6 | # -*- coding: utf-8 -*- import builtins print(builtins.print) # => <built-in function print> | cs |
print 함수는 기본적으로 5개의 매개변수를 받으며, 첫 번째 매개변수를 제외한 4개의 매개변수 모두 키워드-전용(keyword-only) 매개변수이므로 인자를 넘겨줄 경우 직접 명시해주어야 한다.
print(*objects, sep = ' ', end = '\n', file = sys.stdout, flush = False)
1) *objects
*objects 매개변수는 다수의 인자를 받을 수 있는 매개변수이다. 다른 의미로, print 함수에 여러 개의 인자를 동시에 넘길 수 있다는 의미이고, 이는 objects 앞에 asterisk('*')가 붙어있는 이유이다.
다음과 같이 print는 자료형의 개수의 제한 없이 출력할 수 있다. 만약 개수가 1개보다 많을 경우 각 자료형을 연결하여 출력하는 모습을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 | print(1, 2, 3) # 1 2 3 print(2, 3, [4, 5]) # 다른 자료형이 와도 상관없다. # 2 3 [4, 5] print(3, 4, (5, 6), {"1" : "2", "3" : "4"}) # 3 4 (5, 6) {'1': '2', '3': '4'} name = "Alice" print("Hello", 1, name) # 변수가 존재해도 상관없다. # Hello 1 Alice | cs |
또한, objects에는 기본 자료형 아무 종류가 와도 된다. 예를 들어, 다음과 같이 print는 여러 종류의 자료형을 출력할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | print(1) # class 'int' # 1 print(3.1) # class 'float' # 3.1 print(1 + 2j) # class 'complex' # (1+2j) print(True) # class 'bool' # True print("Hello") # class 'str' # Hello print([4, 5, 6]) # class 'list' # [4, 5, 6] print((1, 2, 3)) # class 'tuple' # (1, 2, 3) print({7, 8, 9}) # class 'set' # {7, 8, 9} print({"Alice" : 1, "Bob" : 2}) # class 'dict' # {"Alice" : 1, "Bob" : 2} | cs |
위의 결과를 살펴보면, print에는 직접 출력하고자 하는 값이 있어도 되고, 변수가 있다면 변수의 값을 출력하는 것을 확인할 수 있다.
그렇다면, print 함수는 모든 자료형에 대하여 그대로의 출력을 지원하는가? 이에 대한 답은 아니다 라고 할 수 있다. print 함수 내부적으로 str()를 호출하여 string 형태로 변환한 이후 일괄적으로 문자열 처리를 하기에, 만약 자료형이 str() 및.__str__를 지원하지 않는다면 의도한 출력을 얻지 못할 수 있다.
그렇다면 직접 정의된 객체에 대한 출력을 하기 위하여서는 __str__를 정해 주어야 한다는 결론에 이를 수 있다.
다음 예시를 살펴보자. 먼저 Person이라는 class의 객체를 출력해야 하는 상황을 만든다고 가정하여 보자. 이 경우 class의 객체를 print에 넘겨준다면 다음과 같은 결과를 얻는다
1 2 3 4 5 6 7 8 | class Person: def __init__(self, name, age): self.name = name self.age = age john = Person("john", 23) print(john) # <__main__.Person object at 0x0000028909D26530> | cs |
위의 Person이라는 class에 __str__를 찾지 못하였기에 원하지 않는 출력이 나왔고, 이에 대하여 정의를 해준다면 print의 출력을 직접 정의하여 줄 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return self.name def __repr__(self): return f"Person('{self.name}', '{self.age}')" # ------------------------------------------------------------ person1 = Person("John", 23) person2 = Person("Sally", 25) print(person1, person2) # John Sally print([person1, person2]) # [Person('John', '23'), Person('Sally', '25')] | cs |
실제로 진행하면 원하는 형태로 출력할 수 있다는 것을 확인할 수 있다.
또한, 위 코드에서 __repr__를 추가하였고, 이는 list 안의 객체를 출력할 때와 관련 있는 모습을 확인할 수 있다.
이는 list나 tuple 같은 sequence 자료형은 __str__ 메서드가 호출될 때 먼저 모든 원소들이 __repr__를 거치기 때문이다.
이외의 namedtuple이나 다른 추상 자료형들에도 __str__, __repr__를 추가한다면 print 함수를 원하는 방법으로 활용할 수 있을 것이다.
2) sep = ' '
sep은 separator의 약자로, 만약 여러 개의 인자가 입력되었을 때 출력 시 각 인자 간에 구분시켜줄 때 출력할 문자열이다.
sep에는 문자열 혹은 None이 올 수 있으며, 기본값은 띄어쓰기(' ')로 되어 있다. 만약 sep에 None이 올 경우 기본값인 띄어쓰기로 구분자가 들어가게 된다.
1 2 3 4 5 6 7 8 | print(1, 2, 3, sep = None) # 1 2 3 print(4, 5, 6, sep = ' ') # 4 5 6 print(7, 8, 9, sep = '*') # 7*8*9 print("a", "b", "c", sep = " and ") # a and b and c | cs |
sep은 출력 시 어떻게 출력될지 조절할 수 있는 인자 중 하나이다. 다만, 인자 간 사이에만 영향을 미치므로 양 끝에는 다른 방법을 써야 한다.
3) end = '\n'
end는 print문이 끝났을 때 출력할 문자열과 관련된 매개변수이다.
end에는 문자열 혹은 None이 올 수 있으며, 기본값은 줄바꿈('\n')으로 되어 있다. 만약 end에 None이 올 경우 기본값인 띄어쓰기로 구분자가 들어가게 된다.
1 2 3 4 5 6 7 | print(1, 2, 3, end = " ") print(4, 5, 6) # 1 2 3 4 5 6 print() # '\n' (줄바꿈이 숨어있다.) print(1, 2, 3, end = '*') # 1 2 3* | cs |
이는 왜 print()가 줄바꿈을 해주는 구문인지 알 수 있게 해 준다. 아무 인자가 넘겨지지 않아도 print 함수가 끝났으므로 end의 인자인 줄바꿈('\n')이 실행되었고, 따라서 새로운 줄로 넘어가게 되었다.
end는 여러 개의 print 함수 간 출력을 조절할 때 유용하게 사용할 수 있는 매개변수이다.
4) file = sys.stdout
많은 사람들이 착각하는 게 print 함수는 '출력해주는 함수'라고 생각하는 것이다.
이는 굉장히 큰 오해이다. print 함수 자체는 콘솔 출력에 대하여 구현되어 있지 않다. 입출력 자체는 바이트 단위의 저수준 layer에서 다루어야 하는 영역이고, print는 단순히 무엇을 다루어야 할지 제공해주는 역할만 한다.
조금 더 자세하게 다루자면, print는 하나의 인터페이스를 제공해주는 함수로, 입출력이나 읽기/쓰기와 관련된 함수라 생각하면 될 거 같다.
다른 말로, print문은 콘솔에 출력하는 역할 말고, 읽기/쓰기와 관련된 기능을 할 수 있다는 이야기이다.
(이에 대한 자세한 내용은 별도의 포스팅으로 다루겠다.)
file 매개변수는 어떤 방식으로 출력할 것인지 정하여 주는 역할을 하며, 기본값은 sys 모듈의 stdout, 즉 콘솔 출력과 관련되었다고 할 수 있다. (sys.stdout은 정확히 파일 쓰기와 비슷하게 콘솔에 출력하게 하는 것이다.)
만약, 쓰기를 하고 싶다면 다음과 같이 파일 쓰기를 활용할 수 있다. 다만, binary 데이터에 대해서는 사용하면 안 된다.
1 2 3 | with open('file.txt', 'w') as file_obj: print("print function can write!", file = file_obj) file_obj.close() | cs |
실제 이 코드를 실행하여 보면, 콘솔에 문자열이 출력되지 않고, file.txt에 문자열이 입력되어 있는 것을 확인할 수 있다.
5. flush = False
flush는 출력 버퍼와 관련된 매개변수이다. (버퍼는 입출력 시 성능 향상을 위하여 사용하기 위한 임시 공간이라고만 알고 있자. 이 글에서는 자세히 다루지 않을 것이다.)
만약, 3초 타이머를 print문을 통해 매 초마다 출력하도록 코드를 작성한다고 가정하자. 다음과 같이 time 모듈과 print를 조합하여 작성하였다.
1 2 3 4 5 6 7 8 9 | import time num_seconds = 3 for countdown in reversed(range(num_seconds + 1)): if countdown > 0: print(countdown, end='...') time.sleep(1) else: print("Go!") | cs |
IDE에서 실행한다면 원하는 대로 매 초마다 실행되지만, 콘솔 창에서 직접 출력한다면 다음과 같이 원하는 대로 출력이 되지 않는 것을 확인할 수 있다.
왜 그럴까? 운영체제마다 빠르고 안전한 입출력을 위하여 버퍼를 사용하지만, 이 경우 버퍼의 동작 방식으로 인하여 버퍼 안에 담아두었다가 한 번에 모든 print를 출력하기 때문이다.
따라서 이 경우를 방지하기 위하여, flush 매개변수에 True 인자를 추가하여 강제로 출력시킬 수 있다. 실제로 다음과 같이 flush 매개변수에 True를 추가한다면 원하는 방식으로 동작하는 것을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 | import time num_seconds = 3 for countdown in reversed(range(num_seconds + 1)): if countdown > 0: print(countdown, end='...', flush=True) time.sleep(1) else: print("Go!") | cs |
버퍼에 대한 설명은 다른 포스팅으로 찾아뵙겠다.
이것으로 print 함수에 관한 모든 매개변수를 알아보았다. 다만, print 함수의 활용은 정말로 무궁무진하다.
내용이 길어질 것 같으니 별도의 포스팅으로 이어서 작성하겠다.
'Python' 카테고리의 다른 글
- Total
- Today
- Yesterday
- effective async
- Python
- BOJ
- Proactor
- MIN
- 알고리즘
- 함수
- bomblab
- JS
- 구현
- docker
- C++
- C
- 백준
- for
- react
- 시간복잡도
- 헤더
- Max
- BRONZE
- 프로그래밍
- 사칙연산
- Network
- equal
- 문자열
- 수학
- CSAPP
- GDSC
- 제어문
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |