점프 투 파이썬 정리 4. 파이썬 날개달기

lilyoh·2020년 7월 15일
1
post-custom-banner

🥰 점프 투 파이썬 내용 중에서 기억해야 할 것들 정리

1. 클래스

클래스와 객체

과자 틀 -> 클래스
과자 틀에 의해 만들어진 과자 -> 객체

객체는 객체마다 고유한 성격을 가진다 (과자 틀로 만들어진 과자에 구멍을 뚫거나 조금 베어 먹어도 다른 과자에는 아무 영향이 없다, 객체는 서로 영향을 주지 않는다)

class Cookie:
    pass

a = Cookie()
b = Cookie()

껍질뿐인 클래스도 객체를 생성할 수 있다

! 객체와 인스턴스의 의미
클래스로 만든 객체를 인스턴스라고도 한다
인스턴스라는 말은 특정 객체가 어떤 클래스의 객체인지를 관계 위주로 설명할 때 사용한다
'a 는 인스턴스' 라는 말보다 'a 는 객체' 라는 표현이 어울리며
'a 는 Cookie 의 객체' 라는 말보다 'a 는 Cookie 의 인스턴스' 라는 표현이 어울린다

사칙연산 클래스 만들기

class FourCal:
    def setdata(self, first, second):
        self.first = first
        self.second = second

    def add(self):
        result = self.first + self.second
        return result

    def mul(self):
        result = self.first * self.second
        return result

    def sub(self):
        result = self.first - self.second
        return result

    def div(self):
        result = self.first / self.second
        return result
  1. 클래스 구상하기
  • 클래스는 무작정 만들기보다 클래스로 만든 객체를 중심으로 어떤 식으로 동작하게 할 건지 미리 구상을 하고 만들어야 한다
  • 먼저 a = FourCal() 을 입력해서 a 라는 객체를 FourCal 클래스에 만들고 ➡ a.setdata(4, 2) 처럼 입력해서 숫자를 a 에 지정해주고 ➡ a.add() 를 수행하면 두 수를 합한 결과를 출력해주는 식으로 사칙연산 클래스를 만들 것
  1. 클래스 구조 만들기
class FourCal:
    pass

클래스 FourCal 은 아무 변수나 함수도 포함하지 않지만
객체 a 를 만들 수 있는 기능은 가지고 있다

>>> a = FourCal()
>>> type(a)
<class '__main__.FourCal'>

type 내장함수를 통해 객체 a 가 FourCal 클래스의 객체임을 확인했다

  1. 객체에 숫자 지정할 수 있게 하기
    객체 a 는 아무런 기능도 할 수 없다
    사칙연산을 하는 객체를 따로 만들어줘야 하는데
    그 전에 사칙연산을 할 때 사용할 2 개의 숫자를 알려주기 위해서
    연산을 수행할 대상을 객체에 지정할 수 있게 만들자
class FourCal():
    def setdata(self, first, second):
        set.first = first
        set.second = second

1) setdata 메서드의 매개변수

a = FourCal()
a.setdata(4, 2)

(객체를 통해 클래스의 메서드를 호출하려면 도트(.) 연산자를 사용해야 한다)

setdata 메서드에는 self, first, second 총 3개의 매개변수가 필요한데 실제로는 4, 2 처럼 두 개의 값만 전달했다 그 이유는 a.setdata(4, 2) 처럼 호출하면 setdata 메서드의 첫 번째 매개변수 self 에는 setdata 의 메서드를 호출한 객체 a 가 자동으로 전달되기 때문이다

2) setdata 메서드의 수행문

def setdata(self, first, second):
    self.first = first
    self.second = second

# a.setdata(4, 2) 를 호출하면 
# 각각 매개변수 first, second 에 값 4, 2 가 전달된다
self.first = 4
self.second = 2

# self 는 객체 a 이므로
a.first = 4
a.second = 2

결국, a 객체에 객체변수 first 가 생성되고 값 4가 저장된 것
(객체에 생성되는 객체만의 변수를 '객체변수' 라고 함)

  1. 더하기 기능 만들기
class FourCal:
    def setdata(self, first, second):
        self.first = first
        self.second = second
        
    def add(self):
        result = self.first + self.second
        return result

생성자

>>> a = FourCal()
>>> a.add()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in add
AttributeError: 'FourCal' object has no attribute 'first'

FourCal 클래스의 인스턴스 a 에 setdata 메서드를 수행하지 않고 add 메서드를 수행하면 오류가 발생한다 setdata 메서드를 수행해야 객체 a 의 객체변수 first 와 second 가 생성되기 때문이다

객체에 초깃값을 설정해야 할 필요가 있을 때는 생성자를 구현하는 것이 안전하다 생성자 (constructor) 란 객체가 생성될 때 자동으로 호출되는 메서드를 말한다

파이썬 메서드 이름으로 init 를 사용하면 생성자가 된다

def __init__(self, first, second):
    self.first = first
    self.second = second

init 메서드는 setdata 메서드와 다르게 생성자로 인식되어 객체가 생성되는 시점에 자동으로 호출된다

>>> a = FourCal()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 2 required positional arguments: 'first' and 'second'

a = FourCal() 를 수행할 때 생성자가 호출되어 오류가 난다
생성자의 매개변수 first 와 second 에 해당하는 값이 전달되지 않았기 때문이다
오류를 해결하기 위해서는 아래와 같이 해야한다

a = FourCal(4, 2)

클래스의 상속

어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있도록 하는 것
a 의 b 제곱을 연산하는 기능을 추가해보자

class MoreFourCal(FourCal):
    pass

! 상속을 사용하는 이유?
상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용한다 기존 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황일 수 있기 때문

class MoreFourCal(FourCal):
    def pow(self):
        result = self.first ** self.second
        return result

메서드 오버라이딩

>>> a = FourCal(4, 0)
>>> a.div()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    result = self.first / self.second
ZeroDivisionError: division by zero

4를 0으로 나누려고 했기 때문에 오류가 났다

class SafeFourCal(FourCal):
    def div(self):
        if self.second == 0:
            return 0
        else:
            return self.first / self.second

SafeFourCal 메서드는 FourCal 클래스에 있는 div 메서드를 동일한 이름으로 다시 작성했다 이렇게 부모 클래스에 있는 메서드를 동일한 이름으로 다시 만드는 것을 메서드 오버라이딩이라고 한다 메서드를 오버라이딩하면 부모클래스의 메서드 대신 오버라이딩한 메서드가 호출된다

클래스 변수

>>> class Family:
       lastname = '김' # lastname 은 클래스 변수이다
    
>>> print(Family.lastname)
'김'

# Family 클래스로 만든 객체를 통해서도 클래스 변수를 사용할 수 있다
>>> a = Family()
>>> b = Family()
>>> print(a.lastname)
'김'
>>> print(b.lastname)
'김'

>>> Family.lastname = '박'

>>> print(a.lastname)
'박'
>>> print(b.lastname)
'박'
# 클래스 변수는 클래스로 만든 모든 객체에 공유된다

2. 모듈

모듈이란 함수나 변수 또는 클래스를 모아 놓은 파일
다른 파이썬 프로그램에서 불러와 쓸 수 있게 만든 파이썬 파일

모듈 만들기

# mod1.py
def add(a, b):
    return a + b
    
def sub(a, b):
    return a - b

mod1.py 파일이 모듈이다
파이썬 확장자 .py 로 만든 파이썬 파일은 모두 모듈이다

모듈 불러오기

명령 프롬프트 창을 열고 mod1.py 를 저장한 디렉토리 (C:\doit) 로 이동한 다음 대화형 인터프리터를 실행한다

  • 윈도우: 윈도우키 + r 을 해서 명령 프롬프트 창을 연다
  • C:\project 를 입력해 mod1.py 를 저장한 디렉토리로 이동한다(예제 파일을 C 드라이브의 project 폴더에 생성하였다)

반드시 mod1.py 를 저장한 디렉토리로 이동한 다음 예제를 진행해야한다

>>> import mod1
>>> print(mod1.add(3, 4))
7
>>> print(mod1.sub(4, 2))
2

mod.add 처럼 쓰지 않고 add 만 쓰고 싶은 경우

>>> from mod1 import add
>>> add(3, 4)
7

add 함수와 sub 함수 둘 다 사용하고 싶은 경우

# 1번 방법
from mod1 import add, sub

# 2번 방법
from mod1 import *

ifname=='main': 의 의미

#mod1.py

def add(a, b):
    return a + b
def sub(a, b):
    return a - b

print(add(1, 4))
print(sub(4, 2))

mod1 모듈에 위와 같이 print 를 두 번 추가하고
명령 프롬프트를 열어 mod1 을 import 하면
바로 5와 2가 출력된다
우리는 단지 add 와 sub 함수를 사용하고 싶었을 뿐인데!!!
이 문제를 해결하기 위해서...

#mod1.py

def add(a, b):
    return a + b
def sub(a, b):
    return a - b

if __name__ == "__main__":
    print(add(1, 4))
    print(sub(4, 2))

if name == "main" 을 사용하면
1. C:project>python mod1.py 처럼 직접 이 파일을 실행했을 때는 if 문이 참이 되어 다음 문장이 수행되고
2. 대화형 인터프리터나 다른 파일에서 이 모듈을 불러서 사용할 때는 if 문이 거짓이 되어 다음 문장이 수행되지 않는다

위와 같이 수정한 후 대화형 인터프리터를 열고 실행하면 아무 결과값도 출력되지 않는다

! name 변수란?
파이썬의 name변수는 파이썬이 내부적으로 사용하는 특별한 변수
만약 직접 파일을 실행할 경우 name 변수에는 main 값이 저장된다 하지만 파이썬 셸이나 다른 파이썬 모듈에서 파일을 import 할 경우 name 변수에는 모듈의 이름 값인 mod1 이 저장된다

클래스나 변수 등을 포함한 모듈

모듈은 함수 뿐만 아니라 클래스나 변수도 포함할 수 있다

#mod2.py

PI = 3.141592

class Math:
    def solv(self, r):
        return PI * (r ** 2)

def add(a, b):
    return a + b

아래와 같이 사용 가능

>>> import mod2
>>> print(mod2.PI)
3.141592
>>> a = mod2.Math()
>>> print(a.solv(2))
12.566368
>>> print(mod2.add(mod2.PI, 4.4))
7.5415920000000005

다른 파일에서 모듈 불러오기

#modtest.py
import mod2
result = mod2.add(3, 4)
print(result)

에디터로 위와 같은 파일을 만들었다
실행 결과는 7
다른 파일에서도 import mod2 로 mod2 모듈을 불러와 쓸 수 있다
단, modtest 파일과 mod2 파일은 동일한 디렉토리 안에 있어야 한다

모듈을 불러오는 또다른 방법

<sys.path.append(모듈을 저장한 디렉토리) 사용하기>
지금껏 명령 프롬프트를 창을 열어 디렉토리로 이동한 다음에 모듈을 사용했는데 모듈을 저장한 디렉토리로 이동하지 않고 사용하는 방법

C:\Users\오승하>cd C:\project

C:\project>mkdir mymod

C:\project>move mod2.py mymod
        1개 파일을 이동했습니다.

명령 프롬프트를 열어 mod2 파일을 mymod 폴더로 이동한다

C:\project>python
Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys

sys 모듈은 파이썬을 설치할 때 함께 설치되는 라이브러리 모듈
sys 모듈을 사용하면 파이썬 라이브러리가 설치되어 있는 디렉토리를 확인할 수 있다

>>> sys.path
['', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\python38.zip', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\DLLs', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\lib', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\lib\\site-packages']

sys.path 는 파이썬 라이브러리가 설치되어 있는 디렉토리를 보여준다
만약 파이썬 모듈이 위 디렉토리에 들어 있다면 모듈이 저장된 디렉토리로 이동할 필요없이 바로 불러서 사용할 수 있다
sys.path 의 결과값이 리스트이므로 우리는 다음과 같이 디렉토리를 추가할 수 있다

>>> sys.path.append("C:\project\mymod")
>>> sys.path
['', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\python38.zip', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\DLLs', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\lib', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32', 'C:\\Users\\오승하\\AppData\\Local\\Programs\\Python\\Python38-32\\lib\\site-packages', 'C:\\project\\mymod']

리스트 가장 마지막에 mod2 가 저장된 폴더인 mymod 가 추가된 것을 알 수 있다

>>> import mod2
>>> print(mod2.add(3,4))
7

<PYTHONPATH 환경 변수 사용하기>

C:\project>set PYTHONPATH=C:\project\mymod
C:\project>python
>>> import mod2
>>> print(mod2.add(3,4))
7

set 명령어를 이용해 PYTHONPATH 환경변수에 mod2.py 파일이 있는 C:\project\mymod 디렉토리를 설정하면 디렉토리 이동이나 별도의 모듈 추가 작업 없이 mod2 모듈을 불러와서 사용할 수 있다

3. 패키지

패키지란

패키지는 파이썬 모듈을 계층적으로 관리하게 해준다
파이썬 패키지는 디렉터리와 모듈로 이루어지며 구조는 다음과 같다

# 가상 game 의 패키지 예
game/
    __init__.py
    sound/
        __init__.py
        echo.py
        wav.py
    graphic/
        __init__.py
        screen.py
        render.py
    play/
        __init__.py
        run.py
        test.py

gmae, sound, graphic, play 는 디렉터리 이름
.py 가 붙은 것은 파이썬 모듈
game 디렉터리가 이 패키지의 루트 디렉터리
sound, graphic, play 는 서브 디렉터리

패키지 만들기

패키지 기본 구성 요소 만들기

C:/doit/game/__init__.py
C:/doit/game/sound/__init__.py
C:/doit/game/sound/echo.py
C:/doit/game/graphic/__init__.py
C:/doit/game/graphic/render.py

# echo.py
def echo_test():
    print ("echo")
    
# render.py
def render_test():
    print ("render")

game 패키지를 참조할 수 있도록 명령 프롬프트 창에서 set 명령어로 PYTHONPATH 환경 변수에 C:\doit 디렉터리를 추가한다 그리고 파이썬 인터프리터를 실행한다

C:\> set PYTHONPATH=C:/doit
C:\> python
Type "help", "copyright", "credits" or "license" for more information.
>>> 

패키지 안의 함수 실행하기

패키지 안의 함수를 실행하는 방법은 3가지
(import 예제를 실행하기 전에는 항상 새창에서 실행)

<echo 모듈을 import 하여 실행하는 방법>

>>> import.game.sound.echo
>>> game.sound.echo.echo_test()
echo

<echo 모듈이 있는 디렉터리를 from ... import 하여 실행하는 방법>

>>> from game.sound import echo
>>> echo.echo_test()
echo

<echo 모듈의 echo_test 함수를 직접 import 하여 실행하는 방법>

>>> from game.sound.echo import echo_test
>>> echo_test()
echo

아래와 같이 echo_test 함수를 사용하는 것은 불가능하다
import game 을 수행하면 game 디렉터리의 모듈 또는 game 디렉터리의 init.py 에 정의한 것만 참조할 수 있다

>>> import game
>>> game.sound.echo.echo_test()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'sound'

도트 연산자를 사용해서 import a.b.c 처럼 import 할 때 가장 마지막 항목인 c 는 반드시 모듈 또는 패키지여야만 한다

>>> import game.sound.echo.echo_test
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
ImportError: No module named echo_test

init.py 의 용도

init.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할
만약 game, sound, graphic 등 패키지에 포함된 디렉터리에 init.py 파일이 없다면 패키지로 인식되지 않는다

>>> from game.sound import *
>>> echo.echo_test()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
NameError: name 'echo' is not defined

game.sound 패키지에서 모든 것() 을 import 하였으므로 echo 모듈을 사용할 수 잇어야 하는데 echo 라는 이름이 정의되지 않았다는 오류가 뜬다
특정 디렉터리의 모듈을
을 사용해 import 할 때는 다음과 같이 해당 디렉터리의 init.py 파일에 all 변수를 설정하고 import 할 수 있는 모듈을 정의해 주어야 한다

# C:/doit/game/sound/__init__.py
__all__ = ['echo']

all 은 sound 디렉터리에서 기호를 사용해 import 할 경우 이곳에 정의된 echo 모듈만 import 된다는 의미
(from game.sound.echo import
) 는 all과 상관없이 무조건 import 된다 all과 상관없이 무조건 import 되는 경우는 from a.b.c import * 에서 from 의 마지막 항목인 c 가 모듈인 경우

relative 패키지

graphic 디렉터리의 render.py 모듈이 sound 디렉터리의 echo.py 모듈을 사용하고 싶다면 render.py 모듈을 다음과 같이 수정한다

# render.py
from game.sound.echo import echo_test
def render_test():
    print ("render")
    echo_test()

위와 같이 전체 경로를 사용해 import 할 수도 있지만 relative 하게 import 하는 것도 가능하다

# render.py
from ..sound.echo import echo_test

def render_test():
    print ("render")
    echo_test()

.. 은 부모 디렉토리를 의미한다
. 은 현재 디렉토리를 의미한다

relative 접근자는 모듈 안에서만 사용가능하다 (인터프리터에서는 사용불가)

4. 예외 처리

어떤 오류가 발생하는가

  1. 디렉터리 안에 없는 파일을 열려고 시도할 때
>>> f = open("나없는파일", 'r')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '나없는파일'
  1. 0으로 다른 숫자를 나누려고 할 때
>>> 4 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
  1. 얻을 수 없는 값을 리스트에서 얻으려 할 때
>>> a = [1,2,3]
>>> a[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

오류 예외처리 기법

< try, except 문 >

try:
    ...
except [발생 오류[as 오류 메시지 변수]]:
    ...

try 블럭 수행 중 오류가 발생하면 except 블럭이 수행된다
try 블럭에서 오류가 발생하지 않으면 except 블럭은 수행되지 않는다
except 구문의 [] 안의 내용은 생략 가능
1. try, except 만 쓰는 방법

try:

except:
  1. 발생오류만 포함한 except 문
try:
    ...
except 발생 오류:
    ...
  1. 발생 오류와 오류 메시지 변수까지 포함한 except 문
try:
    ...
except 발생 오류 as 오류 메시지 변수:
    ...

4를 0으로 나누려고 하면 ZeroDivisionError 가 발생해서 except 블럭이 실행되고 변수 e 에 담기는 오류 메시지를 출력한다

try:
    4 / 0
except ZeroDivisionError as e:
    print(e)

division by zero

< try ... finally >

f = open('foo.txt', 'w')
try:
    # 무언가를 수행한다.
finally:
    f.close()

finally 절은 오류 발생 여부와 상관없이 항상 수행된다
파일을 쓰기모드로 연 후에 try 문을 수행 후 finally 절에서 f.close()로 파일을 닫을 수 있다

< 여러개의 오류 처리하기 >

try:
    a = [1,2]
    print(a[3])
    4/0
except ZeroDivisionError as e:
    print(e)
except IndexError as e:
    print(e)
# 인덱싱 오류가 먼저 발생했기 때문에 0으로 나누는 오류 메시지는 출력되지 않는다
    
# 함께 처리할 수도 있다
try:
    a = [1,2]
    print(a[3])
    4/0
except (ZeroDivisionError, IndexError) as e:
    print(e)

오류 회피하기

try:
    f = open("나없는파일", 'r')
except FileNotFoundError:
    pass

오류 일부러 발생시키기

오류를 일부러 발생시켜야 할 경우 raise 명령어를 사용한다
예를 들어 Bird 클래스를 상속받는 자식 클래스는 반드시 fly 함수를 구현하도록 만들고 싶을 때

class Bird:
    def fly(self):
        raise NotImplementedError

class Eagle(Bird):
    pass
    
eagle = Eagle()
eagle.fly()

자식 클래스인 Eagle 이 fly 함수를 구현하지 않았기 때문에 아래와 같은 오류 메시지가 뜬다

Traceback (most recent call last):
  File "...", line 33, in <module>
    eagle.fly()
  File "...", line 26, in fly
    raise NotImplementedError
NotImplementedError

# 오류가 발생하지 않으려면...
class Eagle(Bird):
    def fly(self):
        print("very fast")

eagle = Eagle()
eagle.fly()

예외 만들기

예외는 파이썬 내장 클래스인 Exception 클래스를 상속해서 만들 수 있다

class MyError(Exception):
    pass
    
def say_nick(nick):
    if nick == '바보':
        raise MyError()
    print(nick)
    
# 실행
print('천사')
print('바보')

결과값으로 천사가 한 번 출력되고 MyError 가 발생한다

천사
Traceback (most recent call last):
  File "...", line 11, in <module>
    say_nick("바보")
  File "...", line 7, in say_nick
    raise MyError()
__main__.MyError

예외 처리 기법을 사용해서 MyError 발생을 예외 처리 해보자

try:
    say_nick("천사")
    say_nick("바보")
except MyError:
    print("허용되지 않는 별명입니다.")
    
# 또는
try:
    say_nick("천사")
    say_nick("바보")
except MyError as e:
    print(e)
    
# print(e)로 오류 메시지를 출력하기 위해서는
# 오류 클래스에 __str__ 메서드를 구현해야 한다
# __str__ 메서드는 오류 메서드를 print 문으로 출력할 경우에 호출되는 메서드
class MyError(Exception):
    def __str__(self):
        return "허용되지 않는 별명입니다."

5. 내장함수

필요할 때 찾아보자

6. 라이브러리

필요할 때 찾아보자

post-custom-banner

0개의 댓글