파이썬의 탄생
1989년 12월, 30대 중반의 네덜란드의 컴퓨터 과학자 귀도 반 로섬(Guido Van Rossum)은 여러 프로그래밍 언어들의 한계로 크리스마스 프로젝트로 새로운 언어를 직접 만들어보기로 결심했으며, 원칙은 간단했다.
1. 읽기 쉬워야 하고,
2. 사용자가 원하는 모듈 패키지를 만들 수 있어야 했으며,
3. 약간 독특하고 신비한 이름을 원했다.
그 후 30년이 지난 지금 어느덧 파이썬은 세상에서 가장 인기있는 프로그래밍 언어로 자리매김했다.
무엇보다도 문법이 매우 쉬워 입문자들에게 가장 먼저 추천하는 언어이면서 "실행 가능한 수도코드(Executable Pseudocode)"라는 별칭으로도 불린다.
원래 수도코드란 실행이 되지 않는, 알고리즘만을 기술하는 코드를 말하는데 파이썬은 알고리즘만을 기술할 정도로 간결하면서도 실행까지 가능하다
그렇다고 실무에서는 사용할 수 없는 교육용 언어에 불과하지 않고, 한쪽에서는 입문용으로 배우고, 다른 한쪽에서는 컴퓨터과학 박사들이 세계 최고 수준의 학회에 제출할 논문을 작성하는 데 사용할 정도로 다방면에서 활약하고 있는 프로그래밍 언어다.
다음은 PYPL지수(Popularity of Programming Language Index)는 구글의 튜토리얼 검색량에 따라 프로그래밍 언어의 인기도를 측정하는 지표이다.
[출처-https://yozm.wishket.com/magazine/detail/1833/]
파이썬 문법
- PEP
파이썬의 개발은 파이썬 개선 제안서(PEP, Python Enhancement Proposals) 프로세스를 통해 진행된다.
PEP 프로세스는 새로운 기능을 제안하고, 커뮤니티의 의견을 수렴하여 파이썬의 디자인 결정을 문서화하는 파이썬의 주요 개발 프로세스를 일컫는다.
대표적으로 파이썬의 코딩 스타일 가이드는 PEP 8이다
# 파라미터가 시작되는 부분을 기준으로 인덴트를 맞췄을 때
foo = long_funtion_name(var_one, var_two,
val_three, val_four)
# 다른 행과 구분하여 인덴트를 맞췄을 때
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
foo = long_function_name(
var_one, var_two,
var_three, var_four)
# 스네이크 케이스(Snake Case)
snake_case = "Python"
# 카멜 케이스(Camel Case)
camelCase = "Java"
# 파스칼 케이스(Pascal Case)
PascalCase = "Upper Camel Case"
# 타입 힌트를 사용하지 않을 경우
def fn(a):
...
# 타입 힌트를 사용할 경우, 인자값을 정수로 넘기면 Ture, False 중 한 개가 반환된다는 것을 인지할 수 있다.
def fn(a: int) -> bool:
...
# 기존 리스트에 새로운 값을 넣을 경우
n_list = []
for i in range(10):
if i % 2 == 1:
n_list.append(i * 2)
# 리스트 컴프리헨션을 활용하여 위와 같은 코드를 작성할 경우
n_list = [i * 2 for i in range(10) if i % 2 == 1]
'''
리스트 컴프리헨션으로 다중 조건을 줄 수 있다.
여기서 주의할 점은 OR연산자는 직접 작성하여 해결할 수 있지만, AND 연산자는 직접 작성할 경우 SystaxError가 발생된다.
때문에 if 조건을 연속적으로 사용하면 AND연산을 할 수 있다.
'''
# SystaxError
n_list = [i for i in range(10) if i % 2 == 1 and if i % 3 == 0]
# AND 연산
n_list = [i for i in range(10) if i % 2 == 1 if i % 3 == 0]
# OR 연산
n_list = [i for i in range(10) if i % 2 == 1 or i % 3 == 0]
리스트 컴프리헨션으로 리스트만을 다뤘지만, 컴프리헨션 식으로 리스트뿐만 아니라 셋(Set), 튜플(Tuple), 딕셔너리(Dictionary)도 가능하다.
# 간단한 예시 1
def three_generator():
yield 1
yield 2
yield 3
t_gen = three_generator() -> 1번째 출력 예시
next(t_gen) -> output 1
next(t_gen) -> output 2
next(t_gen) -> output 3
for i in three_generator(): -> 2번째 출력 예시
print(i)
-> output
1
2
3
# 간단한 예시 2
def infinite_generator():
count = 0
while True:
count += 1
yield count
infinite_g = infinite_generator() -> 출력 예시
for i in range(10):
print(next(infinite_g))
-> output
1
2
3
4
5
6
7
8
9
10
# 제너레이터 함수를 각각 2개의 변수에 선언하면 서로가 다른 객체이며, 각기 따로 동작합니다.
one = test_generator()
two = test_generator()
print(one == two)
-> False
print(one is two)
-> False
# 파라미터(Parameter)가 1개인 경우
print(list(range(3)))
-> [0, 1, 2]
# 파라미터(Parameter)가 2개인 경우
print(list(range(5, 8)))
-> [5, 6, 7] # 첫 번째 인자부터 두 번째 인자 - 1까지의 수를 반환
# 파라미터(Parameter)가 3개인 경우_1
print(list(range(1, 13, 3)))
-> [1, 4, 7, 10]
# 파라미터(Parameter)가 3개인 경우_2
print(list(range(13, 1, -3)))
-> [13, 10, 7, 4]
range() 함수는 보통 for문에서 많이 사용합니다.
또한, 신기한 기능이 있는데 이전 Generator처럼 메모리를 절약하는 방법이 있다.
first_range = [n for n in range(1000000)]
second_range = range(1000000)
len(first_range)
-> 1000000
len(second_range)
-> 1000000
# 하지만 차이점은 first_range는 이미 생성된 값이 담겨 있고,
second_range는 생성햐야 한다는 조건만 존재합니다.
print(second_range)
-> rnage(0, 1000000)
print(type(second_range))
-> <class 'range'>
# 둘 사이의 메모리 점유율을 비교해보면 차이점이 있습니다.
sys.getsizeof(first_range)
-> 8448728
sys.getsizeof(second_range)
-> 48
# 여기서 굉장한 것은 미리 생성하지 않은 값은 인덱스에 접근이 안될 거라 생각되지만,
인덱스로 접근 시에는 바로 생성하도록 구현되어 있다.
second_range[999]
-> 999
n_list = [1, 2, 1, 3, 45, 3, 6]
print(n_list) -> [1, 2, 1, 3, 45, 3, 6]
list(enumerate(n_list)) -> [(0, 1), (1, 2), (2, 1), (3, 3), (4, 45), (5, 3), (6, 6)]
c_list = ['a4', 'a7', 'a1']
for idx, val in enumerate(c_list):
print(idx, val)
-> 0 a4
-> 1 a7
-> 2 a1
# /를 사용할 경우 나눗셈
5 / 3
-> 1.6666666666666667
# //를 사용할 경우 몫
5 // 3
-> 1
# 추가로 나머지를 구할때는 %
5 % 3
-> 2
# 몫과 나머지를 동시에 구하려면 divmod() 함수를 사용
divmod(5, 3)
-> (1, 2)
a = 'A1'
b = 'B2'
# 콤마(,) 를 사용할 경우 띄어쓰기로 값을 구분하여 출력해줍니다.
print(a, b)
-> A1 B2
# sep 파라미터로 구분자를 콤마(,)로 지정해줄 수도 있습니다.
print(a, b, sep=',')
-> A1,B2
# sep 파라미터로 줄바꿈을 생략할 수도 있습니다.
print(a, sep=' ')
print(b)
-> A1 B2
# 리스트를 출력할 때는 join() 으로 묶어서 출력이 가능합니다.
alphabet = ['A', 'B', 'C', 'D']
print(' '.join(alphabet))
-> A B C D
# 더하기(+) 연산자를 활용하면 붙혀서 사용할 수 있습니다.
print(a+b)
-> A1B2
# 여기서 주의할 점은 str 끼리 더하면 붙어서 출력되고 int 끼리 더하면 더한 값이 출력되지만,
str 과 int를 더하면 TypeError가 발생됩니다
# format 함수를 활용할수도 있고, f-string(formated string literal)도 가능합니다.
c = 3
print("{} : {}".format(c, a))
-> 3 : A1
print(f"{c} : {a}")
-> 3 : A1
이 외에도 여러가지 출력 방법이 있습니다.
# 빈 자리는 빈공간으로 두고, 오른쪽 정렬을 하되, 총 10자리 공간을 확보
print("{0: >10}".format(500))
# 양수일 땐 앞에 + 표시, 음수일 땐 앞에 - 표시
print("{0: >+10}".format(500))
print("{0: >+10}".format(-500))
# 왼쪽 정렬하고, 나머지 빈 칸은 _으로 채움
print("{0:_<10}".format(500))
# 3자리수 마다 콤마를 찍어주기
print("{0:,}".format(10000000000))
# 3자리수 마다 콤마 & +,-부호도 붙이기
print("{0:+,}".format(10000000000))
print("{0:+,}".format(-10000000000))
# 3자리수 마다 콤마 & +,-부호 & 자릿수 확보한 후 빈 자리는 _로 채우기
print("{0:_<+30,}".format(10000000000))
# 소수점을 특정 자리수까지만 표시
print("{0:.6f}".format(5/3))
output
500
+500
-500
500_______
10,000,000,000
+10,000,000,000
-10,000,000,000
+10,000,000,000_______________
1.666667
[출처 : https://blackhippo.tistory.com/entry/Python-print%EB%AC%B8-%EC%98%A4%EB%A5%B8%EC%AA%BD%EC%99%BC%EC%AA%BD-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0-%EC%9D%80%ED%96%89%EB%B2%88%ED%98%B8%ED%91%9C-%ED%91%9C%ED%98%84%ED%95%98%EA%B8%B0]
class My_class(object):
def method_a(self):
pass
def method_b(self):
for i in range(10):
if i % 2 == 0:
pass
a = 'abc'
b = ['a', 'b', 'c']
c = 242
locals()
-> {..., 'a': 'abc', 'b': ['a', 'b', 'c'], 'c': 242}
# 가독성을 위해 pprint를 사용하면 좋습니다.
import pprint
pprint.pprint(locals())
# 메소드 업데이트
locals()['f'] = c + 5
locals()
-> {..., 'a': 'abc', 'b': ['a', 'b', 'c'], 'c': 242, 'f': 247}
locals()['c'] = 10
locals()
-> {..., 'a': 'abc', 'b': ['a', 'b', 'c'], 'c': 10, 'f': 247}
클래스 메소드 내부의 모든 로컬 변수를 출력해 주기 때문에 디버깅에 많은 도움이 됩니다.
코딩 스타일
좋은 코드의 정답은 없지만 많은 사람이 선호하는 방식이거나 현업에서는 현업에서 사용되는 방식에 맞춰서 사용해야 된다고 합니다.
파이썬의 PEP 8(https://www.python.org/dev/peps/pep-0008/)과 구글의 파이썬 스타일 가이드(https://google.github.io/styleguide/pyguide.html)는 좀 더 실용적인 관점에서 좋은 파이썬 코드를 작성하는데 많은 도움이 됩니다.
특히 파이참(Pycharm) 같은 IDE를 사용하게 되면 PEP 8기준으로 자동으로 경고를 띄어주고 수정해주므로 많은 도움이 될 것입니다.
구글 파이썬 스타일 가이드
이미 파이썬의 공식 가이드인 PEP 8이 있지만, 구글 파이썬 스타일 가이드는 PEP 8에서는 설명하지 않는 좋은 코드를 위한 지침들이 여럿 있습니다.
특히 가독성을 높이기 위한 지침들이 많습니다.
몇가지만 살펴보자면
먼저, 함수의 기본 값으로 "가변 객체(Mutable Object)"를 사용하지 않아야 됩니다.
함수가 객체를 수정하면 기본값이 변경되기 때문입니다.
그렇기 때문에 "불변 객체(Immutable Object)"를 사용해야 되고, None을 명시적으로 할당하는 것도 좋은 방법입니다.
# 잘못된 예시
def My_function(a, b=[]):
...
def My_function(a, b: Mapping = {}):
...
# 좋은 예시
def My_fucntion(a, b=None):
if b is None:
b = []
def My_function(a, b: Optional[Sequence] = None):
if b is None:
b = []
"가변 객체(mutable object)" - list, set, dict
"불변 객체(immutable object)" - int, float, bool, tuple, string, unicode
True, False를 판별할 때는 암시적(Implicit)인 방법을 사용하는 편이 간결하고 가독성이 높습니다.
# 길이가 없다는 말은 값이 없다는 뜻으로 not user로 충분합니다.
if len(users) == 0: -> if not users:
print("no users")
# 정수를 처리할 떄는 암시적으로 거짓 여부를 판별하기보다는 비교 대상이 되는 값을 직접 비교하는 편이 덜 위험합니다.
if orders is not None and not orders: -> orders == 0
self.is_orders()
# 모듈로 연산 결과가 0인 것을 정수로 처리하지 않고 암시적(Implicit) 거짓 여부로 판별하는 것은 위험하기 때문에,
명시적(Explicit)으로 값을 비교하는 편이 좋습니다.
if not order % 10: -> order % 10 == 0
self.group_order()
그 외에도 세미클론으로 줄을 끝내거나 세미클론을 사용하여 같은 줄에 두 문장을 써서도 안됩니다.
줄의 가로길이는 길어서는 안 된다는 암묵적인 약속이 있어 최대 줄 길이는 80자를 넘지 않도록 유의해야 합니다.