10 객체 지향 프로그래밍

2023. 10. 24. 22:58프로그래밍 공부/Python

10.1 객체 지향 프로그래밍이란?

10.1.1 클래스란?

클래스(class): 개체 유형을 정의하기 위해 "함께 묶"되는 인스턴스 변수 및 메서드 집합을 지정한다.

 

ex)

String Class

name: 'John Smith'

street: '8 Crown Street'

city/state: 'Baltimore, Maryland'

zip_code: '21284'

 

name.isdigit() ➝ False 

address.isdigit() ➝ False    

city_state.isdigit() ➝ False

zip_code.isdigit() ➝ True

 


10.1.2 객체 지향 프로그래밍의 세 가지 기본 속성

캡슐화(encapsulation), 상속(heritance), 다형성(polymorphism)은 객체 지향 프로그램의 설계를 지원하는 세 가지 기본 특징이다.

 

객체 지향 프로그래밍에서의 매세지 전달

Object A                                 Object B

                    message              

method A1 ------------->.  method B1

 

10.2 캡슐화

10.2.1 캡슐화란?

캡슐화(encapsulation): 인스턴스 변수와 메서드를 결합하여 특정 클래스 멤버에 대한 액세스를 제한하는 방법

 

Fraction 객체 frac1 접근

frac1.__numerator = 4 NOT ALLOWED
frac1.__denominator = 6 NOT ALLOWED

 

frac1.getNumerator() ALLOWED
frac1.getDenominator() ALLOWED
frac1.setNumerator(4) ALLOWED
frac1.setDenominator(6) ALLOWED

 

이러한 메서드는 클래스의 개인 인스턴스 변수를 가져오고(반환) 설정(할당)하는 것이 목적이므로 getter 및 setter라고 한다.

getter 및 setter 메서드를 통해 인스턴스 변수에 대한 액세스를 제한하면

메서드가 할당되는 값이 무엇인지(분모에 0이 할당되지 않도록 허용하지 않는 것과 같이) 검색할 때 어떻게 표시되는지 제어할 수 있다.

따라서 클래스의 인스턴스 변수는 일반적으로 비공개가 되고 클래스의 메서드는 일반적으로 공개가 된다.


10.2.2 파이썬에서의 클래스 정의하기

Fraction 클래스 정의하기

클래스 키워드는 Python에서 클래스를 정의하는 데 사용된다.

두 개의 밑줄 문자로 시작하는 클래스 멤버는 클래스의 개인 멤버로 사용된다.

이는 네임 맹글링(name mangling)을 사용하여 Python에서 효과적으로 수행된다.

 

class Fraction(object):
	def __init__(self, numerator, denominator): #Special Methods
    	"""Inits Fraction with values numerator and denominator."""
        
        self.__numerator = numerator
        self.__denominator = denominator
        self.reduce()
        
    def getNumerator(self): #Getter and Setter Methods
    	"""Returns the numerator of a Fraction."""
        
        return self.__numerator
   
   def getDenominator(self):
    	"""Returns the denominator of a Fraction."""
        
        return self.__denominator
   
   def setNumerator(self, value): 
    	"""Sets the numerator of a Fraction to the provided value."""
        
        self.__numerator = value
  
  def setDenominator(self, value): 
    	"""Sets the denominator of a Fraction to the provided value.
          
          Raises a ValueError exception if a value of zero provided.
        """
        if value == 0:
        	raise ValueError('Divide by Zero Error')
        
        self.__denominator = value

파이썬에서의 특별한 메서드

Python의 특별한 메소드는 두 개의 밑줄 문자로 시작과 끝으로 시작하는 이름을 가지며, Python에서 자동으로 호출된다.

__init__ 메서드: Fraction(분수) 객체 생성시 자동 생성

 

파이썬의 또 다른 특별한 두 가지 방법은 __str_와 __repr_이다.

이 방법들은 객체의 값을 문자열로 표현하는데 사용된다.

__str__ 메서드: 객체가 프린트를 사용하여 표시될 때(그리고 str 변환 함수를 사용할 때) 호출된다.

__repr_ 메서드: 파이썬 셸에 객체의 값이 표시될 때(파이썬을 사용하여 상호작용할 때) 호출된다.

 

class DemoStrRepr(): 
    def __repr__(self): 
        return '__repr__ called' 

 

    def __str__(self): 
        return '__str__ called' 

 

>>> s = DemoStrRepr()

>>> print(s)

__str__ called

>>> s

__repr__ called

 

추가적인 특별한 메서드 repr

class Fraction(object):
	def __repr__(self): #Additional Special Methods
    	"""Returns Fraction value as x/y."""
        
		return str(self.__numerator) + '/' + str(self.__denominator)

 

Fraction 클래스에 산술 연산자 추가하기

Python에는 산술 연산자 및 관계 연산자를 위한 특별한 방법이 있으며, 이는 주어진 객체 유형에 대해 연산자를 평가하는 방법을 결정하기 위해 구현될 수 있다.

산술 연산자 특별한 메서드

Operator Example Use Special Use
-(negation) -frac1 __neg__
+(addition) frac1 + frac2 __add__
-(subtraction) frac1 - frac2 __sub__
*(multiplication) frac1 * frac2 __mul__

 

Fraction 클래스의 산술 연산자

class Fraction(object):
	def __neg__(self):  #Arithmetic Operator Special Methods
    	"Returns a new fraction equal to the negation of self."""
        
        return Fraction(-self.__numerator, self.__denominator)
        
    def __add__(self, rfraction):
    	"Returns a new reduced Fraction equal to self + rfraction."""
        
        numer = self.__numerator * rfraction.getDenominator() + \
                rfraction.getNumerator() * self.__denominator
        denom = self.__denominator * rfraction.getDenomiator()
        
        resultFrac = Fraction(numer, denom)
        return resultFrac.reduce()
   
   def __sub__(self, rfraction):
    	"Returns a new reduced Fraction equal to self - rfraction."""
        
        return self + (-rfraction)
   
   def __mul__(self, rfraction):
    	"Returns a new reduced Fraction equal to self * rfraction."""
        
        numer = self.__numerator * rfraction.getNumerator()
        denom = self.__denominator * rfraction.getDenomiator()
        
        resultFrac = Fraction(numer, denom)
        resultFrac.reduce()
        
        return resultFrac

 

Fraction 클래스에 관계 연산자 추가하기

관계 연산자 특별한 메서드

관계 연산자 예시 사용 특별한 메서드
< (less than) frac1 < frac2 __lt__
<= (less than or equal to) frac1 <= frac2 __le__
== (equal) frac1 == frac2 __eq__
!= (not equal) frac1 != frac2 __ne__
> (greater than) frac1 > frac2 __gt__
>= (greater than or equal to) frac1 >= frac2 __ge__

 

Fraction 클래스의 관계 연산자

class Fraction(object):
	def __eq__(self, rfraction) : #Relational Operators
    	"""Returns True if self arithmetically equal to rfraction.
           Otherwise, returns False.
        """
        
        temp_frac1 = self.copy()
        temp_frac2 = rfraction.copy()
        temp_frac1.reduce()
        temp_frac2.reduce()
        
        return temp_frac1.getNumerator() == temp_frac2.getNumerator() \
           temp_frac1.getDenominator() == temp_frac2.getDenominator()

	def __neq__(self, rfraction):
    	"""Returns True if self not arithmetically equal to rfraction.
           Otherwise, returns False.
        """
        
        return not __eq__(rfraction)
    
    def __lt__(self, rfraction) : 
    	"""Returns True if self less than rfraction."""
        
        if self.getDenominator() == rfraction.getDenominator():
        	return self.getNumerator() == rfraction.getNumerator()
        else:
        	temp_frac1 = self.copy()
        	temp_frac2 = rfraction.copy()
            
            saved_denom = temp_frac1.getDenominator()
        	temp_frac1.__adjust(temp_frac2.getDenominator())
        	temp_frac2.__adjust(saved_denom)
        
        return temp_frac1.getNumerator() < temp_frac2.getNumerator()
    
    def __le__(self, rfraction) : 
    	"""Returns True if self less than or equal to rfraction."""
        
        return not (rfraction < self)
    
    def __gt__(self, rfraction) : 
    	"""Returns True if self greater than rfraction."""
        
        return not (self <= rfraction)
        
    def __ge__(self, rfraction) : 
    	"""Returns True if self greater than or equal to rfraction."""
        
        return not (self < rfraction)

 

10.3 상속

10.3.1 상속이란?

상속(inheritance): 한 클래스가 다른 클래스의 멤버를 자신의 정의의 일부로 상속하는 능력

슈퍼 클래스(subperclass): 상속된 클래스 ("기본 클래스(base class)" 또는 "부모 클래스(parent class)"라고도 함)

서브 클래스(subclass): 상속하는 클래스 ("파생 클래스(derived class)" 또는 "자녀 클래스(child class)"라고도 함)  

슈퍼 클래스가 다른 클래스로부터 상속됨으로써 그림 10-13과 같은 클래스의 계층이 생성될 수 있다(상속된 클래스 멤버는 회색이다).

 

클래스의 계층

 

10.3.2 하위종류

하위 유형(subtype): 해당 상위 유형(및 해당 상위 유형 등)으로 대체되고 동작할 수 있는 것

 

예시) 동물의 특성 계층화

 

10.3.3 파이썬에서 하위클래스 정의하기
파이썬에서 내장형 클래스 이름

내장 함수 유형 type: 사용하여 Python의 모든 값의 유형(클래스 이름)을 확인할 수 있다.
내장된 기능 도움말 help: 사용하여 내장된 유형의 클래스 설명을 얻을 수 있다.

 

예시

>>> type(12) 
< class 'int' >
>>> type([]) 
< class 'list' >

 

>>> help(str)

 

폭발 문자열 유형 정의하기

기본 제공 문자열 클래스가 주어지면 문자열 클래스와 동일한 새로운 문자열 유형을 쉽게 만들 수 있으며, 또한 "폭발"되는 옵션을 제공할 수 있다.

폭발 문자열(exploded string): 모든 문자 사이에 공백(공백 문자)이 있는 문자열

예를 들어 'H e l l o'와 같은 이 새로운 클래스를 ExplosedStr이라고 하며, 그림 10.16에 정의를 제시한다.

하위 클래스를 정의할 때 클래스 이름 다음에 괄호 안의 상위 클래스 이름이 나온다.

 

그림 10.16 내장된 str 클래스의 서브클래스로서 Explodedstr 타입

# Exploded String Class

class ExplodedStr(str):
	def __init__(self, value = ''):
    	# call to init of str class
		str.__init__(value)
        
    
    def explode(self):
    	
        # empty str returned unaltered
        if len(self) == 0:
        	return self
        else:
        	# created exploded string
            empty_str = ''
            blank_chr = ' '
            temp_str = empty_str
            
            for k in range(0, len(self) - 1):
				temp_str = temp_str + self[k] + blank_char
            
            # append last char without following blank
            temp_str = temp_str + self[len(self) - 1]
            
            # return exploded str by joining all chars in list
            return temp_str

 

>>> reg_str = 'Hello'  # defining a regular string
>>> ex_str = ExplodedStr('Hello')  # defining an exploded string
>>> reg_str  # value of regular string
'Hello'
>>> ex_str  # value of exploded string
'Hello'
>>> ex_str.explode()  # call to explode method
H e l l o
>>> reg_str == ex_str  # comparing the strings
True
>>>

 

10.4 다형성

10.4.1 다형성이란?

객체 지향 프로그래밍에서 다형성(polymorphism) 은 각각의 고유한 행동을 가진 다른 유형의 객체를 동일한 일반 유형으로 취급할 수 있게 한다.

 

그림 10-19 다형적 Shape 클래스

 

 

 

추상적인 Shape 클래스

class Shape:
	def __init__(self, x, y):
    	self.__x = x
        self.__y = y
        
    def getXYLoc(self):
    	return (self.__x, self.__y)
    
    def setXYLoc(self, x, y):
    	self.__x = x
        self.__y = y
    
    def calcArea(self):
    	raise NotImplementedError("Method calcArea not implemented")

 

서브클래스 Circle, Square, 그리고 Triangle

class Circle(Shape):
	def __init__(self, x, y, r):
    	Shape.__init__(self, x, y)
        self.__radius = r
    
    def calcArea(self):
    	return math.pi * self.__radius ** 2

class Square(Shape):
	def __init__(self, x, y, s):
    	Shape.__init__(self, x, y)
        self.__side = s
    
    def calcArea(self):
    	return self.__side ** 2

class Triangle(Shape):
	def __init__(self, x, y, s):
    	Shape.__init__(self, x, y)
        self.__side = s
    
    def calcArea(self):
    	return (self.__side ** 2) * math.sqrt(3) / 4.0

 

shapes_list = (circle1, circle2, square1, triangle1, triangle2)

 

total_area = 0
for shape in shapes_list:
    total_area = total_area + shape.calcArea()


10.4.2 다형성의 이용
다형성은 프로그래머가 프로그램 개발 중에 좀 더 추상적인 수준으로 사고할 수 있도록 하며, 유지 및 업데이트가 용이한 프로그램 개발을 지원한다.

덕 타이핑(duck typing): 동적 타이핑의 한 종류로, 객체의 변수 및 메소드의 집합이 객체의 타입을 결정하는 것

 

덕 타이핑에 따른 Circle, Square, Triangle Class의 방법

class Shape:
	def __init__(self, x, y):
    	self.__x = x
        self.__y = y
        
    def getXYLoc(self):
    	return (self.__x, self.__y)
    
    def setXYLoc(self, x, y):
    	self.__x = x
        self.__y = y
    
    def draw(self):
    	raise NotImplementedError("Method draw not implemented")
        
    def calcArea(self):
    	raise NotImplementedError("Method calcArea not implemented")
    
    def resize(self):
    	raise NotImplementedError("Method resize not implemented")

 

다형성 코드

# Create Appropriate Object

if selected_shape == 1:
	fig = Circle(0, 0, 1)
elif selected_shape == 2:
	fig = Square(0, 0, 1)
elif selected_shape == 3:
	fig = Triangle(0, 0, 1)

# draw
fig.draw()

# calc area
area = fig.calcArea()

# resize
fig.resize(selected_percentage)

# reposition
fig.setXYLoc(x, y)

 

새로운 Rectangle 타입을 통합하기 위해 업데이트된 다형적인 코드

# Create Appropriate Object

if selected_shape == 1:
	fig = Circle(0, 0, 1)
elif selected_shape == 2:
	fig = Square(0, 0, 1)
elif selected_shape == 3:
	fig = Triangle(0, 0, 1)
elif selected_shape == 4:
	fig = Rectangle(0, 0, 1)

 

10.5 UML을 이용한 객체 지향 디자인

10.5.1 UML이란?

UML("Unified Modeling Language"): 객체 지향 설계를 지정하기 위한 표준화된 언어 독립 그래픽 모델링 언어

 

10.5.2 UML 클래스 다이어그램

클래스 다이어그램(class diagram): 객체 지향 설계의 정적인 측면을 표현하는 데 사용된다.

상호 작용 다이어그램(interaction diagram): 프로그램 실행 중에 객체 간 메서드 호출의 순서를 나타내기 위해 사용된다.

 

클래스의 표현

클래스는 UML에 3가지 부분으로 표시된다: 클래스 이름(class name), 클래스 속성(class attributes)(인스턴스 변수(instance variables)) 그리고 메서드(method)

 

Class name
instance variables
Methods

 

Shape 와 Circle 클래스에 대한 UML 클래스 다이어그램

 

Shape {abstract}
-x:Integer
-y:Integer
+create(x:Integer, y:Integer)
+getXYLoc():Integer
+setXYLoc(x:Integer, y:Integer)
+draw()
+calcArea():Float
+resize(amt)

 

Circle
-radius: Integer
+create(x:Integer, y:Integer, r:Integer)
+draw()
+calcArea():Float
+resize(amt)

 

클래스 간 연관 관계 표시

연결 실선(및 가능한 화살표 헤드)으로 표시되는 두 클래스 간의 연관성은 한 클래스의 메서드가 다른 클래스의 메서드를 호출하는 것을 나타낸다.

 

서브클래스 관계 표시

UML의 하위 클래스 관계는 하위 클래스에서 수퍼 클래스로 향하는 닫힌 화살표 헤드가 있는 실선을 사용하여 표시된다.

Circle 클래스는 Shape 클래스의 서브클래스이다.

 

합성 대 집합 표시

합성(composition): 클래스 간의 "부분" 관계로 UML에서 채워진 다이아몬드 헤드로 표시된다.

집합(aggregation): "그룹화" 관계로 채워지지 않은 다이아몬드 헤드로 표시된다.

 

UML에서 클래스 합성

 

UML에서 클래스 집합

 

클래스 다이어그램의 예시

승객 차 UML 클래스 다이어그램

 

 

'프로그래밍 공부 > Python' 카테고리의 다른 글

12 컴퓨팅과 개발 과정  (0) 2023.10.27
11 재귀  (0) 2023.10.27
9 딕셔너리와 세트  (0) 2023.10.24
8 텍스트 파일  (0) 2023.10.24
7 모듈러 디자인  (1) 2023.10.23