현대 오토에버 클라우드 스쿨

파이썬 프로그래밍

Gom3rye 2025. 5. 20. 09:36

1. Data Type

  • tuple
    • list와 유사한데 읽기 전용 → 함수의 리턴에 많이 이용된다. ex. 데이터 분석
    • 생성 방법: 하나의 데이터를 가지고 생성할 때는 데이터, or tuple(다른 순차형 데이터)
    • list와 tuple은 데이터를 나누어서 저장하는 것이 가능: unpacking
    • 데이터를 나눌 때 *을 사용하면 *이 없는 곳에 나누어주고 나머지를 가지고 list로 만들어서 대입한다.
    • tu = (10, 20, 30) a, b, c = tu print(c) # 30 a, b, _ = tu # 대입하지 않을 데이터는 _로 매칭 print(b) # 20 a, *b = tu # *은 나머지 전부를 list로 만들어준다. print(b) # [20, 30]
    • tuple을 가지고 if else에서 조건에 따라 다른 값을 대입하는 것을 수행할 수 있다. (2개의 데이터 나열)[조건]: 조건이 True면 뒤의 데이터가, False면 앞의 데이터가 대입된다.
    • score = 59 pandan = ("합격", "불합격")[score < 60] # (거짓일 때, 참일 때) 조건 입력 print(pandan) # 불합격
    • collections 모듈의 namedtuple 함수를 이용하면 필드명과 클래스 이름을 추가한 서브 클래스를 생성해준다.
    • 이 자료형으로 만들어진 튜플은 이름을 이용해서 접근이 가능하다.
    • python에서 DTO(Data Transfer Object)나 VO(Variable Object)를 만들 때 일반 클래스로 만들기도 하고 dict나 namedtuple을 이용하기도 한다.
    • 사용자 요청 → Controller → Service → DAO(Data Access) → Persistence Store (3계층) (이때 계층 사이를 이동하는 걸 DTO라고 하고, 자기 계층에서만 쓰이는 걸 VO(domain)이라고 한다. (DTO : serialization(직렬화) O, VO : 직렬화 x)
    • class DTO: def __init__(self, num=0, name="noname", age=1): self.num = num self.name = name self.age = age dto = DTO(1, "명준", 33) print(dto.name) # 명준 from collections import namedtuple # 첫번째 매개변수: 이 클래스의 설명, 두번째 매개변수: 문자열로 필드명을 공백과 함께 나열 VO = namedtuple("VO", "num name age") # swift vo = VO(2, "명지", 22) print(vo.name) # 명지
  • set
    • 데이터를 순서에 상관없이 중복 제거한 형태로 저장하는 자료구조
    • 내부 데이터 변경할 수 있는 set과 변경할 수 없는 frozenset을 제공
    • 생성 방법
      • 빈 객체: set( )
      • 다른 자료형을 가지고 생성: set(데이터)
      • 기본 데이터를 가지고 생성: {데이터 나열}
      (%→ hash, 주민등록번호 만들 때(%11) 사용
      • 검색 속도가 빨라짐 But, 메모리를 좀 쓴다 ex. 데이터 하나 더 들어오면 리해시해야 함 -> 데이터의 삽입과 삭제가 빈번하면 이 자료구조는 굉장히 느려진다.)
      hashset = {"Hello World"}
      print(hashset) # {'Hello World'}
      hashset = set("Hello World")
      print(hashset) # {'o', 'H', ' ', 'd', 'r', 'e', 'l', 'W'} 순서 상관 없이 나옴
      # 스마트폰 앱 : hash와 set사용함 -> 순서 없이 중복 없음 => 똑같은 앱 2번 안 깔리고 모든 앱 접근 속도 동일
      # 있다 없다도 빠르게 찾을 수 있음
      
  • dict
    • key와 value를 쌍으로 저장하는 자료형
    • 생성 방법
      • 비어있는 상태: { } or dict( )
      • 데이터를 가지고 생성: {키: 값, 키: 값 …}
    • 키를 이용해서 가져오기
    • dict[키] or get 메서드 이용
    • dict[키] 에서 키가 존재 x → 에러
    • get(키, 키가 없을 때 사용할 값)
    • 데이터 삽입이나 수정
    • dict[키] = 값 (이때 없으면 생성되고 있으면 수정된다. ⇒ key가 set으로 만들어지기 때문에 키는 중복 생성 x)
    dict1 = {"num": 3, "name": "명준", "age": 50}
    dict1["height"] = 180
    print(dict1) # {'num': 3, 'name': '명준', 'age': 50, 'height': 180}
    print(dict1["name"]) # 명준
    # print(dict1["weight"]) # KeyError: 'weight'
    print(dict1.get("weight", "없는 키")) # 에러 안 나고 '없는 키' 출력
    
    # __iter__가 있기 때문에 순회하면 키의 값이 순차적으로 리턴된다.
    for temp in dict1:
        print(temp) 
    # 결과
    num
    name
    age
    height
    
    for temp in dict1:
        print(temp, dict1[temp])
    # 결과
    num 3
    name 명준
    age 50
    height 180
    
    • MVC (Model View Controller)
    • Model: 실제 비즈니스 로직
    • View: 화면에 보여지는 부분
    • Controller: 둘 사이를 연결해주는 부분 이 부분을 나누어서 구현하고 하나의 변화가 다른 하나에 영향을 거의 미치지 않도록 하는 프로그래밍 방식
      kia = ["김도영", "이의리", "전상현"]
      hanwha = ["안치홍", "문동주", "김서현", "고삼례"]
      lg = ["김명준"]
      kbo = [kia, hanwha, lg]
      
      # 팀이 하나 추가되면 출력부분 바꿔야 한다.
      for team in kbo:
          if team == kbo[0]:
              print("기아: ", end='')
          elif team == kbo[1]:
              print("한화: ", end='')
          else:
              print("LG: ", end='')
          for player in team:
              print(player, end=' ')
          print("")
          
      # 키를 하나 더 써서 데이터에 이름을 붙여놓는 것 -> 출력할 때 더 편해진다.
      kbo = [{"team": "기아", "data": kia}, {"team": "한화", "data": hanwha}, {"team": "LG", "data": lg}]
      for teams in kbo:
          print(f"{teams["team"]}: ", end='')
          for player in teams["data"]:
              print(player, end=' ')
          print()
      
      # 결과
      기아: 김도영 이의리 전상현 
      한화: 안치홍 문동주 김서현 고삼례
      LG: 김명준
      
    • # 자바에서 선호하는 방식 class DTO: def __init__(self, num=0, name="noname", age=1): self.num = num self.irum = name self.age = age dto = DTO(1, "명준", 33) print(dto.num) print(dto.irum) # 명준 print(dto.age) # 파이썬에서 선호하는 방식 vo = {"num":2, "name":"삼례", "age":33} for key in vo: # key이름을 출력한 적 없음 -> 전체 데이터 출력할 때는 영향 안 받음 print(key, vo[key])
    • 기존의 list나 set 그리고 tuple을 가지고 생성
    • dict(zip(키의 모임, 값의 모임))
    • keys, values, items 메서드를 이용해서 키의 집합, 값의 집합, 키와 값의 튜플로 리턴해준다.
    • del dict[키]를 이용해서 데이터 삭제 가능
    • popitem( )을 이용해서 마지막 하나의 데이터를 반환하고 제거
    • clear( )을 이용해서 모든 데이터 삭제
  • enumerate
    • 인덱스와 데이터를 하나의 튜플로 리턴해주는 함수
    • list, set, tuple의 경우 인덱스와 데이터를 리턴, dict는 인덱스와 키를 리턴
  • comprehension
    • 데이터 순회문을 가지고 list나 tuple 그리고 set등을 만드는 것
li1 = ["삼례", "명준", "명지"]
li2 = [name for name in li1]
print(li2) # li1 == li2

li3 = [["!", "?", "@"], ["%", "$", "&"]] # 2차원 list
# 2차원 list를 가지고 1차원 list 생성
li4 = [j+"k" for i in li3 for j in i] # map보다 속도 빠름
print(li4) # ['!k', '?k', '@k', '%k', '$k', '&k']
li1 = ["삼례둥이", "명준빵", "명지"]
li2 = [name for name in li1 if len(name) >= 3]
print(li2) # ['삼례둥이', '명준빵']
  • collections 모듈: 다양한 자료구조를 제공하는 모듈 → 이랑 iter 툴 쓸 수 있는지 물어봐야 한다.(코테)
    • deque: 뎈 자료구조를 위한 클래스
    • Counter: 데이터 개수를 파악하거나 집계를 편리하게 해주는 클래스 (word count 할 때 자주 사용, 각 단어가 몇 번씩 나왔는지)
      from collections import Counter
      portfolio = [('GOOG', 100, 490.1), ('IBM', 50, 91.1), ('GOOG', 76, 490.1), ('APPLE', 102, 500.1)]
      
      total = Counter()
      for name, shares, price in portfolio: # 튜플이나 리스트는 분해 가능
          total[name] += price
      print(dict(total)) # {'GOOG': 980.2, 'IBM': 91.1}
      print(total.most_common(2)) # 상위 2개 출력 [('GOOG', 980.2), ('APPLE', 500.1)]
      
    • data = ["참외", "수박", "수박", "토마토", "수박", "참외"] counting = {} # dict를 하나 만들어놓고 for fruit in data: cnt = counting.get(fruit, 0) # get(): 지금까지 몇 번 나왔는지 확인, 없으면 0 주기 cnt += 1 counting[fruit] = cnt # 위 3코드 = counting[fruit] = counting.get(fruit, 0) + 1 print(counting) # {'참외': 2, '수박': 3, '토마토': 1} from collections import Counter counter = Counter(data) print(counter["참외"]) # 2 print(dict(counter)) # {'참외': 2, '수박': 3, '토마토': 1}
    • defaultdict: 하나의 key에 여러 개의 데이터를 저장할 수 있는 dict
    • 데이터를 추가로 저장할 때 append 사용
    • # 하나의 키에 여러 개의 데이터를 list로 보관 holdings = defaultdict(list) for name, shares, price in portfolio: holdings[name].append((shares, price)) for x in holdings: print(x, ":", holdings[x]) # 결과 GOOG : [(100, 490.1), (76, 490.1)] IBM : [(50, 91.1)] APPLE : [(102, 500.1)]
    • OrderedDict: dict는 key를 set으로 만들기 때문에 순서를 알 수 x, But, OrderedDict는 입력한 순서대로 key에 추가하기 때문에 순서를 기억할 수 있다.
    • from collections import OrderedDict keys = ["one", "two", "three"] values = (1, 2, 3) dic = OrderedDict(zip(keys, values)) for key in dic: print(key,":", dic[key]) # 결과 one : 1 two : 2 three : 3
    • itertools 모듈

2. 예외 처리

  • 오류 종류
  • 컴파일 오류: 문법적 오류
  • 예외(Exception): 실행 도중 외부 요인이나 잘못된 입력으로 발생하는 오류
  • Java에서는 file handling, network server, database server 만지는 일은 예외 처리 강제한다.
  • 논리적 오류: 알고리즘 잘못으로 잘못된 결과가 만들어지는 경우 → 이를 찾기 위해 boundary value analysis 같이 테스트를 해야 한다.
  • assert(단언): 오류나 예외가 아니지만 개발자가 고의적으로 특정 조건을 만족하지 않으면 고의적으로 중단시키는 것
  • 예외 처리를 하는 이유 → 크롤링에 많이 쓰임
  • 프로그램이 정상적으로 종료되도록 하기 위해서
  • 예외가 발생하더라도 프로그램을 계속 수행하기 위해서
  • 정상적인 값으로 수정해서 수행하기 위해서
  • 예외 처리 방법 → 로깅 중요!
  • try: 예외가 발생할 것 같은 코드 except: 예외가 발생했을 때 처리할 코드
  • 특정한 형태의 예외만 처리하고자 할 때 → https://docs.python.org/3/library/exceptions.html try: 예외가 발생할 것 같은 코드 except 예외 클래스 이름: 예외가 발생했을 때 처리할 코드
  • 예외 클래스 이름을 추가해서 작성→ except를 여러 개 연속해서 사용해도 된다.
  • except는 if처럼 동작하기 때문에 클래스이름이 없는 경우는 맨 뒤에 놓아야 함 → 앞에서 전체 에러를 다 잡아버리면 어떤 에런지 알 수가 없으니까
  • else 절에 예외가 발생하지 않을 때 수행할 코드 작성
  • finally는 예외 발생 여부에 상관없이 수행할 문장 작성
  • 강제로 예외 발생
  • raise 예외처리클래스이름(”메시지”)
  • 예외 객체를 사용할 때 except 예외클래스이름 as 변수명
def ten_div(x: int) -> float:
    if x == 10:
        raise Exception("강제로 예외 발생")
    return 10/x

print(ten_div(5))
try:
    print(ten_div(10))
except TypeError:
    print("자료형이 잘못된 경우 처리")
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")
except Exception as e:
    print(e) # 강제로 예외 발생
    print("앞에서 처리하지 못한 예외를 처리")
else: # 예외가 발생하지 않을때
    print("예외가 발생하지 않았을 때 처리")
finally:
    print("무조건 수행되는 처리")

print("정상 종료")

# 결과
2.0
강제로 예외 발생
앞에서 처리하지 못한 예외를 처리
무조건 수행되는 처리
정상 종료
  • assertion 만들기
  • assert 조건식
  • assert 조건식, 에러메시지 ⇒ 조건식이 False가 되면 AssertionError가 발생
x = 5
assert x >= 10, "x는 10보다 크거나 같아야 합니다."
# AssertionError: x는 10보다 크거나 같아야 합니다.

3. 파이썬 내장 모듈

  • os.path : 파일 경로를 생성 및 수정하고 파일 정보를 다룰 수 있도록 해준다.
import os.path
print(dir(os.path))
print(os.path.abspath("0502")) # 절대 경로 나온다. C:\\Users\\kimmy\\Documents\\python\\0502\\0502
  • 파일에서 중요한 정보는 수정 시간이나 생성 시간 등
    • getatime(마지막으로 접근한 시간), getmtime(마지막으로 수정된 시간), getctime(생성 시간 (Windows))
    • import os.path, time print(time.gmtime(os.path.getatime("exceptionhandling.py"))) print(os.path.getmtime("exceptionhandling.py")) print(os.path.getctime("exceptionhandling.py"))
  • glob : 경로 안의 파일이나 디렉토리를 순회할 수 있도록 해주는 모듈 → AI할 때 많이 쓰임
import glob
print(glob.glob("./*.*")) # ['.\\\\datatype.py', '.\\\\exceptionhandling.py', '.\\\\pyvenv.cfg']
  • os 모듈 : 디렉토리를 생성하거나 목록을 확인하거나 할 때 사용하는 모듈
import os
os.system("calc") # 계산기 실행
os.system("notepad") # 메모장 실행
  • sys 모듈 : 파이썬 인터프리터 관련 정보와 기능을 제공한다.
    • argv: 파이썬 파일이 실행될 때 넘어온 매개변수 정보
    • exec_info: 예외가 발생했을 때 넘어온 매개변수 정보
    • prefix, exec_prefix: 파이썬이 설치된 경로
    • exit(정수): 파이썬 프로그램 종료, 정수는 0이면 정상 종료고 그 이외의 숫자는 에러코드
    • executable: 파이썬 인터프리터 실행 파일 경로
    • getrefcount(데이터): 데이터의 참조 횟수를 리턴하는데 1증가해서 나옴 → 공부할 때 사용 ㅊㅊ
    • path: 모듈을 찾는 순서
    • getdefaultencoding: 현재 인코딩 방식
    • modules: 현재 로딩된 모듈의 목록을 리턴
import sys
print(f"현재 인코딩 방식: {sys.getdefaultencoding()}") # utf-8
  • 복사
    • 파이썬은 기본적으로 변수를 다른 변수에 대입하면 참조를 복사
    • 변수의 데이터를 수정하면 원본 데이터도 수정될 수 o
    • 스칼라 데이터는 참조가 복사되지만 값이 복사되는 형태로 동작한다.
    • copy 모듈에서 copy 메서드와 deepcopy 메서드를 제공하는데 copy는 얕은 복사를 수행해주고 deepcopy는 깊은 복사를 수행한다.
    • 1차원 데이터 묶음에서는 copy와 deepcopy가 차이가 없지만 2차원 이상 데이터 묶음에서 copy는 참조를 복사하는 형태로 동작하고 deepcopy는 재귀적으로 복제를 수행하므로 2차원 이상에서도 값을 복제하는 효과가 나타난다.
scala = 10
li = [100, 200, 300]

# 실제로는 참조가 복사되지만 작업을 수행하면 값이 복사되는 형태로 동작 -> id 찍어보면 앎
scalacopy = scala
print(id(scala))
print(id(scalacopy)) # 둘이 똑같음

licopy = li
print(id(li))
print(id(licopy)) # 둘이 똑같음

# 스칼라 데이터는 복제본에서 작업을 수행해도 원본에 영향이 없음
scalacopy = 20
print(scala) # 10 -> 10 변화 x
# 벡터 데이터는 복제본 전체를 교체하는 것은 원본에 아무런 영향을 주지 않지만
# 세부 데이터를 수정하는 것은 원본에 영향을 준다.
licopy[0] = 10000
print(li) # [100, 200, 300] -> [10000, 200, 300] 변화 o

# 일차원 벡터 데이터를 복제를 해주고자 하는 경우는 copy 모듈의 copy 메서드를 사용하면 된다.
import copy
weakcopy = copy.copy(li)
weakcopy[0] = 1
print(li) # [10000, 200, 300] -> [10000, 200, 300], li에 영향 x
print(weakcopy) # [1, 200, 300]

li = [[1,2,3], [4,5,6]]
weakcopy = copy.copy(li)
weakcopy[0][0] = 1000
print(li) # [[1000, 2, 3], [4, 5, 6]] -> 바뀌어 버림 => 2차원 이상은 copy하면 안됨
# weakcopy = copy.deepcopy(li) # 하면 재귀적으로 값 복사하기 때문에 li 안 바뀜 [[1, 2, 3], [4, 5, 6]]
728x90
반응형