Skip to content

Latest commit

 

History

History
411 lines (349 loc) · 14.3 KB

211103.md

File metadata and controls

411 lines (349 loc) · 14.3 KB

Day 33

한 권으로 개발자가 원하던 파이썬 심화 A to Z

멀티 라인문

a = 100 +\
    200 +\
    300

b = (100 +
    200 + 
    300)

튜플, 리스트 등을 리터럴 표기법으로 작성할 때는 닫는 괄호를 만날 때까지는 한 문장이 끝나지 않은 것으로 인식하고 다음줄에 있는 내용을 자동으로 확인한다.

t = (1, 2, 3, 4,
    5)

l = [1, 2, 3, 4,
    5]

여러줄에 문자열을 작성할 때는 따옴표 3개 사용

s = """문자열
Hi
"""

딕셔너리나 집합을 리터럴 표기법으로 사용할 때는 닫는 괄호를 만나야 완성된 문장으로 인식한다.

d = {'a' : 10,
    'b' : 20}

st = {1, 2, 3,
    4}

여러 문장을 한 줄에 작성

세미 콜론으로 문장을 구분해준다.

a = 10; b = 20; c = 30;

반복문과 제어문도 한줄에 문장을 연결해서 작성할 수 있다.

for i in range(3): print(i); print("Hi"); # print(i), print("Hi") 두 문장 모두 반복된다.

if a < b: print(c); print("Hi");

제어문의 경우 else문을 사용하면 두 문장의 구분이 필요해서 다른 줄에 작성해야 한다.

if (a > b): print(c);
else: print("Hi");

특정 숫자를 임의로 추출하기

내부에 수식을 작성해서 간단하게 표시하는 방식을 컴프리헨션이라고 한다.

a = [x for x in range(1, 46)] # 1~45까지의 수로 이루어진 list가 생긴다.

random 모듈은 임의의 확률 분포를 처리하는 모듈이다.

import random
random.shuffle(a) # 여러 원소를 가진 객체를 인자로 받아 내부 원소를 무작위로 섞어준다.
random.choices(a, k=6) # 특정 개수의 원소를 추출해준다.

suffle : 리스트의 모든 원소를 임의로 섞을 때 사용한다.
choices : 특정 원소를 임의로 선택한다.

파이썬의 random 모듈은 한 번 나온 값이 다시 나올 수 있어 numpy 모듈로 random 함수를 전달해서 처리할 수 있다.

import numpy as np
random.shuffle(a, random=np.random.random)
random.choices(a, k=6)

# 직접 numpy 모듈의 shuffle 함수를 사용해 처리하는 것도 가능하다.
np.random.shuffle(a)
random.choices(a, k=6)

함수와 클래스의 문서화

모듈의 문서화는 속성 __doc__에 저장해서 이를 다른 변수에 할당한 후에 print로 출력하면 간단한 설명을 볼 수 있다.

import math

a = math.__doc__ # 모듈 내 독스트링을 관리하는 __doc__을 변수에 할당
print(a)

주피터 노트북에서도 %%writefile 명령어로 모듈을 만들 수 있다.

%%writefile add.py
""" 이 모듈에는 덧셈을 계산하는 함수를 제공합니다. """

def add(x, y):
    retrun x + y

이렇게 작성한 모듈의 문서화 정보를 가져올 수 있다.

import add
b = add.__doc__
print(b) # "이 모듈에는 덧셈을 계산하는 함수를 제공합니다." 문장이 출력이 된다.

문서화할 때 첫 번째 문자열만 문서화로 들어간다. 따라서 문서화는 항상 첫 번째에 작성해야 한다.

def func(x, y):
    """ 첫 번째 매개변수 체크 """
    if not isinstance(x, int):
        raise TypeError(" x 정수가 아님")

    """ 두 번째 매개변수 체크 """
    if not isinstance(y, int):
        raise TypeError(" y 정수가 아님 ")

    return x + y

func.__doc__ # "첫 번째 매개변수 체크" 문장이 출력

주석

주석은 문서화 처리가 되지 않는다.

%%writefile test.py
# 테스트 처리 모듈입니다.

def test(n):
    print(n)

test.__doc__ # 아무 것도 출력되지 않는다.

주피터 노트북에서 모듈을 로딩해서 편집하려면 %load 명령어를 사용하면 된다.

%load test.py

def test(n):
    print(n + 1)

변수에 타입 힌트 사용

파이썬에도 변수에 특정 자료형을 타입으로 지정하는 방식을 제공하지만 변수를 정의하는 것이 아닌 변수에 관한 주석을 추가하는 기능이다.

타입 힌트 사용

x : int

try :
    x
except Exception as e:
    print(e)

"name 'x' is not defiend" 에러가 출력이 된다. 타입 힌트는 변수 할당이 아니라 변수 이름에 주석을 표시하는 것이기 때문에 실제 변수 x가 정의되지 않았기 때문이다.

함수 내부에서도 타입 힌트를 지정할 수 있다.

def add(x, y):
    z : int     # 변수 z에 저장된 자료형은 int라고 타입 힌트를 작성
    z = x + y   # 변수 z에 x, y를 더해서 할당
    return z

__annotations__ 속성은 매개변수 타입 힌트를 보관한다. 단, 함수 내부의 타입 힌트는 외부에서 볼 수 없다

add.__annotations__ # 이 함수의 매개변수에 타입 힌트를 지정하지 않아서 아무 것도 없다.

변수에 값을 할당하는 문장

파이썬은 변수를 정의하면 이름공간에 변수의 이름을 key로 만들고 객체를 값으로 저장한다.

모듈에는 변수를 관리하는 전역 이름공간이 만들어지는데, 이 전역 이름 공간을 globals 함수로 조회하면 딕셔너리로 관리하는 전역 이름공간이 표시된다. 검색은 변수 이름, 함수 이름 등이 가능하다.

var = 100
globals()['var'] # 100

def func(x, y):
    return x, y
globals()['func'] # <function __main__.func(x, y)>

# 둘의 처리 방식은 같음
globals()['func'](5, 5) # (5, 5) 출력
func(5, 5) # (5, 5) 출력

클래스를 정의해도 함수와 같은 방식으로 클래스 이름이 변수로 사용되고 클래스 객체가 값으로 할당된다.

class K: # 클래스 정의
    def __init__(self, name): # 매개변수로 self와 name을 정의
        self.name = name # 클래스 이름을 변수로 클래스 객체를 전역 이름공간에 저장.

globals()['K'] # __main__.K, 클래스가 출력된다. 

__init__() : 초기화 메소드(인스턴스를 만들 때 항상 실행된다.)

클래스 객체를 전역 이름공간에서 검색한 후에 바로 실행도 가능하다.

k = globals()['K']("name") # 전역 이름공간에서 조회한 클래스로 객체를 생성
k.__dict__ # 객체도 속성을 관리하는 이름공간 __dict__가 있다.

#{'name': 'name'} 출력, __init__에 정의한 속성이다.

객체의 이름공간 __dict__를 검색하면 속성명 name은 key로, 인자로 전달된 문자열 "name"은 값으로 저장된다.

여러 값을 여러 변수에 할당하는 것도 가능하다.
하지만 여러 개의 변수에 리스트나 문자열을 할당하려면 원소의 개수가 같아야 일대일 매핑이 된다.

a, b, c = [1, 2, 3]
a, b, c # (1, 2, 3) 출력

d, e, f = "가나다"
d, e, f # ('가', '나', '다') 출력

변수의 개수와 튜플 원소 개수가 일치하지 않는 경우, 변수 이름 앞에 별표를 붙여 여러 개의 원소를 받을 수 있다는 것을 표시해줘야 한다.
이 처리를 하면 별표 붙은 변수에 일대일 매핑된 결과를 제외한 나머지 모든 원소를 가진 새로운 리스트 객체가 저장된다.

x, *y = (1, 2, 3)
x, y # (1, [2, 3]) 출력, x에 1이 저장되고, 남은 2와 3을 가진 리스트를 생성해 y에 저장된 것이다.

두 변수의 값을 일대일로 교환하는 경우는 바로 변수의 이름 순서를 변경해서 처리하면 된다.
이게 가능한 이유는 튜플은 쉼표로 구분하여 앞과 뒤가 다 튜플 객체로 변경되는 구조이기 때문이다.

x, y = y, x # 변수 간 값을 이동하는 스와핑 처리는 변경되는 변수와 할당하는 변수의 위치를 바꿀 수 있다.
x, y # ([2, 3], 1) 출력

tuple : list와 비슷하지만, 각 원소를 수정하거나 삭제할 수 없다.

pass

for i in range(5):
    pass # 아무 결과도 반환하지 않지만, 이 부분이 없으면 문장이 완성되지 않아서 예외가 발생한다.

_ 사용

변수의 이름을 지정하지 않고 _를 사용해 변수 대신 처리하는 것이 가능하다. 이 변수는 실제 사용하지 않는다는 표시이다.

for _ in range(2):
    print("Hi")

map 클래스

map 클래스는 여러 원소를 가진 리스트 등을 전달받아서 각 원소의 값을 변형할 때 사용한다.
map 클래스는 전달되는 함수는 항상 하나의 매개변수만 받아야하는데, 그 이유는 리스트 등을 전달받아 원소를 하나씩 함수에 전달해서 변형하기 때문이다.

import math

m = map(math.sqrt, [1, 2, 3, 4]) # 리스트를 전달해서 이 값을 sqrt로 변형하는 하나의 객체를 생성
print(type(m)) # <class 'map'> 출력

이 때 클래스로 객체를 생성한 것은 map 클래스의 객체이지 리스트의 원소를 변형한 리스트 객체가 아니다.
다시 map 객체를 사용해서 내부의 리스트 우너소를 처리하라는 뜻이다.

리스트 컴프리헨션으로 표시하면 for문을 작성해야 하지만, 별표를 사용하면 언패킹한다.
언패킹 : 모든 원소를 자동으로 하나씩 추출해서 리스트의 원소로 저장하는 것

[*m] # 리스트 내에 map 객체를 넣고 앞에 *를 붙이면, map 객체의 처리 결과가 리스트에 원소로 만들어진다.

단순 조건을 처리하는 단순 제어문

조건식을 작성할 때 특정 조건을 반대로 처리할 필요가 있는데, 이때는 부정하는 예약어 not을 사용한다.

if 100 is not 200:
    print("True")
else:
    print("False")

파이썬에서 예약어 is에 대한 부정은 항상 not을 뒤에 표기한다.

None은 아무것도 하지 않는 객체이다. 이 객체는 항상 논릿값을 False로 처리한다.

예약어 None은 아무것도 처리하지 않는 객체가 들어있고, 이 객체의 값은 다른 값과 비교해도 항상 거짓을 표시하고 자기 자신을 비교할 때는 True를 표시한다.

bool(None) # False
None is None # True
None == None # True

조건 연산자를 스페셜 메소드로 처리

<=의 스페셜 메소드는 __le__이다.

1 <= 10 # True
(1).__le__(10) # True

if (1).__le__(10):
    print("메소드")

<는 __lt__, >는 __gt__, and는 __and__이다.

a = 10
b = 20

(a < b) and (b > a) # True
a.__lt__(b).__and__(b.__gt__(a)) # True

반복할 수 있는 객체를 순환하는 for문 처리

반복형(iterable)과 반복자(iterator)를 알아보려면 추상 클래스로 확인해야한다.
원소를 여러 개 가지는 자료구조를 보관하는 collections 모듈 내에 추상 클래스를 관리하는 abc 모듈이 있다.

반복형 추상 클래스 Iterable의 특징은 원소를 여러 개 가진 정적인 객체를 만든다. 문자열, 리스트 등은 객체를 만들면 항상 메모리에 올라갈 원소들을 확정한다.

반복자 추상 클래스 Iterator는 동적 처리를 하기 위해 별도의 핸들러를 만든 후에 내부의 원소를 하나씩 꺼내어서 처리하는 기능을 제공한다.

보통 클래스를 정의하면 그 클래스가 추상 클래스의 인터페이스인 메소드들을 상속해서 구현 클래스를 만든다.
issubclass 함수는 상속 관계를 확인해준다. 서브 클래스와 수퍼 클래스를 전달, 실행하여 상속 관계가 성립하면 True를 반환한다.

import collections.abc as cols

l = [1, 2, 3, 4, 5]

issubclass(type(l), cols.Iterable) # True, 반복형 추상 클래스를 상속했다.
issubclass(type(l), cols.Iterator) # False, 반복자 추상 클래스를 상속하지 않았다.

이를 통해 리스트 클래스는 정적 처리가 가능한 반복형이지만, 반복자가 아니라는 것을 알 수 있었다.

range 클래스도 순환문으로 처리하면 리스트 객체와 같은 처리 결과가 나온다.

for i in l:
    result += i

for i in range(1, 6):
    result += i

range 클래스와 추상 클래스 반복형과 반복자의 상속 관계를 확인한다.

issubclass(range, cols.Iterable) # True
issubclass(range, cols.Iterator) # False

반복형에만 True인 것으로, 반복형만 상속해서 range를 구현한 것을 알 수 있다.

반복문에 else문 추가

반복문에도 else를 추가할 수 있다. 정상적으로 반복이 처리되면 추가로 else문 내부를 실행하고, break문으로 강제 종료되면 else문은 실행되지 않는다.

for i in range(101):
    result += i
else:
    print("정상 종료")

while result <= 5050:
    result += i
    if result > 4000:
        break
else:
    print("정상 종료")

다양한 원소를 갖는 객체의 원소 추출하기

리스트 객체가 원소와 그 원소가 속한 인덱스 정보를 함께 가져올 때는 enumerate 클래스에 리스트 객체를 인자로 전달하여 사용한다.

l = [1, 2, 3, 4]

for i, v in enumerate(l):
    print("index: ", i, "value : ", v)

"""결과
index: 0 value: 1
index: 1 value: 2
index: 2 value: 3
index: 3 value: 4
"""

두 개의 리스트 객체를 전달해서 순서 쌍을 만들고 싶을 때는 zip 클래스에 인자로 전달하면 된다.
그러면 두 개의 리스트 객체 중에 작은 원소의 개수만큼 순서 쌍을 만든다.

ll = [4, 5, 6, 7, 8]

for i in zip(l, ll):
    print(i)

"""결과
(1, 4)
(2, 5)
(3, 6)
(4, 7)
"""

chain을 사용하면 2개의 리스트를 하나의 리스트로 만들지는 않지만, 두 리스트를 연결해서 출력할 수 있다.

for i in it.chain(l, ll):
    print(i)

"""결과
1
2
3
4
5
6
7
8
"""

combinations는 여러 개의 원소를 묶어서 순서 쌍을 만든다.

for i in it.combinations(l, 3): # 리스트 원소 중에서 3개를 선택해서 중복 없는 경우의 수를 만드는 조합으로 반복
    pritn(i)

"""
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
"""

permutations는 특정한 n개의 원소를 순서에 맞춰 순서 쌍을 만들어 준다.

count = 0
for i in it.permutations(l, 3): # 리스트 원소중 3개를 선택해 순서에 따라 나열하는 원소 순열로 처리
    print(i, end=", ")
    coutn += 1
    if count % 5 == 0:
        print()

"""
(1, 2, 3), (1, 2, 4), (1, 3, 2), (1, 3, 4), (1, 4, 2),
...
...