예제
- TV 객체 추상화
- 상태(속성) → 상태를 동작 보다 먼저 정의
- 전원상태, 볼륨, 채널번호
- 동작(기능)
- 전원 켜기/끄기, 볼륨 변경하기, 채널 변경하기
- 상태(속성) → 상태를 동작 보다 먼저 정의
- TV 클래스 모델링
- 필드(인스턴스 변수) → 상태(속성) 표현
- on, volume, channel
- 메소드(멤버함수) → 동작(기능) 표현
- turnOn() , turnOff() , changeVolume(), changeChannel()
- 필드(인스턴스 변수) → 상태(속성) 표현
class Tv:
def __init__(self, on, ch, vol): # 생성자 - 필드 초기화
self.on = on # 필드 = 인스턴스 변수 생성
self.channel = ch
self.volume = vol
def print(self): # 메소드 = 멤버 함수
print(self.on, self.channel, self.volume)
def turnOn(self):
self.on = True
def turnOff(self):
self.on = False
def changeChannel(self, ch):
self.channel = ch
def changeVolume(self, vol):
self.volume = vol
t1 = Tv(False, 11, 10) # Tv 객체 생성
t1.turnOn() # 메소드 호출
t1.print()
res : True 11 10
파이썬의 필드는 init (생성자)안에 선언
예제2
- 자동차 객체 추상황
- 상태(속성) → 모델, 색상, 속도
- 동작(기능) → 가속, 감속
- 클래스 모델링 : Car
- 필드(인스턴스 변수) → model, color, speed
- 메소드(멤버함수) → init() , speedUp(), speedDown(), str()
- str() 메소드 → 객체의 상태를 문자열로 변환(반환)
- 객체를 화면 출력 시 , 문자열로 변환 시 자동 호출
class Car:
def __init__(self, m, c, s): # 생성자
self.model = m
self.color = c
self.speed = s
def speedUp(self):
self.speed += 10
def speedDown(self):
self.speed -= 10
def __str__(self):
return "Car[Model=%s:color=%s:speed=%d]" \
% (self.model, self.color, self.speed)
c = Car('BMW', 'Black', 100)
c.speedDown()
print(c)
res : Car[Model=BMW:color=Black:speed=90]
클래스 변수
- 클래스 변수는 클래스 당 하나만 생성된다 or 클래스 간 공유를 한다.
class Tv:
serialnum = 0 # 클래스 변수 정의(초기화)
def __init__(self, on, ch, vol): # 생성자 - 초기화
self.on = on # 필드 = 인스턴스 변수 생성
self.channel = ch
self.volume = vol
Tv.serialnum += 1 # 객체 생성 시 클래스 변수 증가
self.num = Tv.serialnum
def print(self): # 메소드 = 멤버 함수
print(self.on, self.channel, self.volume, self.num)
t1 = Tv(False, 11, 10) # Tv 객체 생성
t1.print()
t2 = Tv(True, 9, 20)
t2.print()
상수
- 상수 : 한 번 정의 되면 값이 바뀌지 않는 수
- 값이 바뀌지 않기 때문에 메모리 절약을 위해 클래스당 하나 만 있으면 되기때문에 클래스 변수에 정의
- 상수도 클래스의 필드에 정의하지 않고 클래스 변수에 정의한다.
class Circle:
PI = 3.14 # 클래스 변수 정의(초기화) - 상수 정의
def __init__(self, r):
self.radius = r
def area(self):
return Circle.PI*self.radius**2
def perimeter(self):
return 2*Circle.PI*self.radius
radius = float(input('반지름의 길이 입력 :'))
c1 = Circle(radius)
print("원의 넓이 = ", c1.area())
print("원의 둘레길이 = ", c1.perimeter())
특수 메소드(연산자 함수)
class Vector2D: # 2차원 벡터
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, o): # 연산자 == 함수
return self.x == o.x and self.y == o.y # 1번째 클래스의 x 값과 2번째 클래스의 x값이 같은가
def __add__(self, o): # 연산자 + 함수
return Vector2D(self.x + o.x, self.y + o.y)
def __str__(self):
return '(%d, %d)' % (self.x, self.y)
v1 = Vector2D(2, 3)
v2 = Vector2D(1, 2)
print(v1 == v2) # 연산자에서 객체가 오면 객체의 연산자 함수를 사용한다
v3 = v1 + v2
print(v3)
res : False (3, 5)
예제. 분수 클래스 작성
class Fraction:
def __init__(self, ja, mo):
self.ja = ja
self.mo = mo
def __add__(self, fr): # 더하기 연산자 수행
return Fraction(self.ja + fr.ja, self.mo + fr.mo) # class 사용으로 문자열 출력 사용
def __mul__(self, fr): # 곱셈 연산자 수행
return Fraction(self.ja * fr.ja, self.mo * fr.mo)
def __str__(self): # 문자열 출력
return "%d/%d" %(self.ja, self.mo)
f1 = Fraction(2,3)
f2 = Fraction(1,2)
print(f1+f2)
print(f1*f2)
답 :
3/5
2/6
상속
- 기존 클래스로부터 멤버(필드, 메소드)를 물려 받는다.
- 기존 클래스 → 부모 클래스
- 상속 받은 클래스 → 자식 클래스
- 파이썬에서는 부모 클래스로부터 메소드를 물려 받음
- 필드는 부모 클래스 메소드가 호출될 때 물려 받는다.
- 메소드 사용전에는 필드가 메모리상에 없어 공간절약 가능
- 필드는 부모 클래스 메소드가 호출될 때 물려 받는다.
- 클래스 간의 “is - a “ 관계 성립 시 활용
- A is-a B → B 부모 클래스, A 자식 클래스
- 학생은 사람이다.
- 자동차는 차량이다.
상속 필요성
- 코드의 재 사용
- 중복 줄이기
- 상속으로 중복 감소의 예
- 3가지 종류의 자동차 클래스 작성
- ex)Car, Truck, Bus
- 공통 메소드 포함 → 클래스에서 공통 메소드 각각 작성 : 코드 중복
- ex) setSpeed(), turn()
- 3가지 종류의 자동차 클래스 작성
- 공통 메소드를 가지는 부모 클래스 작성
- ex) Vehicle
- 다른 클래스는 자식 클래스로 작성
- 공통 메소드를 물려 받음
- 공통 메소드는 한번만 작성 → 중복 감소
부모 클래스의 생성자 호출
- 파이썬 부모 클래스 생성자 호출 방법
- super().init() → self 생략
- 부모 클래스 생성자 호출로 부모 클래스의 필드를 물려 받음
다중 상속 : 부모 생성자 호출
super 에서는 self 를 안쓰는 이유는 super() 자체가 객체 이기 때문에 self를 안써도됨
# 다중 상속 : 부모 생성자 호출
class Car :
def __init__(self):
print('Car')
class Plane :
def __init__(self):
print('Plane')
class FlyingCar(Car, Plane): # 다중 상속
def __init__(self):
super().__init__() # Car 출력 -> 첫번째 부모 클래스
Car.__init__(self) # Car 출력
Plane.__init__(self) # Plane 출력
fc = FlyingCar()
Car Car Plane
has - a 관계 : 포함
- 객체 A,B 간에 A “has-a” B 성립
- ex)원기둥 has-a 원(밑면)
포함은 → 필드로 자연스럽게 포함
# has-a 관계 : 포함
class Circle:
def __init__(self, r):
self.radius = r
def __str__(self):
return "Circle[radius=%d]" % (self.radius)
class Cylinder:
def __init__(self, c, h):
self.underside = c # 필드1: 밑면(원) => 원기둥 has-a 원(밑면)
self.height = h # 필드2: 높이
def __str__(self):
return "Cylinder[underside=%s, height=%d]" %(self.underside, self.height)
c = Circle(10) # 반지름 10인 원 생성
cyl = Cylinder(c, 20) # 밑면 반지름 10인 원, 높이 20인 원기둥 생성
print(cyl)
→ 원기둥은 밑면에 원을 포함한다 그러므로 상속관계가 아니라 포함관계이다. 원기둥의 필드에 원이 포함되었다.
클래스메소드와 정적메소드 차이
- 정적메소드는 전달받는 값이 없다, 클래스메소드는 클래스명으로 전달받을 수 있다.
중요!!
class Parent:
def __str__(self):
return "Parent"
@classmethod
def method1(cls) : # 클래스 메소드 호출 시 클래스명을 cls로 전달 받는다.
return cls()
@staticmethod
def method2() : # 클래스 메소드 호출 시 클래스명을 전달받을 수 없음
return Parent()
class Child(Parent):
def __str__(self):
return "Child"
obj1 = Child.method1() # 자식 클래스이름으로 클래스 메소드 호출
print(obj1)
obj2 = Child.method2() # 자식 클래스 이름으로 정적 메소드 호출
print(obj2)
Child Parent
메소드 오버라이딩
- 자식 클래스에서 부모 클래스로부터 상속받은 메소드를 재정의(overriding)
class Shape:
def draw(self):
print('Shape Draw')
class Circle(Shape):
def draw(self): # 메소드 재정의(Overriding)
print('Circle Draw')
c = Circle()
c.draw()
다형성
- 많은(poly) + 모양(morph)
- 하나의 식별자로 다양한 타입(클래스)을 처리
- 다형성 성립 조건
- 클래스 작성
- 상속 관계
- 메소드 재정의(오버라이딩)
- 객체 생성
- 업캐스팅 → 리스트에 자식 객체 저장
- 재정의된 메소드 호출 → 다형성 발생
- 클래스 작성
# 상속과 다형성 예: 도형
class Shape:
def __init__(self, name):
self.name = name
def getArea(self): # 추상메소드 => 구현 생략
pass
class Circle(Shape):
def __init__(self,name, radius):
super().__init__(name)
self.radius = radius
def getArea(self): # 메소드 오버라이딩
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, name, width, height):
super().__init__(name)
self.width = width
self.height = height
def getArea(self): # 메소드 오버라이딩
return self.width * self.height
shapeList = [ Circle("c1", 10) , Rectangle("r1", 10, 10) ] # 업캐스팅(객체 리스트)
for s in shapeList :
print(s.getArea()) # 재정의된 메소드 호출 ==> 다형성 발생 !!!!!!!!
314.0 100
Object 클래스
- 파이썬 클래스 계층구조의 최상위 클래스
- 모든 클래스의 부모 클래스
- object 클래스 메소드 → 파이썬 모든 클래스가 상속받음
- 각 클래스에서 재정의(overriding) 해서 사용
repr() 메소드 재정의
- 객체를 표현하는 문자열을 반환하는 특수 메소드
- str() 메소드와 차이
- 주로 개발자가 사용하는 것
- str() 메소드 미구현 시
- repr() 메소드가 호출된다.
class Book :
def __init__(self, title, isbn):
self.__title = title
self.__isbn = isbn
def __repr__(self):
return "Book('%s', '%s')" %(self.__title, self.__isbn)
def __str__(self):
return "ISBN1 : " + self.__isbn+ "; TITLE : " + self.__title
book = Book("The Python Tutorial", "0123456")
print(book)
print(repr(book))
ISBN1 : 0123456; TITLE : The Python Tutorial Book('The Python Tutorial', '0123456')
call() 메소드
- 호출 가능한 객체를 만드는 메소드
- 객체 참조변수를 함수처럼 사용 시 호출됨
- 메소드 형식
- def call(self, …) :
class Foo :
def __init__(self, x=0) : # 생성자
self.x = x
def __str__(self): # 객체 => 문자열 메소드
return '[Foo : x=%d]' % self.x
def __call__(self, *args): # 호출 가능 객체, 가변 인자
print('__call__ method', args)
f1 = Foo(5) # __init__ 메소드 호출
print(f1) # __str__ 메소드 호출
f1() # __call__ 메소드 호출
f1(1,2,3) # __call__ 메소드 호출
[Foo : x=5] __
call__
method () __
call__
method (1, 2, 3)
callable() 함수
- callable(obj) 함수
- 참조변수 obj가 호출 가능한 객체인지를 체크
- 호출 가능하면 True, 아니면 False 반환
- 참조변수 obj가 호출 가능한 객체인지를 체크
class A:
def __call__(self):
return 'test...'
class B:
pass
a = A()
b = B()
print(callable(a)) # True
print(callable(b)) # False
True False
예제
- TV 객체 추상화
- 상태(속성) → 상태를 동작 보다 먼저 정의
- 전원상태, 볼륨, 채널번호
- 동작(기능)
- 전원 켜기/끄기, 볼륨 변경하기, 채널 변경하기
- 상태(속성) → 상태를 동작 보다 먼저 정의
- TV 클래스 모델링
- 필드(인스턴스 변수) → 상태(속성) 표현
- on, volume, channel
- 메소드(멤버함수) → 동작(기능) 표현
- turnOn() , turnOff() , changeVolume(), changeChannel()
- 필드(인스턴스 변수) → 상태(속성) 표현
class Tv:
def __init__(self, on, ch, vol): # 생성자 - 필드 초기화
self.on = on # 필드 = 인스턴스 변수 생성
self.channel = ch
self.volume = vol
def print(self): # 메소드 = 멤버 함수
print(self.on, self.channel, self.volume)
def turnOn(self):
self.on = True
def turnOff(self):
self.on = False
def changeChannel(self, ch):
self.channel = ch
def changeVolume(self, vol):
self.volume = vol
t1 = Tv(False, 11, 10) # Tv 객체 생성
t1.turnOn() # 메소드 호출
t1.print()
res : True 11 10
파이썬의 필드는 init (생성자)안에 선언
예제2
- 자동차 객체 추상황
- 상태(속성) → 모델, 색상, 속도
- 동작(기능) → 가속, 감속
- 클래스 모델링 : Car
- 필드(인스턴스 변수) → model, color, speed
- 메소드(멤버함수) → init() , speedUp(), speedDown(), str()
- str() 메소드 → 객체의 상태를 문자열로 변환(반환)
- 객체를 화면 출력 시 , 문자열로 변환 시 자동 호출
class Car:
def __init__(self, m, c, s): # 생성자
self.model = m
self.color = c
self.speed = s
def speedUp(self):
self.speed += 10
def speedDown(self):
self.speed -= 10
def __str__(self):
return "Car[Model=%s:color=%s:speed=%d]" \
% (self.model, self.color, self.speed)
c = Car('BMW', 'Black', 100)
c.speedDown()
print(c)
res : Car[Model=BMW:color=Black:speed=90]
클래스 변수
- 클래스 변수는 클래스 당 하나만 생성된다 or 클래스 간 공유를 한다.
class Tv:
serialnum = 0 # 클래스 변수 정의(초기화)
def __init__(self, on, ch, vol): # 생성자 - 초기화
self.on = on # 필드 = 인스턴스 변수 생성
self.channel = ch
self.volume = vol
Tv.serialnum += 1 # 객체 생성 시 클래스 변수 증가
self.num = Tv.serialnum
def print(self): # 메소드 = 멤버 함수
print(self.on, self.channel, self.volume, self.num)
t1 = Tv(False, 11, 10) # Tv 객체 생성
t1.print()
t2 = Tv(True, 9, 20)
t2.print()
상수
- 상수 : 한 번 정의 되면 값이 바뀌지 않는 수
- 값이 바뀌지 않기 때문에 메모리 절약을 위해 클래스당 하나 만 있으면 되기때문에 클래스 변수에 정의
- 상수도 클래스의 필드에 정의하지 않고 클래스 변수에 정의한다.
class Circle:
PI = 3.14 # 클래스 변수 정의(초기화) - 상수 정의
def __init__(self, r):
self.radius = r
def area(self):
return Circle.PI*self.radius**2
def perimeter(self):
return 2*Circle.PI*self.radius
radius = float(input('반지름의 길이 입력 :'))
c1 = Circle(radius)
print("원의 넓이 = ", c1.area())
print("원의 둘레길이 = ", c1.perimeter())
특수 메소드(연산자 함수)
class Vector2D: # 2차원 벡터
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, o): # 연산자 == 함수
return self.x == o.x and self.y == o.y # 1번째 클래스의 x 값과 2번째 클래스의 x값이 같은가
def __add__(self, o): # 연산자 + 함수
return Vector2D(self.x + o.x, self.y + o.y)
def __str__(self):
return '(%d, %d)' % (self.x, self.y)
v1 = Vector2D(2, 3)
v2 = Vector2D(1, 2)
print(v1 == v2) # 연산자에서 객체가 오면 객체의 연산자 함수를 사용한다
v3 = v1 + v2
print(v3)
res : False (3, 5)
예제. 분수 클래스 작성
class Fraction:
def __init__(self, ja, mo):
self.ja = ja
self.mo = mo
def __add__(self, fr): # 더하기 연산자 수행
return Fraction(self.ja + fr.ja, self.mo + fr.mo) # class 사용으로 문자열 출력 사용
def __mul__(self, fr): # 곱셈 연산자 수행
return Fraction(self.ja * fr.ja, self.mo * fr.mo)
def __str__(self): # 문자열 출력
return "%d/%d" %(self.ja, self.mo)
f1 = Fraction(2,3)
f2 = Fraction(1,2)
print(f1+f2)
print(f1*f2)
답 :
3/5
2/6
상속
- 기존 클래스로부터 멤버(필드, 메소드)를 물려 받는다.
- 기존 클래스 → 부모 클래스
- 상속 받은 클래스 → 자식 클래스
- 파이썬에서는 부모 클래스로부터 메소드를 물려 받음
- 필드는 부모 클래스 메소드가 호출될 때 물려 받는다.
- 메소드 사용전에는 필드가 메모리상에 없어 공간절약 가능
- 필드는 부모 클래스 메소드가 호출될 때 물려 받는다.
- 클래스 간의 “is - a “ 관계 성립 시 활용
- A is-a B → B 부모 클래스, A 자식 클래스
- 학생은 사람이다.
- 자동차는 차량이다.
상속 필요성
- 코드의 재 사용
- 중복 줄이기
- 상속으로 중복 감소의 예
- 3가지 종류의 자동차 클래스 작성
- ex)Car, Truck, Bus
- 공통 메소드 포함 → 클래스에서 공통 메소드 각각 작성 : 코드 중복
- ex) setSpeed(), turn()
- 3가지 종류의 자동차 클래스 작성
- 공통 메소드를 가지는 부모 클래스 작성
- ex) Vehicle
- 다른 클래스는 자식 클래스로 작성
- 공통 메소드를 물려 받음
- 공통 메소드는 한번만 작성 → 중복 감소
부모 클래스의 생성자 호출
- 파이썬 부모 클래스 생성자 호출 방법
- super().init() → self 생략
- 부모 클래스 생성자 호출로 부모 클래스의 필드를 물려 받음
다중 상속 : 부모 생성자 호출
super 에서는 self 를 안쓰는 이유는 super() 자체가 객체 이기 때문에 self를 안써도됨
# 다중 상속 : 부모 생성자 호출
class Car :
def __init__(self):
print('Car')
class Plane :
def __init__(self):
print('Plane')
class FlyingCar(Car, Plane): # 다중 상속
def __init__(self):
super().__init__() # Car 출력 -> 첫번째 부모 클래스
Car.__init__(self) # Car 출력
Plane.__init__(self) # Plane 출력
fc = FlyingCar()
Car Car Plane
has - a 관계 : 포함
- 객체 A,B 간에 A “has-a” B 성립
- ex)원기둥 has-a 원(밑면)
포함은 → 필드로 자연스럽게 포함
# has-a 관계 : 포함
class Circle:
def __init__(self, r):
self.radius = r
def __str__(self):
return "Circle[radius=%d]" % (self.radius)
class Cylinder:
def __init__(self, c, h):
self.underside = c # 필드1: 밑면(원) => 원기둥 has-a 원(밑면)
self.height = h # 필드2: 높이
def __str__(self):
return "Cylinder[underside=%s, height=%d]" %(self.underside, self.height)
c = Circle(10) # 반지름 10인 원 생성
cyl = Cylinder(c, 20) # 밑면 반지름 10인 원, 높이 20인 원기둥 생성
print(cyl)
→ 원기둥은 밑면에 원을 포함한다 그러므로 상속관계가 아니라 포함관계이다. 원기둥의 필드에 원이 포함되었다.
클래스메소드와 정적메소드 차이
- 정적메소드는 전달받는 값이 없다, 클래스메소드는 클래스명으로 전달받을 수 있다.
중요!!
class Parent:
def __str__(self):
return "Parent"
@classmethod
def method1(cls) : # 클래스 메소드 호출 시 클래스명을 cls로 전달 받는다.
return cls()
@staticmethod
def method2() : # 클래스 메소드 호출 시 클래스명을 전달받을 수 없음
return Parent()
class Child(Parent):
def __str__(self):
return "Child"
obj1 = Child.method1() # 자식 클래스이름으로 클래스 메소드 호출
print(obj1)
obj2 = Child.method2() # 자식 클래스 이름으로 정적 메소드 호출
print(obj2)
Child Parent
메소드 오버라이딩
- 자식 클래스에서 부모 클래스로부터 상속받은 메소드를 재정의(overriding)
class Shape:
def draw(self):
print('Shape Draw')
class Circle(Shape):
def draw(self): # 메소드 재정의(Overriding)
print('Circle Draw')
c = Circle()
c.draw()
다형성
- 많은(poly) + 모양(morph)
- 하나의 식별자로 다양한 타입(클래스)을 처리
- 다형성 성립 조건
- 클래스 작성
- 상속 관계
- 메소드 재정의(오버라이딩)
- 객체 생성
- 업캐스팅 → 리스트에 자식 객체 저장
- 재정의된 메소드 호출 → 다형성 발생
- 클래스 작성
# 상속과 다형성 예: 도형
class Shape:
def __init__(self, name):
self.name = name
def getArea(self): # 추상메소드 => 구현 생략
pass
class Circle(Shape):
def __init__(self,name, radius):
super().__init__(name)
self.radius = radius
def getArea(self): # 메소드 오버라이딩
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, name, width, height):
super().__init__(name)
self.width = width
self.height = height
def getArea(self): # 메소드 오버라이딩
return self.width * self.height
shapeList = [ Circle("c1", 10) , Rectangle("r1", 10, 10) ] # 업캐스팅(객체 리스트)
for s in shapeList :
print(s.getArea()) # 재정의된 메소드 호출 ==> 다형성 발생 !!!!!!!!
314.0 100
Object 클래스
- 파이썬 클래스 계층구조의 최상위 클래스
- 모든 클래스의 부모 클래스
- object 클래스 메소드 → 파이썬 모든 클래스가 상속받음
- 각 클래스에서 재정의(overriding) 해서 사용
repr() 메소드 재정의
- 객체를 표현하는 문자열을 반환하는 특수 메소드
- str() 메소드와 차이
- 주로 개발자가 사용하는 것
- str() 메소드 미구현 시
- repr() 메소드가 호출된다.
class Book :
def __init__(self, title, isbn):
self.__title = title
self.__isbn = isbn
def __repr__(self):
return "Book('%s', '%s')" %(self.__title, self.__isbn)
def __str__(self):
return "ISBN1 : " + self.__isbn+ "; TITLE : " + self.__title
book = Book("The Python Tutorial", "0123456")
print(book)
print(repr(book))
ISBN1 : 0123456; TITLE : The Python Tutorial Book('The Python Tutorial', '0123456')
call() 메소드
- 호출 가능한 객체를 만드는 메소드
- 객체 참조변수를 함수처럼 사용 시 호출됨
- 메소드 형식
- def call(self, …) :
class Foo :
def __init__(self, x=0) : # 생성자
self.x = x
def __str__(self): # 객체 => 문자열 메소드
return '[Foo : x=%d]' % self.x
def __call__(self, *args): # 호출 가능 객체, 가변 인자
print('__call__ method', args)
f1 = Foo(5) # __init__ 메소드 호출
print(f1) # __str__ 메소드 호출
f1() # __call__ 메소드 호출
f1(1,2,3) # __call__ 메소드 호출
[Foo : x=5] __
call__
method () __
call__
method (1, 2, 3)
callable() 함수
- callable(obj) 함수
- 참조변수 obj가 호출 가능한 객체인지를 체크
- 호출 가능하면 True, 아니면 False 반환
- 참조변수 obj가 호출 가능한 객체인지를 체크
class A:
def __call__(self):
return 'test...'
class B:
pass
a = A()
b = B()
print(callable(a)) # True
print(callable(b)) # False
True False