이번 포스팅에서는 Python pandas의 DataFrame에서 문자열 변수들을 가지고 일부 포맷 변형을 한 후에 새로운 변수를 만드는 방법을 소개하겠습니다. 이게 얼핏 생각하면 쉬울 것 같은데요, 또 한번도 본적이 없으면 어렵습니다. ^^; lambda, apply() 함수와 문자열 처리 메소드 등에 대해서 알고 있으면 이해가 쉽습니다. 



(1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

    (Left padding with zeros so that make 5 positions)


(2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에 새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)






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


 

In [1]: import pandas as pd


In [2]: df = pd.DataFrame({'id': [1, 2, 10, 20, 100, 200], 

   ...:                    'name': ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']})


In [3]: df

Out[3]: 

    id name

0    1  aaa

1    2  bbb

2   10  ccc

3   20  ddd

4  100  eee

5  200  fff





  (1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

     (Left padding with zeros so that make 5 positions)


lambda 로 format() 함수를 만들어서 apply() 로 적용을 하여 5자리 중에서 빈 자리를 '0'으로 채웠습니다.



In [4]: df['id_2'] = df['id'].apply(lambda x: "{:0>5d}".format(x))


In [5]: df

Out[5]: 

    id      name   id_2

0      1   aaa      00001

1      2   bbb     00002

2    10   ccc     00010

3    20  ddd    00020

4  100  eee     00100

5  200  fff      00200



다양한 숫자 포맷(number format) 함수는 https://mkaz.blog/code/python-string-format-cookbook/ 를 참고하세요. 




 (2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에

새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)


그리고 역시 lambda 로 '_'를 중간 구분자로 해서 두 변수의 문자열을 결합('_'.join)하는 함수를 정의한 후에 apply() 로 적용하였습니다, 'axis = 1'을 설정해준 점 주의하시기 바랍니다. 


 

In [6]: df['id_name'] = df[['id_2', 'name']].apply(lambda x: '_'.join(x), axis=1)


In [7]: df

Out[7]: 

       id    name   id_2         id_name

0      1    aaa      00001    00001_aaa

1      2    bbb     00002    00002_bbb

2    10    ccc     00010     00010_ccc

3    20   ddd    00020    00020_ddd

4  100   eee    00100     00100_eee

5  200   fff     00200     00200_fff 





여기서 끝내면 좀 허전하고 아쉬우니 몇 가지 데이터 포맷 변경을 더 해보겠습니다.



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


(4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


(5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


"{:.2f}".format() 함수를 사용하여 소숫점 두번째 자리까지 표현하였습니다. 



In [8]: df['id_3'] = df['id'].apply(lambda x: "{:.2f}".format(x))


In [9]: df





  (4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


upper() 문자열 내장 메소드를 사용하여 소문자를 대문자로 변경하였습니다. 



In [10]: df['name_3'] = df['name'].apply(lambda x: x.upper())


In [11]: df

 




  (5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



In [14]: df['id_name_3'] = df[['id_3', 'name_3']].apply(lambda x: ':'.join(x), axis=1)


In [15]: df

 



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

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame에서 범주형 변수의 항목(class)을 기준 정보(mapping table, reference table)를 이용하여 일괄 변환하는 방법을 소개하겠습니다. 


(1) 범주형 변수의 항목 매핑/변환에 사용한 기준 정보를 dict 자료형으로 만들어 놓고, 


(2) dict.get() 함수를 이용하여 매핑/변환에 사용할 사용자 정의 함수를 만든 후에 


(3) map() 함수로 (2)번에서 만든 사용자 정의 함수를 DataFrame의 범주형 변수에 적용하여 매핑하기



차근차근 예를 들어서 설명해보겠습니다. 


먼저, 간단한 예제 데이터프레임을 만들어보겠습니다. 



import pandas as pd

from pandas import DataFrame


df = DataFrame({'name': ['kim', 'KIM', 'Kim', 'lee', 'LEE', 'Lee', 'wang', 'hong'], 

                'value': [1, 2, 3, 4, 5, 6, 7, 8], 

                'value_2': [100, 300, 200, 100, 100, 300, 50, 80]

               })


df

namevaluevalue_2
0kim1100
1KIM2300
2Kim3200
3lee4100
4LEE5100
5Lee6300
6wang750
7hong880

 




위의 df 라는 이름의 DataFrame에서, name 변수의 (kim, KIM, Kim) 를 (kim)으로, (lee, LEE, Lee)를 (lee)로, 그리고 (wang, hong)을 (others) 라는 항목으로 매핑하여 새로운 변수 name_2 를 만들어보려고 합니다. 



  (1) 범주형 변수의 항목 매핑/변환에 사용할 기준 정보를 dict 자료형으로 만들기



name_mapping = {

    'KIM': 'kim',

    'Kim': 'kim', 

    'LEE': 'lee', 

    'Lee': 'lee', 

    'wang': 'others', 

    'hong': 'others'

}


name_mapping

 {'KIM': 'kim',

 'Kim': 'kim',
 'LEE': 'lee',
 'Lee': 'lee',
 'hong': 'others',
 'wang': 'others'}




  (2) dict.get() 함수를 이용하여 매핑/변환에 사용할 사용자 정의 함수 만들기


dict 자료형에 대해 dict.get() 함수를 사용하여 정의한 아래의 사용자 정의 함수 func는 '만약 매핑에 필요한 정보가 기준 정보 name_mapping dict에 있으면 그 정보를 사용하여 매핑을 하고, 만약에 기준정보 name_mapping dict에 매핑에 필요한 정보가 없으면 입력값을 그대로 반환하라는 뜻입니다. 'lee', 'kim'의 경우 위의 name_mapping dict 기준정보에 매핑에 필요한 정보항목이 없으므로 그냥 자기 자신을 그대로 반환하게 됩니다. 



func = lambda x: name_mapping.get(x, x)

 




  (3) map() 함수로 매핑용 사용자 정의 함수를 DataFrame의 범주형 변수에 적용하여 매핑/변환하기


위의 기준정보 name_mapping dict를 사용하여 'name_2' 라는 이름의 새로운 범주형 변수를 만들어보았습니다. 



df['name_2'] = df.name.map(func)


df

namevaluevalue_2name_2
0kim1100kim
1KIM2300kim
2Kim3200kim
3lee4100lee
4LEE5100lee
5Lee6300lee
6wang750others
7hong880others

 




  (4) groupby() 로 범주형 변수의 그룹별로 집계하기


범주형 변수에 대해서 항목을 매핑/변환하여 새로운 group 정보를 만들었으니, groupby() operator를 사용해서 새로 만든 name_2 변수별로 연속형 변수들('value', 'value_2')의 합계를 구해보겠습니다. 



# aggregation by name

df.groupby('name_2').sum()

valuevalue_2
name_2
kim6600
lee15500
others15130

 




'name_2'와 'name' 범주형 변수 2개를 groupby()에 함께 사용하여 두개 범주형 변수의 계층적인 인덱스(hierarchical index) 형태로 'value_2' 연속형 변수에 대해서만 합계를 구해보겠습니다. (아래의 결과에 대해 unstack()을 하면 name 변수를 칼럼으로 올려서 cross-tab 형태로 볼 수도 있겠습니다.)



df.groupby(['name_2', 'name'])['value_2'].sum()

name_2  name
kim     KIM     300
        Kim     200
        kim     100
lee     LEE     100
        Lee     300
        lee     100
others  hong     80
        wang     50
Name: value_2, dtype: int64

 



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


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 GroupBy 를 사용하여 그룹별로 반복 작업(iteration over groups)하는 방법을 소개하겠습니다. 

pandas의 GroupBy 객체는 for loop 반복 시에 그룹 이름과 그룹별 데이터셋을 2개의 튜플로 반환합니다. 이러한 특성을 잘 활용하면 그룹별로 for loop 반복작업을 하는데 유용하게 사용할 수 있습니다. 


[ GroupBy로 그룹별로 반복 작업하기 ]




예제로 사용할 데이터는 UCI machine learning repository에 등록되어 있는 abalone 공개 데이터셋입니다. 



import numpy as np

import pandas as pd


abalone = pd.read_csv("/Users/ihongdon/Documents/Python/abalone.txt", 

                      sep=",", 

                      names = ['sex', 'length', 'diameter', 'height', 

                               'whole_weight', 'shucked_weight', 'viscera_weight', 

                               'shell_weight', 'rings'], 

                      header = None)



abalone['length_cat'] = np.where(abalone.length > np.median(abalone.length), 

                                 'length_long', 

                                 'length_short')



abalone.head()

sexlengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightringslength_cat
0M0.4550.3650.0950.51400.22450.10100.15015length_short
1M0.3500.2650.0900.22550.09950.04850.0707length_short
2F0.5300.4200.1350.67700.25650.14150.2109length_short
3M0.4400.3650.1250.51600.21550.11400.15510length_short
4I0.3300.2550.0800.20500.08950.03950.0557length_short





위의 abalone 데이터셋을 '성별(sex)'로 GroupBy를 한 후에, for loop을 돌려서 그룹 이름(sex: 'F', 'I', 'M')별로 데이터셋을 프린트해보겠습니다. 



for sex, group_data in abalone[['sex', 'length_cat', 'whole_weight', 'rings']].groupby('sex'):

    print sex

    print group_data[:5]

 

F    sex    length_cat  whole_weight  rings

2 F length_short 0.6770 9 6 F length_short 0.7775 20 7 F length_short 0.7680 16 9 F length_long 0.8945 19 10 F length_short 0.6065 14

I    sex    length_cat  whole_weight  rings
4    I  length_short        0.2050      7
5    I  length_short        0.3515      8
16   I  length_short        0.2905      7
21   I  length_short        0.2255     10
42   I  length_short        0.0700      5

M    sex    length_cat  whole_weight  rings
0    M  length_short        0.5140     15
1    M  length_short        0.2255      7
3    M  length_short        0.5160     10
8    M  length_short        0.5095      9
11   M  length_short        0.4060     10





이번에는 두 개의 범주형 변수(sex, length_cat)를 사용하여 for loop 반복문으로 그룹 이름 (sex와 leggth_cat 의 조합: F & length_long, F & length_short, I & length_long, I & length_short, M & length_long, M & length_short)과 각 그룹별 데이터셋을 프린트해보겠습니다. 


참고로, 아래 코드에서 '\' 역슬래쉬는 코드를 한줄에 전부 다 쓰기에 너무 길 때 다음줄로 코드를 넘길 때 사용합니다. 



for (sex, length_cat), group_data in abalone[['sex', 'length_cat', 'whole_weight', 'rings']]\

.groupby(['sex', 'length_cat']):

    print sex, length_cat

    print group_data[:5]

 

F length_long
   sex   length_cat  whole_weight  rings
9    F  length_long        0.8945     19
22   F  length_long        0.9395     12
23   F  length_long        0.7635      9
24   F  length_long        1.1615     10
25   F  length_long        0.9285     11
F length_short
   sex    length_cat  whole_weight  rings
2    F  length_short        0.6770      9
6    F  length_short        0.7775     20
7    F  length_short        0.7680     16
10   F  length_short        0.6065     14
13   F  length_short        0.6845     10
I length_long
    sex   length_cat  whole_weight  rings
509   I  length_long        0.8735     16
510   I  length_long        1.1095     10
549   I  length_long        0.8750     11
550   I  length_long        1.1625     17
551   I  length_long        0.9885     13
I length_short
   sex    length_cat  whole_weight  rings
4    I  length_short        0.2050      7
5    I  length_short        0.3515      8
16   I  length_short        0.2905      7
21   I  length_short        0.2255     10
42   I  length_short        0.0700      5
M length_long
   sex   length_cat  whole_weight  rings
27   M  length_long        0.9310     12
28   M  length_long        0.9365     15
29   M  length_long        0.8635     11
30   M  length_long        0.9975     10
32   M  length_long        1.3380     18
M length_short
   sex    length_cat  whole_weight  rings
0    M  length_short        0.5140     15
1    M  length_short        0.2255      7
3    M  length_short        0.5160     10
8    M  length_short        0.5095      9
11   M  length_short        0.4060     10





다음으로, 성별(sex)로 GroupBy를 해서 성별 그룹('F', 'I', 'M')을 key로 하고, 데이터셋을 value로 하는 dict를 만들어보겠습니다. 



abalone_sex_group = dict(list(abalone[:10][['sex', 'length_cat', 'whole_weight', 'rings']]

                              .groupby('sex')))

 

abalone_sex_group


{'F':   sex    length_cat  whole_weight  rings
 2   F  length_short        0.6770      9
 6   F  length_short        0.7775     20
 7   F  length_short        0.7680     16
 9   F   length_long        0.8945     19,
 'I':   sex    length_cat  whole_weight  rings
 4   I  length_short        0.2050      7
 5   I  length_short        0.3515      8,
 'M':   sex    length_cat  whole_weight  rings
 0   M  length_short        0.5140     15
 1   M  length_short        0.2255      7
 3   M  length_short        0.5160     10
 8   M  length_short        0.5095      9}





이렇게 그룹 이름을 key로 하는 dict 를 만들어놓으면 그룹 이름을 가지고 데이터셋을 indexing하기에 편리합니다.  예로 성별 중에 'M'인 데이터셋을 indexing해보겠습니다. 



abalone_sex_group['M'] 

sexlengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightringslength_cat
0M0.4550.3650.0950.51400.22450.10100.15015length_short
1M0.3500.2650.0900.22550.09950.04850.0707length_short
3M0.4400.3650.1250.51600.21550.11400.15510length_short
8M0.4750.3700.1250.50950.21650.11250.1659length_short




물론 abalone[:10][abalone['sex'] == 'M']  처럼 원래의 처음 abalone 데이터프레임에 boolean 형태로 indexing을 해도 됩니다. 대신에 dict 로 만들어놓으면 데이터셋 indexing 하는 속도가 더 빠를겁니다. 


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

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
,

이번 포스팅에서는 함수의 호출자(caller)에게 코드를 실행시킨 후의 값(result)을 반환해주는 return 문에 대해서 3가지 유형으로 나누어서 소개하겠습니다. 


(1) return + result => 호출자에게 result 반환

(2) no return & no result => code_block 실행 후 종료 (None 반환)

(3) return (no result) => 함수 즉시 종료




첫번째의 return + result 문은 함수를 사용할 때의 일반적인 형태로서 이미 친숙할 것이라고 예상합니다. 두번째와 세번째는 좀 낯설 수도 있는데요, 이번 포스팅에서 예를 들어가면서 자세히 설명해보겠습니다. 



  (1) return + result => 호출자에게 result 값 반환


아래에 my_sum(3, 5) 함수를 호출하여 sum_all = 3 + 5 = 8 의 result 값을 sum_result 에 반환(return)한 예시입니다. 



In [1]: def my_sum(num1, num2):

   ...:     sum_all = num1 + num2

   ...:     return sum_all;


In [2]: sum_result = my_sum(3, 5)


In [3]: sum_result

Out[3]: 8

 




  (2) no return & no result => code_block 실행 후 종료 (None 반환)


다음으로 함수의 마지막 부분에 return 문을 사용하지 않을 경우, 함수 안의 code_block 만 실행할 뿐 호출자에게는 None을 반환합니다 (즉, 아무값도 반환하지 않음). 


이때 함수를 실행한 후의 결과값을 저장한 객체는 "None Type"이 됩니다. 



In [4]: def my_sum2(num1, num2):

   ...:     sum_all = num1 + num2;


In [5]: sum_result2 = my_sum2(3, 5)


In [6]: sum_result2


In [7]: type(sum_result2) # None

Out[7]: NoneType

 



아래의 예제와 같이 하나의 함수 안에 2개의 return 문을 사용할 수도 있습니다. 하지만 이럴 경우 의도치 않게 실수로 버그를 만들 수도 있으므로 가급적 하나의 return 문을 사용하는 것이 좋습니다. 아래의 예제에서는 argument 로 '0' 값을 받을 경우 return 문이 실행되지 않기 때문에 (의도치않게) None 을 반환하고 있습니다. 



In [8]: def my_abs(num):

   ...:     if num > 0:

   ...:         return num

   ...:     elif num < 0:

   ...:         return (-1)*num;


In [9]: abs_result = my_abs(-1)


In [10]: abs_result

Out[10]: 1


In [11]: abs_result = my_abs(0)


In [12]: abs_result   # no result


In [13]: type(abs_result)   # None

Out[13]: NoneType

 




  (3) return only (no result) => 함수 즉시 종료


마지막으로 return 문만 있고 뒤에 result 가 없는 경우입니다. 이럴 경우에 return 문은 함수 호출자에게 값을 반환(return)하는 의미로 쓰인 것이 아니고 함수를 종료(close)시키는 의미로 쓰였다고 보면 됩니다.  


아래의 예에서는 code block에 i를 loop 돌면서 프린트하는 실행문이 들어있으며, 만약 i 가 1부터 하나씩 증가하다가 3이 되면 return 문을 맞닥드려서 함수를 종료시켜버리는 함수입니다.  따라서 my_print(10) 을 실행시켰을 때 1, 2, 3 까지만 프린트가 실행이 되고 거기서 중단이 되었기 때문에 나머지 4 ~ 10까지는 프린트가 안되었습니다. 



In [14]: def my_print(num):

    ...:     for i in range(1, num+1):

    ...:         print(i)

    ...:         if i == 3:

    ...:             return;


In [15]: my_print(3)

1

2

3


In [16]: my_print(10)

1

2

3

 


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


728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 함수의 매개변수 중에서 매개변수의 수가 함수 정의 시에 정한 개수로 고정되어 있는 위치 매개변수, 키워드 매개변수, 기본값 매개변수에 대해서 소개하였습니다. 


이번 포스팅에서는 매개변수의 수가 상황에 따라서 변화하는 가변 매개변수 (Arbitrary Arguments, Variable Length Arguments)에 대해서 알아보겠습니다. 


가변 매개변수는 

  - (1) *args_tuple : Positional 매개변수 (Tuple)

  - (2) **args_dict : 키워드 매개변수 (Dictionary)

의 두가지가 있습니다. 




함수 중에 보면 매개변수로 * (Asterisk), ** (Two Asterisks) 가 들어있는 경우 처음보는 분이라면 이게 무슨 뜻인가하고 궁금하였을 것입니다. 이번 포스팅을 보고 나면 이해가 될 것이예요. 



먼저 지난번 포스팅에서 소개했던 위치 매개변수를 복습해보겠습니다. line 2 에서 'print_list' 함수를 정의할 때 매개변수 'text_list' 1개만을 정의했으며, line 3에서 print_list() 함수를 호출할 때 mylist 1개를 매개변수 위치에 입력해주었더니 정상적으로 잘 출력을 해주었습니다. 



In [1]: mylist = ['Korea', 'Sweden', 'Mexico', 'Germany']


In [2]: def print_list(text_list):

    ...: for text in text_list:

    ...: print text


In [3]: print_list(mylist)

Korea

Sweden

Mexico

Germany

 



하지만, 아래의 line 4 에서 처럼 print_list() 함수의 매개변수에 ('Korea', 'Sweden', 'Mexico', 'Germany') 의 4개의 값을 입력하자 'TypeError: print_list() takes exactly 1 arguments(4 given)' 이라는 에러 메시지가 떴습니다. 이처럼 매개변수 값으로 여러개의 값을 이용하고자 할 때 쓰는 것이 가변 매개변수(arbitrary arguments, variable length arguments) 입니다. 



# TypeError: print_list() takes exactly 1 argument (4 given)

In [4]: print_list('Korea', 'Sweden', 'Mexico', 'Germany')

Traceback (most recent call last):


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

print_list('Korea', 'Sweden', 'Mexico', 'Germany')


TypeError: print_list() takes exactly 1 argument (4 given)

 


위의 line 4 처럼 했을 때 TypeError 가 났던 것을 Positional 가변 매개변수를 사용하여 정상적으로 작동하도록 고쳐보면 아래와 같습니다. 



  (1) *args_tuple : Positional 매개변수 (Tuple 형태)


괄호 안의 매개변수 입력란에 '* (Asterisk)' 로 시작하는 매개변수 이름을 넣어줍니다. 이 가변 매개변수는 Tuple 형태입니다. 



In [5]: def print_list_2(*text_list):

    ...: for text in text_list:

    ...: print text


In [6]: print_list_2('Korea', 'Sweden', 'Mexico', 'Germany') # more arguments

Korea

Sweden

Mexico

Germany

 



==============================================================


다음으로 사전형(Dictionary Type)을 매개변수 값으로 받아서 프린트를 해주는 함수를 정의해보겠습니다. 



In [7]: worldcup2018 = dict({'A': 'Russia, Saudi, Egypt, Uruguay',

   ...: 'F': 'Korea, Sweden, Mexico, Germany'})


In [8]: def print_group(group):

   ...: for i in group.keys():

   ...: print ("{0} : {1}".format(i, group[i]))


In [9]: print_group(worldcup2018)

A : Russia, Saudi, Egypt, Uruguay

F : Korea, Sweden, Mexico, Germany

 



위의 line 8 에서 정의한 함수에 아래의 line 10 처럼 여러 개의 사전형 값을 매개변수 값으로 입력하면 'TypeError: print_group() got an unexpected keyword argument 'A'' 라는 에러 메시지가 뜹니다. 



# TypeError: print_group() got an unexpected keyword argument 'A'

In [10]: print_group(A='Russia, Saudi, Egypt, Uruguay',

    ...: F='Korea, Sweden, Mexico, Germany')

Traceback (most recent call last):


File "<ipython-input-10-bae24f97b031>", line 2, in <module>

F='Korea, Sweden, Mexico, Germany')


TypeError: print_group() got an unexpected keyword argument 'A'

 



위의 line 10 처럼 매개변수 값으로 다수 개의 사전형 값을 사용하고 싶을 때 **agrs_dict 의 가변형 매개변수를 사용하면 됩니다. 


  (2) **agrs_dict : 키워드 매개변수 (Dictionary 형태)


사전형(Dictionary) 의 Key 값이 키워드 매개변수의 키워드(Keyword) 가 됩니다. 파이썬 라이브러리의 함수들을 보다 보면 **kwagrs 라고 표현된 경우가 있는데요, 이때 kw 가 KeyWord 의 K(ey)W(ord) 의 kw 를 따온 거예요. 



In [11]: def print_group_2(**group):

    ...: for i in group.keys():

    ...: print ("{0} : {1}".format(i, group[i]))


In [12]: print_group_2(A='Russia, Saudi, Egypt, Uruguay',

    ...: F='Korea, Sweden, Mexico, Germany')

A : Russia, Saudi, Egypt, Uruguay

F : Korea, Sweden, Mexico, Germany

 




  (3) 순서 : *args_tuple 먼저, **args_dict 나중에


*args_tuple 과 **args_dict 두 가지 유형의 가변 매개변수를 모두 사용하여 함수를 정의할 수 있습니다. 단, 이때 순서가 중요합니다. *args_tuple 을 먼저 정의하고, **args_dict 를 나중에 이어서 정의해야 합니다. 



#%% arguments sequence matters

In [13]: def print_all(*tup, **dic):

    ...: print(tup)

    ...: print(dic)


In [14]: print_all(1, 2, 3,

    ...: A='Russia, Saudi',

    ...: F='Korea, Sweden')

(1, 2, 3)

{'A': 'Russia, Saudi', 'F': 'Korea, Sweden'}

 



만약 아래의 line 15 처럼 **args_dict 먼저 정의하고, *args_tuple 을 그 뒤에 이어서 정의하게 되면 'SyntaxError: invalid syntax' 라는 에러 메시지가 뜹니다. 



# SyntaxError: invalid syntax

In [15]: def print_all(**dic, *tup):

    ...: print(tup)

    ...: print(dic)

    ...:

    ...:

File "<ipython-input-15-3e33a9bb0436>", line 1

def print_all(**dic, *tup):

^

SyntaxError: invalid syntax

 



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


728x90
반응형
Posted by Rfriend
,