배꼽파지 않도록 잘 개발해요

방송대 파이썬 프로그래밍 기초 - 10강. 객체지향 본문

방송대 컴퓨터과학과/파이썬 프로그래밍 기초

방송대 파이썬 프로그래밍 기초 - 10강. 객체지향

꼽파 2023. 4. 22. 21:06


  • 1. 객체지향의 이해

  • 2. 클래스와 인스턴스

  • 3. 객체지향의 활용

  • 4. 원뿔 계산 프로그램 개선

  • 5. BMI 계산기

  • 1. 객체지향의 이해

    유사성

    '용호의 권'과 '철권'의 공통점

    - 격투게임

    - 2인의 캐릭터 결투

    - 팔, 다리로 다양한 기술 구사

    출처: 강의록

    두 게임의 공통적인 특징을 먼저 구현해 놓고, 별도로 서로 다른 점을 구현하면  훨씬 더 효과적인 대형 프로그램을 만들 수 있음.

    (객체지향형의 시작점)

    하나의 프로그램 내부에서도 서로 다른 객체 사이의 공통점을 또 찾아내 구현함.


    객체지향의 개념

    • 객체와 객체 사이의 상호작용으로 프로그램을 구성하는 프로그래밍 패러다임

    • 프로그램을 유연하고 변경을 쉽게 만들어 대규모 소프트웨어 개발에 사용

     

    • 객체지향 패러다임의 특징

    - 명령형 프로그래밍, 절차적 프로그래밍의 특징은 그대로 계승하지만 아래와 같은 특성이 있음.

    - 추상화 : 공통의 속성이나 기능을 도출

    - 캡슐화 : 데이터 구조와 데이터의 연산을 결합

    - 상속 : 상위 개념의 특징이 하위 개념에 전달

    - 다형성 : 유사 객체의 사용성을 그대로 유지

    - JAVA, C++, C# 등에서 자주 사용되는 개념


    객체와 클래스

    객체는 추상화와 캡슐화의 결과

    실세계의 사물에 대한 상태(데이터)와 연산(메소드)을 표현한 단위

    → 멤버(데이터 필드, 메소드)는 클래스에 의해 결정

    출처: 강의록


    클래스의 정의

    클래스(Class) : 객체(Object)를 만들기 위한 설계도(Blueprint)
    이 설계도에 따라 생성된 객체는 그 설계도에서 정의한 속성(Attribute)메소드(Method)를 가질 수 있음.

    구문형식 메소드와 초기자

    메소드(method) ---(함수)
    - 객체에 대한 행동(연산)을 정의
    - 메소드는 객체에서 사용할 수 있는 함수임

    - 함수의 정의 및 사용 방법과 동일

    초기자(initializer) ---(변수)
    - 객체의 상태를 초기화하는 특수 메소드
    - 항상 __init__으로 명명

    메소드의 정의

    구문형식

    self 매개변수

    - 모든 메소드의 첫번째 매개변수

    - 메소드의 구현에 사용되지만 메소드 호출 시 사용되지 않음

    - 객체 자신을 참조하여 클래스 정의에 포함된 멤버에 접근 시 사용

    - 클래스 내부의 변수에 접근할 때 '데이터 필드'임을 알려주는 self

     

    클래스 설계

    - UML 클래스 다이어그램 통해 데이터필드, 생성자, 메소드 표현 방법 표준화

    - UML(Unified Modeling Language) : 표준화된 모델링 언어

    • 데이터 필드 이름: 데이터 필드 타입

    • 클래스 이름(매개변수 이름: 매개변수 타입)

    메소드 이름(매개변수 이름: 매개변수 타입): 반환값 타입


    2. 클래스와 인스턴스

    객체와 인스턴스

    구문형식

    생성자 호출

    • 클래스의 생성자(constructor)를 통해 클래스의 인스턴스 생성 → 실체화된 대상이 생성됨

    • 객체와 인스턴스는 동일 개념

    • 클래스의 생성자는 클래스의 이름과 동일

    • 클래스의 이름과 초기자의 매개변수를 사용하여 생성자를 호출

    (생성자를 호출할 때 사용하는 파라미터가 초기자의 파라미터와 동일할 뿐임)

    출처: 강의록


    객체의 사용

    • 객체의 데이터 필드 접근 및 메소드 호출

    • 객체 멤버 접근 연산자(.) 사용

     

    객체 접근

    생성자를 통해 만들어진 객체에 접근할 수 있는 지칭 도구가 필요함.

    객체 참조변수를 사용하여 객체를 생성

     

    생성자를 통해 만들어진 객체 :

    클래스 이름 (초기자 파라미터)

     


    이름과 나이를 입력받아 출력하는 프로그램

    # Person Class를 정의함.
    # name과 age라는 두 개의 인스턴스 변수를 갖고 있음.

    class Person:
        def __init__(selfname="Amelia"age=24):
            self.name = name
            self.age = age

    name = input("이름을 입력해주세요."
    age = int(input("나이를 입력해주세요."))
    P1 = Person(name, age)
    print(P1.name, "님의 나이는 ", P1.age, "살입니다.")


    3. 객체지향의 활용

    데이터 타입과 객체

    실제 우리가 알았던 모든 변수는 객체임.

    .을 누르면 str메소드 중 하나를 부르게 됨.

    '.lower()'을 입력하면 소문자로 변형되어 출력됨.

    number = 20
    print(type(number)) # 변수의 자료형을 반환함. → int
    print(id(number))   # 변수가 저장된 메모리의 주소를 반환 
    # 실행할 때마다 값이 다를 수 있음 
    symbol = "파이썬"
    print(type(symbol)) # 변수의 자료형을 반환함. → str
    print(id(symbol))   # 변수가 저장된 메모리의 주소를 반환 
    # 실행할 때마다 값이 다를 수 있음 

    객체로 조작, 연산을 하고 싶다면 객체참조변수에 점(.) 하나만 찍으면 됨.

     

    Colab에서도 점을 찍으면 그 객체에 해당하는 여러 메소드가 일목요연하게 표현됨.

     

    dir(str) # 어떤 메소드를 사용할 수 있는지 나옴


    str 메소드

    메소드 설명
    upper(), lower() 대/소 문자로 변경
    title() 각 단어의 첫 글자를 대문자로 변경
    strip(), rstrip(), Istrip() 양쪽/왼쪽/오른쪽의 특정 문자를 제거
    replace() 문자열 특정 부분을 대체
    split() 구분자로 분할하여 리스트로 변환
    A = "I love python".upper()              # 대문자로 작성
    B = "I love python".replace("o""i"# o와 i 교체
    C = "I love python".title()                 # 각 문장의 첫글자 대문자로 교체
    result = [A, B, C]
    for letter in result:
        print(letter)

    string = " This is a Sample String. "

    # 대문자로 변경경
    print(string.upper()) # " THIS IS A SAMPLE STRING. "

    # 소문자로 변경
    print(string.lower()) # " this is a sample string. "

    # 각 단어의 첫 글자를 대문자로 변경
    print(string.title()) # " This Is A Sample String. "

    # 좌우 공백 제거
    print(string.strip( )) # "This is a Sample String."

    # 좌측 공백 제거
    print(string.lstrip( )) # "This is a Sample String. "

    # 우측 공백 제거
    print(string.rstrip( )) # " This is a Sample String."

    # "Sample" 문자열을 "example"로 대체
    print(string.replace("Sample""example")) # " This is a example String. "

    # 구분자 " "로 분할하여 리스트로 변환
    print(string.split(" ")) # ['This', 'is', 'a', 'Sample', 'String.']

    데이터 필드 감추기

    데이터 은닉(data hiding)

    데이터 필드의 직접 변경을 방지하기 위해 사용자의 직접적 접근을 차단

    public과 다른 private 데이터 필드로 정의

     

    private 데이터 필드

    클래스 내부에서만 접근 가능함.

    앞 두 밑줄(__)로 정의

    ex. self.__r / self.__h

     

    public, protected, private 데이터필드 [참고, 수업 X]

    데이터필드 특징 예시 코드
    public 클래스 내부 및 외부에서 모두 접근 가능  
    protected 클래스 내부와 파생 클래스에서 접근 가능
    외부에서는 직접 접근 불가능
     
    private 클래스 내부에서 접근 가능
    외부에서는 직접 접근 불가능
     

     


    접근자와 변경자

    private으로 정의된 데이터 필드는 객체 외부에서 접근 불가능

    출처: 강의록

     

    private 데이터 필드에 접근하는 메소드

    접근자(accessor) : 데이터 필드 반환

    변경자(mutator) : 데이터 필드 설정


    4. 원뿔 계산 프로그램 개선

    1. 원뿔 클래스의 표현

    UML 클래스 다이어그램

    Cone → 클래스 이름
    r: int
    h: int
    → 데이터 필드와 타입
    Cone(radius = 20: int, height = 30: int) 
    get_vol(): float
    get_surf(): float
    → 생성자
    겉넓이와 부피를 계산해서 실수형으로 반환시켜줌

    PEP8 스타일가이드에서 클래스 이름은 대문자로 지정할 것

    class Cone:
        def __init__(selfradius = 20height = 30):
            self.r = radius
            self.h = height

        def get_vol(self):
            return 1/3 * 3.14 * self.r ** 2 * self.h

        def get_surf(self):
            return 1/3 * 3.14 * self.r ** 2 + 3.14 * self.r * self.h

    '''
    주의사항) 
    데이터 필드의 r과 h라는 것을 알려주기 위해 꼭 앞에 self.를 앞에 표시해주어야 한다
    설계 도면(클래스)을 정의한 것이므로 프로그램 실행 변화가 없는 것은 당연함
    '''

    # 원뿔 계산 함수 정의
    def rtn_cone_vol_surf(rh) :
        if r > 0 and h > 0 :
            # r, h 모두 양수일 때
            vol = 1/3 * 3.14 * r ** 2 * h
            suf = 3.14 * r ** 2 + 3.14 * r * h
            return vol, surf
        else :
            # r, h가 음수일 때
            print("반지름과 높이 값에 양수를 입력하세요")

    Cone 클래스를 정의하는 코드임.
    이 클래스는 원뿔의 부피와 표면적을 계산할 수 있는 메소드인 get_vol과 get_surf를 가지고 있음.

    • get_vol 메소드 : 원뿔의 부피를 계산하여 반환함.
    • get_surf 메소드 : 원뿔의 표면적을 계산하여 반환함.

    __init__ 메소드는 객체가 생성될 때 자동으로 호출되며, radius, height라는 속성을 초기화함.
    기본값으로 radius는 20, height는 30으로 초기화됨.


     

    2. 생성된 객체에 접근하여 출력하기

    단위 원뿔과 반지름과 높이가 각각 50, 100인 원뿔의 부피와 겉넓이를 출력하는 프로그램

    # 아까 정의한 클래스들은 이미 실행을 했기 때문에
    # 여기서는 곧바로 객체를 만들 수 있음

    # 객체 참조변수 = 클래스명 (초기자 파라미터)
    unit_cone = Cone() # 별도로 정의해주지 않으면 위에서 적용한 기본값이 적용됨.
    big_cone = Cone(50100)

    unit_cone.get_vol()
    unit_cone.get_surf()
    # 멤버에 접근하기 위해서는 연산자(.)를 사용한다

    print("단위 원뿔의 겉넓이와 부피는"round(unit_cone.get_surf(),3), round(unit_cone.get_vol(),3), "입니다.")
    print("큰 원뿔의 겉넓이와 부피는"round(big_cone.get_surf(),3), round(big_cone.get_vol(),3), "입니다.")
    # 객체의 멤버에 어떻게 접근해야 하는지 확인할 것


    3. 데이터 은닉

    멤버 r 또는 h에 음수를 입력하면?

    # 원뿔 클래스 정의
    class Cone:
        def __init__(selfradius = 20height = 30):
            self.r = radius
            self.h = height

        def get_vol(self) :
            return 1/3 * 3.14 * self.r ** 2 * self.h
        
        def get_surf(self) :
            return 3.14 * self.r ** 2 + 3.14 * self.r * self.h
        
    unitcone = Cone(50100)
    unitcone.r = -50

    악의적인 사용자가 Cone 클래스에 접근하지 못하도록 제약을 줄 필요가 있음 (데이터 은닉 필요)


    4. 데이터필드 접근자와 변경자 정의

    멤버 __r과 __h에 대한 접근자와 변경자 정의

    Cone 클래스
    __r : int
    __h : int
     
    Cone(radius = 20: int, height = 30: int)
    get_vol(): float
    get_surf(): float
    get_r() : int
    get_h() : int
    접근자
    set_r (radius: int) : None
    set_h (height: int) : None
    변경자
    # 1) 클래스에 잘못된 접근 차단하기

    class private_Cone:
        def __init__(selfradius = 20height = 30):
               self.__r = radius 
               self.__h = height

    # private로 객체 외부에서 반지름과 높이값은 접근 불가능함.

        def get_vol(self): #float
            return 1/3 * 3.14 * self.__r ** 2 * self.__h

        def get_surf(self): #float
            return 1/3 * 3.14 * self.__r ** 2 + 3.14 * self.__r * self.__h

    What = private_Cone(20010
    print(What.get_vol())

    기존에 보였던 r과 h가 나타나지 않음.

     

    # 2) 접근자와 변경자를 통한 private 데이터필드 접근
    # → 별도의 함수를 생성

    class private_Cone:
        def __init__(selfradius = 20height = 30):
            if radius > 0 and height > 0 :
               self.__r = radius 
               self.__h = height

    # private로 객체 외부에서 반지름과 높이값은 접근 불가능함.
    # get 메소드 : 변수의 값을 반환

        def get_vol(self): #float
            return 1/3 * 3.14 * self.__r ** 2 * self.__h

        def get_surf(self): #float
            return 1/3 * 3.14 * self.__r ** 2 + 3.14 * self.__r * self.__h

        def get_radius(self): 
            return self.__r
            
        def get_height(self):
            return self.__h

    # set 메소드 : private 변수에 값을 할당함.
    # 음수값이 입력되지 않도록 if문으로 제어
        def set_radius(selfradius):
            if radius >= 0:
             self.__r = radius

        def set_height(selfheight):
            if height >= 0:
             self.__h = height
     

    이 코드에서 사용자가 r과 h에 음수를 입력하면 출력이 되지 않음.

    '__init__' 생성자에서 조건문을 통해 사용자가 입력한 반지름과 높이 값이 0보다 큰 지 검사하고,

    만약 조건에 부합하지 않으면 해당 변수들이 초기화되지 않음.

    → 음수 값을 입력하면 객체 생성 자체가 되지 않음.

     


    5. BMI 계산기

     

    가상의 이름, 나이, 몸무게, 키를 사용하여 BMI 객체를 사용하는 프로그램

     

    이름, 나이, 몸무게, 키를 사용하여 BMI 수치 및 상태를 반환하는 BMI 클래스를 정의

    BMI → 클래스 이름

    name: str

    age: int
    weight: float
    height: float
    → 데이터 필드와 타입
    사람의 이름

    사람의 나이
    사람의 몸무게
    사람의 키
    BMI(name: str, age: int, weight: float, height: float)
    get_BMI(): float
    get_status(): str
    기본 BMI 객체를 생성한다
    BMI 수치를 반환한다
    BMI 상태를 반환한다

    get_BMI에서 반환된 값을 부르는 방법

     

    ① BMI 클래스 내의 메소드 정의 후 호출

    class BMI:
        def __init__(selfnameageweightheight) :
            self.name = name
            self.age = age
            self.weight = weight
            self.height = height
    # 입력 파라미터에 들어온 값들을 데이터 필드에 넣어주는 작업

        def get_BMI(self) :
            return self.weight / (self.height / 100) ** 2

        def get_status(self) :
            if self.get_BMI() >= 25 :
                return "비만"
            elif self.get_BMI() >= 23 and self.get_BMI < 25 :
                return "과체중"
            elif self.get_BMI() >= 18.5 and self.get_BMI < 23 :
                return "정상"
            else :
                return "저체중"
     

    ② 변수에 메소드 반환값을 저장해 놓고 해당 변수를 호출

    class BMI:
        def __init__(selfnameageweightheight) :
            self.name = name
            self.age = age
            self.weight = weight
            self.height = height
    # 입력 파라미터에 들어온 값들을 데이터 필드에 넣어주는 작업

        def get_BMI(self) :
            return self.weight / (self.height / 100) ** 2

        def get_status(value_BMI) :
            value_BMI = get_BMI()

            if value_BMI >= 25 :
                return "비만"
            elif value_BMI >= 23 and value_BMII < 25 :
                return "과체중"
            elif value_BMI >= 18.5 and value_BMI < 23 :
                return "정상"
            else :
                return "저체중"
    # self.get_BMI 대신 get_status 메소드 안에 value_BMI 변수를 생성해서 넣음

     

    728x90