[N321] TIL 및 회고

Sea Panda·2022년 12월 17일
0

부트캠프 TIL

목록 보기
29/46
post-thumbnail

"파이썬에서는 모든 것이 객체다"

0. 학습목표

  • 파이썬 개발자들이 공통으로 가진 철학을 말할 수 있다.
  • pdb디버거를 반복문, 함수에 사용하여 문제가 일어나는 부분을 찾을 수 있다.
  • 함수와 클래스를 이용해 파이썬 코드를 작성할 수 있다.

1. 주요개념

1. Debugging(디버깅)

디버그(Debug)는 프로그래밍 과정중에 발생하는 오류나 비정상적인 연산, 즉 Bug를 찾고 수정하는 것이다. 이 과정을 디버깅이라 하기도 한다.

Debug의 어원의 유래는 초창기 컴퓨터에 나방이 들어가 고장을 일으킨데에 있다. 그 뒤로 버그는 조작하는데 발생한 오류의 은유적 표현이 되었다.

프로그래밍에서 버그는 일종의 불가항력같은 것이라서 아무리 능력이 좋거나 경험이 많더라도 버그가 없는 프로그램을 만들 수 없다. 다만 요령있는 사람은 오류 지향적인 설계보다는 견고한 설계를 지향하고 버그가 나더라도 잡아낼 수 있도록 유도하여 짠다.

파이썬에서는 디버깅을 위해서 pdb라는 표준 라이브러리에 포함된 디버깅 도구가 있다. 사용 방법은 다음과 같다.

import pdb

def simple_func(num):
	sum = 0
	pdb.set_trace()

	for i in range(1, num + 1):
		pdb.set_trace()
		sum += i

	return sum

simple_func(4)

#다음은 실행결과입니다.

-> for i in range(1,num+1):
(pdb)
파이썬 3.7이상부터는 `breakpoint()`라는 함수를 실행하기만 하면 된다.
즉, `pdb.set_trace()`대신에 `breakpoint()`를 입력하면 된다.

위 코드를 실행하면 (pdb)라는 문구가 뜨고 코드가 여기에 멈춘다. 이렇게 멈춘 상태에서 특정 단계들을 검토한다던지 등 다양한 작업을 할 수가 있다. 위 코드의 결과에서 화살표로 보이는 것은 다음 줄을 표시하는 것이다. 현재는 sum=0 바로 다음 줄에 있는 함수에 멈춰있다. 여기에서 현재 상태에서는 변수에 어떤 것들이 저장되어 있는지 확인할 수 있다.

(pdb)단계에서 쓸 수 있는 pdb command는 다음과 같다.

2. Pythonic

Pythonic은 파이썬답게 코드를 짜는 것을 말한다. 파이썬다운 코드라하면 파이썬의 기능들을 잘 이용하여 작성된 코드이고, 그렇기 때문에 가독성이 좋은 코드를 말할 것이다. 대게 파이썬 커뮤니티의 사람들이 쓰는 패턴을 pythonic(파이썬다운)코드라고 생각한다.

이러한 pythonic을 작성할수 있도록 가이드가 존재한다. (Pythonic Coding Convention) PEP 8이 바로 그것이다. 이 가이드는 절대적인 것은 아니다. 잘 동작하고 읽기 쉽고 유지보수가 쉽다면 그보다 좋은 코드는 없다. 하지만 가이드를 따른다면 나뿐만 아니라 다른 사람에게도 읽기 쉽고, 유지하기 쉬운 코드가 된다. 이처럼 코드의 가독성과 일관성이라는 장점을 얻을 수 있다.

PEP 8의 주요 내용을 정리하여 번역한 codechacha.com를 시간날 때 읽어보자.

3. 함수

파이썬을 사용하면 피할 수 없는 것 중 하나가 함수이다. 파이썬을 설치하면 sum, print 등의 내장함수를 사용할 수 있고, pandas, numpy 등의 외부 라이브러리에도 loc,iloc 등의 함수가 포함되어 있기 때문이다. 또한 파이썬, 라이브러리의 설치와 함께 사용될 수 있는 내장된 함수들이 있다. 이를 "Built-in functions"라고 한다. 이와 별개로 새로 만들어지는 함수들은 "user-defined functions"라고 한다.

함수는 특정한 기능을 반복적으로 실행할 수 있도록 도와주기 때문에 반복적인 작업들을 하지 않아도 된다.

파이썬에서 함수를 만들기 위해서는 몇가지 규칙이 존재한다. 함수의 문법을 정리하면 다음과 같다.

def 함수_이름( 파라미터 ):
   "함수 문서"
   함수 내용
   return [표현식]

위 코드에서도 알 수 있듯이 함수를 정의하게 되면 함수에 포함되는 코드는 들여쓰기를 통해 구별짓는다. 가장 처음으로 오는 줄은 함수에 대한 문서가 될 수 있다(주석 및 설명). 이는 선택사항이라 존재유뮤 자체가 함수의 기능에 영향을 미치지는 않는다.

함수의 파라미터들은 위치에 따라 영향을 받는다. 따라서 파라미터를 어느 순으로 받는지 정한 것에 따라 함수를 호출할 때 순서를 지켜서 인수(Argument)를 넘겨야 한다.

def get_names(name_1, name_2, name_3):
    print(f"{name_1}{name_2}, {name_3}")

get_names("sponge", "bob", "patrick")

코드에서 순서를 바꾼다면 spongebob patrick이 출력되지 않는다.

파이썬에서는 인수들이 함수에 전달될 때 참조로 전달된다. 즉, 객체의 주소값을 전달(ex. int, str)한다. 하지만 immutable인 객체(ex. list, dict)들은 값으로 전달된다.


함수에 전달되는 인수는 크게 3가지로 나눠진다.

- 필수 인수

def person_info(first_name, last_name):
    print(f"Hello {first_name}, {last_name}!")

위의 예시처럼 위치에 따라서 전달되는 인수이다. 순서를 지켜야하며 함수에서 파라미터로 정의했기 때문에 필수로 넘겨야한다. 만일 모든 필수 인자를 전달하지 않고 코드를 실행하면 TypeError가 발생한다.

- 키워드 인수
위치로 전달하지 않고 키워드를 사용하여 전달하는 인수를 의미한다. 아래처럼 키워드를 명시하여 실행 할 수 있다. 필수 인수들을 전달하기만 하면 순서는 상관없다.

person_info(last_name="bob", first_name="sponge")

- 기본 인수
만약에 함수에서 받게 되는 파라미터 값들에 대해서 기본 값들을 설정하면 인수를 넘기지 않을 경우 기본값들이 설정되어 함수를 실행하게 할 수 있다.

def person_data(name, type_p='human'):
    print(f"Hello {name}, you are {type_p}")

위와 같은 경우에는 type_p인수를 넘기지 않으면 함수에서 기본값인 human으로 입력으로 받게된다.
여기서 주의할 점은 기본 값은 설정된 파라미터들은 기본값이 없는 파라미터 뒤에 등장해야 한다는 것이다. 만일 이를 어기면 syntaxError가 발생한다.


파이썬에서는 함수를 종료하기 위해서는 return을 사용하여야 한다. 그리고 호출자에게 표현을 전달하는 기능도 있다. 즉, 함수에서 return을 만나게 되면 함수기 그 즉시 종료된다.

def print_hello_1(name):
    print(f"Hello {name}")
    return None

위 코드에서는 return을 따로 명시하지 않아도 print문이 출력된다. 하지만 이는 함수의 출력은 아니고 함수가 실행되면서 그 안에서 print문에 의해서 실행된 것에 불가하다. 함수의 출력결과는 None이다.

a = print_hello_1('sponge')
print(a)

위의 출력 결과는 이름이 나오겠지만 함수의 출력값인 a는 None이라는 의미이다.

따로 리턴 문구를 넣어서 특정 결과를 리턴하게 할 수 있다.

def print_hello_4(name):
    return f"Hello {name}"

위 처럼 실행하면 함수의 결과값은 "Hello {name}"이 된다.

4. Class와 객체(object, or Instance)

파이썬에서는 모든 것이 객체이다. 그리고 이러한 객체를 만들기 위한 설계 도면이 바로 Class이다. 다시 말하자면, 함수와 비슷하게 설계 중심의 사고와 재사용성을 줄이기 위하여 만들어진 것이다. 조금 더 자세히 함수와 클래스의 차이를 알아보자면, 클래스가 조금 더 큰 범위라고 생각하면 된다. 클래스에서는 함수와 속성 두 가지를 전부 담을 수가 있다. 이때 클래스의 함수는 Method라고 한다. 두 가지를 전부 담을 수 있는 특성으로 인해서 다양한 정보와 기능들을 묶어서 따로 사용할 수 있다.


- 클래스 생성

class Pokemon:
	pokemon_a = 'pikachu'

	def print_pika(self):
		print("pika")

클래스는 위와 같은 형태로 만들 수 있다. 클래스 내에서 속성과 함수 모두를 정의할 수 있다. 위 코드처럼 일반적인 함수와 동일하게 클래스 내에서도 함수를 선선해도 잘 작동한다.

Pokemon.print_pika()

Class 코드에서 보면 self라는 것이 등장한다. 이는 인스턴스(객체)에서 해당 메서드를 실행할 때 자기자신을 인수로 넘겨주기 때문에 그것을 받는 자리이다.

self는 사실 단어 자체가 키워드는 아니다. 따라서 다른 단어로 대체해도 문제는 없다. 하지만 인스턴스에서 메서드나 특성을 사용할 때 첫 번째 인수, 파라미터로 넘겨지고 받아져야 한다는 것은 변함 없다. 여기서 또! 하지만! 위에서 다룬 Pythonic한 코드를 작성하기 위해서는 self를 사용하도록 가이드 하고 있다. 또한 class의 첫 번째 인자 이름은 cls를 사용하도록 권장한다.


- 생성자 함수
클래스의 함수 중에서 생성자 함수는 클래스가 인스턴스화(instantiate)될 때 사용되는 함수이다.만일 이 함수가 따로 정의되어 있지 않으면 기본 생성자 함수를 사용한다. 이 함수는 다음과 같이 사용할 수 있다.

class Pokemon:
    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a

poke_a = Pokemon()
print(poke_a.pokemon_a) 

클래스 기반으로 생성되는 인스턴스는 클래스의 생성자 함수에 따라 인스턴스의 초기 속성들을 받을 수 있다. 하지만 이는 인스턴스마다의 속성이지 클래스 전체에 대한 속성은 아니게 된다. 따라서 클래스 자체에서는 이러한 속성들에 대한 접근이 힘들다. 다음과 같은 코드를 실행하면 Pokemon클래스는 인스턴스의 속성에 접근 못한다는 것을 확인할 수 있다.

Pokemon.pokemon_a
------
-> AttributeError: type object 'Pokemon' has no attribute 'pokemon_a'

5. 객체지향

우리가 실생활에서 쓰는 모든 것을 객체라 하며, 객체 지향 프로그래밍은 프로그램 구현에 필요한 객체를 파악하고 각각의 객체들의 역할이 무엇인지를 정의하여 객체들 간의 상호작용을 통해 프로그램을 만드는 것을 말한다. 객체는 클래스라는 틀에서 생겨난 실체(instance)이다. 따라서 객체 지향 프로그램은 객체와 객체 간의 연결로 되어 있으며 각각의 객체 안에 자료구조와 알고리즘이 들어있다.

객체 지향 모델링에서는 기능이 아닌 객체가 중심이 되며 "누가 어떤 일을 할 것인가?"가 핵심이 딘다. 즉 객체를 도출하고 각각의 역할을 정의해 나가는 것에 초점을 맞춘다.

💡 객체? 인스턴스?
점프 투 파이썬의 예시를 들어보자. 쿠키를 만들기 위한 틀이 있다고 하자. 이 쿠키 틀을 바로 Class라고 한다. 그리고 이렇게 쿠키틀을 이용하여 찍어낸 쿠키가 객체이자 인스턴스가 되는 것이다. 둘의 차이는 관점의 차이이다. 쿠키 그 자체를 객체라고 한다면 인스터는 보다 관계적인 의미이다. "쿠키 틀의 인스턴스는 쿠키이다."라고 할 수 있다.

6. 파이썬 데코레이터(@)

파이썬 데코레이터는 PEP 318에서 어색하고 반복적인 함수의 표현을 줄이기 위해 제안되었다. 현재는 함수뿐만 아니라 클래스, 함수, 제네레이터 등의 다른 타입에서도 사용되고 있다. 이 역시 Pythonic한 코드를 작성하는 것과 관련된 개념으로 DRY원칙(Don't Repeat Yourself)을 따르기 위하여 사용하는 다양한 방법 중 하나로, 다른 형태보다 깔끔하고 간결한 코드를 만들면서 코드의 재사용을 줄이기 위해 많이 추천되는 기술이다.


이번 수업에서는 함수 데코레이터에 대해서 다루었다.

만일 다양한 문장을 출력하는데 겹치는 문장이 있다고 하자.

def my():
    print("데코레이터")
    print("my 라는 함수입니다.")

def mine():
    print("데코레이터")
    print("mine 이라는 함수입니다.")

def iam():
    print("데코레이터")
    print("iam 이라는 함수입니다.")

위 코드는 간단한 문장을 프린트하는 방식이였지만 복잡한 로직이 들어간다면 코드는 길어지고 같은 내용을 반복해서 작성해야할 것이다. 이를 데코레이터를 이용하면 다음과 같이 간단하게 작성할 수 있다.

def first_deco(func): # func 는 실행할 함수입니다.
    def first(): # 실행할 함수를 감싸는(wrap) 함수입니다.
        print("데코레이터")
        func()
    return first #first 함수를 return합니다

@first_deco
def my():
    print("my 라는 함수입니다.")

@first_deco
def mine():
    print("mine 이라는 함수입니다.")

@first_deco
def iam():
    print("iam 이라는 함수입니다.")

만일 사용할 함수에 인자가 주어진 경우에는 인자를 처리하는 구문이 있어야 정상작동된다. 따라서 인자값이 정의되어 있을 땐 데코레이터에도 인자 값을 처리하는 *args, **kwargs구문이 필요하다.

def first_last_deco(func): # func 는 실행할 함수입니다.
    def first_last(*args, **kwargs): # 실행할 함수를 감싸는(wrap) 함수입니다.
        print("first")
        func(*args, **kwargs)
        print("last")
    return first_last

@first_last_deco
def you(name):
    print(f"{name}! Hello")

여기서 *argsarguments의 줄임말이다. 이 지시어는 여러 개의 인자를 함수로 받고자 할 때 사용한다. 몇 개의 입력이 주어지는지 모를 때도 사용할 수 있다.

**kwargs는 {키워드=특정값}의 형태로 함수를 호출할 때 사용하는 키워드이다. 딕셔너리 형태로도 전달할 수 있다. 대표적인 사용예시는 print문에서 end키워드가 있다.

print("end=?가 바로 **kwargs입니다.",end='이것은 키워드 밸류')

키워드 인자를 받는 것이라고 생각하면 된다.

2. 명령어

이제부터는 대다수 필요한 명령어는 주요 개념에서 함께 다루는 것으로 바꿔보았다. 여기에는 과제 명령어나 위에서 다루지 않은 것들만 정리한다.

1. ___

예전의 수업에서도 ___에 대해서 다루었던 기억이 있는데 어딘지 모르겠다. 여하튼 다양한 기능을 내포하고 있는데 이번 수업에서는 변수나 함수명에 특별한 의미 또는 기능을 부여하고자 할 때 사용한다.


- Single underscore
파이썬 클래스 내부에서 따로 변수나 값을 저장할 때 사용된다. 보통 private 접근 제한자를 표현하기 위해서 사용한다. 하지만 여기서 중요한건 이는 관례적인 표현일 뿐 실제로 private해지는 것은 아니다. 여기서 private라는 것은 클래스 내부에서만 접근 가능한 변수 혹은 값을 의미한다.

보통 이 sigle underscore를 쓰면 접근하지 않았으면 좋겠다는 의미이니 여기에 직접 접근하는 것은 별로 좋지 않다.

class Pokemon:
	_pokemon_health = 100

    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a

poke_a = Pokemon()
print(poke_a._pokemon_health) #=> 100

- double underscore (dunderscore)
파이썬 클래스 내부에서만 관리하도록 밑줄을 두 개 사용할 수도 있다.이때 __를 사용하면 진짜 private인 것처럼 결과가 출력되는데 이는 실제로 private한 것이 아니라 파이썬의 Name Mangling으로 발생하는 상황이다. 밑줄을 두 개 사용하게 된다면 정해준 이름을 사용하지 않고 _<클래스 이름>_<변수 혹은 함수 이름>으로 변경된다. 따라서 앞의 형식에 맞게 클래스 밖에서 이름을 선언하면 여전히 접근할 수 있다는 것을 알 수 있다. 하지만 보이는 것과 다르게 변수가 저장되기 때문에 실수로 속성에 접근하는 것을 막아준다.

class Pokemon:
	__pokemon_health = 100

    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a
-----------------------------------------------------------
poke_a = Pokemon()
print(poke_a.__pokemon_health) #=> 에러
print(poke_a._Pokemon__pokemon_health) #=> 100

이렇게 밑줄을 이용하여 클래스의 속성을 바깥에서 접근하지 못하도록 하는 것을 캡슐화라고 한다.

2. getter과 setter: 클래스 특성 가져오기, 저장 변경하기

class Student:

  def __init__(self, name, age):
    self._name = name
    if age <= 10:
      raise ValueError('11살 이상의 학생만 가능합니다')
    self._age = age

stu1 = Student('son', 20)
stu1 = Student('son', 8) # ValueError 발생

# __init__함수의 영향을 받지 않으므로 ValueError가 발생하지 않는다
stu1._age = 8

문제가 없어보이는 코드처럼 보이지만 객체를 생성하고 나서 값을 변경하게 된다면 더 이상 __init__함수의 영향을 받지 않기 때문에 문제가 될 수 있다. 원래는 들어가지 못할 값이 들어가게 되는 것이다.

이러한 문제를 getter메서드와 setter메서드를 구현하여 해결할 수 있다.

class Student:

  def __init__(self, name, age):
    self.__name = name
    if age <= 10:
      raise ValueError('11살 이상의 학생만 가능합니다')
    self.__age = age

  @age.getter
  def age(self):
    return self.__age

  @age.setter
  def age(self, age):
    if age <= 10:
      raise ValueError('11살 이상의 학생만 가능합니다')
    self.__age = age
  -------------------------------------------------
a = Student('son', 21)
a.get_age() # 21
a.set_age(12)
a.get_age() # 12

위는 수동으로 직접 age에 대한 getter메소드와 setter메소드를 만든 것이다. 그리고 마지막에는 age라는 이름으로 property에 등록해주었다. 이런 방식의 특징은 get_age와 set_aget를 직접 사용할 수 있다는 것이다.

getter/setter메서드를 통해서 객체의 내부 데이터에 대한 접근을 조금 더 통제할 수 있게되었지만 기존에 필드명을 바로 사용할 때 보다는 코드가 지저분해 진 것을 알 수 있다. 그리고 호출할 때 역시 getter메서드와 setter 메서드를 호출하여야 하기 때문에 번거롭다. 이 점은 property()를 사용하면 해결할 수 있다. 위의 클래스 코드의 마지막에 다음과 같은 코드를 추가하여 준다.

age = property(get_age, set_age)
-------------------------------
a = Student('son',21)
a.age #=> 21
a.age = 15
a.age #=> 15

위와 같이 gettersetter를 한 번에 property안에 담겨서 값이 변경되고 얻어지는 것을 확인할 수 있다.


위에서는 수동으로 각각의 메서드를 만들었다면 더 간단하게 데코레이터를 이용하여 만들 수 있다. getter를 만들기 위해 @property데코레이터를 사용한다. 함수 이름은 변수명과 동일하게 작성하는 관계가 있다. 그리고 setter를 만들기 위해 @변수.setter를 사용한다. 마찬가지로 변수명과 동일한 함수명을 추천한다.

이렇게 클래스 내부의 변수에__를 덧붙여서 private 속성을 만들고 값이 필요할 때 setter메소드와 getter메소드를 사용하는 것이 보편적인 객체지향 프로그래밍 방법 중 하나다.

class Student:

  def __init__(self, name, age):
    self.__name = name
    if age <= 10:
      raise ValueError('11살 이상의 학생만 가능합니다')
    self.__age = age

  @property
  def age(self):
    return self.__age

  @age.setter
  def age(self, age):
    if age <= 10:
      raise ValueError('11살 이상의 학생만 가능합니다')
    self.__age = age
---------------------------------------------------------------------
a = Student('son',21)
a.age #=> 21
a.age = 15
a.age #=> 15

데코레이터를 사용하면 위의 수동으로 작성한 코드와 다르게 get_age, set_age 메소드는 존재하지 않는다. age는 실제로 메서드이지만 getter역할을 하는 @property로 인하여 외부에서는 속성처럼 사용할 수 있게된다.


@property는 클래스 메소드를 속성처럼 사용할 수 있게 해준다. @property를 붙인 메소드는 일반적으로 어떤 인스턴스 변수에 대한 getter메소드임을 나타낸다.

3. assert: 가정 설정문(코드의 동작 보장)

조건문이 True가 아닐 경우, 예외를 일으킨다. 작동하던 코드에서 추가로 코드를 작성했을 때 예상하지 못한 다른 동작을 방지하기 위해서 주로 사용한다. 즉, 조건이 충족되지 않으면 코드가 실행되지 않는다.

kitai = 100
in_put = 1
assert kitai == in_put, '기대한 값은 [{0}], 입력값은 [{1}]'.format(kitai,in_put)
---
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: 기대한 값은[100], 입력값은[1]

4. isinstance: 주어진 객체는 해당 클래스의 인스턴스 인가?

확인하고자 하는 데이터나 인스턴스와 확인하고자하는 데이터 타입이나 클래스를 입력으로 받는다. 사실, 파이썬의 모든 것은 객체이고 데이터 타입도 일종의 클래스이기 때문에 객체가 클래스에 담겨있는지 확인하는 것이라고 생각하면 된다.

class KimChi:
    pass

kc = KimChi()

result6 = isinstance(kc, School)
print(f' isinstance(kc, School) : {result6}')

result1 = isinstance(100, int)
print(f'isinstance(100, int) : {result1}')
-------------------------------------------------
isinstance(kc, School): False
isinstance(100, int): True

5. raw String: escape문에 영향 받지 않고 출력

\n. \t 등의 escape문에 영향을 받지 않고 문자열을 출력하고 싶을 때 사용한다.

print(r"raw string은 \n과 같은 개행문자가 실행되지 않는다.")
print("일반적으로는 \n 개행문자가 실행된다.")
-----------------------------------------------------
raw string은 \n과 같은 개행문자가 실행되지 않는다.
일반적으로는
개행문자가 실행된다.

6. Class 속성

6-1. __class__

객체 뒤에 위치하면 어떤 class의 인스턴스 인지 확인 가능

6-2. __name__

클래스 이름을 출력한다.

6-3. __doc__

해당 함수 또는 Class에 대한 설명을 출력 또는 변경할 수 있다.

class example:
    pass
print(example.__doc__)
example.__doc__ = "이것은 설명을 추가하는 것입니다."
print(example.__doc__)
----------------------------------------------
None
이것은 설명을 추가하는 것입니다.

documentation은 class시작 전 큰 따옴표 세 개로 둘러싸서 추가할 수도 있다.

6-4. __dict__

클래스 객체의 속성 정보를 확인하기 위해서 사용한다. 객체가 가진 여러가지 속성을 딕셔너리 형태로 편하게 확인할 수 있다.이를 활용하여 객체의 변수를 dict혀애로 변경할 수 있다. dictionary형태로 만들어 두면, 편하게 속성 값들을 가져올 수 있다.

class Test:
	def __init__(self, name):
		self.name = name
		self.test_dict = {'a':1, 'b':2}
		self.test_list = ['1','2','3']

test_object = Test("minimi")
print(type(test_object.__dict__)) # => <class 'dict'>

print(test_object.__dict__)
-------------------------------------------------------
{'name': 'minimi', 'test_dict': {'a': 1, 'b': 2}, 'test_list': ['1', '2', '3']}

속성 값들이 딕셔너리 형태로 저장되기 때문에 key 값으로 조회시 바로 value를 얻을 수 있다.

7. getattr: 속성 값 가져오기

해당 객체에 속해있는 속성 값을 가져온다.

class sample:
    def __init__(self,x):
        self.x = x

c = sample(1)
print(getattr(c,'x'))
-----------------------
1

보통은 c.X와 같은 방법으로 불러올 수도 있다. 하지만 클래스 안에서 선언된 이름이 다른 라이브러리의 메소드와 같아서 충돌을 일으킬 경우 앞의 방식보다 문자열로 전달하는 getattr가 유용하다.

8. str과 repr: 객체의 문자열 표현 반환

두 함수 모두 객체를 문자열로 반환하는 함수이다. 하지만 두 함수에는 약간의 차이가 있다.

a = "Life is too short"
str(a)
------------------------
'Life is too short'
------------------------
repr(a)
------------------------
"'Life is too short'"

repr은 문자열로 다시 객체를 생성하는 것이 목적이기 때문에 eval을 통해서 문자열을 다시 객체로 만들 수 있다. 자세한 설명은 다음 사이트 주소를 참조하자.

💡 위키독스
사이트 주소: https://wikidocs.net/134994

9. with assertRaise: 특정예외 발생 검증

assertRaise에 전달된 어떤 위치 또는 키워드 인자와 함께 callable이 호출되었을 때 예외가 발생하는지 테스트한다. exception이 발생하면 테스트를 통과하고, 다른 예외가 발생하면 에러이고, 아무 예외도 발생하지 않으면 실패이다. 여러 예외 모음을 잡기 위해서 예외 클래스를 포함한 튜플을 expection으로 전달해도 된다.

with self.assertRaises(SomeException) as cm:
    do_something()

위와 같은 형태로 작성하면 된다.

10. issubclass: 자식클래스의 부모클래스 확인

상속 받은 자식 클래스가 부모 클래스에 포함되는지 확인해보기 위해서 사용하는 함수이다.

class Parent:         # 부모가 될 클래스 (기반 클래스)
    pass

class Child(Parent):  # 자식 클래스 (서브 클래스)
    pass

result5 = issubclass(Child, Parent)
print(f'issubclass(Child, Parent) : {result5}')
--------------------------------------------------
issubclass(Child, Parent) : True

11. super: 부모클래스에 정의된 메소드 재정의

인스턴스 속성은 부모클래스 객체가 형성될 때 __init__ 매직 메소드가 실행되면서 생성된다. 그러나 자식 클래스 어디에도 부모 클래스의 객체가 형성된 적이 없다. 그래서 super()라는 함수를 이용하여 부모 클래스의 __init__매직 메소드를 자식 클래스에서 실행하므로 문제를 해결할 수 있다.

class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = '안녕하세요.'
 
class Student(Person):
    def __init__(self):
        print('Student __init__')
        self.school = '파이썬 코딩 도장'
 
james = Student()
print(james.school)
print(james.hello)    # 부모 클래스의 속성을 출력하려고 하면 에러가 발생함
--------------------------------------------------------------
Student __init__
파이썬 코딩 도장
Traceback (most recent call last):
  File "C:\project\class_inheritance_attribute_error.py", line 14, in <module>
    print(james.hello)
AttributeError: 'Student' object has no attribute 'hello' 
--------------------------------------------------------------
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)
-------------------------------------------------------------
Student __init__
Person __init__
파이썬 코딩 도장
안녕하세요.

부모 클래스의 hello 속성을 찾는 과정은 다음과 같은 그림의 순서로 실행된다.

12. try ~ except ~

except란 코드를 실행하는 중에 발생한 에러를 뜻한다. try~except~try 뒤에 오는 코드를 실행하다가 에러가 발생했을 때 에러를 출력하며 코드를 정지하는 것이 아닌 except 뒤에 오는 코드를 실행하게 된다.

try:
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
    print(y)
except:    # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.')
------------------------------------------
나눌 숫자를 입력하세요: 0  # 입력  
예외가 발생했습니다.

3. 회고

연말은 연말인 것 같다. 약속은 많아지고 겨울 잠이 동면하듯이 내 잠도 늘어간다. 자연의 힘은 위대한 것 같다...이번 수업에서는 진짜 많은 것을 배웠다. 진짜 앞에 머신러닝보다 더 많은 것을 배우는 것 같다. 익숙하지 않은 툴과 기초에서 넘어가는 파이썬 내용이 나오니까 Section2까지는 강의 다듣고 과제 다하면 늦어도 13시 30분? 그랬는데 이제는 20시는 되어야 과제가 끝난다. 그리고 약속 있으면 잠깐 나갔다오면 12시...핑계긴 하지만 TIL이 늦어지는 이유....그래도 올해가 끝나기 전까지는 계속 이 TIL을 썼으면 좋겠다.

<12월 21일>
아직도 지난주 금요일에 쓴걸 못끝냈다. 진짜 간단히 간단히 속으로 말하는데 계속 길어진다...진짜 간단하게 해야지...




❗️ 참고자료
1. pdb 명령어
2. 객체 지향
3. getter,setter
4. property 1
5. property 2
6. __dict__

0개의 댓글