Beyond the Basic Stuff with Python [Chapter 06]
Chapter 06 - WRITING PYTHONIC CODE [파이썬스러운 코드 작성히기]
개인의 관점에 따라 다르지만 python다운 코드를 작성하는 일반적인 방법을 제시합니다.
6.1_ Commonly Misused Syntax [일반적으로 잘못 사용되는 구문]
-
range()보다는 enumerate()를 사용하자
animals = ['cat', 'dog', 'moose'] for i in range(len(animals)): print(i, animals[i]) > 0 cat > 1 dog > 2 moose
# Pythonic Example animals = ['cat', 'dog', 'moose'] for i, animal in enumerate(animals): print(i, animal) > 0 cat > 1 dog > 2 moose
index를 출력하거나 사용하기 위해 range(len())을 사용하는 것보다 enumerate가 더 간단히다.
-
open() 및 close() 대신 with문 사용하기
# Unpythonic Example try: fileObj = open('spam.txt', 'w') eggs = 42 / 0 # A zero divide error happens here. fileObj.close() # This line never runs. except: print('Some error occurred.') > Some error occurred.
eggs에서 에러가 생기고 close()가 실행되지 않은 상태로 끝나 파일은 열린 상태로 남아 있게 되고, 파일 손상 버그가 발생하면 try블록까지 추적하기 어렵다.
# Pythonic Example with open('spam.txt', 'w') as fileObj: fileObj.write('Hello, world!')
다음과 같이 with문으로 작성하면, 코드를 벗어날 경우 자동으로 close()를 호출한다.
-
None과 비교할 때 == 대신 is를 사용하자
class SomeClass: def __eq__(self, other): if other is None: return True spam = SomeClass() spam == None > True spam is None > False
==는 두 개체의 값을 비교하지만, is는 두 개체의 ID를 비교한다.
예시 코드처럼 클래스가 == 연산자를 오버로드할 가능성은 희박하지만, 만약을 대비해서== None
보다는is None
을 사용해야 한다.
6.2_ Formatting Strings [문자열 포매팅]
-
문자열에
\
가 많은 경우에는 원시 문자열 사용하기# Unpythonic Example print('The file is in C:\\Users\\Al\\Desktop\\Info\\Archive\\Spam') > The file is in C:\Users\Al\Desktop\Info\Archive\Spam
# Pythonic Example print(r'The file is in C:\Users\Al\Desktop\Info\Archive\Spam') > The file is in C:\Users\Al\Desktop\Info\Archive\Spam
아래의 예시와 같이 원시 문자열을 사용할 때 이스케이프 처리를 간단하게 할 수 있다.
-
f-strings을 통한 문자열 포매팅
원시 문자열 처리에 r이 붙는 것처럼 f가 붙는다.
name, day, weather = 'Al', 'Sunday', 'sunny' f'Hello, {name}. Today is {day} and it is {weather}.' > 'Hello, Al. Today is Sunday and it is sunny.'
width, length = 10, 12 f'A {width} by {length} room has an area of {width * length}.' > 'A 10 by 12 room has an area of 120.'
위의 2가지 예시와 같이 변수를 간단하게 삽입하여 출력 할 수 있다.
spam = 42 f'This prints the value in spam: {spam}' > 'This prints the value in spam: 42' f'This prints literal curly braces: ' > 'This prints literal curly braces: {spam}'
내부에 중괄호를 사용해야 할 경우에는 중괄호를 추가로 사용하면 된다.
6.3_ Making Shallow Copies of Lists [리스트 얕은 복사본 만들기]
spam = ['cat', 'dog', 'rat']
eggs = spam
print(id(spam) == id(eggs))
> True
eggs.append('eggs')
print(eggs, spam)
> ['cat', 'dog', 'rat', 'eggs'] ['cat', 'dog', 'rat', 'eggs']
spam
리스트를eggs
변수에 할당할 때, 실제로 새로운 리스트 객체를 생성하는 것이 아니라,eggs
변수도spam
변수가 가리키고 있는 같은 리스트 객체를 참조하게 된다. 따라서spam
과eggs
는 동일한 객체를 가리키게 되므로id(spam)
과id(eggs)
의 결과가 같게 되고, 둘 중 하나의 리스트에 변화가 생기면 동일한 객체를 가리키게 되면서 연동이 된것처럼 작동하게 된다. 이처럼 의도하지 않은 변화를 예방하기 위해 리스트를 복사 할 때, 주의해야한다.
spam = ['cat', 'dog', 'rat', 'eel']
eggs = spam[:]
print(id(spam) == id(eggs))
> False
리스트를 다음과 같이 shallow copy(얕은 복사)를 하게 되면 서로 다른 리스트 객체를 참조하게 된다.
# Pythonic Example
import copy
spam = ['cat', 'dog', 'rat', 'eel']
eggs = copy.copy(spam)
print(id(spam) == id(eggs))
> False
다음과 같이 copy 모듈의 copy() 함수를 사용하는
[:]
보다 가독성이 좋다.
6.4_ Pythonic Ways to Use Dictionaries [파이썬다운 딕셔너리 사용]
-
딕셔너리에서는 get()과 setdefault()를 사용
- get()
# Unpythonic Example numberOfPets = {'dogs': 2} if 'cats' in numberOfPets: # Check if 'cats' exists as a key. print('I have', numberOfPets['cats'], 'cats.') else: print('I have 0 cats.') > I have 0 cats.
# Pythonic Example numberOfPets = {'dogs': 2} print('I have', numberOfPets.get('cats', 0), 'cats.') > I have 0 cats.
딕셔너리에 get() 메소드로 접슨하면 키가 없을 때 지정한 기본값을 반환하도록 할 수 있다.
- setdefault()
# Unpythonic Example numberOfPets = {'dogs': 2} if 'cats' not in numberOfPets: numberOfPets['cats'] = 0 numberOfPets['cats'] += 10 print(numberOfPets['cats']) > 10
# Pythonic Example numberOfPets = {'dogs': 2} numberOfPets.setdefault('cats', 0) # Does nothing if 'cats' exists. > 0 numberOfPets['cats'] += 10 print(numberOfPets['cats']) > 10
위의 조건문 대신 setdefault() 메소드를 통해 기본값을 지정할 수 있다.
-
기본 값을 위해서 collections.defaultdict을 사용
import collections scores = collections.defaultdict(int) print(scores) > defaultdict(<class 'int'>, {}) scores['Al'] += 1 # No need to set a value for the 'Al' key first. print(scores) > defaultdict(<class 'int'>, {'Al': 1}) print(scores['Zophie']) # No need to set a value for the 'Zophie' key first. > 0 scores['Zophie'] += 40 print(scores) > defaultdict(<class 'int'>, {'Al': 1, 'Zophie': 40})
collections.default 클래스를 사용하여 KeyError를 방지할 수 있다.
아래처럼 int 타입을 지정하면 초기값이 0으로 설정된다.import collections booksReadBy = collections.defaultdict(list) booksReadBy['Al'].append('Oryx and Crake') booksReadBy['Al'].append('American Gods') print(len(booksReadBy['Al'])) > 2 print(len(booksReadBy['Zophie'])) # The default value is an empty list. > 0
다음과 같이 빈 리스트를 초기값으로 설정할 수 있다.
-
switch 문 대신 딕셔너리 사용
# All of the following if and elif conditions have "season ==": if season == 'Winter': holiday = 'New Year\'s Day' elif season == 'Spring': holiday = 'May Day' elif season == 'Summer': holiday = 'Juneteenth' elif season == 'Fall': holiday = 'Halloween' else: holiday = 'Personal day off'
holiday = {'Winter': 'New Year\'s Day', 'Spring': 'May Day', 'Summer': 'Juneteenth', 'Fall': 'Halloween'}.get(season, 'Personal day off')
switch 문 대신 딕셔너리를 사용하여 코드를 간결하게 작성할 수 있다.
get() 메소드를 통해 key가 존재하지 않으면'Personal day off'
를 반환한다.
하지만 switch 문 대신 딕셔너리를 사용할 경우 가독성이 떨어질 수 있어, 사용 여부는 사용자에게 달려있다.
6.5_ 변수값 작업
-
체이닝 할당과 비교 연산자
# Unpythonic Example if 42 < spam and spam < 99: # Pythonic Example if 42 < spam < 99:
파이썬에는 chaining 비교 연산자가 있기 때문에 and를 사용할 필요가 없다.
# Pythonic Example spam = eggs = bacon = 'string' print(spam, eggs, bacon) > string string string # Pythonic Example spam = eggs = bacon = 'string' print(spam == eggs == bacon == 'string') > True
=
할당 연산자도 체이닝이 가능하다. 또한==
비교 연산자도 체이닝 형식으로 사용할 수 있다. -
변수가 여러 값 중 하나인지 확인하기
# Pythonic Example spam = 'cat' print(spam in ('cat', 'dog', 'moose')) > True
spam == 'cat' or spam == 'dog' or spam == 'moose'
같은 기능을 간단하게 구현할 수 있다.
Reference
[Beyond the Basic Stuff with Python_Al Sweigart] - https://inventwithpython.com/beyond/
[Chapter 6 - Choosing Understandable Names] - https://inventwithpython.com/beyond/chapter6.html
CC License - [CC BY-NC-SA 3.0]
Translator - ChatGPT
댓글남기기