파이썬 더 잘 알기
부동소수점 vs 고정소수점
컴퓨터에서 소수는 2진법으로 정확히 표현할 수 없으므로 근사치로 저장된다.
소수를 저장하기 위한 방법은 2가지가 있다.
고정 소수점
- 정수를 표현하는 비트 수, 소수를 표현하는 비트 수를 미리 정해두고 해당 비트만큼 사용해 숫자 표현
- ex: 정수 표현에 4byte(32bit), 부호 1bit, 소수는 15bit 사용하자
- 정수를 표현하는 비트를 늘리면 큰 숫자를 표할 수 있지만 정밀한 숫자표현 어렵
- 소수 표현 비트 늘리면 정밀하게 표현 가능하지만 큰 숫자는 표현이 어렵
부동 소수점
- 소수점의 위치를 고정하지 않고 그 위치를 나타내는 수를 따로 적는것.
- 유효숫자를 나타내는 가수와 소수점의 위치를 풀이하는 지수로 나누어 표현
- IEEE754의 표현을 가장 널리 사용
- 예를들어 -100.34을 부동 소수점으로 표현할 경우 해당 숫자를 이진법으로 만든다.
- 그리고 소수점을 이동시켜 1.XXXX 형태로 바꾼다.
- 고정소수점의 경우 1bit(부호) + 16bit(정수) + 15bit(소수)
- 부동소수점의 경우 1bit(부호) + 8bit(지수) + 23bit(가수)
파이썬의 trade-off
퍼포먼스 bad(실행하는데 오래걸림)
생산성 good
퍼포먼스와 생산성은 trade off 관계
스크립트 언어 vs 컴파일 언어
컴파일 언어
- 실행 전 소스 코드를 컴파일하여 기계어로 변환 후 해당 파일을 실행
- 이미 기계어로 변환된 것을 실행하므로 비교적 빠름
- 컴파일 시점에 소스 코드의 오류를 잡기 쉬움
- 같은 소스 코드도 다른 환경(PC, mobile 등)에서 실행하려면 다시 컴파일(기계어로 변환) 해야함
스크립트 언어(인터프리터 언어)
코드를 작성함과 동시에 인터프리터가 기계어로 번역하고 실행함
코드 번역 과정이 있어 비교적 느림
주 사용 목적이 뚜렷하게 개발되어 사용하기 쉬운 편
명령줄로 코드를 즉시 실행할 수 있음
enumerate()
fruits = ['apple', 'banana', 'watermelon']
for index, fruit in enumerate(fruits):
print(index, fruit)
리스트 컴프리핸션
한줄만으로 리스트 구현
even_numbers = [x for x in range(10) if x % 2 == 0]
print(even_numbers)
제너레이터
- 리스트의 데이터셋을 하나씩 가져와줘서 공급
- append() 대신 yeild() 사용
- 메모리 사용 효율적
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)}')
예외 처리(Exception)
예상가능한 예외
발생 여부를 사전에 인지할 수 있는 예외
사용자의 잘못된 입력, 파일 호출시 파일 없음 등...
개발자가 반드시 명시적으로 정의해야 함
예상불가능한 예외
인터프리터 과정에서 발생하는 예외, 개발자 실수, 리스트 범위 넘는 값 호출, 정수를 0으로 나누는 것 등등
수행 불가시 인터프리터가 자동 호출
try-except-else-finally
예외 처리를 위한 방법
try의 statement 실행 후 에러 발생시 except로 이동
try:
예외 발생 가능성이 있는 코드
except <Exception Type>:
예외 발생시 어떻게 처리할지에 대한 코드
else:
예외가 발생하지 않을 때 동작
finally:
예외 발생 여부와 상관없이 실행
# 사례1: 0으로 나눠버림
for i in range(10):
try:
prize(10/i)
except ZeroDivisionError: #에러타입
print('Not divided by 0')
exception의 종류
IndexError | list index 범위를 넘어갈 때 |
NameError | 존재하지 않는 변수를 호출할 때 |
ZeroDivisionError | 0으로 숫자를 나눌 때 |
ValueError | 변환할 수 없는 문자/숫자를 변환할 때 |
FileNotFoundError | 존재하지 않는 파일을 호출할 때 |
raise <Exception type>(예외정보)
필요에 따라 강제로 exception 발생
if n != 2:
raise ValueError('2가 아님')
assert 예외조건
특정 조건에 만족하지 않을 경우 예외 발생
def get_binary(n):
assert isinstance(n, int) # n이 int형인지 확인
multiprocessing(멀티프로세싱)
컴퓨터가 작업을 처리하는 속도를 높여주는 방법 중 하나
원래는 여러명이 1개의 자전거만으로 이동하지만,
멀티프로세싱을 사용하면 병렬 처리를 통해 여러 대의 자전거로 여러 명이 이동할 수 있다.
그럼 병렬 처리는 어떻게 프로그래밍하는데...?
순차처리 vs 병렬처리
순차처리
import time # 프로그램 작동 시간 계산 위한 라이브러리
num_list = ['p1', 'p2', 'p3', 'p4']
start = time.time() # 시작 시간 저장
def count(name):
time.sleep(5) #5초 대기
print(f"finish:{name}\n")
for num in num_list:
count(num)
print(f'time:{time.time() - start}') #프로그램 완료시간 - 프로그램 시작시간 = 프로그램 총 작동시간
병렬처리
병렬처리가 순차처리에 비해 2배 이상 빠름
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) #병렬 처리시 프로세스 2개 이용(cpu 코어 개수만큼 입력해주면 최대 효과)
# cpu 코어 개수 확인하는법 grep -c processor /proc/cpuinfo
pool.map(count, num_list) #병렬화 시키는 함수. count 함수에 num_list의 원소를 하나하나 넣음 # count('p1'),..., count('p4') 생성
pool.close() #병렬화 종료시
pool.join() #프로세스 종료까지 대기
print("time :", time.time() - start)
클래스 vs 모듈 vs 패키지
클래스
비슷한 역할을 하는 함수드르이 집합
모듈
함수, 변수, 클래스를 모아 둔 파일
import를 통해 사용 가능
패키지(라이브러리)
여러 모듈들의 집합, 폴더로 연결
pip install로 설치
__init__, __main__ 등 키워드 파일명 사용
다양한 오픈소스들은 모두 패키지로 관리됨
cf. 패키지 만드는 법
1. 기능들을 세부적으로 나눠 폴더를 만든다
2. 각 폴더별로 필요한 모듈을 구현한다
3. 폴더별로 __init__.py 파일을 구성한다.
4. __main__.py 파일 만든다.
cf. __init__.py?
- 현재 폴더가 패키지임을 알리는 초기화 스크립트
- 하위 폴더와 py 파일을 모두 포함함
- import, __all__keyword 사용
데이터사이언티스트에게 적합한 함수형 프로그래밍
- 효율성, 버그없는 코드, 병렬 프로그래밍
- 함수로 문제를 분해
- 순수성: 함수 안에 함수 밖에서 가져오는 함수, 변수를 변경하는 코드 없음
- 모듈성
- 디버깅과 테스트 용이성
파이썬의 코드 작성 양식
pep8
- 한줄의 코드 길이는 79자 이하
- 함수, 클래스는 다른 코드 사이에 빈줄2개 넣기
- 클래스 내 함수는 빈줄 하나 넣기
- 변수 할당 앞뒤 스페이스 하나만 사용
- 리스트인덱스, 함수 호출에는 스페이스 사용 안함
- , : ; 앞에는 스페이스 사용 안함
이름 규칙
- 변수 앞 _ : 함수 등 내부에서만 사용되는 변수
- 변수 뒤 _: 라이브러리, 파이썬 기본 키워드와의 충돌 피하고 싶을 때
- 소문자 L, 대문자O, 대문자 I는 사용 지양
- 모듈명은 짧은 소문자로, 필요할 경우 _로 나눔
- 클래스명은 PascalCase
- 함수명은 소문자로 구성, 필요시 _로 나눔
- 상수는 모듈 단위에서만 정의, 변수명 모두 대문자로, 필요시 _로 나눔
네이밍 컨벤션
snake_case
모든 공백은 _, 모든 문자는 소문자
파이썬에서 함수, 변수 명명시
PascalCase
모든 단어가 대문자로 시작
파이썬에서 클래스 명명할 때
camelCase
처음 소문자 이후 대문자
객체 지향 프로그래밍
수많은 변수들을 어떻게 효율적으로 다룰 것인가에서 시작
파이썬의 객체
파이썬에서 모든 것은 객체이다. 그래서 거의 모든 것이 속성(attribute)와 메서드(method)를 갖는다."
이때 변수가 속성, 함수가 메서드이다.
클래스
클래스의 속성과 메서드
속성
state 표현. 변수로 나타냄
메서드
- behavior 표현, def로 나타냄
- 클래스 내 정의된 함수라고 볼 수 있음
- 클래스의 메서드는 첫번째 인자는 self 값을 적어주어야 함
self
클래스를 인스턴스화한 인스턴스 객체를 가리킴
뭔소리여? 예를 들어 설명해보자.
예를 들어
mycar.drive()
라는 코드를 작성하면, 이 코드는 인터프리터 내에서 Car.drive(mycar)이라는 형태로 동작한다.
mycar.drive() == Car.drive(mycar) 인 셈이다.
즉, 메서드 호출시 파이썬 내부에서는 인자 하나를 무조건 사용하고 있으며 그 인자가 바로 파이썬 클래스에 선언된 객체 자신(self)
위와 동일한 이유로, 인스턴스의 속성으로 사용하고 싶은 변수는 self. 사용. 이는 self 인자를 통해 선언된 객체의 값이라는 뜻.
클래스 메서드 내부에서 self 접두사 없이 선언된 변수는 메서드 내부에서만 사용되며 self를 이용해 참조할 수 없음
class Car:
def drive(self, a):
self.a = float(a) + 100
return self.a
def stop(self, b):
b = float(b) #self 참조 불가
return b
t = Car()
t.drive(1) #정상 작동
t.stop(1) #에러 발생
identity()
객체에 부여하는 고유값.
id()를 통해 확인할 수 있다.
메모리 주소라고 보면 된다.
얇은 복사 vs 깊은 복사
얕은 복사
원본 데이터는 그대로 두고, 참조하는 데이터의 id만을 복사
copy.copy()
a = [1, 2, 3] b = a
깊은 복사
동일한 데이터 생성
copy.deepcopy() 사용
객체 지향 프로그래밍(OOP)
객체만을 활용한 프로그래밍
프로그램에서 처리하려는 변수의 묶음을 얼마나 잘 처리할 것인가
절차지향 프로그래밍
데이터 변수를 함수로 어떻게 처리할 것인가
데이터를 묶는데는 관심 없고 함수를 이용한 처리에 관심
분산 환경에서 데이터 처리에 효율적
클래스
class Car:
pass
#id(Car)는 여러번 호출해도 같은 값
print(id(Car))
print(id(Car))
#id(Car())는 Car()가 호출될 때마다 다른 값
print(id(Car()))
print(id(Car()))
# 두 객체의 타입
type print(type(Car)) # type 유형의 객체
print(type(Car())) # __main__.Car. 새로운 Car 타입의 객체
#객체 인스턴스화: 클래스를 이용하기 위해 클래스로 객체 생성
mycar = Car() #mycar 변수에 Car 클래스의 인스턴스 할당
인스턴스화(클래스로 객체생성) 하는 방법과 함수 호출하는 방법은 매우 유사
클래스와 함수명을 다르게 표기하는 방법 사용
클래스명: 카멜케이스, 주로 명사
함수명: 스네이크케이스, 주로 동사
생성자 __init__
클래스에 의해 만든 인스턴스 객체의 속성값을 초기화
클래스를 만들때부터 속성을 지정해주고 싶을 때 사용
class Car:
# 인스턴스 객체의 초기화(initializing instance)
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer = 0
def get_description(self):
return f"{self.year} {self.make} {self.model}"
def read_odometer(self):
return f"Car {self.model} has {self.odometer} miles on it."
def update_odometer(self, mileage):
if mileage >= self.odometer:
self.odometer = mileage
# 인스턴스 객체 선언
car1 = Car(make, model, year)
클래스 변수 vs 인스턴스 변수
class University:
# 클래스 변수
location = 'Seoul'
ranking = 10
def __init__(self, name, major, tuition):
# 인스턴스 변수
self.name = name
self.major = major
self.tuition = tuition
클래스 변수
클래스에 바로 선언된 속성
클래스에 생성된 모든 객체가 같은 값 공유
인스턴스 변수
__init__()안에서 self를 사용해 선언된 변수
객체 인스턴스화 시마다 새로운 값 할당
다른 객체간에는 공유 불가능
상속
class University를 상속하는 클래스 Department를 작성해보자
class University:
# 클래스 변수
location = 'Seoul'
ranking = 10
def __init__(self, name, major, tuition):
# 인스턴스 변수
self.name = name
self.major = major
self.tuition = tuition
def study(self):
print('you are doing well')
class Department(University): # 상속
ranking = 5 # ranking 속성 값 변경
# 새로운 메서드 추가
def grade(self):
print('your grade is...')
#메서드 오버라이드: 기존에 있던 메서드 변경/재정의
def study(self):
print('overriden')
# 상속받은 자식 클래스 사용해보기
math = Department()
math.study()
super()
자식 클래스에서 부모 클래스의 메서드를 호출하고 싶을 때
class C(B): # B를 상속받은 자식 클래스 C
def (부모클래스의)메서드이름():
super().메서드이름()
def method(self, arg):
super().method(arg)
아니 근데 오버라이딩 있는데 super는 왜씀?
- 부모 클래스의 메서드 내용을 바꾸고 이걸 모든 자식 클래스에 적용하고자 할때
- 오버라이딩으로 상속했을 경우 상속받은 클래스 하나하나의 초기화 코드를 변경해야함
- 그러나 super()을 사용할 경우 부모 클래스만 변경하면 됨
즉. super을 사용하면 부모 클래스의 변경사항이 그대로 자식 클래스에 반영됨
파이썬의 파일 처리
# 파이썬의 파일 처리
f = open('file.txt', 'r') # r: 읽기, w: 쓰기, a: 내용 추가
contents = f.read()
f.close()
'AI Theory > key concept of AI' 카테고리의 다른 글
사이킷런 머신러닝 (0) | 2023.06.28 |
---|---|
배열(array)과 파이썬 그래프 (0) | 2023.06.27 |
[boostcourse beyond ai] 딥러닝의 역사 (0) | 2023.05.28 |
분류 평가지표 (0) | 2023.05.07 |
데이터 퀼리티 높이기 (0) | 2022.12.01 |