본문 바로가기
코딩 연습/코딩배우기

파이썬 실천 기술 #01 - PEP20, 제어흐름(if, for, while, Exception)

by good4me 2021. 5. 18.

goodthings4me.tistory.com

 

효율적 개발로 이끄는 파이썬 실천 기술 - PEP20, 제어흐름(if, for, while, Exception)

(참고용으로 사용할 부분만 간략하게 정리) 

▼ 파이썬 설계 가이드라인 - PEP20: The Zen of Python

import this


[결과]
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

type() - 인수로 전달한 객체의 타입을 반환하는 내장함수

  • 파이썬에서는 변수에 저장하거나 인수 또는 반환값으로 이용할 수 있는 데이터(식의 결과, 함수의 반환값 포함)를 모두 객체라고 함
# 먼저, 대화형 모드에서 None을 평가해도 표시되지 않는다.
# 표시가 필요한 경우 print() 함수를 사용하면 편리하다.
# print() 함수는 None을 반환하는 내장함수지만 대화명 모드에서 None이 표시되지 않아 코드 읽기가 쉽다.
 None
print(None)

[결과]
None
str(None)  # 문자열로 변환

[결과]
'None'
str(print('book'))  # book을 출력하고 None을 리턴하기 때문에 'None' 출력

[결과]
book
'None'
type(1)

[결과]
int
print(type(1))  # <class '타입명> 형식으로 출력하기 위해 print() 사용

[결과]
<class 'float'>
print(type(1 + 1.0))  # int + float => float

[결과]
<class 'float'>
return_value = print('Hello World')
print(type(return_value))  # print()의 반환값 타입은 NoneType

[결과]
Hello World
<class 'NoneType'>

▼ dir() - 인수로 전달한 객체의 속성 목록을 리스트로 반환하는 내장함수

  • 파이썬에서는 객체가 가진 변수나 메서드를 모두 속성(property)이라고 함
  • 객체의 속성은 점(.) 연산자로 참조할 수 있음
  • 식의 결과나 외부에서 정의된 함수의 반환값을 확인할 때 활용
s = 'Hello World'

count = 1
for i in dir(s):  # for 문과 dir() 내장함수를 활용하여 s의 속성(변수, 메서드) 알아보기
    print(i, end=',')
    count += 1
    if count % 7 == 0:
        print()

# 앞뒤에 '__'가 붙은 것은 파이썬이 암묵적으로 이용하는 속성임

[결과]
__add__,__class__,__contains__,__delattr__,__dir__,__doc__,
__eq__,__format__,__ge__,__getattribute__,__getitem__,__getnewargs__,__gt__,
__hash__,__init__,__init_subclass__,__iter__,__le__,__len__,__lt__,
__mod__,__mul__,__ne__,__new__,__reduce__,__reduce_ex__,__repr__,
__rmod__,__rmul__,__setattr__,__sizeof__,__str__,__subclasshook__,capitalize,
casefold,center,count,encode,endswith,expandtabs,find,
format,format_map,index,isalnum,isalpha,isascii,isdecimal,
isdigit,isidentifier,islower,isnumeric,isprintable,isspace,istitle,
isupper,join,ljust,lower,lstrip,maketrans,partition,
replace,rfind,rindex,rjust,rpartition,rsplit,rstrip,
split,splitlines,startswith,strip,swapcase,title,translate,
upper,zfill,
# 점(.)을 연결하여 속성 알아보기
print(s.upper)  # 주소 0x7f3c553ce1b0에 있는 문자열(str)객체의 내장 메서드 upper

[결과]
<built-in method upper of str object at 0x7f3c553ce1b0>
print(s.upper())  # 메서드는 ()를 붙여 실행할 수 있음

[결과]
HELLO WORLD

▼ help() - 인수로 전달한 객체의 도움말 페이지를 표시해주는 내장함수

  • 객체 또는 특정 메서드의 인수와 그 반환값을 확인해보고 싶을 때, dir()로 이름을 찾은 뒤 help()에 전달하면 편리함
import json
#print(help(json))  # 실행 시 내용이 너무 많아 주석 처리

# 속성 목록에서 load와 loads가 헷갈릴 때, dir(json)로 속성 유무를 확인하고
help(json.load) 

[결과]
Help on function load in module json:

load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
    Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
    a JSON document) to a Python object.
    
    ``object_hook`` is an optional function that will be called with the
    result of any object literal decode (a ``dict``). The return value of
    ``object_hook`` will be used instead of the ``dict``. This feature
    can be used to implement custom decoders (e.g. JSON-RPC class hinting).
    
    ``object_pairs_hook`` is an optional function that will be called with the
    result of any object literal decoded with an ordered list of pairs.  The
    return value of ``object_pairs_hook`` will be used instead of the ``dict``.
    This feature can be used to implement custom decoders.  If ``object_hook``
    is also defined, the ``object_pairs_hook`` takes priority.
    
    To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
    kwarg; otherwise ``JSONDecoder`` is used.

good4me.co.kr


▼ 모듈, 패키지, 라이브러리

  • 라이브러리 : 프로그램을 재사용 또는 배포할 수 있는 형식으로 모아 둔 것
  • 모듈 : 파이썬 코드를 기술한 파일(확장자 .py로 된 파일)
  • 패키지 : 모듈을 모아둔 것 (패키지 = 라이브러리)
  • 대부분의 모듈이나 패키지는 파이썬 코드 안에서 import해서 사용  

▼ 변수와 상수

  • 값에 이름을 붙여 재사용할 수 있도록 한 것을 변수라 하는데, 사용자가 정의한 변수는 모두 덮어쓸 수 있다.
  • 그리고 파이썬에서는 값이 바뀌지 않는 상수(constant)를 정의할 수 없다.
  • PEP 8에서 변수는 소문자, 상수는 대문자 사용 권장함

▼ 파이썬에서는 다음의 거짓이 되는 객체 외에는 모두 참으로 평가함

  • None
  • False
  • 숫자 값 타입 0, 0.0, 0j(복소수)
  • 문자열, 리스트, 딕셔너리, 집합 등 컨테이너 객체의 빈 객체
  • 메서드 __bool__()이 False를 반환하는 객체
  • 메서드 __bool__()을 정의하지 않고, 메서드 __len__()이 0을 반환하는 객체

*컨체이너 객체(container object)란, 다른 객체를 엘리먼트로 가지고 있는 객체를 총칭하여 부르는 것임

def first_item(item):
    if len(item) > 0:
        return item[0]
    else:
        return None

print(first_item(['book']))

[결과]
book
print(first_item([]))

[결과]
None
# 빈 컨테이너 객체가 거짓이라는 점을 이용하면,
def first_items(item):
    if item:
        return item[0]
    else:
        return None

print(first_items(['note']))

[결과]
note
print(first_items([]))

[결과]
None

▼ in, not in 연산자

  • 컨테이너 객체에서 이용할 수 있는 편리한 연산자
  • 어떤 엘이먼트가 컨테이너 객체 안에 있는지 판
items = ['book', 'note']
print('book' in items)

[결과]
True
count = {'book': 2, 'note': 10}
print('note' not in count)  # 딕셔너리는 키(key)를 사용해 판정함

[결과]
False

▼ 루프(처리 반복) - for 문, while 문

  • for 문은 리스트 등 이터러블 객체(여러 엘리먼트를 가진 객체)를 이용해 엘리먼트 수만큼 블록 안의 처리 반복
  • while 문은 조건식이 거짓이 될 때까지 블록 안의 처리 반
for i in [1, 2, 3]:
    print(f'변수 i의 값은 {i}')
    
[결과]
변수 i의 값은 1
변수 i의 값은 2
변수 i의 값은 3
for i in range(3):
    print(f'변수 {i}번째 처리')

[결과]
변수 0번째 처리
변수 1번째 처리
변수 2번째 처리
for n, char in enumerate('word'):
    print(f'{n}번째 문자는 {char}')

[결과]
0번째 문자는 w
1번째 문자는 o
2번째 문자는 r
3번째 문자는 d
n = 0
while n < 3:
    print(f'변수 n의 값은 {n}')
    n += 1

[결과]
변수 n의 값은 0
변수 n의 값은 1
변수 n의 값은 2
n = 1
while True:
    if n % 2 == 0:
        print('{} / 2 = {}'.format(n, n/2))
    n += 1
    if n == 10:
        break

[결과]
2 / 2 = 1.0
4 / 2 = 2.0
6 / 2 = 3.0
8 / 2 = 4.0

▼ for 문에서 변수의 스코프

  • 파이썬은 for 문의 변수의 스코프를 블록 안으로 한정하지 않기 때문에 for 문 뒤에서 같은 이름의 변수를 이용할 때 주의해야 함
for m in range(3):
    pass

print(f'm의 값은 {m}')  # for 문을 벗어나 m의 값 사용

for m in range(1):  # m의 값이 변경됨
    pass

print(f'm의 값은 {m}')

[결과]
m의 값은 2
m의 값은 0

▼ 왈러스 연산자(Walrus operator) ':='

  • 파이썬 3.8에서 추가된 대입 연산자(:=)
  • if 문이나 루프의 조건식 안 등에서 '변수에 값을 대입'할 수 있는 연산자
  • 식을 사용할 수 있는 위치 어디서나 사용 가능
import random

def lottery1(goods):
    item = random.choice(goods)
    if item:
        return item
    else:
        return 'Miss!!'

books = ['notebook', 'sketchbook', None, None]
result = lottery1(books)
print(result)

# random.choice()로 인해 실행할 때마다 result 값이 달라짐

[결과]
Miss!!


# 왈러스 연산자로 아래과 같이 if 문 조건식에서 대입한 값을 이용할 수 있음
def lottery2(goods):
    if item := random.choice(goods):
        return item
    else:
        return 'Miss!!'

books = ['notebook', 'sketchbook', None, None]
result = lottery2(books)
print(result)

[결과]
sketchbook

▼ 예외처리

  • 파이썬은 예외 발생으로 프로그램 강제 종료 시 예외정보와 함께 트레이스백(Traceback)이라 불리는 예외 발생 위치 정보를 출력함
  • 파이썬 예외 처리문 : try ~ except ~ else ~ finally
  • try 문 : 예외 포착
  • except 절 : 예외 발생 시 실행, 예외 종류 구분, as 키워드를 이용해 발생한 예외 객체를 except 절의 블록에서 이용 가능
  • else 절 : 예외가 발생하지 않았을 때만 실행
  • finally 절 : 예외 발생 여부와 관계없이 반드시 실행(클린업 처리)
def get_book(index):
    items = ['note', 'notebook', 'sketchbook']
    try:
        return items[index]
    except IndexError:
        print(f'예외 발생')
        return '인덱스 범위 밖입니다'

result = get_book(3)
print(result)

[결과]
예외 발생
인덱스 범위 밖입니다
def get_book(index):
    items = ['note', 'notebook', 'sketchbook']
    try:
        return items[index]
    except IndexError as e:
        print(f'예외 발생: {e}')  # as 키워드 활용
        return '인덱스 범위 밖입니다'

result = get_book(3)
print(result)

[결과]
예외 발생: list index out of range
인덱스 범위 밖입니다
# 예외 종류에 대해 except 절을 여러 번 기술, 이때 가장 처음으로 일치한 except 절 실행

def get_book(index):
    items = ['note', 'notebook', 'sketchbook']
    try:
        return items[index]
    except IndexError as e:
        print(f'인덱스 예외 발생')  # as 키워드 e 사용하지 않아도 됨
        return '범위 밖입니다'
    except TypeError as e:
        print(f'타입 예외 발생: {e}')  # as 키워드 활용
        return '타입 에러입니다'

result = get_book('3')
print(result)

[결과]
타입 예외 발생: list indices must be integers or slices, not str
타입 에러입니다
def get_book(index):
    items = ['note', 'notebook', 'sketchbook']
    try:
        return items[index]
    except (IndexError, TypeError) as e:
        print(f'예외 발생: {e}')
        return '인덱스 또는 타입 에러'

result = get_book('3')
print(result)

[결과]
예외 발생: list indices must be integers or slices, not str
인덱스 또는 타입 에러
# else 절은 try 문으로 보호하지 않아도 되는 코드들임
def get_book(index):
    items = ['note', 'notebook', 'sketchbook']
    try:
        book = str(items[index])
    except (IndexError, TypeError) as e:
        print(f'예외 발생: {e}')
    else:
        return book.upper()  # upper()는 str 타입이 반드시 가지고 있는 메서드

result = get_book(2)
print(result)

[결과]
SKETCHBOOK
# finally 절
from io import UnsupportedOperation
f = open('some.txt', 'w')
try:
    f.read()  # 없는 파일 읽기
except UnsupportedOperation as e:
    print(f'Exception: {e}')
finally:
    print('파일을 닫습니다.')
    f.close()
# 파일 읽기, 쓰기 후 파일 닫기
# finally 절은 except 절 없이도 이용할 수 있으나 except 절이 없으면 
# finally 절 실행 후 try 문 결과가 전송되어 프로그램이 중단될 수 있음 

[결과]
Exception: not readable
파일을 닫습니다.

▼ raise 문 : 의도적 예외(Exception) 발생시킴

  • raise 예외종류('에러메시지')
  • raise 문에는 예외 클래스의 클래스 객체 또는 그 인스턴스를 인수로 전달함 (클래스 객체 전달 시 그 클래스 객체에서 인스턴스가 발생함)
  • raise 문 이하 코드는 실행되지 않고 except 절로 넘어감
  • except 절에서 인수없이 raise 문 이용 가능한데, 이 경우 except 절의 예외를 그대로 재전송함 (예외 로그만 필요한 경우 등에 이용 가능)
def raise_ex(n):
    try:
        if n % 5 != 0:
            raise Exception('5의 배수 아님')  # 에러 메시지는 except 절로 넘어감
            print(f'입력 값 : {n}')  # 실행 안됨
    except Exception as e:
        print(f'예외 발생: {e}')
    finally:
        print(f'{n} / 5')
        return ''

print(raise_ex(11))

[결과]
예외 발생: 5의 배수 아님
11 / 5
# 함수 내에 예외 처리 구문(try ~ except) 없이 raise 사용 후 다른 부분에서 예외 처리
def raise_ex(n):
    if n >= 2 and n ** 10 > 1024:
        raise Exception('argument 2 아님')  # 예외를 호출한 곳으로 넘김
    print(f'argument {n}')  # if 문이 실행되면 실행 안됨

try:
    raise_ex(3)
    print('1 or 2 입력')  # raise 문 실행되면 실행 안됨
except Exception as e:  # 
    print(f'예외 발생: {e}')
    
[결과]
예외 발생: argument 2 아님

▼ 사용자 예외 정의

  • Exception 클래스를 상속해 새로운 예외 정의
  • 여러 예외를 전송할 모듈을 만들 때는 예외 베이스 클래스를 만들고, 각각의 예외별로 그 클래스를 상속해서 예외를 만든다
  • 예외 만들 시 init 메서드에 부모 클래스의 init 메서드를 호출하면서 에러메시지를 넣어준다
class PracticeError(Exception):
    '''모듈 고유 베이스 클래스'''

class NotFoundError(PracticeError):
    def __init__(self, message):
        self.message = message

def practice(n):
    try:
        if n not in [1, 2, 3, 4, 5]:
            raise NotFoundError('숫자 없음')
    except NotFoundError as e:
        print(f'예외 발생: {e}')
    finally:
        print(f'입력 숫자: {n}')
        return ''

print(practice(6))

[결과]
예외 발생: 숫자 없음
입력 숫자: 6
# Excenption 클래스 사용
class NotFoundError(Exception):
    def __init__(self, message):
        super().__init__(message)

def practice(n):
    try:
        if n not in [1, 2, 3, 4, 5]:
            raise NotFoundError('입력 숫자 없음')
    except NotFoundError as e:
        print(f'예외 발생: {e}')
    finally:
        print(f'입력 숫자: {n}')
        return ''

print(practice(7))

[결과]
예외 발생: 입력 숫자 없음
입력 숫자: 7

 

[참고/출처] 효율적 개발로 이끄는 파이썬 실천 기술 - 파이썬에 숨겨진 힘을 이용해 개발 효율을 높이자!

 

 

댓글