21.1.25

커피 내리는 그냥 사람·2021년 1월 25일
0

위코드 사전스터디

목록 보기
17/36

<코딩도장 복습 통한 새로 이해한 내용 정리 : Unit : 34~38>

  1. 클래스
    • 클래스에 대한 나의 이해 : 정의가 다양해서 이런 저런걸 다 읽어봤지만 이렇게 이해하는게 제일 마음이 편했다.
      파이썬에서 쓰이는 모든 것은 다 객체다. 그 객체에는 이름이 있고 객체가 쓰이는 스타일, 속성 같은 것이 있는데 이를 분류해준 것이 클래스다. 클래스에 따라 모든 객체는 역할이 달라지고(간단하게는 int, str부터 더 어려운 객체 클래스까지) 그 객체가 사용될 때 쓰이는 함수, 즉 .~다음에 나오는 것을 '메서드'라고 한다.
      self나 __init___에 대해서는 조금 더 이해가 필요할 것 같다. 일단 self가 와야 하는 이유는 인스턴스로 쓰이는 변수 자체이므로 써야 한다 정도만 이해해야겠다.(메모리 상 id값이 같다고 한다.)

(예시)

class Person:
    def __init__(self): # 객체 초기화
        self.hello = '안녕하세요.'
 
    def greeting(self):
        print(self.hello)
 
james = Person()
james.greeting()    # 안녕하세요.
class Person:
    def __init__(self, name, age, address):
        self.hello = '안녕하세요.'
        self.name = name
        self.age = age
        self.address = address
 
    def greeting(self):
        print('{0} 저는 {1}입니다.'.format(self.hello, self.name))
 
maria = Person('마리아', 20, '서울시 서초구 반포동')
maria.greeting()    # 안녕하세요. 저는 마리아입니다.
 
print('이름:', maria.name)       # 마리아
print('나이:', maria.age)        # 20
print('주소:', maria.address)    # 서울시 서초구 반포동
#인스턴스.속성 통한 접근
  • 비공개 속성(클래스 안에 있는 메서드를 통해서만 조작할 수 있는 속성)
class Person:
    def __init__(self, name, age, address, wallet):
        self.name = name
        self.age = age
        self.address = address
        self.__wallet = wallet    # 변수 앞에 __를 붙여서 비공개 속성으로 만듦
 
    def pay(self, amount):
        self.__wallet -= amount   # 비공개 속성은 클래스 안의 메서드에서만 접근할 수 있음
        print('이제 {0}원 남았네요.'.format(self.__wallet))
 
maria = Person('마리아', 20, '서울시 서초구 반포동', 10000)
maria.pay(3000)
# 이제 7000원 남았네요

클래스 바깥으로 드러내고 싶지 않은 값을 위해 사용. 클래스 밖에서는 바꾸면 안 되는 경우일 때.


  1. 클래스 속성과 인스턴스 속성
class Person:
    bag = []
 
    def put_bag(self, stuff):
        self.bag.append(stuff)
 
james = Person()
james.put_bag('책')
 
maria = Person()
maria.put_bag('열쇠')
 
print(james.bag)
print(maria.bag)

['책', '열쇠']
['책', '열쇠']

여기서 self가 인스턴스를 모두 지칭하므로 애매한 부분이 있어서 클래스 속성(def~)이 다 똑같이 나옴. 이 때는 self.bag~ 으로 접근하지 말고 클래스 이름으로 접근해서 분리해주기.

    bag = []
 
    def put_bag(self, stuff):
        Person.bag.append(stuff)    # 클래스 이름으로 클래스 속성에 접근
  • 클래스 속성: 모든 인스턴스가 공유. 인스턴스 전체가 사용해야 하는 값을 저장할 때 사용
  • 인스턴스 속성: 인스턴스별로 독립되어 있음. 각 인스턴스가 값을 따로 저장해야 할 때 사용
  • 비공개 클래스 만들기
class Knight:
    __item_limit = 10    # 비공개 클래스 속성
 
    def print_item_limit(self):
        print(Knight.__item_limit)    # 클래스 안에서만 접근할 수 있음
 
 
x = Knight()
x.print_item_limit()    # 10
 
print(Knight.__item_limit)    # 클래스 바깥에서는 접근할 수 없음

언제 쓰일까? : 클래스에서 공개하고 싶지 않은 속성이 있다면 비공개 클래스를 사용한다.

  • 정적 / 클래스 메서드 : 인스턴스를 안 만들고 클래스에서 바로 호출하는 방법.
class Calc:
    @staticmethod
    def add(a, b):
        print(a + b)
 
    @staticmethod
    def mul(a, b):
        print(a * b)
 
Calc.add(10, 20)    # 클래스에서 바로 메서드 호출
Calc.mul(10, 20)    # 클래스에서 바로 메서드 호출

$ init, self, 인스턴스 등 전혀 하지 않고 만들 수 있음 : 인스턴스 속성에 접근할 수 없으므로 순수함수에 많이 사용.(출력값이 일정해야하는 연산 같은 것)

class Person:
    count = 0    # 클래스 속성
 
    def __init__(self):
        Person.count += 1    # 인스턴스가 만들어질 때
                             # 클래스 속성 count에 1을 더함
 
    @classmethod
    def print_count(cls):
        print('{0}명 생성되었습니다.'.format(cls.count))    # cls로 클래스 속성에 접근
 
james = Person()
maria = Person()
 
Person.print_count()    # 2명 생성되었습니다.

인스턴스를 건너뛰고 호출할 수 있다는 것은 공통적이지만 메서드 안에서 클래스 속성, 클래스 메서드에 접근해야 할 때 사용. (아직은 낯설다.)

  • 정적 메서드 예시 연습문제 35
class Date:
  @staticmethod
  def is_date_valid(date_string):
    year, month, day = map(int, date_string.split('-'))
    return month <= 12 and day <= 31


if Date.is_date_valid('2000-10-31'):
    print('올바른 날짜 형식입니다.')
else:
    print('잘못된 날짜 형식입니다.')

문자열이 올바른지 아닌지만 판단.->정적 메서드 이용.
이후 변수 정한 뒤 조건 만들기.

  • 정적, 클래스 메서드 모두 사용한 심사문제 35
class Time:
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    @classmethod
    def from_string(cls, time_string):
      hour, minute, second = map(int, time_string.split(':'))
      time = cls(hour, minute, second)
      return time


    @staticmethod
    def is_time_valid(time_string):
      hour, minute, second = map(int, time_string.split(':'))
      return hour <= 24 and minute <= 59 and second <= 60


time_string = input()
 
if Time.is_time_valid(time_string):
    t = Time.from_string(time_string)
    print(t.hour, t.minute, t.second)
else:
    print('잘못된 시간 형식입니다.')

문제나 답에 대한 이해가 많이 떨어진다. 클래스 메서드는 너무 어렵다.


  1. 클래스 상속 : 클래스의 중복을 피하기 위해 만들어진 개념
class Person:
    def greeting(self):
        print('안녕하세요.')
 
class Student(Person):
    def study(self):
        print('공부하기')
 
james = Student()
james.greeting()    # 안녕하세요.: 기반 클래스 Person의 메서드 호출
james.study()       # 공부하기: 파생 클래스 Student에 추가한 study 메서드
  • 상속관계 : 위와 같이 동등한 관계

  • 포함관계 : 상속 사용하지 않고 속성에 인스턴스를 넣어 관리하는 형태.

  • 상속받은 클래스가 기반 클래스 속성을 이용하는 방법(super)

class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = '안녕하세요.'
 
class Student(Person):
    def __init__(self):
        print('Student __init__')
        super().__init__()                # super()로 기반 클래스의 __init__ 메서드 호출
        self.school = '파이썬 코딩 도장'
 
james = Student()
print(james.school)
print(james.hello)
  • 메서드 오버라이딩 : 중복되는 기능은 파생 클래스에서 다시 만들지 않고 기반클래스 내용을 사용하면서 간소화 하는 작업
class Person:
    def greeting(self):
        print('안녕하세요.')
 
class Student(Person):
    def greeting(self):
        super().greeting()    # 기반 클래스의 메서드 호출하여 중복을 줄임
        print('저는 파이썬 코딩 도장 학생입니다.')
 
james = Student()
james.greeting()
  • 추상 클래스 : 메서드의 목적만 가지며 상속받는 클래서에서 메서드를 강제 구현하기 위해 사용.
from abc import *
 
class StudentBase(metaclass=ABCMeta):
    @abstractmethod
    def study(self):
        pass
 
    @abstractmethod
    def go_to_school(self):
        pass
 
class Student(StudentBase):
    def study(self):
        print('공부하기')
 
    def go_to_school(self):
        print('학교가기')
 
james = Student()
james.study()
james.go_to_school()
  • 연습문제 36
class AdvancedList(list):
  def replace(self, old, new):
    while old in self:
      self[self.index(old)] = new


x = AdvancedList([1, 2, 3, 1, 2, 3, 1, 2, 3])
x.replace(1, 100)
print(x)

list에서 상속받았으므로 list 상속 받은 뒤 replace를 새로 정의(여기서 replace는 기존에 문자열 메서드가 아니라 내가 새로 정의 해야 할 메서드였음.) 반복문 형태이므로 while을 사용한 뒤 인자는 2개를 받아야 하므로 old, new를 임의로 정한다. 이 때 전 인자가 후 인자로 바뀌어야 하는 식을 index로 찾기를 한 수 바꿔주는 식으로 풀기.

  • (응용) 두 점 사이에 거리 구하기 심사문제 37
import math
 
class Point2D:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
 
length = 0.0
p = [Point2D(), Point2D(), Point2D(), Point2D()]
p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y = map(int, input().split())
---(문제 부분)---
for i in range(len(p)-1):
  if i <= len(p)-1:
    length += math.sqrt((p[i+1].x - p[i].x)**2 + (p[i+1].y - p[i].y)**2)
----(문제 부분)----
print(length)

두 점이 떨어진 거리만큼 for 반복문으로 선을 이어 더한 뒤 sqrt처리 해주는 것.


  1. 예외 처리 : 코드 실행 중 발생한 에러
  • 기본 구조
try:
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
    print(y)
except:    # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.')
  • 예외와 상관 없이 코드가 나오게 하려면?
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
except ZeroDivisionError:    # 숫자를 0으로 나눠서 에러가 발생했을 때 실행됨
    print('숫자를 0으로 나눌 수 없습니다.')
else:                        # try의 코드에서 예외가 발생하지 않았을 때 실행됨
    print(y)
finally:                     # 예외 발생 여부와 상관없이 항상 실행됨
    print('코드 실행이 끝났습니다.')
  • 예외를 발생시키려면?
try:
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)
except Exception as e:                             # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.', e)
    
# 혹은

def three_multiple():
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)                                       # 현재 함수 안에는 except가 없으므로
                                                   # 예외를 상위 코드 블록으로 넘김
 
try:
    three_multiple()
except Exception as e:                             # 하위 코드 블록에서 예외가 발생해도 실행됨
    print('예외가 발생했습니다.', e)
  • 예외를 클래스처럼 만드는 방법
class NotThreeMultipleError(Exception):    # Exception을 상속받아서 새로운 예외를 만듦
    def __init__(self):
        super().__init__('3의 배수가 아닙니다.')
 
def three_multiple():
    try:
        x = int(input('3의 배수를 입력하세요: '))
        if x % 3 != 0:                     # x가 3의 배수가 아니면
            raise NotThreeMultipleError    # NotThreeMultipleError 예외를 발생시킴
        print(x)
    except Exception as e:
        print('예외가 발생했습니다.', e)
 
three_multiple()

심사문제 38. def 위치에 주의하고 회문 넣을 시 회문인지 아닌지도 중요하지만 글자수 제한 뒀을 때 1글자면 return하는 것 잊지 않기

class NotPalindromeError(Exception):
    def __init__(self):
        super().__init__('회문이 아닙니다.')

def palindrome(word):
   if len(word) < 2:
        return True
   if word != word[::-1]:
        raise NotPalindromeError
   print(word)


try:
    word = input()
    palindrome(word)
except NotPalindromeError as e:
    print(e)

어려웠지만 일단 또 한 번 부분 반복 끝냈다. 다른 것 공부하며 다시 필요하면 찾아보자.

profile
커피 내리고 향 맡는거 좋아해요. 이것 저것 공부합니다.

0개의 댓글