<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 1T_디버깅(Debugging), 오류(errors), 예외(Exceptions)처리(Handling)
Step1:  디버깅? de+bugg+ing => 버그를 잡는다
Jupyter는 Python interpreter이다. 이것을 웹 환경에서 쓸 수 있도록 바꾼 것이 Jupyter Notebook 이다.
예전 이름은 Ipython이었다.
실제 환경(cmd에서 python 입력한 환경)에서는 tap키가 안 된다. 그래서 작성하기 어려운 환경이다.
Jupyter Notebook => Multi-user => Jupyterhub. 주피터 노트북은 멀티유저모드로 주피터허브라고 한다. 여러 사람이 같이 쓸 수 있는 환경
디버깅은 IPython에 내장되어 있는 기능
Step2:  만약 안에 있는 것을 exit()로 종료하지 않고 dd로 밖에서 강제 종료할 경우
계속 돌아가기 때문에 다음 명령어가 *에서 멈춘다. 그래서 노트북을 종료하고 재시작해야 한다.
embed는 IPython 내부에서 IPython을 또 띄운 것
Ipython embed가 디버깅 할 때 가장 많이 사용할 방법이다.일반적으로 사용
Step3:  print를 쓰면 직관적으로 볼 수 있지만 안 좋은 이유는 결과에 영향을 미치기 때문
그리고 특정값만 보고 싶은데 전체를 봐야 하기 때문에
(print는 결과에 영향을 안 미치지 않나?) -> 결과값에 영향이 아니라 output에 영향을 준다는 의미
출력을 하기는 한다. 다만, print로 직접 하지는 않는다.
로그를 쌓는다. ( Log ) -> logging
Step4:  cmd에서 python으로 실행할 경우에는 바로 WARNING이 뜬다.
그런데 여기서 바로 안 뜨는 이유는 관리자로 봐야 하기 때문에
주피터 노트북이 돌아가는 웹서버or관리창에 워닝이 기록된다.
데이터분석은 로깅을 쓰는 것보다는 프린터를 쓰는 것이 더 낫다.
머신러닝 프로젝트의 경우 장기적이거나 할 경우에는 로깅 형태가 낫다. 기록을 해 두는 것이 좋다.
오류에 대해서
Step5:  Error
실행 자체가 되지 않은 것
문법 자체의 오류가 있어서 SyntaxError ( ParsingError )
Step6:  오류에는 2가지가 있다.
SyntaxError와 SyntaxError가 아닌 것(Exceptions)
Step7:  Exception이 뜨면 좋은 것이다. 일단 남이 의도하지 않은 방향대로 뭔가 클래스나 함수, 객체 등을 쓰고 있는 것이기 때문
2T_예외를 처리하는 방법
Exception Handling
예외를 처리하는 방법 => AttributeError, FileNotFoundError, AttributeError, ...
우리가 직접 예외를 만드는 방법(FibonacciShouldGetPositiveNumberError => 우리만의 예외)
Class를 만들어야 한다.
Step8:  우리만의 예외처리 class를 만들어보겠다.
Step9:  상속 순서
object -> Exception -> Fibo...
Step10:  예외처리를 포함하여,
Factorial 함수를 구현하세요. n! = n * (n-1) * (n-2) * ... * 2 * 1
<ASSISTANT_TASK:>
Python Code:
for i in range(3):
    a = i * 7 #0, 7, 14
    b = i + 2 #2, 3, 4
    c = a * b # 0, 21, 56
#만약 이 range값이 3017, 5033일 경우에는 무슨 값인지 알 수 없다. 이 때 쉽게 a,b,c값이 무엇인지 찾는 방법을 소개
Explanation: 1T_디버깅(Debugging), 오류(errors), 예외(Exceptions)처리(Handling)
End of explanation
name = "KiPyo Kim"
age = 29
from IPython import embed
embed()
for i in range(3):
    a = i * 7 
    b = i + 2 
    c = a * b
    
    embed()
from IPython import embed; embed()   #보통 디버깅 할 때 이렇게 해서 단축키로 지정하고 많이 쓴다.
for i in range(100):
    from IPython import embed; embed()
#이렇게 하면 무한루프에 빠진다. 커널을 종료하는 수밖에...
Explanation: 디버깅? de+bugg+ing => 버그를 잡는다
Jupyter는 Python interpreter이다. 이것을 웹 환경에서 쓸 수 있도록 바꾼 것이 Jupyter Notebook 이다.
예전 이름은 Ipython이었다.
실제 환경(cmd에서 python 입력한 환경)에서는 tap키가 안 된다. 그래서 작성하기 어려운 환경이다.
Jupyter Notebook => Multi-user => Jupyterhub. 주피터 노트북은 멀티유저모드로 주피터허브라고 한다. 여러 사람이 같이 쓸 수 있는 환경
디버깅은 IPython에 내장되어 있는 기능
End of explanation
def fibo(n):
    if n <= 0:
        return 0
    if n == 1:
        return 1
    embed()
    return fibo(n-1) + fibo(n-2)
fibo(5)
class Student():
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def introduce(self):
        from IPython import embed; embed()
        return (self.name, self.age)
kkp = Student("kipoy", 29)
kkp.introduce()
def fibo(n):
    if n <= 0:
        return 0
    if n == 1:
        return 1
    print("fibo({n}) = fibo({n_1}) + fibo({n-2})".format(n=n, n_1=n-1, n_2=n-2))
    return fibo(n-1) + fibo(n-2)
fibo(3)
fibo(10)
Explanation: 만약 안에 있는 것을 exit()로 종료하지 않고 dd로 밖에서 강제 종료할 경우
계속 돌아가기 때문에 다음 명령어가 *에서 멈춘다. 그래서 노트북을 종료하고 재시작해야 한다.
embed는 IPython 내부에서 IPython을 또 띄운 것
Ipython embed가 디버깅 할 때 가장 많이 사용할 방법이다.일반적으로 사용
End of explanation
import logging
# logging -> debug, info, warning, danger, critical...
logging.warning("hello world")
Explanation: print를 쓰면 직관적으로 볼 수 있지만 안 좋은 이유는 결과에 영향을 미치기 때문
그리고 특정값만 보고 싶은데 전체를 봐야 하기 때문에
(print는 결과에 영향을 안 미치지 않나?) -> 결과값에 영향이 아니라 output에 영향을 준다는 의미
출력을 하기는 한다. 다만, print로 직접 하지는 않는다.
로그를 쌓는다. ( Log ) -> logging
End of explanation
name #뒤에 따라 오는 주석들 Traceback 이 있다.
Explanation: cmd에서 python으로 실행할 경우에는 바로 WARNING이 뜬다.
그런데 여기서 바로 안 뜨는 이유는 관리자로 봐야 하기 때문에
주피터 노트북이 돌아가는 웹서버or관리창에 워닝이 기록된다.
데이터분석은 로깅을 쓰는 것보다는 프린터를 쓰는 것이 더 낫다.
머신러닝 프로젝트의 경우 장기적이거나 할 경우에는 로깅 형태가 낫다. 기록을 해 두는 것이 좋다.
오류에 대해서
End of explanation
if True
     print("hello world")
Explanation: Error
실행 자체가 되지 않은 것
문법 자체의 오류가 있어서 SyntaxError ( ParsingError )
End of explanation
ab
def error(a, b):
    a = b + 1
    print(a)
error
2 + "김기표"
{}["somtthing"]
{}.append()
with open("./not_exist_file", "r") as f:
    pass
Explanation: 오류에는 2가지가 있다.
SyntaxError와 SyntaxError가 아닌 것(Exceptions)
End of explanation
NameError
Exception?
def append_string_to_hello(string):
    return "hello, " + string
append_string_to_hello("world")
append_string_to_hello(13)
"hello, " + 3
#str + int, implicit(함축적)한 방법이다.
#Python은 형변환이 되지 않기 때문이다.(형변환 잘 되는 것은 루비)
#자유도가 높거나 해서 좋은 언어는 아니다. 언어 특성에 맞게 사용하면 된다.
"hello" + str(3)
# str + str(int), explicit(명시적)한 방법이다.
awesome_list = ["world", "hello", "python", 5678, "fastcampus"]
for awesome in awesome_list:
    # 예외 처리가 가능한 장소 [1] => 밑에 있는 케이스에서만 예외처리가 가능
    print(append_string_to_hello(awesome))
def append_string_to_hello(string):
    # 예외처리가 가능한 장소(2) => 함수 불렀을 모든 경우에서 예외 처리 가능
    # 그래서 2번에서 해보겠다.
    return "hello, " + string
def append_string_to_hello(string):
    # 예외처리가 가능한 장소
    # 예외처리 => try:-except: (항상 이런 방법으로 한다.)
    try:
        return "hello, " + string
    except TypeError as err:
        print("오류" * 40)
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except TypeError as err:
        #TypeError는 class다. 즉 에러 하나하나 자체는 클래스로 만들어진 에러 객체이다.
        print(err)
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except TypeError as err:
        return err
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except: #이렇게 할 경우 모든 에러에 대해 예외처리. 일반적으로 많이 사용
        return err
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except {TypeError, AttributeError} as err: #이렇게도 쓰나 잘 쓰진 않는다. 
        return err
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except TypeError as err:
        #알람을 받을 수 있도록 넣을수도 있다.
        print("send_sms")
        raise
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except TypeError as err:
        print("send_sms")
        raise
    except AttributeError as err:
        pass
append_string_to_hello(123)
def append_string_to_hello(string):
    try:
        return "hello, " + string
    except TypeError as err:
        print("send_sms")
        print(err)
#         raise
    except AttributeError as err:
        pass
    finally:                #예외가 발생했던 아니던 어쨌든 실행하고 싶을 때
        print("어쨌던 끝남")
append_string_to_hello(123)
awesome_list = ["world", "hello", "python", 5678, "fastcampus"]
for awesome in awesome_list:
    print(append_string_to_hello(awesome))
Explanation: Exception이 뜨면 좋은 것이다. 일단 남이 의도하지 않은 방향대로 뭔가 클래스나 함수, 객체 등을 쓰고 있는 것이기 때문
2T_예외를 처리하는 방법
Exception Handling
예외를 처리하는 방법 => AttributeError, FileNotFoundError, AttributeError, ...
우리가 직접 예외를 만드는 방법(FibonacciShouldGetPositiveNumberError => 우리만의 예외)
Class를 만들어야 한다.
End of explanation
def fibo(x):
    if x < 0:
#         err = FibonacciShouldNotHaveNegativeNumberError()
#         raise err
        #일반적으로 위와 같은 2줄이 아닌 아래와 같은 1줄로 적는다.
    
        raise FibonacciShouldNotHaveNegativeNumberError()
    
    if x == 0:
        return 0
    if x == 1:
        return 1
    return fibo(x-1) + fibo(x-2)
#Exception Class
class FibonacciShouldNotHaveNegativeNumberError(Exception):
    
    def __init__(self):
        pass
Explanation: 우리만의 예외처리 class를 만들어보겠다.
End of explanation
fibo(-1)
raise FibonacciShouldNotHaveNegativeNumberError()   
# 다른 에러들은 수정 요구 사항까지 나온다.
"hello, " + 5678
# 노란색 에러는 수정 요구 사항
# 이건 어디서 수정 가능하냐? def __str__(self): 여기서!
class FibonacciShouldNotHaveNegativeNumberError(Exception):
    
    def __init__(self):
        pass
    
    def __str__(self):
        return "피보나치 수열은 index 값으로 양수를 받아야 합니다"
raise FibonacciShouldNotHaveNegativeNumberError()
def fibo(x):
    if x < 0:
        raise FibonacciShouldNotHaveNegativeNumberError(x)
    if x == 0:
        return 0
    if x == 1:
        return 1
    return fibo(x-1) + fibo(x-2)
class FibonacciShouldNotHaveNegativeNumberError(Exception):
    
    def __init__(self, n):
        self.n = n
        
    def __str__(self):
        return "이 함수는 index 값으로 양수를 받아야 합니다. (입력받은 값: {n})".format(n=self.n)   #에러메세지 요구사항
fibo(-13)
Explanation: 상속 순서
object -> Exception -> Fibo...
End of explanation
def Factorial(n):
    if n == 1:
        return 1
    return n * Factorial(n-1)
Factorial(5)
def Factorial(n):
    if n < 0:
        raise FactorialShouldGetPositiveIndexError(n)
    if n == 1:
        return 1
    return n * Factorial(n-1)
class FactorialShouldGetPositiveIndexError(Exception):
    def __init__(self, n):
        self.n = n
    
    def __str__(self):
        return "factorial function should get positive index. (input: {n})".format(n=self.n)
Factorial(-3)
Explanation: 예외처리를 포함하여,
Factorial 함수를 구현하세요. n! = n * (n-1) * (n-2) * ... * 2 * 1
End of explanation
<END_TASK>