🍋객체(object): 함수와 변수를 하나의 단위로 묶을 수 있는 방법
🍋객체지향(object-oriented): 객체로 작성된 프로그래밍 방식, 소프트웨어도 객체로 구성하는 방법
->서로 관련 있는 데이터와 함수를 묶어서 객체로 만들고 이들 객체들이 모여서 하나의 프로그램이 됨
현실 세계에서는 사람, 자동차, 세탁기 등의 많은 객체가 존재
객체들은 객체 나름대로 고유한 기능을 수행하면서 다른 객체들과 메시지를 통해 상호작용함
다양한 기능을 하는 소프트웨어 객체들을 작성하고, 이러한 객체들을 조합하여 자기가 원하는 기능을 구현하는 기법
절차 지향과 객체 지향
🍋절차 지향 프로그래밍(procedural programming): 프로시저(procedure)를 기반으로 하는 프로그래밍 방법
프로시져: 함수
전체 프로그램들은 함수들의 집합으로 이루어짐
C언어에서 지원하는 방법
문제를 더 작은 서브 함수로 분해하여 원하는 수준에 도달할 때까지 이 과정을 반복함으로써 문제 해결
-단점: 서로 관련되 데이터와 함수를 묶을 수가 없음-> 변수와 함수를 묶어주는 도구가 없음
객체 지향 프로그래밍
🍋객체 지향 프로그래밍(object-oriented programming): 데이터와 함수를 하나의 덩어리로 묶어서 생각하는 방법
🍋캡슐화(encapsulation): 데이터와 함수를 하나의 덩어리로 묶어서 생각하는 방법
🍋객체(object): 하나의 물건 ex)자동차
🍋속성(attribute): 모델, 연식, 가격 등
🍋객체의 동작(action): 방향전환, 정지
클래스란?
설계도에 의해 각각의 자동차가 만들어짐
🍋클래스(Class): 객체에 대한 설계도, 특정한 종류의 객체들을 찍어내는 형틀 또는 청사진
🍋인스턴스(instance): 클래스로부터 만들어지는 객체
->객체가 너무 광범위한 의미를 지니고 있어서 특정한 클래스로부터 생성된 객체를 그 클래스의 인스턴스라고 함
💡클래스를 통하여 객체를 생성하는 이유
->프로그램에서는 같은 종류의 객체가 많이 필요하기 때문
ex) 슈팅 게임 프로그램 미사일 나타내는 객체 많이 필요. 하나씩 정의해서 생성하기보다 클래스를 만들어두고 필요할 때마다 객체를 생성하는 게 편리함
파이썬에서는 모든 것이 객체이다.
정수도 객체, 문자열도 객체, 리스트도 객체
객체의 특징: 메소드를 가지고 있음
ex)리스트: insert(), remove()
🍋공용 인터페이스(public interface): 클래스에 의하여 제공되는 메소드
어떤 메소드를 사용할 수 있고 메소드가 하는 작업만 알면 됨
🍋캡슐화(encapsulatin): 공용 인터페이스만 제공하고 구현 세부 사항을 감추는 것, 데이터와 알고리즘을 하나로 묶는 것
#Lab: TV클래스 정의
객체=변수+메소드
텔레비전 개체
변수: channel, volume, on
메소드: turnOn(), turnOff(), changeChannel(), changeVolume()
클래스 작성하기
🍋클래스: 객체의 형태를 정의하는 틀(template)
->객체를 찍어낼 수 있는 틀
#Syntax: 클래스 정의
#형식
class 클래스이름:
def __init__(self, ...):
...
def 메소드1(self, ...):
...
def 메소드2(self, ...):
...
#예시
class Counter:
def __init__(sel): #생성자 정의
self.count=0 #인스턴스 변수 생성
def increment(self): #메소드 정의
self.count+=1
클래스 안에서 변수와 함수를 정의
🍋인스턴스 변수: 클래스 안에서 정의된 변수
🍋메소드(method): 클래스 안에 정의되 함수
🍋멤버(member): 인스턴스 변수와 메소드를 합쳐서 클래스의 멤버라고 함
->인스턴스 변수는 객체의 속성을 나타내고 메소드는 객체의 동작을 나타냄
🧄Counter클래스: 경기장이나 콘서트에 입장하는 관객 수를 세기 위해 사용 가능
카운터값을 나타내는 변수를 가지고 있어야 함
파이썬에서는 메소드 안에서 변수 이름 앞에 self.을 붙이고 값을 저장하면 저장하면 인스턴스 변수가 됨
실제 카운터는 사용하기 전에 카운터의 값을 0으로 만들어야 함
생성자 이름은 항상 __init__()
생성자 메소드는 객체가 생성되면 반드시 호출되어서 객체를 초기화하는 함수
실제 카운터에는 사용자가 클릭하면 카운터 값이 하나 증가되는 버튼이 있음
이걸 increment()메소드로 구현함
클래스 정의할 때는 키워드 class로 시작하며 클래스의 이름을 뒤에 붙임
클래스 이름의 첫 글자는 일반적으로 대문자로 함
클래스 본체 안에 메소드 정의
첫 번째 매개 변수는 객체를 가리키는 self 변수여야 함
🧄__init__() 메소드: 생성자(constructor)
init 앞에 반드시 __을 추가해야 함
또 항상 자신을 가리키는 self가 매개변수에 포함됨
인스턴스 변수는 생성자 안에서 초기화해야 함
🧄increment()는 일반적인 메소드
메소드의 첫 번째 매개 변수는 항상 self
인스턴스 변수에 접근할 때도 self.count와 같이 앞에 self를 붙여야 함
객체 생성
클래스 정의는 단순히 객체를 찍어내기 위한 틀을 생성한 것
아직 실제 객체는 생성되지 않음
클래스는 객체를 만들기 위한 설계도에 해당
a=Counter()
클래스 이름에 ()를 붙여서 함수처럼 호출하면 객체 생성됨
객체가 생성되면서 생성자 메소드인 __init__()가 자동으로 호출됨
객체의 멤버 접근
메소드 호출: 객체 이름에 점(.)을 붙이고 메소드 이름을 적어주면 됨
개체가 가지고 있는 변수에 접근할 때도 객체 이름에 점(.)을 붙이고 변수 이름을 적어주면 됨
#Syntax: 객체 멤버 접근
#형식
객체이름.변수
객체이름.메소드(인수)
#예시
a=Counter()
a.increment()
a.count=100
생성되 객체의 인스턴스 변수를 변경해보고, 객체의 메소드 호출하기
class Counter:
def __init__(self):
self.count=0
def increment(self):
self.count+=1
a=Counter()
a.increment()
print("카운터의 값=", a.count)
>>>카운터의 값=1
Counter()라고 호출하면 객체가 생성되고 객체의 참조값을 변수 a에 저장함
객체가 생성되면서 생성자 메소드 __init__()가 호출되어서 인스턴스 변수 count가 생성되고 0으로 초기화됨
a의 increment()를 호출하면 count 값이 하나 증가됨
a.count 하면 객체 a의 변수 count의 현재값을 얻을 수 있음
파이썬에서는 클래스 당 하나의 생성자만을 허용함
맥변수에 기본값을 줄 수 있는 기능을 사용하면 어느 정도 보완됨
생성자도 함수의 일종이므로 매개 변수를 가질 수 있음
ex) Counter 클래스의 생성자에세 카운터의 초기 값을 받아서 그 값으로 초기화한다고 하자.
만약 사용자가 초기값을 주지 않으면 0으로 생각하자.
class Counter:
def __init__(self, initValue=0):
self.count=initValue
#생성자가 매개 변수를 가지고 있으며 만약 사용자가 값을 전달하지 않았으면 0으로 생각
a=Counter(100) #카운터의 초기값은 100이 됨
b=Counter() #카운터의 초기값은 0이 됨
클래스 당 하나의 생성자만 허용됨
매개변수에 기본값을 줄 수 있는 기능을 사용하면 어느 정도 보완됨
생성자도 함수의 일종이므로 매개변수를 가질 수 있음
사용자가 초기값을 주지 않으면 0으로 생각하게 하기
class Counter:
def __init__(self, initVaue=0): #생성자가 매개변수를 가지고 있으며 만약 사용자가 값을 전달하지 않았으면 0으로 생각
self.count=initValue
2가지 방법으로 생성자 호출 가능
a=Conter(100) #카운터의 초기값 100
b=Counter() #카운터의 초기값 0
하나의 클래스로 객체는 많이 만들 수 있다
Counter 클래스를 가지고 2개의 객체 생성
class Counter:
def __init__(self, initValue=0):
self.cont=initValue
def increment(self):
self.count+=1
a=Counter(0) #0으로 초기화 한 Counter 객체 생성
b=Counter(100) #100으로 초기화 한 Counter 객체 생성
객체 a와 b는 모두 자신만의 count 인스턴스 변수를 가짐
객체가 가진 데이터는 객체마다 다르게 설정 가능
🍨Self 매개 변수
self: 객체 자신을 참조하는 변수
self가 있어야만 객체 안에서 멤버에 접근 가능
메소드가 호출될 때, 어떤 객체가 메소드를 호출했는지 알아야 함
a객체가 show()를 호출했는지 b객체가 show()를 호출했는지 show()메소드가 알아야 함
self매개 변수는 어떤 객체가 메소드를 호출했는지를 알려줌
sho()호출할 때 개체를 인수로 넣어서 보내지 않음
sho()를 호출할 때는 항상 "객체.메소드()"와 같은 형식을 사용
점 연산자 앞에 있는 객체가 바로 self로 전달됨
a.show()라고 호출하면 a가 show() 메소드의 self로 전달됨
🍨인스턴스 변수의 범위
생성자 안에서 인스턴스 변수가 생성되면, 이 변수의 범위는 클래스 전체가 됨
__init__()메소드에서 생성된 변수 count의 범위는 counter클래스 전체
*지역변수는 메소드 안에서 생성되 변수, 범위는 메소드 안
지역 변수는 메소드를 벗어나면 사라짐
반면 인스턴스 변수는 클래스 전체에서 사용 가능
🍨인스턴스 변수와 지역 변수의 구별
메소드 안에서 self를 붙이지 않고 변수를 생성하면 지역 변수가 됨
def show(self):
s="현재 설정값" #지역변수
print(s, self.count)
s는 앞에 self가 없어서 지역변수
메소드도 마찬가지
클래스 안에서 다른 메소드를 호출하려면 항상 앞에 self.를 붙여야 함
sho()메소드를 클래스 안에서 사용하려면 self.sho()라고 해야 파이썬이 올바르게 연결함
🍨인스턴스 변수와 지역 변수의 구별
🍋지역변수: 함수 안에서 선언되는 변수
🍋전역변수: 함수 외부에서 선언되는 변수
🍋인스턴스 변수: 클래스 안에 선언된 변수, 앞에 self.가 붙음
💡파이썬에서 생성자를 만드는 이유
1. 객체 초기화: 생성자는 객체가 생성될 때 자동으로 호출되어 객체의 초기 상태를 설정합니다. 클래스에 정의된 변수나 멤버를 초기화하는 데 주로 사용됩니다.
2. 리소스 할당: 생성자는 객체가 생성될 때 필요한 리소스를 할당하는 역할을 합니다. 예를 들어, 파일을 열거나 데이터베이스 연결을 설정하는 등의 작업을 수행할 수 있습니다.
3. 타입 체크 및 유효성 검사: 생성자는 인자의 타입이나 유효성을 검사하는 데도 사용될 수 있습니다. 예를 들어, 특정 인자가 숫자만을 받아야 하는 경우, 생성자에서 이를 확인하고 그렇지 않은 경우 오류를 발생시킬 수 있습니다.
기본적으로, 클래스를 사용하는 이유는 데이터와 그 데이터를 다루는 메소드를 하나의 집합으로 묶어 관리하려는 것입니다. 이런 관점에서 보면, 생성자는 그러한 클래스의 핵심적인 요소인 '데이터'를 적절하게 초기화하고 설정하는 중요한 역할을 합니다. 이는 객체 지향 프로그래밍의 핵심 원칙 중 하나인 캡슐화를 지원합니다.
💡생성자 사용의 이점
클래스의 생성자 (__init__ 메소드)는 객체가 생성될 때 자동으로 호출되는 특별한 메소드입니다. 이 메소드를 사용하면 객체의 초기 상태를 설정할 수 있습니다.
생성자를 사용하면 다음과 같은 이점이 있습니다:
1. 객체의 일관성 보장: 생성자를 통해 객체의 초기 상태를 설정하면, 모든 객체가 동일한 상태 또는 값으로 시작하게 할 수 있습니다. 이렇게 하면 객체의 일관성이 보장되어 코드를 더 안정적이고 예측 가능하게 만들 수 있습니다.
2. 초기화 로직 중복 최소화: 만약 여러 개의 객체가 동일한 방식으로 초기화되어야 한다면, 그 로직을 생성자에 넣어 코드 중복을 줄일 수 있습니다.
3. 객체 생성과 초기화의 분리: 생성자를 사용하면 객체의 생성과 초기화를 분리할 수 있습니다. 객체를 만드는 것과 그 객체를 사용 가능한 상태로 만드는 것은 본질적으로 다른 작업일 수 있습니다. 생성자를 사용하면 이 두 작업을 명확하게 분리할 수 있습니다.
예를 들어, 다음과 같은 BankAccount 클래스를 생각해봅시다
class BankAccount:
def __init__(self, name, balance=0.0):
self.name = name
self.balance = balance
여기서 __init__ 메소드 (생성자)는 BankAccount 객체가 생성될 때 자동으로 호출되며, 각 계좌의 이름과 잔액을 초기화합니다. 이렇게 하면 BankAccount 객체를 생성할 때마다 해당 객체의 이름과 잔액이 항상 설정되므로, 객체의 일관성이 보장됩니다.
💡생성자 이름이 '__init__()'인 이유
파이썬에서 __init__()는 특별한 메서드로서 클래스의 인스턴스가 생성될 때 자동으로 호출되는 생성자 메서드입니다. 이름이 __init__()으로 지정된 이유는 파이썬의 네이밍 컨벤션에 따른 것입니다.
파이썬에서는 특별한 용도로 사용되는 메서드나 변수를 표현할 때 그 이름 앞뒤로 언더스코어(_)를 두 개 사용합니다. 이런 메서드들을 "던더(dunder)" 메서드라고도 부르는데, 이는 "double under(언더스코어 두 개)"의 줄임말입니다.
__init__() 뿐만 아니라 __str__(), __len__(), __del__() 등 다양한 던더 메서드가 있으며, 이들은 파이썬 인터프리터가 특별한 상황에서 자동으로 호출합니다. 이런 메서드들의 이름은 변경할 수 없으며, 이를 따르는 것이 파이썬의 일반적인 프로그래밍 스타일입니다.
따라서 __init__()라는 이름은 파이썬 클래스의 객체가 생성될 때 자동으로 호출되는 초기화 메서드라는 의미를 갖고, 이 이름은 파이썬의 네이밍 컨벤션에 따른 것입니다.
__init__은 "initialization"의 줄임말입니다. 이 메서드는 객체가 생성되고 초기화될 때 호출되므로 이런 이름이 붙여진 것입니다.
객체 지향 프로그래밍에서 객체 초기화는 매우 중요한 개념입니다. 객체는 그 객체가 속한 클래스의 인스턴스입니다. 객체가 생성되면, 그 객체의 속성을 설정하는 초기화 과정이 필요합니다.
파이썬에서는 이 초기화 과정을 __init__ 메서드가 수행합니다. 이 메서드는 객체가 생성될 때 자동으로 호출되어 객체의 속성을 설정합니다. 따라서 __init__이라는 이름은 이 메서드의 기능을 잘 나타내주는 이름입니다.
💡객체(object)
객체(object)는 객체 지향 프로그래밍(OOP)에서 핵심적인 개념입니다. 객체는 데이터(속성이라고도 불림)와 그 데이터를 조작하는 함수(메소드라고도 불림)를 함께 묶은 것을 말합니다.
예를 들어, 파이썬에서 list는 객체입니다. 리스트 객체는 데이터(리스트의 요소들)와 이 데이터를 다루는 메소드(예: append(), remove(), sort() 등)를 포함하고 있습니다.
객체는 특정 데이터 타입을 가지는 개별적인 인스턴스를 말합니다. 예를 들어, ["apple", "banana", "cherry"]는 리스트 객체의 한 예이고, 123은 정수 객체의 한 예입니다.
객체 지향 프로그래밍에서는 객체를 정의하는 클래스를 사용하여 프로그램을 설계합니다. 클래스는 객체를 생성하는 '틀' 같은 것으로, 객체가 가져야 하는 속성과 메소드를 정의합니다. 그리고 이 클래스를 통해 객체를 생성하게 됩니다.
객체는 프로그램 내에서 데이터를 캡슐화하고 조작하는 방법을 제공하며, 복잡한 문제를 더 관리하기 쉽게 만들어줍니다.
💡클래스에서 메소드의 첫 번째 매개변수가 항상 'self'인 이유
self는 클래스 메소드의 첫 번째 매개 변수로, 이 메소드가 호출되는 객체 자신을 참조합니다. 파이썬에서는 클래스의 메소드가 객체를 암시적으로 받도록 설계되어 있어서, 이 매개변수는 메소드 정의 시에는 첫 번째 매개변수로 지정되지만, 실제로 메소드를 호출할 때는 사용되지 않습니다.
이는 파이썬의 클래스와 객체 지향 프로그래밍의 일부로서의 디자인 선택입니다. self를 통해 객체 자신에 접근하면 메소드가 객체의 속성과 다른 메소드에 접근할 수 있습니다.
예를 들어, 다음과 같은 클래스가 있다고 가정해봅시다:
class MyClass:
def __init__(self):
self.value = 10
def print_value(self):
print(self.value)
여기에서 self.value는 MyClass의 인스턴스에 속하는 속성을 참조합니다. self가 없다면, 메소드는 이 속성에 접근할 수 없게 됩니다. 이는 self가 메소드 내에서 객체 자신을 참조하기 위한 수단이기 때문입니다.
그리고 이렇게 정의된 메소드를 호출할 때는 self 매개변수를 전달하지 않습니다. 이 매개변수는 파이썬이 자동으로 제공합니다.
obj = MyClass()
obj.print_value() # 10이 출력됨
위 코드에서 obj.print_value()를 호출하면, print_value 메소드의 self 매개변수에는 obj가 자동으로 전달됩니다.
정보 은닉
학생을 나타내는 클래스
class Student:
def __init__(self, name=None, age=0):
self.name=name
self.age=age
obj=Student("Hong", 20)
obj.age=21
print(obj.age)
>>>21
객체 s의 변수 age에 마음대로 접근해서 값 변경 가능
하지만 이런 식으로 인스턴스 변수의 값을 변경하는 것은 좋은 방법이 아님
-인스턴스 변수 값이 올바르지 않게 변경되 수 있음
obj.age=-10
-클래스를 유지 보수하는 것이 어려워짐
학생의 나이(age)를 저장하지 않고, 생년월일(birthday)을 문자열로 저장하기로 정책을 바꾼 경우
클래스 외부에서 age를 마음대로 사용하고 있었다면 이것 또한 불가능
class Student:
def __init__(self, name=None, birthdya="20010301"):
self.name=name
self.birthday=birthday
🍋정보 은닉(information hiding): 구현의 세부 사항을 클래스 안에 감추는 것
-대표적인 방법: 클래스 안의 데이터를 외부에서 마음대로 변경하지 못하게 하기
클래스 안에 변수를 선언할 때는 private로 만드는 것이 좋음
외부 접근 차단
인스턴스 변수를 private로 정의하려면 변수 이름 앞에 '__'을 붙이면 됨
private이 붙은 인스턴스 변수는 클래스 내부에서만 접근될 수 있음
class Student:
def __init__(self, name=None, age=0):
self.__name=name #__가 변수 앞에 붙으면 외부에서 변경 금지
self.__age=age #__가 변수 앞에 붙으면 외부에서 변경 금지
obj=Student()
print(obj.__age) #오류 발생!
__age는 private 변수이므로 오류가 발생
접근을 제어하게 되면 객체를 잘못 사용하는 것을 방지할 수 있음
올바르게 정의되 메소드만 데이터를 사용할 수 있게 하면 데이터의 값이 부적절한 값이 변경되는 것을 막을 수 있음
메소드도 마찬가지
메소드를 정의할 때도 앞에 '__'을 붙일 수 있고 그 의미는 변수의 경우와 동일
접근자와 설정자
외부에서 private 변수 값이 필요한 경우
어떤 특수한 메소드가 있어서 이들 메소드가 데이터 값을 읽어서 외부로 전달해주면 좋음
접근자(getters): 인스턴스 변수값을 반환
설정자(setters): 인스턴스 변수값을 설정
이러한 메소드는 대개 get이나 set이 메소드 이름 앞에 붙여짐
ex) getAge(): 접근자, setAge(): 설정자
접근자와 설정자 메소드만을 통해 인스턴스 변수에 접근해야 함
class Student:
def __init__(self, name=None, age=0):
self.__name=name
self.__age=age
def getAge(self): #접근자 메소드
return self.__age
def getName(self):
return self.__name
def setAge(self, age): #설정자 메소드
self.__age=age
def setName(self, name):
self.__name=name
obj=Student("Hong", 20)
obj.getName()
>>>Hong
인스턴스 변수가 private로 정의되어 있더라도 외부에서는 접근자나 설정자 메소드를 이용하면 불편 없이 인스턴스 변수의 값을 변경하거나 읽을 수 있음
🍨접근자와 설정자의 사용 이유
🥄접근자와 설정자를 사용해야만 나중에 클래스를 업그레이들 할 때 편함
🥄접근자에서 매개 변수를 통해 잘못되 값이 넘어오는 경우, 사전에 차단 가능
🥄필요할 때마다 인스턴스 변수값을 계산하여 반환 가능
🥄접근자만을 제공하면 자동적으로 읽기만 가능한 인스턴스 변수를 만들 수 있음
접근자(getter)와 설정자(setter)는 객체 지향 프로그래밍에서 객체의 속성에 접근하고 변경하는 방법을 제공하는 메소드입니다.
이들의 주요 이유와 장점은 다음과 같습니다:
1. 캡슐화: 클래스의 내부 데이터를 외부로부터 보호하기 위해 사용됩니다. 객체의 속성을 직접적으로 변경하지 못하게 하여 데이터의 무결성을 유지하고, 객체가 올바른 상태를 유지하도록 합니다.
2. 검증: 설정자를 사용하면 입력값의 유효성 검사를 할 수 있습니다. 예를 들어, 어떤 속성이 특정 범위의 값만 가져야 한다면 설정자를 통해 그 범위를 벗어나는 값이 들어오지 않도록 검사할 수 있습니다.
3. 추상화: 객체의 사용자는 객체의 내부 구현에 대해 알 필요 없이 접근자와 설정자를 사용하여 객체와 상호작용할 수 있습니다. 이는 코드의 유지 보수를 용이하게 하고 오류 가능성을 줄입니다.
4. 확장성: 나중에 클래스의 내부 구현이 변경되더라도 접근자와 설정자의 인터페이스는 변경되지 않으므로 코드의 수정 없이 클래스를 변경하거나 확장할 수 있습니다.
따라서 접근자와 설정자는 클래스 설계에서 중요한 역할을 하며, 객체 지향 프로그래밍의 원칙에 따라 클래스를 잘 디자인하도록 도와줍니다.
객체 참조
파이썬에서 변수는 실제로 객체를 저장하지 않음
변수는 단지 객체의 메모리 주소를 저장함
객체 자체는 메모리의 다른 곳에 생성됨
class Television:
def __init__(self, channel, volume, on):
self.channel=channel
self.volume=volume
self.on=on
def setChannel(self, channel):
self.channel=channel
t=Television(11, 10, True)
생성자 Television()은 새로운 객체에 대한 주소를 반환하고 이것이 변수 t에 저장됨
🍋객체 참조값(object reference): 객체의 주소
일반적으로 메모리 주소이지만 다른 값이 될 수도 있기 때문
객체를 구별하여 참조할 수 있는 값이면 됨
참조 공유
객체의 참조값을 저장하고 있는 변수를 다른 변수로 복사하면 어떻게 될까?
t=Television(11, 10, True)
s=t
객체가 복사되는 것이 아니라 참조값만 복사되어 변수 s에 저장됨
따라서 변수 t와 s가 동일한 객체를 가리키게 됨
s를 통해 객체를 수정하면 어떻게 될까?
t가 가리키는 객체의 값도 변경됨
t=Television(11, 10, true)
s=t
s.channel=9
2개의 변수가 동일한 객체를 참조하고 있는지 검사하는 연산자: is
is: 두 객체가 저장하고 있는 데이터가 동일한지를 비교하지 않음
두 객체가 참조하고 있는 객체가 동일한지만 검사함
None 참조값
변수가 현재 아무것도 가리키고 있지 않다면 None으로 설정하는 것이 좋음
None: 아무것도 참조하고 있지 않다는 것을 나타내는 특별한 값
None을 가지고 있는 변수를 이용하여 어떤 객체의 멤버를 호출하는 것은 오류가 됨
myTv=None
myTv.setChannel(5) #오류
객체를 함수로 전달할 때
객체가 함수로 전달될 때 만약 함수 안에서 객체를 변경하면 어떻게 될까?
어떤 객체가 전달되었느냐에 따라 달라짐
숫자나 문자열과 같은 변경 불가능한 객체가 전달되면 이들 객체는 변경되지 않음
하지만 우리가 작성한 객체가 전달되면 함수가 객체를 변경할 수 있음
#텔레비전을 클래스로 정의
class Television:
def __init__(self, channel, volume, on):
self.channel=channel
self.volume=volume
self.on=on
def show(self):
print(self.channel, self.volume, self.on)
#전달받은 텔레비전의 음량 낮추기
def setSilentMode(t):
t.volume=2
#setSilentMode()을 호출해서 객체 내용이 변경되는지 확인
myTV=Television(11, 10, True);
setSilentMode(myTV)
myTV.show()
>>>11, 2, True
함수에 객체를 전달하면 객체의 참조값이 전달됨
따라서 함수 안에서 객체의 참조값을 이용하여 객체를 찾아서 객체 안의 내용을 변경하면 원본 객체가 변경됨
💡객체를 함수로 전달하면 원본이 전달되는가? 아니면 복사본이 전달되는가?
파이썬에서는 모든 것이 객체로 취급됩니다. 함수에 객체를 전달하면, 원본 객체의 참조(reference)가 전달됩니다. 이는 C++에서의 포인터와 비슷한 개념입니다.
따라서 함수 내에서 인자로 전달받은 객체를 변경하면, 원본 객체에도 그 변경사항이 반영됩니다. 이를 '참조에 의한 호출(Call by Reference)'이라고 합니다.
하지만 파이썬의 기본 데이터 타입들(정수, 실수, 문자열 등)은 불변(immutable) 객체로, 이들에 대한 변경은 새로운 객체를 생성합니다. 따라서 이러한 불변 객체를 함수의 인자로 전달하고 그 값을 변경하려 하면 원본 객체는 변경되지 않습니다.
리스트, 딕셔너리 등의 변경 가능한(mutable) 객체는 함수에서 변경하면 원본 객체도 변경됩니다.
def modify(n, list):
n += 1
list.append('new item')
num = 5
my_list = ['item1', 'item2']
modify(num, my_list)
print(num) # 5 출력. 원본 num은 변경되지 않음
print(my_list) # ['item1', 'item2', 'new item'] 출력. 원본 my_list는 변경됨
위 예제에서 num은 불변 객체로서, 함수에서 변경하더라도 원본 num은 변경되지 않습니다. 반면에 my_list는 변경 가능한 객체로서, 함수에서 변경하면 원본 my_list에 그 변경이 반영됩니다.
클래스 변수
인스턴스 변수는 항상 객체를 통해 사용해야 함
하지만 파이썬에서는 객체를 통하지 않고 사용할 수 있는 변수를 생성하는 것이 가능
🍋클래스 멤버(class member): 모든 객체를 통틀어서 하나만 생성되고 모든 객체가 공유하는 것
클래스를 정의할 때, 메소드와 동일한 수준에서 변수를 정의하면 클래스 변수가 됨
클래스 변수는 클래스당 하나만 생성됨
모든 객체는 하나의 클래스 변수를 공유한다.
인스턴스 변수VS클래스 변수
🍋인스턴스 변수(instance variable): 동일한 클래스 설계도를 이용해 많은 객체들이 생성될 때 각각의 객체(인스턴스)들은 자신만의 변수를 가짐. 이들 변수들은 인스턴스마다 별도로 생성됨
ex) Television 클래스에서 channel, volume, on는 모두 인스턴스 변수
각 객체는 이들 변수에 대해 별도의 기억 공간을 가지고 있으며 각기 다른 값을 가짐
🍋클래스 변수(class variable): 모든 객체에 공통인 변수
하나의 클래스에 하나만 존재
클래스 변수를 만드는 법: 클래스 안이지만 메소드의 외부에 변수를 생성하면 됨
클래스 변수는 인스턴스를 생성하지 않아도 사용 가능
ex) Television 클래스에 정적 변수 serialNumber 추가하기
class Television:
serialNumber=0 #클래스변수
def __init__(self, channel, volume, on):
self.channel=channel
self.volume=volume
self.on=on
Television.SerialNumber+=1 #클래스 변수 하나 증가시킴
#클래스 변수의 값을 객체의 시리얼 번호로 하기
self.numbe=Television.serialNumber
def show(self):
print(self.channel, self.volume, self.on, self.number)
myTV=Television(11, 10, True);
myTV.show()
>>>11 10 True 1
상수 정의
상수들은 흔히 클래스 변수로 정의됨
ex) 게임에서 몬스터의 건강 상태를 몇 개의 상수로 표현하기
class Monster:
#상수 값 정의
WEAK=0
NORMAL=10
STRONG=20
VERY STRONG=30
def __init__(self):
self._health=Monster.NORMAL
몬스터가 먹이를 먹었다면 건강이 좋아짐
eat()메소드가 호출되면 건강 상태를 STRONG으로 변경하기
def eat(self):
self._health=Monster.STRONG
한 번씩 다른 캐릭터를 공격하면 건강이 약해짐
def attack(self):
self._health=Monser.WEAK
💡"하나의 클래스에 하나만 존재한다"라는 표현
"하나의 클래스에 하나만 존재한다"라는 표현은 클래스 변수의 성질을 설명하는 것이지, 클래스 내에 클래스 변수를 하나만 정의할 수 있다는 의미는 아닙니다.
클래스 변수는 해당 클래스의 모든 인스턴스가 공유하는 변수입니다. 즉, 클래스 내부에서 정의되고 클래스 이름을 통해 접근할 수 있는 변수를 말하며, 이 변수는 해당 클래스의 모든 객체에 대해 같은 값을 가집니다.
위에서 WEAK와 NORMAL은 Monster 클래스의 클래스 변수입니다. 즉, Monster 클래스의 모든 인스턴스는 이 두 변수를 공유하게 됩니다. 그래서 클래스 변수는 '하나의 클래스에 하나만 존재'한다고 표현하는 것입니다. 이는 클래스 내에 클래스 변수를 하나만 정의할 수 있다는 뜻이 아니라, 각 클래스 변수는 해당 클래스의 모든 인스턴스 간에 하나만 존재하고 공유된다는 의미입니다.
즉, 클래스 내에는 여러 개의 클래스 변수를 정의할 수 있지만, 각 클래스 변수는 해당 클래스의 모든 인스턴스에 대해 동일한 값을 가지므로 '하나만 존재'한다는 표현을 사용하는 것입니다.
특수 메소드
특수 메소드(special method): 파이썬 연산자(+, -, *, /)에 관련된 연산
특수 메소드를 이용하면 객체의 상황에 맞는 자연스러운 연산 정의 가능
class Circle:
...
def __eq__(self, other):
return self.radius==other.radius
__eq__()메소드가 정의되 객체는 ==연산자를 이용하여 서로 비교 가능
c1=Circle(10)
c2=Circle(10)
if c1==c2:
print("원의 반지름은 동일합니다.")
특수 메소드들과 관련되 연산자들
연산자 | 메소드 | 설명 |
x+y | __add__(self, y) | 덧셈 |
x-y | __sub__(self, y) | 뺄셈 |
x*y | __mul__(self, y) | 곱셈 |
x/y | __truediv__(self, y) | 실수 나눗셈 |
x//y | __floordiv__(self, y) | 정수 나눗셈 |
x%y | __mod__(self, y) | 나머지 |
divmod(x, y) | __divmod__(self, y) | 실수나눗셈과 나머지 |
x**y | __pw__(self, y) | 지수 |
x<<y | __lshift__(self, y) | 왼쪽 비트 이동 |
x>>y | __rshift__(self, y) | 오른쪽 비트 이동 |
x<=y | __le__(self, y) | less than or equal(작거나 같다) |
x<y | __lt__(self, y) | less than(작다) |
x>=y | __ge__(self, y) | greater than or equal(크거나 같다) |
x>y | __gt__(self, y) | greater than(크다) |
x==y | __eq__(self, y) | 같다 |
x!=y | __neq__(self, y) | 같지않다 |
🌟객체와 객체를 "<"나">" 연산자를 사용하여 비교한다면 반드시 __gt__()와 __lt__()메소드를 정의해야 함
💡클래스 객체 비교시 특수메소드를 사용하는 이유
Python에서 객체를 비교하려면 일반적으로 == 연산자를 사용합니다. 그러나 이 연산자는 기본적으로 객체의 식별자를 비교하므로, 동일한 클래스의 두 개의 다른 인스턴스는 항상 다르다고 판단됩니다. 이를 방지하고 원하는 방식으로 객체를 비교하려면 __eq__라는 특수 메소드를 정의해야 합니다.
예를 들어, 다음과 같이 Student 클래스를 정의하고, __eq__ 메소드를 사용하여 두 학생이 동일한 이름을 가지면 같은 학생으로 취급하도록 설정할 수 있습니다.
class Student:
def __init__(self, name):
self.name = name
def __eq__(self, other):
if isinstance(other, Student):
return self.name == other.name
return False
student1 = Student('John')
student2 = Student('John')
student3 = Student('Jane')
print(student1 == student2) # True
print(student1 == student3) # False
__eq__ 메소드를 사용하면 == 연산자의 기능을 원하는 대로 정의할 수 있습니다. 이와 비슷하게, <, >, <=, >= 등의 연산자도 각각 __lt__, __gt__, __le__, __ge__ 등의 특수 메소드를 정의하여 커스텀하게 작동하도록 할 수 있습니다.
따라서 객체를 비교하는데 특수 메소드를 반드시 사용해야 하는 것은 아니지만, 커스텀 비교 로직을 구현하려면 이러한 특수 메소드를 사용하는 것이 일반적입니다.
__str__() 메소드
객체를 출력할 때 개체 안의 정보들을 출력하면 훨씬 도움이 됨
__str__() 메소드는 객체를 print()로 출력할 때 자동적으로 호출됨
일반적으로 객체의 데이터를 문자열로 만들어서 반환함
class Counter:
def __init__(self, count):
self.count=count
def increment(self):
self.count+=1
def __str__(self):
msg="카운트값:"+str(self.count)
return msg
a=Counter(100)
print(a)
>>>카운트값: 100
🌞__str__ 메소드는 객체를 사람이 이해하기 쉬운 문자열 형태로 나타내기 위한 것입니다. 이 메소드를 정의하지 않으면, Python은 기본적으로 객체의 클래스와 메모리 주소를 출력합니다.
'프로그래밍 > Python' 카테고리의 다른 글
📔파워 유저를 위한 파이썬 Express09. GUI 프로그래밍 (0) | 2023.06.02 |
---|---|
📔파워 유저를 위한 파이썬 Express08. 객체와 클래스 Programming 문제풀이 (1) | 2023.05.29 |
📔파워 유저를 위한 파이썬 Express 07. Programming 문제풀이 (0) | 2023.05.27 |
📔파워 유저를 위한 파이썬 Express 07. 파이썬 자료구조 2(튜플, 딕셔너리, 세트, 문자열) (0) | 2023.05.27 |
📔파워 유저를 위한 파이썬 Express 06. 파이썬 자료구조 1(리스트) (0) | 2023.05.19 |