220804_TIL / Python with OOP

신두다·2022년 8월 4일
0

TIL

목록 보기
67/82

Key words

OOP(Object Oriented Programming), 클래스 설계

CS 섹션에 들어오고 하루 이틀 공부해보면서 파이썬 공부를 하는건가보다 싶었는데 그게 아니었다.

  • 그보다는 문제 해결을 위한 컴퓨팅 사고력을 기르는 것과 프로그램을 구성하는 여러 구조를 이해하고 어떻게 하면 잘 설계할 수 있을지 배우는 것이었다.
  • 오늘 공부한 OOP나, class 설계하는 것도 마찬가지다. 프로그램 의 효율적 설계라는 맥락에서 OOP라는 방법론을 배운 것이고, 그 방법론의 대표 언어인 python을 이용해서 class 설계 등을 실습한 것이다. python 배우자는게 우선이 아니라!

앞으로 남은 섹션 공부하면서 이런 맥락을 기억하도록 하자. 지엽적인 것에 빠지지 말고 큰 그림을 생각할 줄 알아야 한다.


1. OOP

  • OOP에 대한 나만의 정리는 4번 실습과제Part2에 적어두었으니 보도록 하자. OOP에 대해서는 나중에 공부노트로 따로 정리해보는 시간을 가지는게 나을 듯 싶다.

대신 오늘 들은 것 중 기억할만한 몇 가지만 옮겨와 보자면..

  • OOP는 하나의 패러다임일 뿐 절차적 프로그래밍 등 기존의 패러다임들과 우열을 가릴 수는 없다는 점. 절대선이 아니라고!
  • OOP의 핵심은 코드의 가독성과 재사용성. 객체 간의 상호작용으로 프로그램이 이루어지는 것인데, 따라서 효율적인 기능 분리 및 설계를 잘하는 것이 중요하다. (적절한 비유일지는 모르겠지만, 자연적으로 발생해 난개발된 도시와 처음부터 잘 계획된 계획도시를 생각하면 될듯?)

OOP 및 파이썬의 관련 기능들 또한 이런 맥락에서 생각해보면 더 잘 기억하기 쉬울 것 같다.

  • 결국 프로그래밍도 제한된 리소스 내에서 최대한의 효율을 추구하면서 하는거고, 일은 한 사람이 아니라 여러 사람이 같이 하는 거니까. 개념이나 방법론이 먼저가 아니라 필요한 상황이나 니즈가 무엇이었을까를 먼저 생각해보는 습관을 가지면 좋을 것 같다.

2. 그 외 기억하고 싶은 것

데이터 캡슐화와 접근제어

  • 예전에 섹션2였나.. 수정되기 싫은 변수들 접근 제어하는거 들었었는데 오늘 다시 나와서 정리해둔다.
    name - public - 어디서나 접근 가능
    _name - Protected - public과 비슷하지만 웬만하면 밖에선 가져다 쓰지 말았음 좋겠다~ 는 의미
    __name - Private 안 보여줄거고 접근도 안되게 할거야!
  • 근데 물론 파이썬의 철학 특성상 private하다고 완전히 감추는 것은 불가능하다고 한다.

파이썬의 자료형도 하나의 클래스이다.

  • str, list 등의 type을 뽑아보면 <class 'int'>식으로 출력되는 걸 볼 수 있다. 어젠가 그림에 class 안에 모듈있고 함수 있던거 기억난다.
  • 그런 의미에서 생각해보면 좋을게 있다. 예를 들어 split 함수는 str(문자열)에 대해서만 쓸 수 있다. 만약 list에 쓰면 'list object has no attribute 'split'과 같은 에러를 볼 수 있는데 이 말은 뭐냐? 바로 split이란 함수는 str이라는 클래스 안에 있다는 것이다!

오늘 OOP 관련 자주 들었던 말도 있다.

  • 현실 세계의 사물의 특징을 컴퓨터가 이해할 수 있도록 클래스를 사용하여 정의할 수 있다는 것.
  • 즉, OOP의 O(객체)를 생각할 때도 현실에 있는 무언가를 컴퓨터가 인식할 수 있도록 표현하는 기술로 생각하는 것이라는 점도 기억하면 좋을 것 같다. 말이 좀 추상적이긴 해.. 객체건 클래스건 뭐건 용어 자체에 대한 집중보다는 그 활용에 대해 더 고민하는게 좋다고 한다.

클래스/객체/인스턴스란?

  • 파이썬에서 객체를 만들기 위해선 클래스를 먼저 선언해줘야 한다. 클래스는 객체의 공통된 속성과 행위를 변수와 함수로 정의한 것이다. 즉, 클래스는 객체를 만들기 위한 기본 틀이고, 객체는 기본 틀을 바탕으로 만들어진 결과이다.
  • 객체와 인스턴스의 차이에 대해선 점투파의 설명이 가장 좋은 것 같다.

3. class 연습

QnA 세션 간 작성해보았던 것이다.

# Car 클래스 선언
class Car:
  pass

# 객체 선언
my_car = Car()
sm_car = Car()

# 객체의 속성값 설정
my_car.color = 'red'
sm_car.color = 'yellow'
print('차 색상 = ', my_car.color) # 차 색상 =  red
print('차 색상 = ', sm_car.color) # 차 색상 =  yellow
  • 클래스로부터 인스턴스를 만들고나면 위와 같이 각각 속성을 지정해줄 수 있다.
# __init__() 초기화 함수
# 객체를 생성하는 것과 동시에 속성값을 지정할 수 있다. 

# Car 클래스 선언
class Car:
  def __init__(self, color):
    self.color = color

my_car = Car('blue') # 기본 속성값을 지정해서 객체를 선언해줘야 한다. 
print('차 색상 = ', my_car.color) # 차 색상 =  blue

'''
class Car:
  def __init__(self, color = 'yellow'): # 이렇게 하면 컬러 디폴트도 지정 가능. 
    self.color = color
'''
  • __init__함수는 잘 알고 있는 것처럼 클래스가 실행되자마자 실행되는 기본 기능이라고 생각하면 된다. 즉, 위 코드에서는 인스턴스를 생성할 때 color를 지정해줘야 한다. 안 그럼 에러.
  • 디폴트 넣을 수 있는 것도 기억!
class Gun():
  def __init__(self):
    self.bullet = 0
  
  def charge(self, num): # 장전
    self.bullet += num # 추가 장전하면 이전에 가지고 있던 총알 수에 더해서 되어야하니까! 

  def shoot(self, num):
    for i in range(num):
      if self.bullet > 0:
        print('탕!')
        self.bullet -=1
      elif self.bullet == 0:
        print("총알이 없습니다.")
        break

gen = Gun()

# 장전
gen.charge(5)

# shoot
gen.shoot(2)
gen.shoot(3)
gen.shoot(2)

"""
탕!
탕!
탕!
탕!
탕!
총알이 없습니다.
"""
gen.charge(5)
gen.charge(7)
print(gen.bullet)

gen.shoot(3)
print(gen.bullet)

"""
12
탕!
탕!
탕!
9 # 이렇게까지 잘 되어야 한다! 여기 놓치기 쉬울 것 같음.
"""
  • 이거 재밌었다. charge하면 그만큼 bullet의 수가 증가하고, shoot을 하면 '탕!'을 프린트하며 bullet 수를 줄여나가다가 다 소진하면 "총알이 없습니다"를 출력하는거다.
  • 이것도 처음에 설계를 잘해야한다. 그리고 스스로 여러 상황을 테스트해보며 디버깅 해봐야한다.
  • 실제로 위 간단해보이는 기능 구현 과정에서도 테스트 하며 예상치 못한 상황이 많이 발생했다. 이거보면서 전 회사 QA팀에서 오류 잡아내려고 이런 저런 테스트하던 것이 생각났다..
  • 참고로 위꺼랑 구조는 거의 같은 걸로 아래같은 것도 적어봄.
"""
고릴라 클래스를 구현해서 고릴라 객체를 만드시오!
 
gorila_1 = Gorilla()
gorila_1.eat(10)
바나나를 10개를 먹었습니다.

gorila_1.shout(3)
우와 ~~~~
우와 ~~~~
우와 ~~~~

# 소리 지를 떄마다 바나나 하나씩 소진
바나나가 다 소진 되었는데 또 소리를 지르려고 하면
배가고파 소리를 지를 수 없습니다. 메세지가 출력되게하시오~
"""

class Gorilla:
  def __init__(self):
    self.banana = 0

  def eat(self, num):
    self.banana += num
    print(f"바나나를 {num}개를 먹었습니다")
  
  def shout(self, num):
    for i in range(num):
      if self.banana > 0:
        print('우와 ~~~~')
        self.banana -= 1
      elif self.banana == 0:
        print("배가 고파 소리를 지를 수 없습니다.")
        break

4. 실습 과제

오늘은 주로 class를 통해 특정 기능을 구현하는 과제였다. 프로그램 설계의 의미로 봐도 될 것 같다. 오늘은 시간 관계상 기본과제만 했다.

[part1]

"""
Bare Minimum Requirements

요구사항:
    아래 문제들을 확인하며 하나씩 문제를 풀어주세요.    
"""

from unicodedata import name


class CarOwner:
    """
    자동차 주인을 나타내는 클래스입니다. 
    자동차 주인은 이름(name)을 가지고 있으며
    자동차 주인은 concentrate 메소드를 통해 이름 + ' can not do anything else'를 반환해야합니다. 
    """
    def __init__(self, name):
        """
        문제 1. 
            CarOwner 클래스의 생성자 메소드를 작성해주세요.
            매개변수를 수정하거나 추가하지 말아주세요.
        """
        self.name = name


    def concentrate(self):
        """
        문제 2. 
            자동차 주인 이름에 따라 
            '{자동차주인이름} can not do anything else'문구를 반환해주는 메소드를 작성해주세요.
        """
        return f"{self.name} can not do anything else"


class Car:
    """
    자동차를 나타내는 클래스입니다. 
    자동차는 주인(owner)을 가지고 있으며, 주인은 CarOwner를 통해서 생성됩니다.
    자동차는 drive 메소드를 통해 
    자동차 주인이 집중하고 있으며(concentrate), 누가 운전하고 있는지 반환해야합니다. 
    '{자동차주인이름} is driving now.'
    """
    def __init__(self, owner_name):
        """
        문제 3. 
            Car 클래스의 생성자 메소드를 작성해주세요.
            매개변수를 수정하거나 추가하지 말아주세요
        """
        self.owner = CarOwner(owner_name) 
        # 어떻게 가져올 수 있을까 헷갈렸는데.. 이렇게 인스턴스 생성해서 쓸 수 있는거 기억하자.


    def drive(self):
        """
        문제 4.
            자동차 주인이 집중하고 있으며, 자동차 주인이 자동차를 운전하고 있다는 내용을 반환해야합니다. 
            '{자동차주인이름} can not do anything else
            {자동차주인이름} is driving now.'

            (hint)
            첫 번째 문장은 CarOwner 클래스의 concentrate() 메소드를 사용해 구현하고,
            두 번째 문장은 아래에서 구현하여 반환되도록 구현해보세요!
        """
        return f"{self.owner.concentrate()}\n{self.owner.name} is driving now."
        # 리턴값 print하면 줄바꿈 잘 보임. colab에서 함수 실행해봤을 때 안되서 왜인지 의아했었음.

class SelfDrivingCar(Car):
    """
    문제 5. 
        이 자동차는 자율주행 자동차입니다! 
        자동차를 상속받아주세요!
    """
    def drive(self):
        """
        문제 6.
            이 자동차는 자동차 주인이 있지만... 자동차 주인이 운전하지 않고 스스로 운전하네요! 
            drive 메소드를 통해
            'Car is driving by itself'를 반환해야합니다. 
            상속을 받았는데.. drive메소드를 다시 선언해도 괜찮을까요?
            메소드 오버라이딩 개념을 다시 생각해보며 코드를 작성해주세요.
        """
        return 'Car is driving by itself'
        # 메소드 오버라이딩은 말 그대로 부모 클래스에 있는 메서드에 덮어쓰는 것이다. 
        # 모듈을 import해서 쓰면 수정할 순 없고 그대로 써야하는데 그럴 때 쓸 수 있다고 하는듯?
  • class에 대해서 이전에 점투파를 통해서 공부는 했지만 실제로 이렇게 뭘 만들어본게 처음이다 보니 굉장히 헷갈렸다.
  • 오늘 배운 것처럼 어떤 프로그램을 만들 때에는 코딩 자체보다는 어떻게 효율적으로 잘 설계할 수 있을지를 생각하는 것이 더 중요한 것 같다. 설계가 머릿속에 없으면 그냥 이리저리 왔다갔다 헷갈리기만 하는거야!
  • 지금이야 연습이니 이렇게 대충 구조 던져주고 채우라는거지만, 실무에서는 그런게 있겠어? 없어! 설계도를 머릿속에 그리는 연습을 해야한다.

[part2]


class Computer:
    """
    ## Computer 클래스의 코드는 수정하지 마세요 ##
    이미 완성된 코드입니다.
    아래 코드를 활용하여 문제를 해결해주세요.
    """
    def __init__(self, cpu, ram):
        self.CPU = cpu
        self.RAM = ram
        
    def browse(self):
        return('browse')

    def work(self):
        return('work')
        

class Laptop(Computer):
    """
    위에 작성된 Computer 클래스를 상속받는 Laptop 클래스를 완성해주세요. 
    """
    def __init__(self, cpu, ram, battery):
        """
        문제 1.
            Laptop 클래스의 생성자 함수를 완성해주세요.
            Laptop은 컴퓨터에 기본적으로 들어가는 부품 이외에 배터리도 추가됩니다.
            Computer 클래스에서 사용하는 변수에 battery를 추가해주세요.

            super키워드를 사용하여 상속받는 클래스에서 부모 클래스의 생성자를 사용하는 방법에 대해 익혀주세요.
        """
        super().__init__(cpu, ram) # 상속
        self.battery = battery # 변수 추가

'''
[기록]
부모 클래스에서 정의한 함수와 자식 클래스에서 정의한 함수 이름이 같은 경우,
부모 클래스의 함수를 호출하려면 두 가지 방법 중 하나를 사용해야 합니다.
1) 부모클래스명.함수명()
2) super().함수명()

즉, super()는 자식클래스에서 부모클래스에 있는 함수를 사용하고 싶고,
플러스 해당 함수명이 자식클래스에 중복되어 있을 때 사용할 수 있는 코드입니다.
'''


def oop_explain():
    """
    문제 2. OOP에 대해서 공부하신 내용들을 최대한 많이 작성해주세요.
    OOP의 구성에 대해서도 설명을 작성해주세요
    """

    answer = """
    OOP 개괄
        - OOP는 프로그래밍 설계의 다양한 방법론 중 하나이다. (즉, 항상 옳은 절대선이 아니다)
        - OOP의 기본 전제는 함수, 변수 등의 기능이 재사용 가능하도록 설계 및 프로그래밍 하는 것이다. (중요!!)
            이해를 돕기 위해 절차적 프로그래밍(Procedural Programming)과 비교하자면,
            => 절차 지향 프로그래밍은 프로그래밍된 순서에 따라 순차적으로만 작동한다.
            => 반면 OOP는 현실에서 발생할 수 있는 object를 컴퓨터에 인식시키고, 이들 간의 상호작용을 통해 프로그래밍이 이루어진다.
                => 즉, 기존에 작성된 기능을 필요시 언제든지 불러와 재사용할 수 있어 코드를 간결하게 만들 수 있는 것이다.
                => 다만 그렇기 때문에 OOP는 여러 개체를 효율적으로 분리하는 것이 중요하며, 프로그래머가 어떻게 기능별 설계를 하는지에 따라 성능이 달라질 수 있다. 
    OOP 구성
        - 캡슐화: class 내부에 객체의 공통된 속성과 행위를 변수와 함수로 정의하는 것을 말한다. (= 기능별로 분리해서 같은 기능끼리 하나로 묶자!)
            => 캡슐화는 OOP의 가장 중요한 기능 중 하나이며, 캡슐화를 통해 유저가 변수나 메서드에 직접 접근하여 실수로 데이터를 변경하는 행위를 방지해준다. 
            => 
        - 상속(inheritance): 상속을 통해 부모 클래스(=상위 클래스)의 모든 기능을 그대로 받아와 사용할 수 있다.
            => 이때 부모 클래스에 있는 기능의 일부를 자식 클래스에서 변경하여 사용할 수 있다. 이를 다형성이라고도 한다. (예. 메서드 오버라이딩/오버로딩)
            => 자식 클래스에서는 부모 클래스에 없는 기능을 추가하여 사용할 수 있다. 단, 부모 클래스에서는 자식 클래스에 있는 추가된 기능을 사용할 수 없다. (자식 이기는 부모 없다!로 기억)
        - 포함 (composition): 상속처럼 아예 부모 클래스의 기능을 가져오는 것이 아니라, 하나의 클래스에 다른 클래스의 일부 기능(함수)만을 가져와 사용하는 것을 말한다.
            => 오늘 과제 중 part1의 'Car' class에 쓰인 것이 그 예시이다. 
        - 추상화: 추상 클래스는 메서드의 목록만 가진 클래스이며, 상속받는 클래스에서 메서드 구현을 강제하기 위해 사용된다고 한다.
            => 추상 클래스로는 인스턴스를 생성할 수는 없고, 오로지 상속을 위해서만 사용된다. 
            => 추상 클래스의 유용성에 대해선 너무 다양한 상황과 제약사항이 있다고 한다. 하나 기억할만한 건 '프로그램에 대한 요약과 표준화'이다.
                => 추상 클래스에 프로그램의 핵심적인 개념 또는 기능을 간추려 제공하고, 또 구현을 강제하기 때문이다.
            # reference: https://continuous-development.tistory.com/71 
    """

    return answer
  • 언제 super()를 쓰고 언제 부모클래스명.함수명()을 쓰는지 잘 기억해두자.
  • OOP에 대해서는 나중에 공부노트로 제대로 정리해봐야겠다. (언제 가능하려나..)

Feeling


  • 따로 준비해야할 것이 겹치며 스트레스가 극심하다.. 이번 섹션에서 배우는게 내가 재밌게 하는 코딩 부분이라 그래도 다행이다.
  • 조금만 버티면 다 지나갈거다. 힘내자.
profile
B2B SaaS 회사에서 Data Analyst로 일하고 있습니다.

0개의 댓글