이번 포스팅은 쉬어가는 코너로, 

 

  - if~ elif~ else~ 조건절을 사용한 프로그래밍

  - for loop 순환문을 사용한 프로그래밍 

 

에 대한 간단한 예제 몇 가지를 소개하겠습니다. 

 

(1) 홀수, 짝수 여부 판별하기 (odd vs. even number)

(2) 소수 여부 판별하기 (prime number)

(3) 팩토리얼 계산하기 (factorial)

(4) 3&5, 3, 5 로 나누어지는 수 판별하기 (FizzBuzz)

(5) 1 ~5를 라인별로 1부터 시작해서 라인 개수 만큼 출력하기

(6) 1~15를 라인별로 라인 번호부터 시작해서 라인개수 만큼 출력하기

 

 

(1) 홀수, 짝수 여부 판별하기 (odd vs. even number)

 

## odd or even number
num = int(input("Enter a number: "))

if (num % 2) == 0:
    print("{num} is Even")
else:
    print("{num} is Odd")

# Enter a number: 6
# 6 is Even

 

 

 

(2) 소수 여부 판별하기 (prime number)

 

소수(the prime number)는 '1과 자기 자신 외의 약수를 가지지 않는 1보다 큰 자연수'를 말합니다. 

 

## Prime number or not
num = int(input("Enter a number: "))

prime_num = True
if num > 1:
    for i in range(2, num):
        if (num % i) == 0 :
            prime_num = False
            break

if prime_num == False:
    print(f"{num} is not a prime number.")
else:
    print(f"{num} is a prime number.")

# Enter a number: 47
# 47 is a prime number.

 

 

 

(3) 팩토리얼 계산하기 (factorial)

 

팩토리얼이란 어떤 양의 정수 n에 대해서, n부터 1까지의 모든 양의 정수를 곱한 값을 말합니다. 보통 "n!"로 표기하며, 팩토리얼은 수학적 계산과 조합론에서 중요한 개념입니다. 예를 들어, 5!는 5 x 4 x 3 x 2 x 1 = 120입니다. 팩토리얼은 주로 순열과 조합을 계산하거나 확률과 통계에서 사용됩니다.

 

## Factorial
num = int(input("Enter a number: "))

factorial = 1
if num < 0:
    print("Factorial does not exist for negative numbers.")
elif num == 0:
    print("The factorial of 0 is 1.")
else:
    for i in range(1, num + 1):
        factorial = factorial * i
    print(f"The factorial of {num} is {factorial}")

# Enter a number: 4
# The factorial of 4 is 24

 

 

 

(4) 3&5, 3, 5 로 나누어지는 수 판별하기 (FizzBuzz)

 

Python coding test 에 나올 법한 문제로 FizzBuzz 문제가 있습니다. 1보다 큰 양의 정수를 3과 5로 모두 나누어지면 "FizzBuzz", 3으로만 나누어지면 "Fizz", 5로만 나누어지면 "Buzz"라고 결과를 출력하는 문제입니다. 

 

# FizzBuzz Problem
# Fizz and Buzz refer to any number that is a multiple of 3 and 5
for i in range(1, 20):
    if i % 3 == 0 and i % 5 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)
        
# 1
# 2
# Fizz
# 4
# Buzz
# Fizz
# 7
# 8
# Fizz
# Buzz
# 11
# Fizz
# 13
# 14
# FizzBuzz
# 16
# 17
# Fizz
# 19

 

 

 

(5) 1 ~5를 라인별로 1부터 시작해서 라인 개수 만큼 출력하기

 

def num(n):
    for i in range(0, n):
        num = 1
        for j in range(0, i+1):
            print(num, end=" ")
            num += 1
        print("\r")
        
        
num(5)
# 1
# 1 2
# 1 2 3
# 1 2 3 4
# 1 2 3 4 5

 

 

 

(6) 1~15를 라인별로 라인 번호부터 시작해서 라인개수 만큼 출력하기

 

def num(n):
    num = 1
    for i in range(0, n):
        for j in range(0, i+1):
            print(num, end=" ")
            num += 1
        print("\r") # the first position of a line
        

num(5)
# 1
# 2 3
# 4 5 6
# 7 8 9 10
# 11 12 13 14 15

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Amazon AWS 의 SageMaker 에서 Python 객체(예: scikit-learn 으로 학습한 로지스틱 회귀모형)를 직렬화(serialization)하여 AWS S3에 저장하는 사용자 정의함수 코드를 소개하겠습니다. 

 

아래 예제 코드에서는 Python 객체를 직렬화하는데 six 모듈의 six.moves.cPickle 메소드를 사용하였으며, protocol 매개변수는 Python 버전에 따라서 Python 3.x 는 protocol=3, Python 2.x 는 protocol=2 를 입력해주면 됩니다. 

 

boto3 모듈로 특정 계정의 AWS S3에 접근해서 put_object() 메소드로 파일을 저장하는데요, 이때 put_object() 메소드에 Bucket (S3 내 버킷 이름), Key (폴더 이름, 키), Body (직렬화한 Python 객체) 매개변수 값을 지정해주면 됩니다.  

 

serialize a Python object using six module and save it to AWS S3 using boto3 module

 

Python 객체의 직렬화(serialization), 역직렬화(de-serialization)는 https://rfriend.tistory.com/525 를 참고하세요. 

 

 

[ Python object를 직렬화해서 AWS S3에 저장하는 사용자 지정 함수 ]

 

def store_object_to_S3(bucket_nm, folder_nm, key_nm, object_nm):
    import boto3
    
    # connect to specific account
    region = boto3.Session().region_name
    session = boto3.Session(region_name=region)
    s3 = session.client('s3')
    
    # serialization using pickle
    import six
    pickle = six.moves.cPickle
    
    try:
        serialized_obj = pickle.dumps(object_nm, protocol=3) # protocol=3 for Python 3.x
    except:
        serialized_obj = pickle.dumps(object_nm, protocol=2) # protocol=3 for Python 2.x
    
    print('[STATUS] serialized')
    
    # store a serialized object to S3 bucket
    key = "{}/{}".format(folder_nm, key_nm)
    s3.put_object(Bucket=bucket_nm, 
                  Key=key, 
                  Body=serialized_obj)
    
    print('[STATUS] saved to S3')

 

 

위에서 정의한 사용자 정의함수에 bucket_nm, folder_nm, key_nm, object_nm (직렬화하고자 하는 Python 객체, 예: 학습된 모델 객체) 을 순서대로 입력해주면 됩니다. 

 

# store the serialized best model to S3 bucket
store_object_to_S3(bucket_nm='my-bucket', 
                   folder_nm='best_model', 
                   key_nm='logitreg_model',
                   object_nm=logitreg_model)

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

728x90
반응형
Posted by Rfriend
,

Python 으로 프로그래밍을 하다보면 의도했던, 의도하지 않았던 간에 예외를 처리해야 하는 상황에 맞닥드리게 됩니다. 이때 에러를 발생(Raise Error) 시킬 수도 있고 회피할 수도 있으며, 필요에 따라서는 예외 처리를 해야 할 수도 있습니다. 


이번 포스팅에서는 Python에서 예외를 처리하는 4가지 try, except, else, finally 절(clause)을 소개하겠습니다. 




  • try 에는 정상적으로 실행할 프로그램 코드(code block) 써줍니다. 

  • except 절에는 의 try 절에서 실행한 코드에 예외가 발생했을 경우에 실행할 코드를 써줍니다. 

  • else 절에는 앞의 try 절에서 실행한 코드에 예외가 발생하지 않은 경우에 실행할 코드를 써줍니다. 

  • finally 절에는 try 절에서의 예외 발생 여부에 상관없이 항상(always execute) 마지막에 실행할 코드를 써줍니다. 


간단한 예로서, 두개의 숫자를 매개변수로 받아서 나눗셈을 하는 사용자 정의함수를 try, except, else, finally 의 예외절을 사용하여 정의해 보겠습니다. 


try, except, else, finally 절의 끝에는 콜론(:)을 붙여주며, 그 다음줄에 코드 블락은 들여쓰기(indentation)을 해줍니다. 

except의 경우 'except:' 만 사용해도 되고, 아래 예의 'except ZeorDivisionError as zero_div_err:' 처럼 Built-in Exception Class를 사용해서 에러를 명시적으로 써줄 수도 있습니다. (본문의 제일 밑에 Python Built-in Exception Class 의 계층 구조 참조)



# Python Exceptions: try, except, else, finally

def divide(x, y):

    try:

        result = x / y

    except ZeroDivisionError as zero_div_err:

        print("Except clause:", zero_div_err)

    else:

        print("Else clause: The result of division is", result)

    finally:

        print("Finally clause is executed.")

 




(1) try 절 정상 실행 시 (executed nomally): try --> else --> finally


두 숫자 x, y를 인자로 받아서 나눗셈을 하는 사용자 정의함수 divide(x, y) 에 x=1, y=2 를 넣어서 정상적으로 코드가 수행되었을 때의 결과를 보겠습니다.  마지막으로 finally 절이 실행되었습니다. 


In [1]: def divide(x, y):

   ...: try:

   ...: result = x / y

   ...: except ZeroDivisionError as zero_div_err:

   ...: print("Except clause:", zero_div_err)

   ...: else:

   ...: print("Else clause: The result of division is", result)

   ...: finally:

   ...: print("Finally clause is executed.")

   ...:

   ...:

In [2]: divide(1, 2)

Else clause: The result of division is 0.5

Finally clause is executed. 




(2) try 절 실행 중 예외 발생 시 (exception occurred): try --> except --> finally


1을 '0'으로 나누라고 하면 'except ZeroDivisionError as zero_div_err:' 의 except 절이 실행됩니다. 그리고 마지막으로 finally 절이 실행되었습니다. 


In [3]: divide(1, 0)

Except clause: division by zero

Finally clause is executed.




(3) try 절 Error 발생 시: try --> finally  --> Raise Error


divide(x, y) 사용자 정의함수에 숫자를 인자로 받는 매개변수에 문자열(string)을 입력하는 경우 TypeError가 발생하겠지요? 이때 finally를 먼저 실행하고, 그 후에 TypeError 를 발생시킵니다. 


In [4]: divide("1", "2")

Finally clause is executed.

Traceback (most recent call last):


File "<ipython-input-4-bbf78a5b43b9>", line 1, in <module>

divide("1", "2")


File "<ipython-input-1-78cbb56f9746>", line 3, in divide

result = x / y


TypeError: unsupported operand type(s) for /: 'str' and 'str'


Traceback (most recent call last):


File "<ipython-input-4-bbf78a5b43b9>", line 1, in <module>

divide("1", "2")


File "<ipython-input-1-78cbb56f9746>", line 3, in divide

result = x / y


TypeError: unsupported operand type(s) for /: 'str' and 'str' 




(4) Try Except 절에서 에러 발생 시 Exception Error 이름과 Error message 출력



import numpy as np


arr = np.array([[1., 2., 3.], [4.0, 5., 6.]])

print(arr)

[Out]
[[1. 2. 3.]
 [4. 5. 6.]]


# It will not work and eraise an error

try:

    arr.reshape(5, -1)

except Exception as e:

    print(f"{type(e).__name__}: {e}")

[Out] 
ValueError: cannot reshape array of size 6 into shape (5,newaxis)





[ 참고: The Class Hierarchy of Python Built-in Exceptions ]

(* source: https://docs.python.org/3/library/exceptions.html)


 BaseException

 +-- SystemExit

 +-- KeyboardInterrupt

 +-- GeneratorExit

 +-- Exception

      +-- StopIteration

      +-- StopAsyncIteration

      +-- ArithmeticError

      |    +-- FloatingPointError

      |    +-- OverflowError

      |    +-- ZeroDivisionError

      +-- AssertionError

      +-- AttributeError

      +-- BufferError

      +-- EOFError

      +-- ImportError

      |    +-- ModuleNotFoundError

      +-- LookupError

      |    +-- IndexError

      |    +-- KeyError

      +-- MemoryError

      +-- NameError

      |    +-- UnboundLocalError

      +-- OSError

      |    +-- BlockingIOError

      |    +-- ChildProcessError

      |    +-- ConnectionError

      |    |    +-- BrokenPipeError

      |    |    +-- ConnectionAbortedError

      |    |    +-- ConnectionRefusedError

      |    |    +-- ConnectionResetError

      |    +-- FileExistsError

      |    +-- FileNotFoundError

      |    +-- InterruptedError

      |    +-- IsADirectoryError

      |    +-- NotADirectoryError

      |    +-- PermissionError

      |    +-- ProcessLookupError

      |    +-- TimeoutError

      +-- ReferenceError

      +-- RuntimeError

      |    +-- NotImplementedError

      |    +-- RecursionError

      +-- SyntaxError

      |    +-- IndentationError

      |         +-- TabError

      +-- SystemError

      +-- TypeError

      +-- ValueError

      |    +-- UnicodeError

      |         +-- UnicodeDecodeError

      |         +-- UnicodeEncodeError

      |         +-- UnicodeTranslateError

      +-- Warning

           +-- DeprecationWarning

           +-- PendingDeprecationWarning

           +-- RuntimeWarning

           +-- SyntaxWarning

           +-- UserWarning

           +-- FutureWarning

           +-- ImportWarning

           +-- UnicodeWarning

           +-- BytesWarning

           +-- ResourceWarning



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 사용자 정의함수(User Defined Function)에서 가변 매개변수(variable-length arguments, arbitrary arguments)의 위치에 따라서 일반 매개변수의 Keyword Argument 호출 여부에 따른 SyntaxError, TypeError 가 발생하는 현상을 살펴보고, 올바른 사용법을 소개하겠습니다. 


이게 은근히 헷갈리는 면이 있으므로 이번에 정확하게 알아두면 좋겠습니다. 





매개변수의 수를 다르게 해서 사용할 수 있는 가변 매개변수(variable-length arguments, arbitrary arguments) 위치가 뒤에 있는지, 아니면 앞에 있는지에 따라 2개 유형으로 나누어서 Keyword argument 의 올바른 사용법과 Error 발생 유형을 설명하겠습니다. 



  (1) 이름을 지정한 일반 매개변수를 앞에, 가변 매개변수를 뒤에 정의한 함수

      (Keyword argument First, Variable-length Arguments Second)


(1-1) 올바른 사용법: Keyword argument 사용 안함 (Without Keyword argument name)


In [1]: def repeat_str_KeyArg_first(repeat_num, *strings):

   ...: result = [repeat_num * strings]

   ...: return result

   ...:

   ...:


In [2]: repeat_str_KeyArg_first(3, 'a', 'b', 'c')

   ...:

Out[2]: [('a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c')] 



(1-2) Keyword argument를 사용해서 SystaxError가 발생하는 틀린 사용법
 => SyntaxError: positional arguments follows keyword argument


In [1]: def repeat_str_KeyArg_first(repeat_num, *strings):

   ...: result = [repeat_num * strings]

   ...: return result

   ...:

In [3]: repeat_str_KeyArg_first(repeat_num = 3, 'a', 'b', 'c') # SyntaxError

   ...:

File "<ipython-input-3-2047f5ba4020>", line 1

repeat_str_KeyArg_first(repeat_num = 3, 'a', 'b', 'c')

^

SyntaxError: positional argument follows keyword argument


File "<ipython-input-3-2047f5ba4020>", line 1

repeat_str_KeyArg_first(repeat_num = 3, 'a', 'b', 'c')

^

SyntaxError: positional argument follows keyword argument





  (2) 가변 매개변수를 앞에, 이름을 지정한 일반 매개변수를 뒤에 정의한 함수

      (Variable-length Arguments First, Keyword argument Second)


(2-1) 올바른 사용법: Keyword argument 사용함 (With Keyword argument name)


In [4]: def repeat_str_VarArg_first(*strings, repeat_num):

   ...: result = [repeat_num * strings]

   ...: return result

   ...:

   ...:


In [5]: repeat_str_VarArg_first('a', 'b', 'c', repeat_num = 3) # works well

   ...:

Out[5]: [('a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c')] 



(2-2) TypeError 발생하는 틀린 사용법 : 

  => TypeError: function() missing 1 required keyword-only argument: 'keyword_arg'


In [4]: def repeat_str_VarArg_first(*strings, repeat_num):

   ...: result = [repeat_num * strings]

   ...: return result

   ...:

   ...:

In [6]: repeat_str_VarArg_first('a', 'b', 'c', 3) # TypeError

   ...:

Traceback (most recent call last):


File "<ipython-input-6-13d658b54364>", line 1, in <module>

repeat_str_VarArg_first('a', 'b', 'c', 3)


TypeError: repeat_str_VarArg_first() missing 1 required keyword-only argument: 'repeat_num'


Traceback (most recent call last):


File "<ipython-input-6-13d658b54364>", line 1, in <module>

repeat_str_VarArg_first('a', 'b', 'c', 3)


TypeError: repeat_str_VarArg_first() missing 1 required keyword-only argument: 'repeat_num' 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 for loop 반복문의 진척율을 콘솔창에 출력해서 확인하는 방법을 소개하겠습니다. 


먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


df = pd.DataFrame(np.arange(10000).reshape(-1, 2),

                  columns=['x1', 'x2'])


df.head()


x1x2
001
123
245
367
489

 




for loop 예문은 DataFrame의 매 3의 배수 행은 'x1'과 'x2'를 더해서 'x3'를 만들고, 3의 배수 행이 아닌 경우는 'x1'에서 'x2'를 뺀 값으로 'x3'를 만드는 것입니다. 



for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']



df.head(10)

x1x2x3
0011.0
123-1.0
245-1.0
36713.0
489-1.0
51011-1.0
6121325.0
71415-1.0
81617-1.0
9181937.0





가장 간단하게 for loop 반복문의 진척율을 확인할 수 있는 방법은 전체 행 중에서 반복문이 수행 중인 행의 위치를 매번 콘솔창에 프린트하도록 하는 방법입니다. 



for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']

    

    progress = 100*(i+1)/df.shape[0]

    print("===== %d%% completed =====" % progress)

 

 



그런데 위처럼 프린트문을 사용하면 for loop문이 실행되는 매 행마다 한줄씩 프린트가 되므로 콘솔 창이 매우 길게 늘어질 수 있습니다. 


이때 sys.stdout 을 사용하면 아래 예시처럼 매 진척율을 같은 줄에서 진척율만 숫자가 바뀌면서 나타낼 수 있습니다. 



from sys import stdout


for i in range(df.shape[0]):

    if i % 3 == 0:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] + df.loc[i, 'x2']

    else:

        df.loc[i, 'x3'] = df.loc[i, 'x1'] - df.loc[i, 'x2']

    

    progress = 100*(i+1)/df.shape[0]

    stdout.write("\r ===== %d%% completed =====" % progress)

    stdout.flush()

stdout.write("\n")

 




많은 도움이 되었기를 바랍니다. 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 함수나 클래스의 구현을 미룰 때 쓰는 pass statement 에 대해서 알아보겠습니다. 


Python은 함수나 클래스를 정의할 때 { } 를 사용하지 않고 들여쓰기(indentation)로 함수나 클래스가 실행할 코드 블록을 정의하는데요, 만약 코드 블록 부분에 실행해야 할 코드가 없다면 def function_name:  이후의 줄에 아무것도 없게 되어 Python은 'SyntaxError: unexpected EOF while parsing' 에러를 발생시킵니다. 



In [1]: def null_func():

   ...:

   ...:

File "<ipython-input-1-85c822900a5a>", line 2

^

SyntaxError: unexpected EOF while parsing


 




이처럼 함수나 클래스 이름 정의 후에 ':' 다음 줄에 아무것도 실행시키지 않으려면 'pass' 문을 명시적으로 표기해주어야만 SyntaxError 가 발생하지 않습니다. 





다음은 아무것도 실행할 것이 없는 null_func() 라는 이름의 함수에 pass 문을 사용한 예입니다. 



In [2]: def null_func():

   ...:       pass

 




다음은 분류 모델의 BaseClassifier() 클래스를 정의할 때 fit() 함수에 pass 문을 사용한 예입니다. 



In [3]: from sklearn.base import BaseEstimator

   ...:   class BaseClassifier(BaseEstimator):

   ...:       def fit(self, X, y=None):

   ...:           pass

   ...:       def predict(self, X):

   ...:           return np.zeros((len(X), 1), dtype=bool)

 



'빈 구현'을 만드는 pass 문의 사용 용도 만큼이나 이번 포스팅은 별 내용이 없네요. ^^; 





이전에 for loop 포스팅을 했을 때 pass, continue, break 문을 비교해서 설명했던 적이 있는데요, 이번에 함수 파트에서 pass 문이 다시 나온만큼 복습하는 차원에서 pass와 continue문을 간단한 예를 들어서 한번 더 비교해서 설명하겠습니다. 



  pass : 아무것도 실행하지 않고 다음 행으로 넘어감



In [4]: for i in [1, 2, 3, 4, 5, 6]:

   ...:       if i == 4:

   ...:           pass

   ...:           print("This pass block will be printed before 4")

   ...:       print("The number is ", i)

   ...:   print("The end")

   ...:


The number is 1

The number is 2

The number is 3

This pass block will be printed before 4

The number is 4

The number is 5

The number is 6

The end

 




  continue : 다음 순번의 loop로 되돌아가서 loop문을 실행함



In [5]: for i in [1, 2, 3, 4, 5, 6]:

   ...:       if i == 4:

   ...:           continue

   ...:           print("This continue block and No. 4 will not be printed")

   ...:       print("The number is ", i)

   ...:   print("The end")


The number is 1

The number is 2

The number is 3

The number is 5

The number is 6

The end

 



많은 도움이 되었기를 바랍니다. 


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 함수 안에 isinstance() 함수를 사용하여 매개변수의 형식(Type)을 확인하고, 함수에서 필요로 하는 데이터 형식이 아니면 에러 메시지를 반환하여 함수 사용자로 하여금 에러를 수정하는데 참고할 수 있는 정보를 제공할 수 있도록 하는 방법에 대해서 소개하겠습니다. 


  • 데이터 형식 확인 함수: isinstance(my_number, int), isinstance(my_string, str)
  • raise TypeError("message")
  • raise ValueError("message")




  (1) 정수형이 아니면 TypeError, 양수가 아니면 ValueError 반환하기


아래의 [1] 번 함수는 숫자를 input으로 넣으면 짝수(even number) 인지 아니면 홀수(odd number) 인지를 판단해주는 예제입니다. 


- if not isinstance(number, int) 조건문 함수를 사용하여 정수인지 여부를 확인하고, 

- if number <= 0 조건문으로 양수가 아닌지를 확인하여 

정수가 아니면 TypeError, 양수가 아니면 ValueError 를 반환하도록 하였습니다. 



In [1]: def even_odd_num_checker(number):

   ...:

   ...:     # TypeError, ValueError handling

   ...:     if not isinstance(number, int):

   ...:         raise TypeError("'number' is not an integer.")

   ...:     if number <= 0:

   ...:         raise ValueError("'number' must be positive.")

   ...:

   ...:     if number % 2 == 0:

   ...:         print("This number is EVEN")

   ...:     else:

   ...:         print("This number is ODD")


In [2]: even_odd_num_checker(10)

This number is EVEN


In [3]: even_odd_num_checker(9)

This number is ODD

 



아래의 [4]번에서는 '10'이라는 문자열을 입력했을 때 TypeError 가 발생한 예이며, [5]번은 매개변수 값으로 5.5 를 입력했을 때 정수(integer)가 아니기 때문에 역시 TypeEror가 발생한 예입니다. 그리고 [6]번 예는 -2 가 양수가 아니기 때문에 ValueError가 발생하였습니다.  위의 [1]번에서 정의한 함수대로 잘 작동하고 있음을 알 수 있습니다. 



In [4]: even_odd_num_checker('10') # TypeError

Traceback (most recent call last):


File "<ipython-input-4-858c9cb910a4>", line 1, in <module>

even_odd_num_checker('10') # TypeError


File "<ipython-input-1-b74b94983114>", line 5, in even_odd_num_checker

raise TypeError("'number' is not an integer.")


TypeError: 'number' is not an integer.



In [5]: even_odd_num_checker(5.5) # TypeError

Traceback (most recent call last):


File "<ipython-input-5-1125bee4ec3f>", line 1, in <module>

even_odd_num_checker(5.5) # ValueError


File "<ipython-input-1-b74b94983114>", line 5, in even_odd_num_checker

raise TypeError("'number' is not an integer.")


TypeError: 'number' is not an integer.

 


In [6]: even_odd_num_checker(-2) # ValueError

Traceback (most recent call last):


File "<ipython-input-6-8bb80e5d6ca4>", line 1, in <module>

even_odd_num_checker(-2) # ValueError


File "<ipython-input-1-b74b94983114>", line 7, in even_odd_num_checker

raise ValueError("'number' must be positive.")


ValueError: 'number' must be positive.





  (2) 문자열이 아니면 TypeError 반환하기


다음으로 [8]번에서 문자열을 매개변수 값으로 입력하면 문자열의 길이를 반환해주는 함수를 정의해보겠습니다. 이때 매개변수 값이 문자열(string)이 아니면 TypeError 를 반환하도록 하였습니다. 


[9]번에서 string_length() 함수에 "hello world" 값을 넣으니 길이가 11이라고 정상적으로 작동함을 알 수 있습니다. 



In [7]: import sys


In [8]: def string_length(string_input):

   ...:

   ...: # Python version check

   ...:     if sys.version_info[0] == 3:

   ...:         string_arg = str # Python 3x version

   ...:     else:

   ...:         string_arg = basestring # Python 2x version

   ...:

   ...: # TypeError handling

   ...:     if not isinstance(string_input, string_arg):

   ...:         raise TypeError("'string' is not a string")

   ...:

   ...:     string_length = len(string_input)

   ...:     print("The length of the string is {0}".format(string_length))


In [9]: string_length("hello world")

The length of the string is 11

 



[10]번에서 string_length() 함수에 숫자 10을 넣으면 문자열이 아닌 정수형이므로 TypeError 메시지를 반환합니다. 



In [10]: string_length(10) # TypeError

Traceback (most recent call last):


File "<ipython-input-10-997e19d6acd3>", line 1, in <module>

string_length(10) # TypeError


File "<ipython-input-8-8363558b8e40>", line 11, in string_length

raise TypeError("'string' is not a string")


TypeError: 'string' is not a string

 


많은 도움이 되었기를 바랍니다. 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 함수 중에서도 '함수 안의 함수 (Function in Function)' 로서 


- (1) 중첩 함수 (Nested Function): 함수 안에 정의된 함수

- (2) 재귀 함수 (Recursive Function): 함수 안에 자기 자신을 호출하는 함수


에 대해서 알아보겠습니다. 


[ Python 함수 안의 함수 (Function in Function) ]




  (1) 중첩 함수 (Nested Function): 함수 안에 정의된 함수


def 로 시작하는 함수 안에 또 다른 하나의 def 로 시작하는 함수를 정의할 수 있는데요, 이때 함수 안에 정의된 또 다른 함수를 중첩 함수 (Nested Function) 이라고 합니다. 


중첩 함수는 자기가 속한 원래 함수의 매개변수를 받아서 사용할 수 있습니다. 아래의 예시는 outer() 함수의 num 매개변수를 중첩함수인 inner() 함수의 매개변수 input 으로 사용해서 최종 결과값인 result2 를 반환하도록 하는 함수입니다. 



In [1]: def outer(num):

   ...:

   ...:     def inner(n): # nested function

   ...:         result1 = n + 1

   ...:         return result1

   ...:

   ...:     result2 = inner(num)**2

   ...:     return result2;


In [2]: outer(9) # (9+1)**2

   ...:

Out[2]: 100

 




중첩 함수 (nested function)는 자신이 속한 원래 함수 안에서만 역할을 하며, 원래 함수의 밖에서는 인식이 안됩니다.  아래의 [3] 처럼 중첩함수인 inner() 함수를 호출하려고 하면 'NameError: name 'inner' is not defined' 라는 에러메시지가 뜹니다. 



In [3]: inner(10) # NameError

Traceback (most recent call last):


File "<ipython-input-3-4050b378a5f5>", line 1, in <module>

inner(10) # NameError


NameError: name 'inner' is not defined

 




중첩함수를 사용하면 복잡한 수식도 여러개의 함수를 하나의 함수로 묶어서 정의할 수 있습니다. 아래의 예시는 모집단의 표준편차(population standard deviation) 사용자 정의 함수를 중첩함수를 사용해서 정의한 것입니다. 





In [4]: import math


In [5]: def stddev(*args):

   ...:

   ...:     def mean_func():  # nested function

   ...:         mean = sum(args)/len(args)

   ...:         return mean

   ...:

   ...:     def variance(mean):  # nested function

   ...:         squared_deviation = 0

   ...:         for arg in args:

   ...:             squared_deviation += (arg - mean)**2

   ...:             var = squared_deviation / len(args)

   ...:         return var

   ...:

   ...:     v = variance(mean_func())

   ...:     return math.sqrt(v)


In [6]: stddev(1.0, 2.0, 3.0, 4.0, 5.0)

Out[6]: 1.4142135623730951



# standard deviation using numpy std()

In [7]: import numpy as np


In [8]: np.std([1.0, 2.0, 3.0, 4.0, 5.0])

Out[8]: 1.4142135623730951

 




  (2) 재귀 함수 (Recursive Function): 함수 안에 자기 자신을 호출하는 함수


재귀 함수(Recursive Function)는 함수 안에서 자기 자신을 호출하는 함수를 말합니다. 




함수가 호출자이자 동시에 피호출자가 되어서 반복에 반복에 반복을 계속하는 함수입니다. 



왼쪽의 사진을 보면 재귀함수가 자기 자신을 계속해서 반복하면서 호출을 하는 것이 무엇을 의미하는지 이해가 될 것 같습니다. 


(*출처: http://www.itcuties.com/java/recursion-and-iteration/)












재귀함수(recursive function)와 중첩함수(nested function)를 같이 이용해서 Factorial 계산하는 함수를 정의해보겠습니다. 



In [9]: def factorial(number):

   ...:

   ...: # error handling

   ...:     if not isinstance(number, int):

   ...:         raise TypeError("Oops. 'number' must be an integer.")

   ...:     if number < 0:

   ...:         raise ValueError("Oops. 'number' must be zero or positive.")

   ...:

   ...:     def inner_factorial(number): # nested function

   ...:         if number == 0:

   ...:             return 1

   ...:         elif number > 0:

   ...:             return inner_factorial(number-1)*number # recursive function

   ...:

   ...:     return inner_factorial(number)


In [10]: factorial(5)

Out[10]: 120

 



위의 factorial(5) 함수를 실행하면 아래에 각 절차를 풀어서 설명해 놓은 것과 같이 자기 자신의 factorial() 함수를 계속 반복해서 호출하면서 값을 계산해서 최종값을 반환하게 됩니다. 




재귀함수는 편리한 점도 있지만 호출 비용이 크므로 성능이 중요한 경우에는 재귀함수 대신 반복문을 사용하는 것이 좋겠습니다.  그리고 재귀함수를 짤 때는 반드시 종료 조건을 정의해 주어야 무한반복의 함정에 빠지지 않습니다. (파이썬이 지정해 놓은 재귀 함수 반복 호출 최대 회수를 초과하면 'RuntimeError: maximum recursion depth exceeded while calling a Python object' 에러가 발생합니다)


많은 도움이 되었기를 바랍니다. 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 변수의 유효범위(scope of variables)에 대해서 알아보겠습니다. 


파이썬은 변수의 유효범위에 따라 


(1) 프로그램 전체에서 유효한 전역변수(Global Variable)

(2) 함수의 코드블록 안에서만 유효한 지역변수(Local Variable)


의 두가지 종류가 있습니다. 


변수의 유효범위에 대해서 정확하게 이해하지 못하면 Name Error, Unbound Local Error 등과 같은 의도치 않은 에러를 유발할 수 있으므로 주의가 필요합니다. 


[ Python Variable Scope : Global Variable vs. Local Variable ]




간단한 예를 들어서 설명해보겠습니다. 


  전역변수(Global Variable) vs. 지역변수(Local Variable)


아래의 [1]번 행에서 var_scope() 함수를 정의하면서 s = "Python is easy" 라는 지역변수(Local variable)을 정의하였습니다. 이 지역변수는 var_scope() 함수가 호출될 경우에만 함수가 실행이 되면서 메모리에 생성이 되었다가 함수 실행이 끝나면 메모리에서도 사라져버립니다. 


반면에 [2]번 행에서 s = "Python is not easy" 라고 할당한 문자열은 전역변수(Global variable)로서 바로 메모리에 저장이 되며, 프로그램을 전체에서 유효하며, 프로그램이 살아있는 한 계속 같이 살아있으면서 이용이 가능합니다. [4]번 행에서 print(s)를 했을 때 전역변수인 s = "Python is not easy"가 출력이 되었으며, var_scope() 함수 안에서만 유효한 지역변수인 s="Python is easy"는 출력이 안되었습니다. 



In [1]: def var_scope():

   ...:     s = "Python is easy" # Local variable

   ...:     print(s)


In [2]: s = "Python is not easy" # Global variable


In [3]: var_scope() # Local variable

Python is easy


In [4]: print(s) # Global variable

Python is not easy

 




  지역변수가 할당되기 전에 호출하려 할 때 발생하는 Unbound Local Error


아래의 [5]번 행에서 정의한 var_scope() 함수의 코드블록을 보면, 지역변수 's'가 할당이 아직 안된 상태에서 빨간색의 print(s) 를 실행하도록 했더니 "Unbound Local Error: local variable: local variable 's' referenced before assignment' 라는 에러 메시지가 떴습니다. 



In [5]: def var_scope():

   ...:     print(s) # Unbound Local Error

   ...:     s = "Python is easy" # local variable

   ...:     print(s); # local variable


In [6]: s = "Python is not easy"


In [7]: var_scope()

Traceback (most recent call last):


File "<ipython-input-7-51074fe12940>", line 1, in <module>

var_scope()


File "<ipython-input-5-5b8d90c83375>", line 2, in var_scope

print(s) # UnboundLocalError


UnboundLocalError: local variable 's' referenced before assignment

 




  함수 안에서 전역변수를 사용할 수 있게 해주는 global keyword


위의 UnblundLocalError 를 해결하려면 아래의 [8]번행에서 함수를 정의할 때 밑줄 그은 'global s' 처럼 global keyword 를 사용해서 전역변수를 사용하겠다고 선언을 해주면 됩니다. 그러면 [9]번 행에서 전역변수로 할당한 s = "Python is not easy"를 var_scope() 함수의 코드블록 안에서 지역변수가 선언되기 전에도 전역변수를 가져다가 함수 호출 시 이용할 수 있습니다. 



In [8]: def var_scope():

   ...:     global s # global variable

   ...:     print(s)

   ...:     s = "Python is easy" # local variable

   ...:     print(s);


In [9]: s = "Python is not easy"


In [10]: var_scope()

Python is not easy     <--- global variable 's'

Python is easy           <--- local variable 's'

 




  지역변수를 함수 밖에서 호출할 때 발생하는 Name Error


함수의 코드블록 안에서 정의된 지역변수는 함수의 밖에서 사용할 수 없습니다. 만약 함수 밖에서 함수안의 지역변수를 호출하려고 하면 "Name Error: name 's2' is not defined' 라는 에러 메시지가 발생합니다. 프로그래밍을 처음하는 분의 경우 지역변수, 전역변수의 변수의 유효범위 개념을 모를 경우 이게 왜 에러가 발생하는가 하고 의아해 할 수 있습니다. 



In [11]: def var_scope2():

    ...:     s2 = "I love Python"

    ...:     print(s2);


In [12]: print(s2) # NameError: name 's2' is not defined

Traceback (most recent call last):


File "<ipython-input-12-ccb37084217a>", line 1, in <module>

print(s2) # NameError: name 's2' is not defined


NameError: name 's2' is not defined

 


많은 도움이 되었기를 바랍니다. 


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 lambda 를 사용한 이름이 없는 익명 함수(the anonymous functions), 한 줄로 간단하게 다음 함수 안에 넣어서 사용할 수 있는 인 라인 함수 (inline functions)에 대해서 알아보겠습니다. 


R 사용하다가 Python 넘어올 때 처음으로 lambda 익명함수를 봤을 때 '이게 뭔가?' 했었는데요, 아래의 예제들을 같이 살펴보시면 어렵지 않게 이해할 수 있을 겁니다. 





(1) 익명 함수 (the anonymous functions) lambda 


이전 포스팅에서 소개했던 def function_name(argments): expression return result; 형태의 함수 정의와는 다르게, 함수의 이름 부여 없이도 lambda 를 사용하면 함수를 정의할 수 있습니다. 



[ lambda 함수 문법 ]

lambda arg1, agr2, ... arg n: expression

 


아래는 input으로 받은 숫자를 제곱해주는 함수를 def 와 lambda 로 각각 정의해 본 예제입니다. 


lambda 익명함수 정의는 한줄 짜리 간단한 함수를 작성해서 편하게 쓴다든지, 메모리를 아끼고 가독성을 높이는데 유용하게 쓸 수 있습니다. (lambda는 이름 없는 익명함수이기 때문에 한번 실행되고 다음줄로 넘어가면 heap 메모리 영역에서 증발되므로 메모리를 아낄 수 있습니다.)


def 를 사용한 이름을 가진 함수 정의

lambda 를 사용한 이름 없는(익명) 함수 정의

 

In [1]: def my_pow(x):

   ...:     result = x**2

   ...:     return result;


In [2]: my_pow(3)

Out[2]: 9


[3]: my_pow2 = lambda x: x**2


In [4]: my_pow2(3)

Out[4]: 9




다음으로 List, Tuple, String과 같은 순서열 데이터에 대해 lambda 함수와 함께 map(), filter(), reduce(), apply() 함수의 안에 익명 함수로 INLINE으로 사용하는 예제, 리스트 축약(list comprehension) 도 더불어 소개하겠습니다.  



 (2) map(lambda arg:expression, list) : lambda 함수를 리스트 원소에 적용하기


리스트의 여러개의 각 원소에 lambda 함수를 적용하고 싶으면 map() 함수를 lambda 와 같이 사용하면 됩니다.  (리스트에 문자열이나 튜플과 같은 순서열 자료형이 들어가도 동일함)


만약 리스트에 lambda 함수를 그냥 적용하면 아래처럼 TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int' 처럼 TypeError 가 납니다. 



In [5]: my_list = [1, 2, 3, 4]


In [6]: my_pow2(my_list) # TypeError

Traceback (most recent call last):


File "<ipython-input-6-0c500bc490e4>", line 1, in <module>

my_pow2(my_list) # TypeError


File "<ipython-input-3-6e25a06562c4>", line 1, in <lambda>

my_pow2 = lambda x: x**2


TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

 



아래는 숫자를 제곱해주는 lambda 함수를 map() 함수를 사용하여 리스트의 각 원소에 적용하고, 이 결과값을 리스트(list)로 반환하도록 한 예제입니다. Python3 에서는 map(lambda ) 를 실행하면 리스트가 아니라 map 객체로 반환이 되며, 리스트로 반환을 받고 싶으면 list() 로 명시적으로 변환을 해주어야 합니다. 



In [5]: my_list = [1, 2, 3, 4]


In [7]: list(map(lambda i: i**2, my_list))

Out[7]: [1, 4, 9, 16]

 




  (3) filter(lambda arg: expression, list) : lambda 함수로 리스트의 원소 필터링 하기


다음 예제는 filter(lambda ) 를 사용하여 여러개의 숫자를 원소로 가지는 리스트에서 '짝수(even number)'만을 선별(filter) 하여 리스트로 반환하는 lambda 익명함수 예제입니다. 



In [8]: num_list = range(10)


In [9]: even_list = list(filter(lambda x: (x%2 == 0), num_list))


In [10]: even_list

Out[10]: [0, 2, 4, 6, 8]

 



위의 예제를 리스트 축약(List Comprehension) 으로도 똑같은 결과 값을 구현할 수 있습니다. 메모리 절약 측면에서는 lambda 익명함수가 더 낫고, 코드 가독성 면에서는 리스트 축약이 좀더 나아보입니다. 



In [11]: new_list_2 = [x for x in num_list if x%2 == 0] # list comprehension


In [12]: new_list_2

Out[12]: [0, 2, 4, 6, 8]

 




  (4) reduce(lambda arg: expression, list): 리스트의 원소에 누적으로 함수 적용


reduce 는 lambda 함수를 리스트의 각 원소에 누적으로 계속 적용할 때 사용합니니다. 아래 예제는 reduce(lambda ) 를 사용하여 숫자 1 ~ 4까지 계속 곱하여 1*2*3*4 의 값을 구한 것입니다(4! 을 구한 것임). 



In [13]: from functools import reduce # python3


In [14]: my_list = [1, 2, 3, 4]


In [15]: reduce(lambda x, y: x*y, my_list)

 




  (5) apply(lambda x: pd.Series({'key': function(x)}) 

     : lambda 함수 적용한 칼럼들로 Pandas DataFrame 만들기


마지막으로, 아래는 DataFrame에 카테고리 변수를 기준으로 groupby() 를 하여 apply(lambda ) 함수로 DataFrame 내 각 칼럼별 요약 집계를 한 결과를 가지고 새로운 DataFrame을 만들어보는 예제입니다. 



데이터 전처리, 집계할 때 알아두면 유용합니다. 



In [16]: import pandas as pd


In [17]: aa = pd.DataFrame({'id': ['a', 'a', 'a', 'b', 'b'],

                                     'val': [1, 2, 3, 4, 5]})

In [18]: aa

Out[18]:

  id  val

0  a    1

1  a    2

2  a    3

3  b    4

4  b    5


In [19]: df = aa.groupby('id').apply(lambda x:

                 pd.Series({'clst_obs_cnt': len(x)

                               , 'clst_val_sum': sum(x.val)

                               , 'id_list': x.id.tolist()})).reset_index()


In [20]: df

Out[20]:

  id  clst_obs_cnt  clst_val_sum    id_list

0  a             3             6        [a, a, a]

1  b             2             9        [b, b]




많은 도움이 되었기를 바랍니다. 

728x90
반응형
Posted by Rfriend
,