무냐의 개발일지

[1/4] 파이썬 기초 본문

Data Scientist Bootcamp

[1/4] 파이썬 기초

무냐코드 2024. 1. 5. 16:36

파이썬

파이썬이라는 이름은 파이썬의 창시자 귀도 반 로섬이 좋아하는 코미디인〈Monty Python's Flying Circus〉에서 따온 것입니다. 이름에서 고대신화에 나오는 커다란 뱀을 연상하는 경우도 있겠지만 이와는 무관합니다. 다만 로고에는 뱀 두마리가 형상화되어 있습니다

 

이게 뱀이었대

 

함수와 변수

함수 :  불려진 시점에 특정한 작업을 수행하며, 입력값과 출력값은 있을 수도 없을 수도 있다

def : 함수를 정의(define) 할 때 사용하는 일종의 예약어이며, 함수명은 임의로 만들 수 있다. 

에러 IndentationError : 들여쓰기 된 코드 블럭, 즉 indented block을 기대했는데 왜 빼먹었냐는 불평입니다.

전역스코프(global scope), 지역스코프(local scope) : 함수 내에서 정의되는 변수

 매개변수(parameter): 입력값으로 주어진 인자(argument)를 받는 변수 (아래 예시에서 name), 땡땡은 인자

def say_hi_somebody(name):
print(name + ', 안녕!')

 

say_hi_somebody('땡땡') 

 

def say_hi_default(name='somebody'):
print('안녕, ' + name + '!')
 
여기서는 인자를 넣으면 그 인자로, 안 넣으면 'somebody'로 출력된다.

입력값이 여러 개일 때 : 각 입력값마다 기본값을 지정해 줄 수 있다.

   단, 이 경우 기본값이 있는 인자들이 기본값이 없는 필수 인자들의 뒤에 와야 한다.

 

def say_hi_couple_default(name1, name2='nobody') 요렇게 해야 맞음

제어문

if문 : if, elif, else, 논리연산자(and, or) 등

while문 : 조건이 참인 동안만 반복(루프 loop), 코드 블록을 실행하기 전 가장 먼저 조건문을 검사해서, 조건문이 참이면 코드 블록을 실행

while 뒤에는 참과 거짓이 갈릴 수 있는 명제가 온다

for문 : 하나씩 반복, for 뒤에는 a in A의 형태로, 값이 여러 개 들어있는 목록 A에서 값을 하나씩 꺼내서 a 변수에 담아 반복문을 실행

이렇게 줄줄이 굴비처럼 엮여서 하나씩 순서대로 넘길(iterate) 수 있는 값들을 반복 가능한(iterable, 이터러블) 객체

 

자료형

정수(integer, int), 부동소수점(float), 

참고) 1 + 1.0의 결과와 같이, 정수와 소수를 더하면 그 결과가 소수 자료형을 가지게 된다

참고) 고정소수점 : 소수점의 위치를 고정

 NoneType: 어떤 것으로도 정의되지 않는 유형

Boolean: True, False(None/ 0일때)

따옴표를 그대로 쓰고 싶을 때 \ 를 앞에 붙여준다

print('Quote(\') and double quote(\")')
Quote(') and double quote(")

 

튜플 ()

: 값 변경 불가, 튜플 안에 튜플 삽입 가능, 색인으로 특정 위치 값 읽을 수 있음, 덧셈으로 합치기 가능, in연산으로 값 존재여부 확인 가능

nested_tuple = ((1, 2), (3, 4))
print(nested_tuple[1][0])
3

 

리스트 []

: 값 변경 가능, append를 통해 삽입, remove로 삭제 가능, pop으로 특정 순서값 삭제 가능

 

딕셔너리(Dictionary)

: 직접 인덱스를 원하는대로 지정 가능 (key, value묶음)

: 값 추가 가능  (그냥 추가하면 됨)

conductor['gender'] = 'male'

 

: 제거 가능 ( .pop(key))

: for 루프문 사용시 키만 반환

: items() 를 쓰면 키, 값 둘다 반환

conductor = {'first_name': '단테', 'last_name': '안'}
for key, value in conductor.items():
print(key + ' : ' + value)

 

 

재귀함수(recursive function)

스스로를 호출하는 함수를 뜻한다. 대표적으로 피보나치 함수가 있다.

 
def fibonacci(n):
if n <= 2:
return 1
else:
return fibonacci(n-2) + fibonacci(n-1)

 

 

 

퍼포먼스(성능) : 어떤 언어든 코드를 짜서 실행을 시켰을 때 얼마나 빨리 처리가 되는가

생산성 : 똑같은 기능을 하는 프로그램을 얼마나 빨리 작성할 수 있는가

퍼포먼스 & 생산성은 역의 상관관계가 있음 (파이썬 : 퍼포먼스 낮음, 생산성 높음)

 

파이선의 특징

1. 높은 생산성

2. 간결한 코드

3. 빠른 개발 속도

4. 스크립트 언어 (=인터프리터 언어)

컴파일 언어 :

  • 실행 전 소스 코드를 컴파일하여 기계어로 변환 후 해당 파일을 실행
  • 이미 기계어로 변환된 것을 실행하므로 비교적 빠름
  • 컴파일 시점에 소스 코드의 오류를 잡기 쉬움
  • 같은 소스 코드도 다른 환경(PC, mobile 등)에서 실행하려면 다시 컴파일(기계어로 변환) 해야함

스크립트 언어 : 

  • 코드를 작성함과 동시에 인터프리터가 기계어로 번역하고 실행함
  • 코드 번역 과정이 있어 비교적 느림
  • 주 사용 목적이 뚜렷하게 개발되어 사용하기 쉬운 편
  • 명령줄로 코드를 즉시 실행할 수 있음

 

For 문 활용

- enumerate() : 리스트, 문자열, 튜플 등이 있는 경우 순서와 리스트의 값을 함께 반환

my_list = ['a','b','c','d']

for i, value in enumerate(my_list):
print("순번 : ", i, " , 값 : ", value)
 
이중 for문 생성 가능
 
range(start, stop, step)
range(1,10,2) : 1~9까지 2씩 증가하는 [1,3,5,7,9] 생성
list(range(1,10,2)) 로 묶으면 리스트로 받아올 수 있다
 
 

리스트 컴프리헨션(list Comprehension) : 쉽게 말해, 순회형을 이용해 코드를 한 줄로 짜는 거! 리스트 등 순회형 컨테이너 객체로부터 이를 가공한 새로운 리스트를 생성하는 아주 간결하고 편리한 방법, 셋(Set), 딕셔너리(Dict)에 대해서도 적용 가능

 
result_list = [(i, j) for i in range(2) for j in my_list]
 
# Q. 리스트 컴프리헨션을 활용해서 자연수 1부터 100까지 중 홀수를 출력하세요.
odd = [i for i in range(1,101,2)]
 
 
 
제너레이터(Generator) : 머신러닝을 하면 매우 많은 데이터를 다루게 되므로, 데이터셋을 하나씩 가져와 공급해주는 것. 현재 처리해야 할 데이터를 1개씩 로드해서 사용할 수 있게 됩니다. 이것은 빅데이터를 처리해야 할 머신러닝 상황에서 매우 요긴합니다.
 
my_list = ['a','b','c','d']

# 인자로 받은 리스트로부터 데이터를 하나씩 가져오는 제너레이터를 리턴하는 함수
def get_dataset_generator(my_list):
     for i in range(2):
          for j in my_list:
               yield (i, j)        # 이 줄이 이전의 append 코드를 대체했습니다
               print('>> 1 data loaded..')

dataset_generator = get_dataset_generator(my_list)
for X, y in dataset_generator:
     print(X, y)

 

yield : 코드 실행의 순서를 밖으로 "양보"합니다. 즉, dataset_generator = get_dataset_generator(my_list) 을 실행해도 "generator object" 만 반환할 뿐, 저희가 원하는 값을 바로 반환하고 있지 않습니다. 

 

 

Try-Except 예외 처리

에러를 잡기 위한 방법으로, 예외 처리는 코드를 수행하다가 예외(에러)가 발생했을 때 그 예외(에러)를 무시하게 하거나 예외(에러) 대신 적절한 처리를 해주게 하는 등의 작업

Try 코드 수행 -> 에러 발생 시 Except 내의 코드 실행 

a = 10

b = 0
try:
     print(a/b)
 
except:    #에러가 발생했을 때 처리하는 코드
     print('에러가 발생했습니다.')

 

Multiprocessing 

컴퓨터가 작업을 처리하는 속도를 높여주는 방법

 

-parallel processing (병렬처리) : 동시에 처리시키기

-serial processing (순차처리)

 

import multiprocessing
#병렬 처리하고싶은 함수

def count(name):
    time.sleep(5)
    print("finish:"+name+"\n")

 

num_list = ['p1','p2', 'p3', 'p4']

 

#코드 시작점을 여기로 하라(main)
if __name__ == '__main__':
    pool = multiprocessing.Pool(processes = 2)  --> 병렬 처리 시, 2개의 프로세스를 사용하도록 합니다. CPU 코어의 개수만큼 입력해 주면 최대의 효과를 볼 수 있습니다(코어 갯수: 터미널에 grep -c processor /proc/cpuinfo   입력)
    pool.map(count, num_list). --> 병렬화를 시키는 함수로, count 함수에 num_list의 원소들을 하나씩 넣어 놓습니다. 여기서 num_list의 원소는 4개이므로 4개의 count 함수에 각각 하나씩 원소가 들어가게 됩니다.
    pool.close()  --> 병렬화 부분이 끝나면 나옵니다. 더 이상 pool을 통해서 새로운 작업을 추가하지 않을 때 사용
    pool.join() --> 프로세스가 종료될 때까지 대기하도록 지시하는 구문

 

 

참고) 코드의 실행 시간을 측정하는 방법 : time 함수

import time
start = time.time() # 시작 시간 저장

a = 1
for i in range(100):
     a += 1
 
# 작업 코드
print("time :", time.time() - start) # 결과는 '초' 단위 입니다.

 

 

 

5-3. for문 잘 써보기 - enumerate()와 이중 for문

my_list = ['a','b','c','d']

for i, value in enumerate(my_list):
    print("순번 : ", i, " , 값 : ", value)
my_list = ['a','b','c','d']
result_list = []

for i in range(2):
    for j in my_list:
        result_list.append((i, j))
        
print(result_list)

 

*enumerate() : 리스트, 문자열, 튜플 등이 있는 경우 순서와 리스트의 값을 함께 반환해 주는 기능

 

참고 - range() • range(start, stop, step) : range(2)와 같이 정수가 들어가야 하며, list() 함수를 통해 값을 뽑아오면 0부터 시작해서 2개인 list ([0, 1]) 를 만듭니다. 즉, stop 까지 포함하지는 않습니다. range(1, 10, 2) 이라면 1부터 9까지 2씩 증가하는 [1, 3, 5, 7, 9] 를 만듭니다. 하지만 실제로 반환하는 타입은 range이며, 리스트로 받아오고 싶다면 list(range(1, 10, 2))를 하시면 됩니다.

이중 for문을 이용하여 아래와 같이 별을 하나씩 추가하여 찍는 코드를 작성해 보세요.

 

  ****print('*' * i)****

 

리스트 컴프리헨션(list Comprehension) : 리스트 등 순회형 컨테이너 객체로부터 이를 가공한 새로운 리스트를 생성하는 아주 간결하고 편리한 방법

 

5-4. Try - Except 예외 처리하기

a = 10
b = 0
try:
    #실행 코드
    print(a/b)
		
except:
    #에러가 발생했을 때 처리하는 코드
    print('에러가 발생했습니다.')

 

5-5. Multiprocessing

컴퓨터가 작업을 처리하는 속도를 높여주는 방법

import multiprocessing
import time

num_list = ['p1','p2', 'p3', 'p4']
start = time.time()

def count(name):
    time.sleep(5)
    print("finish:"+name+"\\n")
    

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes = 2)
    pool.map(count, num_list)
    pool.close()
    pool.join()

print("time :", time.time() - start)

if __name__ == '__main__': 코드 시작점을 여기로 하라는 명령어

pool = multiprocessing.Pool(processes = 2) : 병렬 처리 시, 2개의 프로세스를 사용하도록 합니다. CPU 코어의 개수만큼 입력해 주면 최대의 효과를 볼 수 있습니다. (코어의 개수는 터미널에 grep -c processor /proc/cpuinfo 명령어를 입력하여 확인할 수 있습니다. CPU에 대한 자세한 정보를 알고 싶다면 터미널에 cat /proc/cpuinfo 명령어를 입력하여 확인해볼 수 있습니다.) pool.map(count, num_list) : 병렬화를 시키는 함수로, count 함수에 num_list의 원소들을 하나씩 넣어 놓습니다. pool.close() : 일반적으로 병렬화 부분이 끝나면 나오며, 더 이상 pool을 통해서 새로운 작업을 추가하지 않을 때 사용합니다. pool.join() : 프로세스가 종료될 때까지 대기하도록 지시하는 구문

 

5-6. 같은 코드 두 번 짜지 말기

함수(Function) : 적어야 하는 양을 줄여준다

def function_f(input_x):
	output_x = input_x*input_x
	return output_x

최댓값을 구하는 함수

list_data = [10, 20, 30, 40]
list_data2 = [20, 30, 40, 50]

length = len(list_data)
max_result = list_data[0]
for i in range(length):
    if max_result < list_data[i]:
        max_result = list_data[i]
        
print("최댓값은 ", max_result)

length = len(list_data2)
max_result = list_data2[0]
for i in range(length):
    if max_result < list_data2[i]:
        max_result = list_data2[i]
        
print("최댓값은 ", max_result)

이걸 줄여주면

list_data = [10, 20, 30, 40]
list_data2 = [20, 30, 40, 50]

def max_function(x):
    length = len(x)
    max_result = x[0]
    for i in range(length):
        if max_result < x[i]:
            max_result = x[i]
    return max_result

print("최댓값은 ", max_function(list_data))
print("최댓값은 ", max_function(list_data2))

장점 : 코드의 효율성, 재사용성, 가독성 향상

 

5-7. 같은 코드 두 번 짜지 말기

pass

기타 제어 흐름 도구로써, 함수 이름과 입력 정도만 먼저 만들어 놓고 나중에 내부 구현 하고싶을 때 사용 (프로그램이 특별히 할 일이 없을 때 사용)

함수 연달아 사용

def say_something(txt):
    return txt

def send(function, count):
    for i in range(count):  
        print(function)
    
send(say_something("안녕!"), 2)

안녕!

안녕!

 

함수 안의 함수 & 2개 이상의 return

하나의 함수에서 여러개의 return값을 받고 싶을 때, 함수 안에 함수를 만들 수 있음 (단, 함수 안에서 만든 함수는 해당 함수 내부에서만 사용할 수 있음)

list_data = [30, 20, 30, 40]

def minmax_function(x_list):
        
    def inner_min_function(x):
        length = len(x)
        min_result = x[0]
        for i in range(length):
            if min_result > x[i]:
                min_result = x[i]
        return min_result
    
    def inner_max_function(x):
        length = len(x)
        max_result = x[0]
        for i in range(length):
            if max_result < x[i]:
                max_result = x[i]
        return max_result
        
    x_min = inner_min_function(x_list)
    x_max = inner_max_function(x_list)
    
    minmax_list = [x_min, x_max]

    return minmax_list

print("최솟값, 최댓값은 : ", minmax_function(list_data))
print("최솟값은 : ", minmax_function(list_data)[0])
print("최댓값은 : ", minmax_function(list_data)[1])

return 되는 것은 minmax_list 1개이지만 그 안에 2개의 값을 넣어놨기 때문에 2개 이상을 반환하는 것과 같은 효과를 보게 되는 것

 

 

여러 변수로 반환하기

list_data = [30, 20, 30, 40]

def minmax_function(x_list):
        
    def inner_min_function(x):
        length = len(x)
        min_result = x[0]
        for i in range(length):
            if min_result > x[i]:
                min_result = x[i]
        return min_result
    
    def inner_max_function(x):
        length = len(x)
        max_result = x[0]
        for i in range(length):
            if max_result < x[i]:
                max_result = x[i]
        return max_result
        
    x_min = inner_min_function(x_list)
    x_max = inner_max_function(x_list)
    
    return x_min, x_max

min_value, max_value = minmax_function(list_data)

print("최솟값은 : ", min_value)
print("최댓값은 : ", max_value)

 

 

제너레이터(Generator) : 많은 데이터를 더 효율적으로 구현

my_list = ['a','b','c','d']

# 인자로 받은 리스트로부터 데이터를 하나씩 가져오는 제너레이터를 리턴하는 함수
def get_dataset_generator(my_list):
    for i in range(2):
        for j in my_list:
            yield (i, j)   # 이 줄이 이전의 append 코드를 대체했습니다
            print('>>  1 data loaded..')

dataset_generator = get_dataset_generator(my_list)
for X, y in dataset_generator:
    print(X, y)

print(f'메모리 사용량 : {sys.getsizeof(dataset_generator)}')

,(콤마)를 이용해 여러 개의 값을 반환


5-8. 같은 코드 두 번 짜지 말기 (3) 람다 표현식

람다 표현식: 한 줄의 식 형태로 되어 있음

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

print( (lambda x,y: x + y)(10, 20) )

<aside> 💡 print( (lambda x,y: x + y)(10, 20) )

</aside>

x, y 는 입력값을 의미합니다.

x + y는 return 부분과 같습니다

  • map() 함수는 입력받은 자료형의 각 요소가 함수에 의해 수행된 결과를 묶어서 map iterator 객체로 출력하는 역할 : map(f, iterable)
def list_mul(x):
     return x * 2

result = list(map(list_mul, [1, 2, 3]))
print(result)

이렇게 한 방에 각각 multiply를 해준다

result = list(map(lambda i: i * 2 , [1, 2, 3]))
print(result)

 

 

filter(), reduce() 등 람다 표현식과 자주 쓰이는 함수가 많이 있습니다.

# Q. filter()와 lambda를 활용해서 다음 nums 변수에 담겨있는 숫자 중 홀수만 출력해보세요.
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(filter(lambda n : n%2 != 0 , nums))

 

 

5-9. 클래스, 모듈, 패키지

클래스 : 비슷한 역할을 하는 함수들의 집합

class all_calc():

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a + self.b
 
    def mul(self):
        return self.a * self.b

    def sub(self):
        return self.a - self.b

    def div(self):
        return self.a / self.b

모듈 : 함수, 변수, 클래스를 모아 놓은 파일 (이미 만들어져 있는 모듈을 가져와 쓸 수도 있고 아니면 우리가 직접 모듈을 만들어서 사용할 수도 있다)

# mycalculator.py

test = "you can use this module."

def add(a, b):
    return a + b
 
def mul(a, b):
    return a * b

def sub(a, b):
    return a - b

def div(a, b):
    return a / b

 

패키지 : 패키지(=라이브러리)는 전 세계의 파이썬 사용자들이 만든 유용한 프로그램을 모아 놓은 보물 주머니로, 거의 모든 패키지는 pip 명령어를 통해서 설치 가능

pip install pandas

https://pypi.org/ 여기 사이트에서 패키지 검색해서 다운로드 받을 수 있음

 

 

5-10. 프로그래밍 패러다임과 함수형 프로그래밍

패러다임(Paradigm) : 어떤 한 시대의 사람들의 견해나 사고를 근본적으로 규정하고 있는 테두리. 프로그래머에게 프로그래밍의 관점을 갖게 해 주고, 결정하는 역할을 함.

  • 절차 지향 프로그래밍

일이 진행되는 순서대로 프로그래밍하는 방법

-장점 : 코드가 순차적으로 작성되어 있어 순서대로 읽기만 하면 이해가 가능합니다.

-단점 : 순차적으로 작성되어 있기 때문에 위에서 하나가 잘못되면 아래도 연쇄적으로 문제가 생겨서 유지 보수가 어렵습니다. 일반적으로 코드 길이가 길어서 코드를 분석하기 어렵습니다.

  • 객체 지향 프로그래밍(Object Oriented Programming)

개발자가 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 함

-장점 : 코드를 재사용하기 쉽습니다. 코드 분석이 쉬우며 아키텍처를 바꾸기 쉽습니다.

-단점 : 객체 간의 상호작용이 있기 때문에 설계에서 많은 시간이 소요되며 설계를 잘못하면 전체적으로 바꿔야 할 수도 있습니다.

데이터 사이언티스트에게 적합한 프로그래밍 패러다임

-장점 : 함수형 프로그래밍은 효율성, 버그 없는 코드, 병렬 프로그래밍

1)순수성 : 그냥 함수 안에만 있는 변수만 가지고 결과내는거

부작용이 전혀 없는 함수를 순수함수라고 한다. 순수성이 있는 함수는, 함수 안에 함수 밖에서 바로 가져오는 함수나 아니면 밖에 있는 변수를 변경시키는 코드가 없습니다

2)모듈성 : 문제를 작은 조각으로 분해하여, 한 가지 작업을 수행하는 작은 함수들로 쪼개어 만드는 것

3)디버깅 & 테스트 용이성 :

 

 

5-11. 파이써닉하게 코드를 짜보자 (https://pep8.org/#code-lay-out)

코드 스타일 가이드 pep8 을 따른다

 

변수 할당 앞뒤에 스페이스를 하나만 사용

리스트 인덱스, 함수 호출에는 스페이스를 사용하지 않습니다.

쉼표(,), 쌍점(:), 쌍반점(;) 앞에서는 스페이스를 사용하지 않습니다.

변수명 앞에 _(밑줄)이 붙으면 함수 등의 내부에서만 사용되는 변수를 일컫습니다. _my_list = []

변수명 뒤에 _(밑줄)이 붙으면 라이브러리 혹은 파이썬 기본 키워드와의 충돌을 피하고 싶을 때 사용합니다. import_ = "not_import"

함수와 클래스는 다른 코드와 빈 줄 두 개로 구분

클래스에서 함수는 빈 줄 하나로 구분

소문자 L, 대문자 O, 대문자 I를 가능하면 사용하지 마세요

모듈(Module) 명은 짧은 소문자로 구성되며, 필요하다면 밑줄로 나눕니다. my_module.py

클래스 명은 파스칼 케이스(PascalCase) 컨벤션으로 작성 class MyClass():

함수명은 소문자로 구성하되 필요하면 밑줄로 나눕니다. def my_function():

 

-네이밍 컨벤션(Naming convention)

snake_case

모든 공백을 "_"로 바꾸고 모든 단어는 소문자입니다.파이썬에서는 함수, 변수 등을 명명할 때 사용합니다.

ex) this_snake_case

PascalCase

모든 단어가 대문자로 시작합니다.

UpperCamelCase, CapWords라고 불리기도 합니다.

파이썬에서는 클래스를 명명할 때 사용합니다.

ex) ThisPascalCase

camelCase

처음은 소문자로 시작하고 이후의 모든 단어의 첫 글자는 대문자

lowerCamelCase라고 불리기도 합니다.

파이썬에서는 거의 사용하지 않습니다 (Java 등에서 사용)

ex) thisCamelCase