데이터를 객체(object)로 취급하며, 이러한 객체가 바로 프로그래밍의 구현의 중심인 프로그래밍 개발 방식.
객체지향 언어에서 데이터인 객체(instance) 를 어떻게 구성할지 정의한 설계도/템플릿을 클래스 라고 한다.
Class에는 다음 두가지를 정의한다.
1. Attribute/State
객체지향 프로그래밍이란 Data와 Data를 처리하는 함수를 분리하지 않고 하나로 묶어 모듈로 개발하는 방식이다. 그래서 어떤 값들과 어떤 함수를 묶을 것인지를 class로 정의한다. 그리고 그 class로 부터 객체(instance) 를 생성(instantiate)해서 사용한다.
class는 Data type이고 instance는 value 이다.
class 클래스이름: #선언부
#클래스 구현
#메소드들을 정의
클래스 이름의 관례
class로 부터 생성된 값(value)로 클래스에서 정의한 attribute를 behavior를 이용해 처리한다.
변수 = 클래스이름()
attribute는 객체의 데이터, 객체를 구성하는 값들, 객체의 상태값들을 말한다.
값을 저장하므로 변수로 정의한다. 그래서 instance 변수 라고 한다.
객체의 속성 추가(값 변경)
속성 값 조회
- 객체.속성명
객체._dict
- 객체가 가지고 있는 Attribute들을 dictionary로 반환한다.
#Person 클래스 생성 class Person : pass#Person 클래스의 객체(instance)를 생성 p = Person()#Person 객체 p에 속성(Person-사람과 관련된 변수들)을 추가 - 이름, 나이, 주소 p.name = "이순신" p.age = 20 p.address = "서울"#p의 속성들을 조회 print("이름", p.name) print("나이", p.age) print("주소", p.address)이름 이순신
나이 20
주소 서울
객체를 생성할 때 호출되는 특수메소드로 attribute들 초기화에 하는 코드를 구현한다.
구문
def init(self [,매개변수들 선언]): #[ ] 옵션.
# 구현 -> attribute(instance변수) 초기화
self.속성명 = 값
변수 초기화: 처음 변수 만들어서 처음 값 대입하는 것.
#Person -> name, age, address 속성을 가지는 클래스. class Person : def __init__(self, name, age, address) : #instance 변수에 따라 파라미터로 받은 값들을 할당해서 초기화. #instance 변수 : self.변수명 self.name = name self.age = age self.address = address print("객체생성완료")p1 = Person("이순신", 20, "서울") #객체 생성 #1. 메모리에 instance 변수들을 저장할 영역이 생성. #2. class에 정의된 __init__()메소드를 호출. #1에서 생성된 instance를 __init()의 첫번째 파라미터(self)에 전달. #객체 생성시 전달한 argument들을 __init__()의 두번째 파라미터부터 전달.객체생성완료
print(p1.name, p1.age, p1.address)이순신 20 서울
p2 = Person("유관순", 30, "부산") print(p2.name, p2.age, p2.address)객체생성완료
유관순 30 부산p2.age = 32 p2.address = "광주" print(p2.name, p2.age, p2.address)유관순 32 광주
객체가 제공하는 기능
객체의 attribute 값을 처리하는 기능을 구현한다.
구문
def 이름(self [, 매개변수들 선언]):
# 구현
# attribute 사용(조회/대입)
self.attribute
self 매개변수 - 메소드를 소유한 객체를 받는 변수 - 호출할 때 전달하는 argument를 받는 매개변수는 두번째 부터 선언한다.

메소드 호출
메소드는 반드시 한개 이상의 parameter를 선언해야 하고 그 첫번째 parameter는 관례적으로 변수명을 self로 한다.
메소드 호출시 그 메소드를 소유한 instance가 self parameter에 할당된다.
Initializer의 self
메소드의 self
Caller에서 생성자/메소드에 전달된 argument들을 받을 parameter는 두번째 변수부터 선언한다.
class Person : def __init__(self, name, age, address) : #instance 변수에 따라 파라미터로 받은 값들을 할당해서 초기화. #instance 변수 : self.변수명 self.name = name self.age = age self.address = address #메소드 def get_info(self) : #메소드에서 instance 변수/메소드 호출 -> self.변수명, self.메소드명() info = f"이름:{self.name}, 나이:{self.age}, 주소:{self.address}" return info def add_age(self, age) : #instance 변수 age를 파라미터로 받은 age를 더한 값으로 변경. self.age = self.age + age def print_info(self) : #person의 정보들을 출력 txt = self.get_info() #메소드에서 메소드 호출 : self.호출할메소드 print(txt)p = Person("이순신", 20, "서울") s = p.get_info() print(s) p.add_age(15) p.print_info() #p가 print_info의 self에 연결하고 이게 get_info의 self에 연결이름:이순신, 나이:20, 주소:서울
이름:이순신, 나이:35, 주소:서울p2 = Person("유관순", 30, "대전") s2 = p2.get_info() print(s2)이름:유관순, 나이:30, 주소:대전
Attribute의 값을 caller(객체 외부)가 마음대로 바꾸지 못하게 하기 위해 직접 호출을 막고 setter/getter 메소드를 통해 값을 변경/조회 하도록 한다.
Attribute 직접 호출 막기
p.name = "박순신" p.print_info()이름:이순신, 나이:22, 주소:서울
p.age = -40 p.print_info()이름:이순신, 나이:22, 주소:서울
class Person : def __init__(self, name, age, address) : #name : 두글자 이상 #age : 양수 #address : 특별한 규칙이 없다. self.__name = name self.__age = age self.address = address #__name, __age : 클래스 밖에서는 호출 못하게 처리. #address는 클래스 밖에서도 호출 가능. #메소드 def get_info(self) : #메소드에서 instance 변수/메소드 호출 -> self.변수명, self.메소드명() #__변수명 으로 접근을 제한한 변수는 같은 클래스에서는 호출 가능. info = f"이름:{self.__name}, 나이:{self.__age}, 주소:{self.address}" return info def add_age(self, age) : #instance 변수 age를 파라미터로 받은 age를 더한 값으로 변경. self.__age = self.__age + age def print_info(self) : #person의 정보들을 출력 txt = self.get_info() #메소드에서 메소드 호출 : self.호출할메소드 print(txt) #__변수 를 반환하는 메소드 - get_이름() : getter def get_name(self) : return self.__name #__변수 의 값을 변경하는 메소드 - set_이름() : setter def set_name(self, name) : if len(name) >= 2 : #두글자 이상일 때만 self.__name = name def get_age(self) : return self.__age def set_age(self, age) : if age >= 0 : #양수 self.__age = agep = Person("이순신", 22, "서울") p.print_info()코드를 입력하세요
이름:이순신, 나이:22, 주소:서울p.set_age(40)
p.print_info()이름:이순신, 나이:40, 주소:서울p.set_age(-10) #안바뀜
p.print_info()이름:박순신, 나이:40, 주소:서울p.set_name("박순신")
p.print_info()이름:박순신, 나이:40, 주소:서울p.set_name("강") #안바뀜
p.print_info()이름:박순신, 나이:40, 주소:서울print(p.get_name(), p.get_age())
박순신 40p.address = "경기도"
print(p.address)
p.print_info()경기도 이름:박순신, 나이:40, 주소:경기도
은닉된 instance 변수의 값을 사용할 때 getter/setter대신 변수를 사용하는 방식으로 호출할 수 있도록 한다.
setter/getter 메소드이름을 변수처럼 선언 (보통은 같은 이름으로 지정)
getter메소드 : @property 데코레이터를 선언
setter메소드 : @getter메소드이름.setter 데코레이터를 선언.
getter/setter의 이름을 Attribute 변수처럼 사용한다.
주의: getter/setter 메소드를 직접 호출 할 수 없다. 변수형식으로만 호출가능하다.
#name, age -> 메소드 #__name, __age, address -> attribute #객체.name = 값 (대입) ==> name(값) 호출 (setter - @name.setter) #print(객체.name) (조회) ==> name() 호출 (getter - @property) #객체 : 외부 - p.name class 내부 : self class Person : def __init__(self, name, age, address) : #name : 두글자 이상 #age : 양수 #address : 특별한 규칙이 없다. self.name = name #setter name 메소드 호출 @name.setter self.name(name) self.age = age #setter age 메소드 호출 @address.setter self.address = address #__name, __age : 클래스 밖에서는 호출 못하게 처리. #address는 클래스 밖에서도 호출 가능 #setter/getter 데코레이터를 이용해 구현 #get_name() @property #getter/setter는 일반 메소드 전에 작성해야됨. def name(self) : print("get_name") return self.__name @name.setter def name(self, name) : print("set_name") if len(name) >= 2 : self.__name = name @property def age(self) : return self.__age @age.setter def age(self, age) : if age >= 0 : self.__age = age #메소드 def get_info(self) : #메소드에서 instance 변수/메소드 호출 -> self.변수명, self.메소드명() #__변수명 으로 접근을 제한한 변수는 같은 클래스에서는 호출 가능. info = f"이름:{self.name}, 나이:{self.age}, 주소:{self.address}" return info def add_age(self, age) : #instance 변수 age를 파라미터로 받은 age를 더한 값으로 변경. self.age = self.age + age def print_info(self) : #person의 정보들을 출력 txt = self.get_info() #메소드에서 메소드 호출 : self.호출할메소드 print(txt)p = Person("이순신", 22, "서울") p.print_info()set_name
get_name
이름:이순신, 나이:22, 주소:서울p.name = "장순신"set_name
print(p.name)get_name
장순신p = Person("이순신", 22, "서울") p.print_info()set_name
get_name
이름:이순신, 나이:22, 주소:서울p.name = "박순신" p.print_info() p.name = "강" p.print_info()set_name
get_name
이름:박순신, 나이:22, 주소:서울
set_name
get_name
이름:박순신, 나이:22, 주소:서울
기존 클래스를 확장하여 새로운 클래스를 구현한다.
기반(Base) 클래스, 상위(Super) 클래스, 부모(Parent) 클래스
물려 주는 클래스.
파생(Derived) 클래스, 하위(Sub) 클래스, 자식(Child) 클래스
상위 클래스와 하위 클래스는 계층관계를 이룬다.
다중상속
MRO (Method Resolution Order)
object class
class Parent1:
...
class Parent2:
...
class Sub(Parent1, Parent1):
...
class Person : # def __init__(self, name, age) : #self.name = name #self.age = age #def print_info(self) : #print(f"이름:{self.name}, 나이:{self.age}") def go(self) : print("갑니다.") def eat(self) : print("점심을 먹는다")#Student, Teacher => Person class를 상속 class Student(Person) : def study(self) : print("공부를 합니다.") class Teacher(Person) : def teach(self) : print("가르칩니다.")s = Student() s.study() s.go() s.eat()공부를 합니다.
갑니다.
점심을 먹는다t = Teacher() t.teach() t.go() t.eat()가르칩니다.
갑니다.
점심을 먹는다
상위 클래스에 정의한 메소드의 구현부를 하위 클래스에서 다시 구현하는 것. 상위 클래스는 모든 하위 클래스들에 적용할 수 있는 추상적인 구현 밖에는 못한다.
하위 클래스에서 그 기능을 자신에 맞게 좀 더 구체적으로 재구현할 수 있게 해주는 것을 Method Overriding이라고 한다.
하위 클래스에서 상위 클래스의 instance를 사용할 수있도록 해주는 함수. 상위클래스에 정의된 instance 변수, 메소드를 호출할 때 사용한다.
구문
상위 클래스의 Instance 메소드를 호출할 때 – super().메소드()
class Person : def go(self) : print("갑니다.") def eat(self) : print("먹습니다") class Student(Person) : def study(self) : print("공부를 합니다.") def eat(self) : print("학교 식당에서 급식을 먹습니다.") class Teacher(Person) : def teach(self) : print("가르칩니다.") def eat(self) : print("외부 식당에 갑니다.") #print("먹는다.") super().eat() # => 부모클래스(객체)의 eat()s = Student() s.eat()학교 식당에서 급식을 먹습니다.
t = Teacher() t.eat()외부 식당에 갑니다.
먹습니다class Person : #Person -> Teacher/Student 의 공통 attribute, method를 정의하는 클래스 def __init__(self, name, age) : self.name = name self.age = age def get_info(self) : return f"이름:{self.name}, 나이:{self.age}" def print_info(self) : print(self.get_info())#하위 클래스의 메소드 : self => 하위클래스의 속성, 메소드 호출 # super() => 부모클래스의 속성, 메소드 호출 class Student(Person) : #Student attribute : name, age (Person) + grade def __init__(self, name, age, grade) : super().__init__(name, age) #부모의 init()을 호출 self.grade = grade def get_info(self) : return f"{super().get_info()}, 성적:{self.grade}"s = Student("김학생", 10, 20)s.name, s.age, s.grade('김학생', 10, 20)
s.get_info()'이름:김학생, 나이:10, 성적:20'
isinstance(객체, 클래스이름-datatype) : bool
객체.dict
p = Person("홍길동", 10)
s = Student("이순신", 15, 20)
isinstance(p,Person)
True
isinstance(p, Student)
False
isinstance(s, Student)
True
#부모클래스는 모든 자식클래스 객체들의 타입이 된다.
isinstance(s, Person)
True
a = 10
if isinstance(a, (int, float)) : # a가 int 또는 float 타입인지?
print(a + 20)
30
파이썬 실행환경(Python runtime)이 객체와 관련해서 특정 상황 발생하면 호출 하도록 정의한 메소드들. 그 특정상황에서 처리해야 할 일이 있으면 구현을 재정의 한다.
__init__(self [, …])
__call__(self [, …])
객체를 함수처럼 호출 하면 실행되는 메소드
class Test :
def __call__(self, a) :
print("call", a)
return "리턴값"
t = Test()
t(1000) # t() ==> t.__call__()
call 1000
'리턴값'
__str__(self)
#str(값) ==> string class Person : #Person -> Teacher/Student 의 공통 attribute, method를 정의하는 클래스 def __init__(self, name, age) : self.name = name self.age = age def __str__(self) : return f"이름: {self.name}, 나이: {self.age}"p = Person("홍길동", 30) str(p) # str() => p.__str__()'이름: 홍길동, 나이: 30'
print(p)이름: 홍길동, 나이: 30
연산자의 피연산자로 객체를 사용하면 호출되는 메소드들
다항연산자일 경우 가장 왼쪽의 객체에 정의된 메소드가 호출된다.
비교 연산자
산술 연산자
#str(값) ==> string class Person : #Person -> Teacher/Student 의 공통 attribute, method를 정의하는 클래스 def __init__(self, name, age) : self.name = name self.age = age def __str__(self) : return f"이름: {self.name}, 나이: {self.age}" def __add__(self, other) : # a + b => a.__add__(b) ==> self : a, other : b # other : int - self.age + other, other : Person : self.age + other.age if isinstance(other, int) : return self. age + other elif isinstance(other, Person) : return self.age + other.age else : raise Exception(f"{type(other)}는 계산할 수 없는 타입입니다.") # 에러발생. def __eq__(self, other) : # self와 other의 attribute 값이 같은지 여부를 확인 # p1 == p2, p1:self, p2:other if not isinstance(other, Person) : return False if self.name == other.name and self.age == other.age : return True return Falsep = Person("이순신", 20) p + 1030
p2 = Person("유관순", 50) p + p270
p + "aaa"Exception Traceback (most recent call last)
Cell In[164], line 1
----> 1 p + "aaa"
Cell In[161], line 17, in Person.add(self, other)
15 return self.age + other.age
16 else :
---> 17 raise Exception(f"{type(other)}는 계산할 수 없는 타입입니다.")
Exception: <class 'str'>는 계산할 수 없는 타입입니다.p1 = Person("이순신", 20) p2 = Person("이순신", 20) p3 = Person("유관순", 10) print(p1 == p2) print(p1 == p3) p1 == "aaa"True
False
False
class변수
class 메소드
클래스의 메소드로 클래스 변수와 상관없는 단순기능을 정의한다.
구현
class Person: # 변수 정의 -> class 변수 (class 소유) GENDER_MALE = '남성' GENDER_FEMALE = '여성' # class 변수를 다루는 메소드 -> class method @classmethod def print_gender_constants(clzz) : # clzz : 클래스 자체를 받는 변수 print(clzz.GENDER_MALE, clzz.GENDER_FEMALE) @staticmethod def static_method() : print("static method") def __init__(self, name, age, gender) : self.name = name self.age = age self.gender = gender def __str__(self) : return f"{self.name}, {self.age}, {self.gender}"p = Person("이순신", 20, Person.GENDER_MALE) p2 = Person("강감찬", 30, Person.GENDER_MALE) p3 = Person("유관순", 22, Person.GENDER_FEMALE) print(p) print(p2) print(p3)이순신, 20, 남성
강감찬, 30, 남성
유관순, 22, 여성Person.print_gender_constants()남성 여성
Person.static_method()static method