[Effective Python] 1. Pythonic Thinking

Stop._.bmin·2023년 1월 27일
0

Effective-python

목록 보기
1/5
post-custom-banner

내용이 상당히 뒤로갈 수록 이해하기가 어려워서 관련 깃허브를 참고하며 학습하였습니다.

Item 1: Know Which Version of Python You're Using

파이썬 버전

주로 Python3open-source libraries 와 가장 호환성이 좋다. 그래서 필자는 Python3 을 적극 추천한다.

  • 우리 책에서 예제로 다룬 버전은 3.7 이다. 3.8 도 일부 있다.
    아래 명령을 통해서 파이썬의 버전을 확인할 수 있다.
$ python --version

sys모듈을 통해 버전 확인하기

아래의 코드를 통해 확인할 수 있다.

import sys
print(sys.version)

또한 아래의 코드를 통해서 major, minor, micro 버전을 확인할 수 있는데

import sys

sys.version_info

sys.version_info.major
sys.version_info.minor
sys.version_info.micro


>>> sys.version_info(major=3, minor=5, micro=2, releaselevel='final', serial=0)
>>> 3
>>> 5
>>> 2
  • 3은 major
  • 5는 minor
  • 2는 micro

1편 요약:

  • 파이썬 3을 강추

Item2: Follow the PEP 8 Style Guide

What is PEP?

Python Enhancement Proposal == 파이썬 개선 제안서
파이썬 코드를 어떻게 구성할지 알려주는 스타일 가이드 이다.
책에서 일부 설명한 rules가 있다.

PEP 1. Whitespace (공백)

  • tabs 대신에 spaces로 들여쓴다.
  • 4개의 spaces로 문법적으로 의미 있는 수준마다 들여쓰기를 한다.
  • 한 줄에 79 문자 내외로 한다
  • 긴 문장은 다음 줄로 이어질 경우 4개의 space를 들여쓴다
  • 파일에서 함수와 클래스는 2줄로 구분되어야한다
  • 딕셔너리에는 각 key와 colon과 붙여써야하며, value값은 같은 열에 하나의 space를 두고 써야한다.
  • = 연산자 앞 뒤에 space 해준다.
  • 주석에 벨류 이름과 콜론 사이에 띄어쓰지 않아야하며 타입 정보 전에 띄어쓰기를 해준다.

PEP 2. Naming (명명)

PEP 8 에서는 이것 또한 특별한 스타일로 추천하고 있다.

  • 함수 이름, 변수이름은 lowercase_underscore형식을 지킨다
  • 보호 인스턴스 속성은 _leading_underscore 형식을 지킨다
  • 비공개 인스턴스 속성은 _double_leading_underscoreg형식을 지킨다.
  • 클래스와 예외는 CapitalizedWord 형식을 지킨다.
  • 모듈 수준 상수는 ALL_CAPS 형식을 지킨다.
  • 클래스 인스턴스 메소드의 첫 번째 파라미터는 self로 한다
  • 클래스 메서드에서는 첫 번째 파라미터를 cls로 한다.

PEP 3. Expressions and Statements (표현식과 문장)

"There should be one-and preferably only one-obivous way to do it"

  • 인라인 부정을 사용하자. (if a is not b를 사용하자 if not a is b 대신)
  • 빈 값을 확인할 때 len 대신에 if not ~ : 를 활용하자
  • 한 줄의 if문이나 while 루프를 피하자.

PEP 4. Import

  • 항상 import 문은 가장 위에 배치한다.
  • absolute이름으로 import 한다.
  • 잘 드러나는 표현으로 import 한다.
  • 임포트는 '표준 라이브러리의 모듈, 서드파티 모듈, 자신이 만든 모듈' 섹션 순으로 구분해야 한다. 각각의 하위 섹션에서는 알파벳 순서로 임포트한다.

Item 3: Know the Differences Between bytes and str

바이트 vs 문자열

  • ASCII encoding
    만약 CP949로 파일을 처리할 수 있는데 클라이언트가 UTF-8로 본내 경우 중간에 binary stream으로 변환하여 그 bytes를 다시 cp949로 인코딩해야한다. (이거 교양 프로그래밍 시간에 이유는 못듣고 그냥 안되면 이렇게하라고 말씀하셨었다)
  • 수많은 운영체제를 활용한다면 서로 다른 인코딩 문자의 입력을 해결해야 할 경우가 있다.

이때 helper functions 을 통해서 convert한다.

def to_str(bytes_or_str):
	if isinstance(bytes_or_str, bytes):
		values = bytes_or_str, bytes):
    else:
    	value = bytes_or_str
    return value
def to_bytes(bytes_or_str):
	if isintance(bytes_or_str, str):
    	value = bytes_or_str.encoude('utf-8')
    else:
    	value = bytes_or_str
    return value

bytes 인스턴스

  • 바이트는 문자열 앞에 b를 붙여야 한다.

Item 4: Prefer Interpolated F-Strings Over C-style Format Strings and str.format

  • % 연산자를 통해서 사용하는 게 흔하다.
    • 이것이 C-style Format string인데 필자는 이것의 4가지 문제점을 제시한다.

The format Built-in and str.format

  • Python 3 에서는 advanced string formatting을 지원하는데 이것이 c-style보다 더 expressive하다.
    아래의 예제 코드를 참고
a = 1234,5678
formatted = format(a, ',.2f')
print(formatted)

b = 'my string'
formatted =format(bm '^20s')
print('*', formatted, '*)

>>>
1,234.57
*	my string	*
key = 'my_var'
formatted = '{:<10} = {:.2f}'.format(key,value)

>>>
my_var = 1.23

Item 5: Write Helper Functions Instead of Complex Expressions

  • 파이썬으로 한 줄의 간결한 문장을 통해서 많은 로직을 쉽게 설명하자.
  • 반환값이 none이라면 아래의 코드를 통해서 bool로 처리할 수 있다.
red = my_values.get('red', [''])[0] or 0
green = my_values.get('green', [''])[0] or 0
opacity = my_values.get('opacity', [''])[0] or 0

print('Red     :     %r' % red)
print('Green   :     %r' % green)
print('Opacity :     %r' % opacity)

Red     :     '5'
Green   :     0
Opacity :     0
  • 그러나 위의 코드는 가독성이 떨어지고, 이 로직을 재사용해야한다. 이럴 때는 아래의 helper function을 활용하여 작성하도록 한다
def get_first_int(values, key, defalut = 0):
	found = values.get(key,[''])
    if found[0]:
    	return int(found[0])
    return defalut

Item 6: Prefer Multiple Assignment Unpacking Over Indexing

  • 파이썬에서 tuple은 immutable하다. 또한 oprdered sequences of values하다.
  • 한 쌍의 경우 keys와 values 가 되는 것이다. 아래의 코드 참고
snack_calories = {
	'chips': 140,
    'popcorn': 80,
    'nuts': 190,
}
items = tuple(snack_calories.items())
print(items)

>>>
(('chips', 140), ('popcorn', 80), ('nuts), 190))
  • bubble sort를 구현한 코드도 있었다. 이를 기반으로 해서 배열을 한 후에 f-string을 활용하여 메뉴의 이름과 가격을 출력하는 방법을 소개하고 있다.
  • enumerate를 활용하는 것도 필자는 선호한다 1학기 컴프입 떄 조환규 교수님도 enumerate 애용하셨다
for rank, (name,calories) in enumerate(snacks, 1):
	print(f'#{rank}: {name} has {calories} calories')

Item 7: Prefer enumerate Over range

range보다 enumerate를 애용하자

item 6번과 비슷한 맥락같아보인다.
range문 대신에 enumerate를 활용하자..

from random import randint

random_bit = 0
for i in range(64):
    if randint(0, 1):
        random_bit |= (1 << i)

print(random_bit)

>>> 13250328546979216565
  • 아래의 코드는 iterate over 한 data구조가 있을 때 직접 루프를 실행하는 것
flavor_list = ['vanilla', 'chocolate', 'pecan', 'strawberry']

for flavor in flavor_list:
    print('{} is delicious'.format(flavor))

>>> vanilla is delicious
>>> chocolate is delicious
>>> pecan is delicious
>>> strawberry is delicious
  • 아래의 코드는 range를 통해서 자신이 좋아하는 아이스크림 맛 순위를...
for i in range(len(flavor_list)):
    flavor = flavor_list[i]
    print(f'{ i + 1}: {flavor}')

>>> 1 : vanilla
>>> 2 : chocolate
>>> 3 : pecan
>>> 4 : strawberry
  • 아래 코드는 필자가 좋아하는 enumerate를 활용한 것이다. much clearer하다고 강조한다.
for i, flavor in enumerate(flavor_list):
    print('{index} : {flavor}'.format(
        index=i + 1,
        flavor=flavor))

>>> 1 : vanilla
>>> 2 : chocolate
>>> 3 : pecan
>>> 4 : strawberry

Item 8: Use zip to Process Iterators in Parallel

  • 기컴프에서 잠시 간단히 다룬 내용이다.
names = ['Park', 'Lee', 'Kim']
letters = [len(name) for name in names]
  • 리스트의 아이템과 소스 리스트의 아이템은 서로 인덱스로 연관이 되어있다. zip은 lazy generator로 iternable 2개 이상을 감싸기에 명료하다.
    • 근데 만약 input iterators가 각자 다른 길이라면, 예상치못한 결과를 낼 수 있다.

Item 9: Avoid else Blocks After for and while Loops

1. for 문에서의 else

파이썬의 루프에는 추가적인 기능이 있는데 루프에서 반복되는 내부 블록 다음에 else블록을 쓸 수 있다

  • if/else 에서 else문은 이전 구문이 실행되지 않으면 실행된다는 의미인데, 파이썬에서는 break문을 사용해야 else 블록을 건너뛸 수 있다고 한다.
    • 아래 코드에서 break문을 만나서 else 블록이 실행되지 않음을 확인할 수 있다.
for i in range(3):
    print('Loop {}'.format(i))
    if i == 1:
        break
else:
    print('Else block!')
    
>>> Loop 0
>>> Loop 1
for x in []:
    print('Never runs')

else:
    print('Not else block!')
    
>>> Not else block!
while False:
    print('Never runs')
else:
    print('While else block!')

>>> While else block!

Item 10: Prevent Repetition with Assignment Expression

  • walrus operator 은 python 3.8에 나온 새로운 syntax다. a := b 꼴로 활용되며 "a walrus b"라고 해석한다.

이제 아래의 코드를 통해서 어떻게 쓰이는지 .. 확인할 수 있다

def make_lemonade(count):
	...
 
 def out_of_stock():
 	...

count = fresh_fruit.get('lemon',0)
if count:
	make_lemonade(count)
else:
	out_of_stock()

if count  := fresh_fuit.get('lemon',0):
	make_lemonade(count)
else:
	out_of_stock()
bottles = []
while fresh_fruit := pick_fruit():
	for fruit, count in fresh_fruit.items():
    batch = make_juice(fruitm count)
	bottles.extend(batch)
profile
원하는 만큼만
post-custom-banner

0개의 댓글