학습일지/K-Digital Traing

[KDT] AIaaS 마스터클래스 9주차 - Python 입문

tierr 2025. 5. 19. 17:49

객체를 복사하는 법

❗아래 코드에서 list1과 list2가 같은 객체를 공유하고 있기 때문에 한쪽을 수정하면 다른쪽도 영향을 받는다.

list1 = [1, 2, 3]
list2 = list1        # list1과 list2는 같은 객체를 공유
list2[0] = 10
print(list1)         # 👉 [10, 2, 3] 출력됨

 

그렇다면 list1과 list2를 별개로 만드는 법은?

-> 복사를 하면 된다

# 방법 1: copy() 메서드
list2 = list1.copy()
# 방법 2: 슬라이싱
list2 = list1[:]
# 방법 3: list() 생성자
list2 = list(list1)
# 방법 4: copy 모듈의 deepcopy()
# (※ 리스트 안에 또 리스트가 있는 중첩 구조일 경우 필요)
import copy
list2 = copy.deepcopy(list1)

 

✅ 참고: 얕은 복사 vs 깊은 복사

복사 방식 중첩 구조(리스트 안에 리스트)에 안전한가?
copy() / [:] / list() ❌ 얕은 복사
copy.deepcopy() ✅ 깊은 복사 (중첩까지 복사)

round(반올림) 함수에 대해서

# 기본 문법
# round(number, ndigits)
# ㄴnumber : 반올림할 숫자
# ㄴndigits: 소수점 아래 몇 자리까지 반올림할지, 기본값은 0 (optional)

# 사용 예시 1 : 소수점없이 반올림
round(3.6)   # 👉 4
round(3.2)   # 👉 3

# 사용 예시 2: 소수점 아래 반올림
round(3.14159, 2)  # 👉 3.14
round(2.675, 2)    # 👉 2.67 (주의: 부동소수점 영향)

 

딥다이브) 왜 0.1 + 0.2가 0.3이 아닌 0.30000000000000004일까?

더보기
더보기

이유: 컴퓨터는 10진수를 2진수로 정확히 표현할 수 없음

  • 0.1, 0.2는 2진수로 정확히 표현되지 않는 유리수예요.
  • 마치 1/3이 무한소수 0.3333...처럼, 0.1은 2진수로 무한소수 → 근사값으로 저장됨.
  • 그래서 계산하면 오차가 누적되어 0.30000000000000004 같은 결과가 나옴.

✅ 해결 방법

# 문제
print(0.1 + 0.2) # 0.30000000000000004

# 해결 방법 1: round() 함수로 반올림
print(round(0.1 + 0.2, 2))  # 👉 0.3

# 해결 방법 2: Decimal 모듈 사용 (정밀 계산)
from decimal import Decimal

a = Decimal("0.1")
b = Decimal("0.2")
print(a + b)  # 👉 0.3

f-string의 기본 사용법 ( Python 3.6 이상 사용 가능 )

name = "Alice"
age = 25
print(f"My name is {name} and I am {age} years old.")

불리언 (bool)

print(bool(1))        # True
print(bool(0))        # False
print(bool(5))        # True (0이 아닌 모든 숫자는 True)

// 비어있지 않은 문자열, 리스트 등 = True
print(bool(123))      # True
print(bool("hello"))  # True
print(bool([1,2]))    # True

// 빈 문자열, 리스트 등 = False
print(bool(None))     # False
print(bool(""))       # False
print(bool([]))       # False

논리연산자

파이썬은 C/자바 스타일 논리 연산자(&&, ||, !)를 사용하지 않음

x = True
y = False

print(x and y) # False
print(x or y)  # True 
print(not x)   # False

객체 비교

같은 객체냐, 같은 값을 가지고 있냐는 별개의 문제이므로, 비교하는 구문을 구분해서 사용해야한다.

# 객체 비교
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1

print(list1 == list2)   # True  (내용이 같음)
print(list1 is list2)   # False (다른 객체)
print(list1 is list3)   # True  (같은 객체)

 

바교 의미
== 값(내용)이 같은가
is 동일한 객체인가 (id가 같은가)

데이터 타입 변환하는 법

# 정수로 변환
int(3.14)        # 3
# 실수로 변환
float(5)         # 5.0
# 문자열로 변환
str(True)        # "True"
# 불리언으로 변환
bool("Hello")    # True

 

컬렉션 타입이란?

여러 값을 하나로 묶어서 저장할 수 있는 자료형

 

✅ 파이썬의 주요 컬렉션 타입

타입 형태 중복 허용 여부 특징
리스트(List) [1, 2, 3] ✅ 허용 순서 있음, 변경 가능
튜플(Tuple) (1, 2, 3) ✅ 허용 순서 있음, 변경 불가
집합(set) {1, 2, 3} 불허용 순서 없음, 중복 제거
딕셔너리(Dictionary) {"a": 1, "b": 2} 키 중복 불가 키-값 쌍, 순서 있음 (Python 3.7+)

List 생성법

# 1. 대괄호로 생성
fruits = ["사과", "바나나", "체리"]
empty_list1 = []

# 2. list() 함수 사용
numbers = list(["사과", "바나나", "체리"])
empty_list2 = list()

리스트 컴프리헨션(List Comprehension)

기본 문법

[표현식 for 변수 in 반복가능한_객체]
[표현식 for 변수 in 반복가능한_객체 if 조건]

 

예제

squares = [x**2 for x in range(1, 6)]
# 👉 [1, 4, 9, 16, 25]

evens = [x for x in range(10) if x % 2 == 0]
# 👉 [0, 2, 4, 6, 8]

words = ["apple", "banana", "cherry"]
lengths = [len(word) for word in words]
# 👉 [5, 6, 6]

리스트 슬라이싱 (List Slicing)

# 리스트 접근과 슬라이싱
fruits = ["사과", "바나나", "체리", "딸기", "오렌지"]

# 슬라이싱: 범위 지정 [시작:끝:단계]
print(fruits[1:4])     # ['바나나', '체리', '딸기'] (1번부터 3번까지)
print(fruits[:3])      # ['사과', '바나나', '체리'] (처음부터 2번까지)
print(fruits[2:])      # ['체리', '딸기', '오렌지'] (2번부터 끝까지)
print(fruits[::2])     # ['사과', '체리', '오렌지'] (처음부터 끝까지 2단계씩)
print(fruits[::-1])    # ['오렌지', '딸기', '체리', '바나나', '사과'] (역순)

리스트 수정

# 리스트에서 특정 아이템을 명시해서 제거
fruits = ["사과", "바나나", "체리"]
fruits.remove("토마토") # "토마토"라는 항목이 없기 때문에 ERROR

결과: ValueError: list.remove(x): x not in list

.remove(x)는 리스트에 x가 없으면 ValueError 예외가 발생합니다.

 

✅ 해결 방법: 에러 안 나게 안전하게 제거하는 3가지 방법

 

1. if x in list: 로 체크 후 제거

✔️ 가장 일반적이고 명확한 방법

fruits = ["사과", "바나나", "체리"]
if "딸기" in fruits:
    fruits.remove("딸기")  # 딸기가 없으면 실행 안 함

 

2. try-except로 예외 처리

✔️ 여러 개의 항목을 반복 제거할 때 유용

try:
    fruits.remove("딸기")
except ValueError:
    pass  # 없으면 그냥 넘어감

 

3. 리스트 컴프리헨션으로 새 리스트 만들기

✔️ 원본 리스트에서 "딸기"를 제거한 새 리스트를 만들기

fruits = [x for x in fruits if x != "딸기"]

✅ 참고)「i for」 vs「for i」

# 1. i for ... → 리스트 컴프리헨션
nums = [1, 2, 3, 4]
squares = [i**2 for i in nums]

# 2. for i, ... → 반복문에서 다중 변수 언패킹
pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
for i, char in pairs:
    print(i, char)
  • i for ...는 리스트를 만들기 위한 표현식
    new_list = [표현식 for 변수 in 반복가능한_객체]
  • for i, char in ...은 각 튜플을 자동으로 나누어 변수에 할당함
    이걸 튜플 언패킹(unpacking)이라고 부른다.

구문 용도 예시
i for i in ... 리스트 컴프리헨션 [i for i in range(5)]
for i, x in ... 반복문에서 다중 변수 언패킹 for i, x in enumerate(lst):

enumerate

• 반환 형태 : (인덱스, 요소) 형태의 튜플을 반환

letters = ['a','b','c']
enum_obj = enumerate(letters)

# enumerate 리스트로 변환하기
print(list(enum_obj)) # {0: 'a', 1: 'b', 2: 'c'}
# ⚠️ enumerate는 한 번만 순회되는 이터레이터라서 list(enum_obj)를 하면 그 이후엔 비어 있음
print(list(enum_obj)) # {}

 

0부터 시작하는게 거슬리면 start옵션으로 인덱스 시작번호를 바꿀 수 있음

# index는 0부터 시작이지만 start=1로 지정 가능
# start 외 다른 옵션은 없음
for index, fruit in enumerate(fruits, start=1):
    print(f"{index}번: {fruit}")

 

* zip

zip은 두 리스트의 요소를 순서대로 짝 지어 튜플로 만듬

# 여러 리스트 동시 순회 (zip과 함께 사용)
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for i, (name, score) in enumerate(zip(names, scores), start=1) :
    print(f"학생 {i}: {name}, 점수: {score}")
# enumerate(..., 1)에서 1은 시작 인덱스를 의미 (1부터 카운팅)

 

예제 ) 할일 목록 관리 

# 할 일 목록 (전역변수)
todo = []

# 할 일 추가
def add_task(task) :
    print("---------add_task---------")
    todo.append(task)
    print(f"'{task}' 추가 완료")

# 할 일 목록 조회
def view_tasks() :
    print("---------view_tasks---------")
    for index, task in enumerate(todo, start=1) :
        print(f"{index} 번째 할 일 : {task}")

# 할 일 완수
def complete_task(i) :
    print("---------complete_task---------")
    removed = todo.pop(i-1)
    print(f"{i}번째 할 일 '{removed}'를 완수했습니다.")


# 프로그램 실행 예시
add_task("파이썬 공부하기")
add_task("장보기")
add_task("운동하기")
view_tasks()
complete_task(1)    # "장보기" 완료
view_tasks()

중첩 리스트

리스트 컴프리헨션을 사용해서 중첩리스트 만드는 법

# 직접 생성
nested = [[1, 2, 3], [4, 5, 6]]

# 반복문으로 생성
rows, cols = 3, 4

# 리스트 컴프리헨션으로 생성 (더 간결한 방법)
matrix = [[i * cols + j for j in range(cols)] for i in range(rows)]

print(matrix)

# 결과
# [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]

얕은 복사 vs 깊은 복사

✅ 얕은 복사의 문제점 예시 (shallow copy)

# 얕은 복사의 문제점 예시
original = [1, 2, [3, 4]]
shallow = original.copy()

# 내부 리스트 요소 변경
shallow[2][0] = 30

print(original)  # [1, 2, [30, 4]] - 원본도 변경됨!
print(shallow)   # [1, 2, [30, 4]]

 

✅ 깊은 복사 (deep copy)

import copy

original = [1, 2, [3, 4]]
deep = copy.deepcopy(original)

# 내부 리스트 요소 변경
deep[2][0] = 30

print(original)  # [1, 2, [3, 4]] - 원본은 변경되지 않음
print(deep)      # [1, 2, [30, 4]]

중첩 리스트 생성시 주의할 점

# 잘못된 방법
row = [0] * 3
bad_matrix = [row] * 3
bad_matrix[0][0] = 1  # 모든 행의 첫 번째 요소가 1로 변경됨
print(bad_matrix)     # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

# 올바른 방법
good_matrix = [[0 for _ in range(3)] for _ in range(3)]
good_matrix[0][0] = 1
print(good_matrix)    # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

 

왜 이런 차이가 생길까?

❌ bad_matrix = [row] * 3 는 "얕은 복사(shallow copy)"

  • row 하나만 만들고 그걸 3번 반복해서 같은 객체를 참조한 상태
  • 즉, bad_matrix[0], bad_matrix[1], bad_matrix[2]는 모두 같은 리스트 객체를 가리킴

✅ good_matrix는 "완전히 새로운 리스트 생성"

  • for _ in range(3) 반복할 때마다 new list를 새로 만들어서 서로 다른 리스트 객체 3개를 생성
  • 그래서 한 줄을 수정해도 다른 줄엔 영향이 없음.

참고) Python 코딩 규약 - PEP 8

✅ PEP 8이란?

Python Enhancement Proposal #8제목: "Style Guide for Python Code"파이썬 코드를 일관되고,
가독성 있게 작성하도록 규정한 문서
작성자: Guido van Rossum (파이썬 창시자 외)

 

▶ 변수는 소문자 + 밑줄(_)

user_name = "Alice"
total_count = 10

 

▶ 클래스 이름은 파스칼 케이스 (대문자 시작)

class UserProfile:

 

▶ 상수는 모두 대문자 + 밑줄

MAX_USERS = 100
PI = 3.14

 

 PEP 8은 여전히 파이썬의 공식 스타일 가이드이며, 최신이다.


Quiz

변수 Quiz)

# 문제 다음 코드의 출력 결과는 무엇인가요?
x = 10
y = 20
x, y = y, x
print(x, y)

 

변수 Quiz 정답)


데이터 타입 Quiz)

# 문제 1: 다음 코드의 출력 결과는 무엇인가요?
a = 0.1 + 0.1 + 0.1
b = 0.3
print(a == b)

# 문제 2: 다음 중 False로 평가되지 않는 것은 무엇인가요?
a) 0  
b) ""  
c) [0]  
d) None  
e) False  

# 문제 3: 다음 코드의 실행 결과와 그 이유를 설명하세요.
text = "Python"
text[0] = "J"
print(text)

 

데이터 타입 Quiz 정답)

더보기
더보기

1. 부동소수점으로 인해 컴퓨터가 표현가능한 2진수의 한계가 있기 때문에, 내부적으로 근사치로 계산된다. 

0.30000000000000004가 되므로 b = 0.3과 다르다고 판단됨.

 

2. 정답은 c.  비어있지 않은 리스트는 boolean 값이 True이다.

 

3. 문자열 str은 immutable(불변형) 자료형이기 때문에 인덱스를 이용해 직접 수정할 수 없음.


컬렉션 타입 - 리스트 Quiz 문제)

# 1. 다음 코드의 실행 결과는?
numbers = [1, 2, 3, 4, 5]
numbers[1:4] = [10, 20]
print(numbers)

# 2. 다음 코드의 실행 결과는?
def modify_list(lst):
    lst.append(4)
    lst = [7, 8, 9]
    return lst

original = [1, 2, 3]
result = modify_list(original)
print(original)
print(result)

# 3. 다음 코드를 실행하면 어떤 오류가 발생할까요?
matrix = [[0] * 3] * 3
matrix[0][0] = 1
matrix[1][1] = 2
print(matrix)

 

컬렉션 타입 - 리스트 Quiz 정답)

더보기
더보기

1.
[1, 10, 20, 5]

2.
[1, 2, 3, 4]
[7, 8, 9]

3. 모든 행의 값이 동시에 바뀌어버림
[[1, 2, 0], [1, 2, 0], [1, 2, 0]]


과제

학생들의 이름과 점수 정보를 리스트로 관리하는 코드 구현
다음 기능을 구현하세요:

  • 학생 추가: 이름과 점수를 입력 받아 목록에 추가
  • 학생 삭제: 이름을 입력 받아 해당 학생 정보 삭제
  • 성적 수정: 이름을 입력 받아 해당 학생의 점수 수정
  • 전체 목록 출력: 모든 학생의 이름과 점수 출력
  • 통계 출력: 최고 점수, 최저 점수, 평균 점수 계산 및 출력
더보기
더보기
# pyhon/StudentManager.py
from collections import Counter

# 과제 1. 학생들의 이름과 점수 정보를 리스트로 관리하는 코드 구현
# 학생 추가: 이름과 점수를 입력 받아 목록에 추가
def add_students(students:list):
    while True:
        name = input(f"이름을 입력하세요: ")
        try:
            score = int(input(f"점수를 입력하세요: "))
            if not (0<= score <= 100):
                raise ValueError
        except ValueError:
            print("점수는 0~100 사이의 숫자로 입력해주세요.")
            continue
        student_names = [student[0] for student in students]
        if is_student_exist(name, students) :
                print(f"* 해당 이름({name}) 중복입니다. 자동으로 뒤에 인덱스가 붙습니다.")
                count = 1
                new_name = f"{name}{count}"
                while new_name in student_names:
                    count += 1
                    new_name = f"{name}{count}"
                name = new_name
        print(f"새로운 학생을 추가합니다. 이름: {name}, 점수 {score}")
        students.append([name, score])
        if input(f"계속 입력하시겠습니까? (y/n) : ") == "y":
            continue
        else:
            print(f"처리를 종료합니다.")
            print(f"입력내용: {students}")
            break

# 학생 삭제: 이름을 입력 받아 해당 학생 정보 삭제
def delete_student(students:list):
    name = input(f"삭제할 학생의 이름을 입력하세요: ")
    if not is_student_exist(name, students) :
        return
    for i, student in enumerate(students):
        if student[0] == name:
            del students[i]
            print(f"학생 {name}을 삭제했습니다.")
            break

# 성적 수정: 이름을 입력 받아 해당 학생의 점수 수정
def modify_student(students:list):
    name = input(f"점수를 수정할 학생의 이름을 입력하세요: ")
    if not is_student_exist(name, students) :
        return
    try:
        new_score = int(input(f"점수를 입력하세요: "))
        if not (0<= new_score <= 100):
            raise ValueError
    except ValueError:
        print("점수는 0~100 사이의 숫자로 입력해주세요.")
        return
    for i, student in enumerate(students):
        if student[0] == name:
            student[1] = new_score
            print(f"학생 {name}의 점수를 수정했습니다.")

# 전체 목록 출력: 모든 학생의 이름과 점수 출력
def print_student_list(students:list):
    if not students:
        print("학생 정보가 없습니다.")
        return
    for i, student in enumerate(students, start=1) :
        print(f"----{i} 번째 학생----")
        print(f"이름: {student[0]}")
        print(f"점수: {student[1]}")
    return

# 통계 출력: 최고 점수, 최저 점수, 평균 점수 계산 및 출력
def print_stats(students:list):
    print("<<<<< 통계 출력 >>>>>")
    if not students:
        print("학생 정보가 없습니다.")
        return
    scores = [int(student[1]) for student in students]
    print(f"최고 점수 : {max(scores)}")
    print(f"최저 점수 : {min(scores)}")
    print(f"평균 점수 : {sum(scores) / len(scores)}")
    return

# 학생 확인: 이름을 입력 받아 해당 학생의 정보가 존재하는지 확인
def is_student_exist(name, students_list:list):
    student_names = [student[0] for student in students_list]
    if name not in student_names :
        print(f"해당하는 학생이 존재하지 않습니다 : {name}")
        return False
    return True

students_list = [["김철수", 85], ["이영희", 99]]
while(True):
        print("----------학사 정보 시스템----------")
        print(" 1: 학생 추가 \n 2: 학생 삭제\n 3: 성적 수정 \n 4: 목록 출력 \n 5: 통계 출력\n 기타: 종료")
        choice = input(f"작업을 입력하세요: ")
        if choice == "1" :
            add_students(students_list)
        elif choice == "2" :
            delete_student(students_list)
        elif choice == "3" :
            modify_student(students_list)
        elif choice == "4" :
            print_student_list(students_list)
        elif choice == "5" :
            print_stats(students_list)
        else : break

본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 (B-log) 리뷰로 작성 되었습니다.