데이터 스쿨 - 객체

박서앵·2025년 6월 17일

데이터 분석 기초

목록 보기
4/4

객체지향 프로그래밍

  • 객체를 이용한 프로그램으로 객체는 속성과 기능으로 구성됨
    ex. 계산기(객체)
    속성 : 숫자 / 기능 : 덧셈,뺄셈
    ex. 자동차(객체)
    속성 : 색상, 길이, 가격 / 기능 : 전진,후진,정지
    즉, 객체(object) = 속성(attribute) + 기능(function)

  • 장점
    코드 재사용 및 모듈화에 좋음

  • 객체는 클래스에서 생성됨
    즉 하나의 클래스를 만들어두면 여러개의 객체를 만들 수 있음
    ex. 자동차 클래스
    속성 : 색상,길이,가격
    기능 : 전진,후진,정지
    이후에 여러 자동차 객체를 찍어낼 수 있음

클래스 만들기

  • class키워드 + 속성(변수) + 기능(함수)를 이용해서 만듬
class Car: #클래스는 변수명을 대문자로 만드는게 관례임

	def __init__(self,col,len):   #생성자,속성
    	self.color = col
        self.length = len
        
    def doStop(self):         #기능
    	print('stop')
    
    def doStart(self):       #기능
    	print('start')

객체 생성

class Car: #클래스는 변수명을 대문자로 만드는게 관례임

	def __init__(self,col,len):   #생성자,속성
    	self.color = col
        self.length = len
        
    def doStop(self):         #기능
    	print('stop')
    
    def doStart(self):       #기능
    	print('start')
    
    def carInfo(self):
    	print(f'color : {self.color})
        print(f'length : {self.length})

위와 같은 Car 라는 클래스가 생성된 후엔 아래와 같이 객체를 만듬

car1 = Car('red',200)     #빨간, 200길이의 차
car2 = Car('blue',300)

car1.carInfo
car2.carInfo
car1.doStart

2개의 객체가 생성된 것임

객체 속성 변경

class NewPc:
	
    def __init__(slef, n, cpu, mem, ssd):
    	self.name = n
        self.cpu = cpu
        self.memory = mem
        self.ssd = ssd
        
    def doExcel(self):
    	print('excel run')
    
    def doPhoto(self):
    	print('photoshop run')
    
    def pcInfo(self):
    	print(f'name : {self.name}')
        print(f'cpu : {self.cpu}')
        print(f'memory : {self.memory}')
        print(f'ssd : {self.ssd}')

위와 같이 클래스를 먼저 생성

myPc = NewPc('my pc','i5','16G','256G')
myPc.pcInfo()

friendPc = NewPc('friendPc','i6','18G','512G')
friendPc.pcInfo()

my pc와 friendPc 를 비교해 보았을 때 친구 pc가 더 좋음.
나도 더 좋아지고 싶음

myPc.cpu = 'i9'
myPc.memory = '32G'
myPc.ssd = '1T'
myPc.pcInfo()

이후에 객체를 새로운 값으로 정의하고 변경하면 됨.

얕은 복사

클래스를 만들고 해당 데이터에 대한 주소만 복사하고 싶다면 얕은 복사가 가능함

<예시>
calss명 : Temcls
tc1 = Temcls(10,'blue')
tc2 = tc1

여기서 만약에 tc2를 바꾼다면 tc1도 바꿈
tc2.number = 12
tc2.color = 'red'

깊은 복사

클래스를 만들고 데이터에 대한 주소만 복사하는 것이 아닌, 또 다른 하나의 객체로 존재하게 됨
깊은 복사 사용 방법

import copy
<방법 1>
tc1 = Temcls(10,'blue')
tc2 = copy.copy(tc1)

<방법2>
scores = [9, 7, 2, 3]
scoresCopy = []

scoresCopy = scores
for s in scores:
	scoresCopy.append(s)
    
<방법3>
scoresCopy.extend(scores)

<방법4>
scoresCopy = scores.copy()

<방법5>
scoresCopy = scores[:]

클래스 상속

클래스는 또 다른 클래스를 상속해서 내 것처럼 사용할 수 있음

  • 클래스 () 안에 상속하고 싶은 클래스 이름을 넣어주면 됨
class NormalCar:
    def drive(self):
        print('[NormalCar]drive() called')

    def back(self):
        print('[NormalCar]back() called')


class TurboCar(NormalCar):
    def turbo(self):
        print('[TurboCar]turbo() called')


turboCar = TurboCar()

turboCar.turbo()
turboCar.back()
turboCar.drive()

---------------------------
[TurboCar]turbo() called
[NormalCar]back() called
[NormalCar]drive() called

생성자

객체가 생성될 때 생성자를 호출하면 init()가 자동 호출됨

class Cal:
    def __init__(self):
        print('__init__ called')

cal = Cal()
__________________________
__init__ called

init()가 속성을 초기화 함

<예시1>
class Cal:
    def __init__(self,n1,n2):
        print('__init__ called')
        self.num1 = n1
        self.num2 = n2

cal = Cal(10,20)
print(f'cal.num1 : {cal.num1}')
print(f'cal.num2 : {cal.num2}')
------------------------------------
<예시2>
class Cal:
    def __init__(self):
        print('__init__ called')
        self.num1 = 10
        self.num2 = 100

cal = Cal()
print(f'cal.num1 : {cal.num1}')
print(f'cal.num2 : {cal.num2}')
______________________________________
<예시3>
class Cal:
    def __init__(self,n):
        print('__init__ called')
        self.num1 = n
        self.num2 = 100

cal = Cal(3.14)
print(f'cal.num1 : {cal.num1}')
print(f'cal.num2 : {cal.num2}')

super()
상속을 하면 최초 클래스는 초기화가 됨

<예시>
class P_Class:

    def __init__(self,n1,n2):
        print('[P_Class] __init__() called')
        self.num1 = n1
        self.num2 = n2

class C_Class(P_Class):

    def __init__(self, n1, n2):
        print('[C_Class] __init__() called')
        self.num1 = n1
        self.num2 = n2

cls = C_Class(10,20)
_______________________________
[C_Class] __init__() called

만약 P_Class도 호출하고 싶다면?

<예시1>
class P_Class:

    def __init__(self,n1,n2):
        print('[P_Class] __init__() called')
        self.num1 = n1
        self.num2 = n2

class C_Class(P_Class):

    def __init__(self, n1, n2):
        print('[C_Class] __init__() called')

        P_Class.__init__(self,n1,n2)  #강제로 불러오기
        self.num1 = n1
        self.num2 = n2

cls = C_Class(10,20)

<예시2>
class P_Class:

    def __init__(self,n1,n2):
        print('[P_Class] __init__() called')
        self.num1 = n1
        self.num2 = n2

class C_Class(P_Class):

    def __init__(self, n1, n2):
        print('[C_Class] __init__() called')

        super().__init__(n1,n2)
        self.num1 = n1
        self.num2 = n2

cls = C_Class(10,20)

super()를 쓰면 상위 속성을 불러와서 사용할 수 있음.
super()는 self쓰지 않아도 됨.

다중상속

하나의 클래스 안에 2개 이상의 클래스를 상속할 수 있음

class Car01:
    def drive(self):
        print('drive() method called')
        
class Car02:
    def turbo(self):
        print('turbo() method called')


class Car03:
    def fly(self):
        print('fly() method called')
        
class Car(Car01,Car02,Car03):
    def __init__(self):
        pass

myCar = Car()
myCar.fly()
myCar.turbo()

오버라이딩

하위 클래스에서 상위 클래스의 메서드를 재정의함.
예 :
로봇 클래스 안에 fire() : 총알발사
뉴 로봇 클래스 안에 fire() : 레이저 발사로 바꾸는 것

class Robot:
    def __init__(self, c, h, w):
        self.color = c
        self.height = h
        self.weight = w


    def fire(self):
        print('미사일 발사')


    def printInfo(self):
        print(f'색상 : {self.color}')
        print(f'높이 : {self.height}')
        print(f'무게 : {self.weight}')


class NewRobot(Robot):
    def __init__(self, c, h, w):
        super().__init__(c,h,w)

    def fire(self):
        print('레이저 발사')

myRobot = NewRobot('red', 200, 300)
myRobot.printInfo()
myRobot.fire()
-------------------------------
색상 : red
높이 : 200
무게 : 300
레이저 발사

추상 클래스

상위 클래스에서 하위 클래스에 메서드 구현을 강요함.
예 : 선언만 되어 있던 클래스를 하위 클래스에서 구현하도록 하게함.
추상 클래스 사용을 위해선 아래 모듈을 불러와야함
from abc import ABCMeta
from abc import abstractmethod

그리고 필수로 구현하게 하고 싶은 것에 @abstractmethod 사용
다른 클래스에 상속하더라도 그 클래스에도 필수 메서드를 써야함

from abc import ABCMeta
from abc import abstractmethod

class Airplane(metaclass=ABCMeta):

    @abstractmethod
    def flight(self):   #상속 받는 애가 반드시 구현하게 하고 싶음
        pass

    def forward(self):
        print('전진')

    def backward(self):
        print('후진')

class Airliner(Airplane):
    def __init__(self,c):
        self.color = c

    def flight(self):
        print('시속 400 비행')

al = Airliner('red')
al.flight()
al.forward()

예외

예상하지 못한 문제로 프로그램 실행이 어려운 상태(문법적인 문제는 없음)
에러와는 다름(에러는 문법적,네트워크 등의 문제)

def add(n1,n2):
    print(n1+n2)

def div(n1,n2):
    print(n1/n2)

firstNum = int(input('n1 : '))
secondNum = int(input('n2 : '))

add(firstNum,secondNum)
div(firstNum,secondNum)
-------------------------------
n1 : 10
n2 : 0
10

ZeroDivisionError: division by zero

ZeroDivisionError: division by zero 처럼 0을 나눌 수 없을 때 나오는 것!

예외 종류
🔹 1. 문법 관련 예외
SyntaxError 문법 오류 (예: 괄호 닫힘 누락 등)
IndentationError 들여쓰기 오류
TabError 탭과 스페이스 혼용 등으로 생긴 들여쓰기 오류

🔹 2. 이름 및 참조 오류
NameError 정의되지 않은 변수나 함수 사용
UnboundLocalError 지역 변수 참조 전에 할당하지 않은 경우 (NameError의 하위 클래스)

🔹 3. 타입 및 값 오류
TypeError 잘못된 타입의 값 사용 (예: 숫자 + 문자열)
ValueError 타입은 맞지만 부적절한 값 사용 (예: int("abc"))

🔹 4. 인덱스 및 키 관련
예외명 설명
IndexError 리스트/튜플 등에서 존재하지 않는 인덱스 접근
KeyError 딕셔너리에 존재하지 않는 키 접근

🔹 5. 파일 및 입출력
예외명 설명
FileNotFoundError 존재하지 않는 파일 열기 시도
IOError (또는 OSError) 입출력 작업 중 오류 발생 (파일, 디스크 등)

🔹 6. 숫자 연산 오류
ZeroDivisionError 0으로 나누기 시도
OverflowError 수치 계산 결과가 너무 커서 표현 불가능할 때
FloatingPointError 부동소수점 연산 오류 (일반적으로 직접 발생시키지 않음)

🔹 7. 속성 및 모듈 오류
AttributeError 존재하지 않는 속성 접근
ImportError 모듈을 불러올 수 없을 때
ModuleNotFoundError 존재하지 않는 모듈을 import할 때 (ImportError 하위 클래스)

🔹 8. 기타 예외
AssertionError assert 문 실패 시 발생
NotImplementedError 아직 구현되지 않은 메서드를 호출할 때
RuntimeError 다른 구체적인 에러로 설명할 수 없는 일반적인 실행 오류
StopIteration next()에서 더 이상 반환할 것이 없을 때
KeyboardInterrupt 사용자(보통 Ctrl+C)에 의해 인터럽트 발생

예외처리 방법

예외가 프로그램 전체 실행에 영향이 없도록 처리함
프로그램을 실행 중 중간에 예외가 발생하면 멈추게되니 중간 예외가 발생해도 종료까지 진행되도록 하는 것

try~except

<예시>
n1 =10; n2 = 0
print(n1/n2)
print(n1*n2)
print(n1-n2)
-----------------------
    print(n1/n2)
          ~~^~~
ZeroDivisionError: division by zero

이렇게 예외가 발생하게 되었을 때 해결하는 방법

<예시>
n1 =10; n2 = 0

try:
    print(n1/n2)
except:
    print('예상치 못한 예외가 발생했습니다.')
    print('다음 프로그램은 정상 실행')

print(n1*n2)
print(n1-n2)
----------------------------------
예상치 못한 예외가 발생했습니다.
다음 프로그램은 정상 실행
0
10

예문 : 숫자만 포함되는 리스트 만들기

nums = []

n = 1
while n < 6:
    try:
        num = int(input('입력 : '))
    except:
        print('예외 발생')
        continue

    nums.append(num)
    n += 1

print(f'{nums}')

try~except~else
예외가 발생하지 않은 경우 실행하는 구문
즉, try구문이 정상적으로 진행되면 실행하도록 하는 구문.

<예시> 짝수만 리스트에 넣기
nums = []

n = 1
while n < 6:
    try:
        num = int(input('입력 : '))
    except:
        print('예외 발생')
        continue                 #컨티뉴로 1회로 치지 않도록하기
    else:
        if num % 2 == 0:
            nums.append(num)
            n+=1

        else:
            print('입력한 숫자는 홀 수 입니다.')
            print('다시 입력하세요.')
            continue

print(f'{nums}')

<예시2> 짝수, 홀수, 소수 구분하기
even = []; odd = []; floatlist = []

n = 1
while n <= 5:
    try:
        num = float(input('입력 : '))
    except:
        print('숫자만 입력하세요!')
        continue
    else:
        if num - int(num) != 0:
            floatlist.append(num)
        else:
            if num % 2 == 0:
                even.append(int(num))

            else:
                odd.append(int(num))

    n += 1

print(even)
print(odd)
print(floatlist)

finally
예외 발생과 상관없이 항상 실행함.

<예시> 어떤 정보를 입력하던지 입력데이터를 보여준다
try:
    inputData = input('num : ')
    numInt = int(inputData)
except:
    print('예외 발생! 숫자가 아닙니다.')
else:
    if numInt % 2 == 0:
        print('짝수')
    else:
        print('홀수')
finally:
    print(f'입력 데이터 : {inputData}')

<예시2> 짝수,홀수,소수리스트를 보여주고, 입력한 데이터를 전체 보여줘
even = []; odd = []; floatlist=[]; datalist = []


n = 1
while n <=5:
    try:
        inputData = input('num : ')
        floatData = float(inputData)
    except:
        print('예외 발생! 숫자가 아닙니다.')
        continue
    else:
        if floatData - int(floatData) != 0:
            print('소수')
            floatlist.append(floatData)
        else:
            if floatData % 2 == 0:
                print('짝수')
                even.append(int(floatData))
            else:
                print('홀수')
                odd.append(int(floatData))

        n+=1
    finally:
        datalist.append(inputData)


print(f'입력 데이터 : {datalist}')
print(f'짝수 : {even}')
print(f'홀수 : {odd}')
print(f'소수 : {floatlist}')

Exception 클래스

예외를 담당하는 클래스 > Exception

<예시>
n1 = int(input('n1 : '))
n2 = int(input('n2 : '))

try:
	print(f'n1 / n2 = {n1 / n2}')
except Exception as error:
    print('0ㅇ으로 나눌 수 없습니다.')
    print(f'exceoption : {error}')
    
   ------------------------------
n1 : 10
n2 : 0
0ㅇ으로 나눌 수 없습니다.
exceoption : division by zero  

except Exception 를 사용하면 무슨 예외가 발생했는지 알 수 있음
Exception — 모든 예외의 최상위 클래스
ValueError, TypeError 같은 예외들의 부모 클래스
커스텀 예외를 만들거나, 모든 예외를 포괄하고 싶을 때 사용해

raise : 강제로 예외를 발생시키는 것

def divCalculator(n1, n2):
    if n2 != 0:
        print(f'{n1} / {n2} = {n1 / n2}')
    else:
        raise Exception('0으로 나눌 수 없습니다.')   #예외를 강제로 만들어 냄
 
num1 = int(input('input number1: '))
num2 = int(input('input number2: '))
 
 try:
    divCalculator(num1, num2)
except Exception as e:                           
    print(f'Exception: {e}')

num2에 0이 들어가고, try~except 구문이 없다면 실행이 중단되면서 예외가 발생됨.
그래서 try~except 구문을 넣어서 만들어둔 예외 코드를 출력함

<예시> 글자 수에 따라 sms,mms 발송됨
def sendSMS(msg):
    if len(msg) > 10:
        raise Exception('길이 초과!! MMS전환 후 발송!!', 1)
    else:
        print('SMS 발송!!')

def sendMMS(msg):
    if len(msg) <= 10:
        raise Exception('길이 미달!! SMS전환 후 발송!!', 2)
    else:
        print('MMS 발송!!')

msg = input('input message: ')

try:
    sendSMS(msg)
except Exception as e:
    print(f'e: {e.args[0]}')  # 에러 메시지
    print(f'e: {e.args[1]}')  # 전환 번호

    if e.args[1] == 1:
        sendMMS(msg)   # 길이 초과 → MMS 전환
    elif e.args[1] == 2:
        sendSMS(msg)   # 길이 미달 → SMS 전환

사용자 예외 클래스

사용자가 사용할 예외 클래스를 만들고 그걸 사용할 수 있음
class 이름 아무거나(Except)를 받아주면됨

<예시>
#사용자 정의 예외 클래스
class NotUseZeroException(Exception):
    def __init__(self, n):
        super().__init__(f'{n}은 사용할 수 없습니다!!')

#계산 함수
def divCalculator(num1, num2):
    if num2 == 0:
        raise NotUseZeroException(num2)
    else:
        print(f'{num1} / {num2} = {num1 / num2}')

#사용자 입력 및 예외 처리
num1 = int(input('input number1: '))
num2 = int(input('input number2: '))

try:
    divCalculator(num1, num2)
except NotUseZeroException as e:
    print(e)
<예시>
class PasswordLengthShortException(Exception):
    def __init__(self,pw):
        super().__init__(f'{pw} : 길이 5미만!')

class PasswordLengthLongException(Exception):
    def __init__(self,pw):
        super().__init__(f'{pw} : 길이 10초과!')

class PasswordWrongException(Exception):
    def __init__(self,pw):
        super().__init__(f'{pw} : 잘못된 비밀번호')


n=1
while n < 6:
    pw = input('비밀번호 : ')
    try:
        if len(pw) < 5:
            raise PasswordLengthShortException(pw)
        elif len(pw) > 10:
            raise PasswordLengthLongException(pw)
        elif pw == 'admin1234':
            print('비밀번호 확인 완료!')
            break
        else:
            raise PasswordWrongException(pw)
    except (PasswordLengthShortException, PasswordLengthLongException, PasswordWrongException) as e:
        print(f'{e}')
        n += 1

else:
    print('비밀번호 5회 이상 오입력')
profile
포기가 제일 쉽다 박배추

0개의 댓글