개요에서 말했던 것처럼, Python 코드는 '가장 아름다운 하나의 답이 존재한다'라는 철학을 만족시키는 하나의 스타일로 진화해 왔습니다. 이 '가장 아름다운 하나의 답'을 공식적으로 정리해 두는 곳이 있는데, 바로 PEP(Python Enhancement Proposals, 파이썬 개선 제안서)입니다. 코딩 컨벤션은 개발 조직마다 하나씩 존재하긴 하지만, 일반적으로 그 언어의 문화를 공유하는 공동체에서 인정하는 컨벤션은 대부분 통일되어 있으며, 그런 것들이 PEP에서 지속적으로 문서화되고 있습니다.

PEP는 파이썬 개선 제안서라는 이름대로, Python을 개선하기 위한 제안서를 뜻합니다. PEP1, PEP2, PEP3처럼 넘버링 방식으로 이름짓습니다. PEP에 대해 다루고 있는 PEP 1에서는 PEP를 아래의 3가지로 구분합니다.

  • Standard Track : 새로운 기능이나 구현 제안
  • Informational : 디자인 이슈나 일반적인 지침, 커뮤니티에 정보를 제안. 새로운 기능을 제안하지는 않음
  • Process : Python 개발 과정의 개선을 제안

PEP8

PEP8은 Python 코드를 위한 스타일 가이드를 정리해 둔 Process 타입의 제안서입니다. 수많은 PEP 문서들 중 가장 많은 조회수를 기록했고, PEP가 유명해진 이유입니다. 대부분의 Python 개발자들이 사용하는 일관된 스타일PEP8을 기본으로 하고 있습니다. PEP8에서는 아래와 같은 요소들에 대한 스타일 가이드를 다룹니다.

  • Indentation
  • Tab vs Space
  • 한 라인의 최대 글자 수
  • 각 요소(함수, 메소드, 클래스 등) 간의 blank line 갯수
  • import 스타일
  • 문자열을 감싸는 따옴표
  • Block 주석과 Inline 주석
  • 네이밍 컨벤션
  • ...

PEP20

PEP20은 Python 개발을 위한 철학을 다루고 있는 Informational 타입의 제안서입니다. Python 인터프리터에서 import this를 작성하면 나오는 이스터에그형 출력문과 동일한 내용입니다.

  • Beautiful is better than ugly(아름다움이 추함보다 낫다).
  • Explicit is better than implicit(명시가 암시보다 낫다).
  • Simple is better than complex(단순함이 복잡함보다 낫다).
  • Readability counts(가독성을 계산하라).
  • ...

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 입출력  (0) 2018.07.10
[Python] Comprehension  (0) 2018.07.09
[Python] 패키지  (0) 2018.07.07
[Python] 모듈  (0) 2018.07.06
[Python] 사용자 정의 에러  (0) 2018.07.05

패키지는 Python 모듈을 계층적으로 관리할 수 있게 해줍니다. Java의 패키지 개념과 매우 유사합니다. 패키지는 디렉토리와 파이썬 모듈로 이루어집니다. 아래는 Python 패키지로 구조화한 프로젝트의 예입니다.

디렉토리마다 존재하는 __init__.py는 특별한 용도로 사용됩니다. 이는 해당 디렉토리가 패키지임을 알려주는 역할을 하며, 패키지를 대상으로 와일드카드 import를 수행할 때 반환될 모듈을 결정하기도 합니다. 패키지의 여러 모듈들은 일반적인 모듈과 비슷한 방법으로 불러올 수 있습니다.

예를 들어 Sound/ 디렉토리의 load.py 모듈은 import Sound.load 구문으로 불러올 수 있습니다. 이 구문은 Sound/에서 __init__.py 파일을 찾고, 해당 패키지의 모든 구문을 실행한 후 load.py 모듈에 정의된 모든 요소를 Sound.load라는 네임스페이스에서 사용할 수 있게 됩니다. 일반적으로 __init__.py는 빈 파일로 남겨두는 게 일반적이며 좋은 습관입니다.

import-asfrom-import를 통해서 매우 깊은 패키지의 모듈을 불러올 수 있습니다. import very.deep.module as module이나, from very.deep import module이 이에 해당합니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] Comprehension  (0) 2018.07.09
[Python] PEP  (0) 2018.07.08
[Python] 모듈  (0) 2018.07.06
[Python] 사용자 정의 에러  (0) 2018.07.05
[Python] 에러 처리  (0) 2018.07.04

모듈은 함수나 변수, 클래스 등을 모아 둔 .py 확장자를 가진 하나의 Python 파일입니다. 지금까진 모든 코드를 하나의 모듈에서 관리했는데, 프로젝트의 크기가 커지면 모든 코드를 하나의 모듈에 몰아두기 어렵습니다. 기능별로 별도의 모듈에 분산시켜야 하지요.

import

아래는 몇가지 계산 함수를 가진 calculator.py 모듈과, 해당 모듈의 요소를 불러와 사용하는 main.py 모듈의 예입니다.

main.py에서 calculator.py 모듈을 불러오기 위해 import calculator라는 표현을 사용했습니다. import는 이미 만들어진 Python 모듈을 사용할 수 있게 해주는 예약어입니다. 이렇게 import하고 나면, calculator.[요소]처럼 calculator라는 네임스페이스에 도트 연산자를 붙여 해당 모듈의 변수, 함수 등의 요소들을 사용할 수 있습니다.

import-as

import 대상의 이름에 별명을 지어줄 수도 있습니다.

대상의 이름이 매우 길거나, 현재 모듈의 요소와 이름이 겹치는 경우 등을 해결할 수 있습니다

from-import

모듈 이름을 가지고 도트 연산하여 calculator.sum처럼 쓰지 않고, sum과 같이 모듈 내 요소처럼 사용하고 싶을 때도 있습니다. from-import를 사용합니다.

와일드카드로 해당 모듈 내의 모든 요소를 import할 수도 있습니다 asterisk(*) 기호를 사용합니다.

name

종종 Python 코드를 보다 보면, 아래와 같은 표현을 볼 수 있습니다.

__name__은 해당 모듈의 이름을 가지고 있으며, python 혹은 python3 커맨드를 통해 직접 실행한 Python 모듈의 경우 이는 __main__이 됩니다. 따라서 해당 모듈을 직접 실행했을 때만 코드가 실행되도록 하기 위해 이와 같은 표현을 사용합니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] PEP  (0) 2018.07.08
[Python] 패키지  (0) 2018.07.07
[Python] 사용자 정의 에러  (0) 2018.07.05
[Python] 에러 처리  (0) 2018.07.04
[Python] 연산자 오버로딩  (0) 2018.07.02

Java에서 Exception 클래스를 상속받아 사용자 정의 예외 클래스를 만들었듯, Python에서도 Exception 클래스를 상속받아 직접 에러를 만들 수 있습니다. Number라는 클래스를 인스턴스화할 때, 음수를 전달하면 에러를 발생시키도록 하겠습니다.

Exception 클래스를 상속받아 NegativeNumberError라는 클래스를 생성했고, __str__이라는 매직 메소드를 정의하여 객체가 print 함수에 전달될 경우 객체 내부의 메시지를 리턴하도록 했습니다. Number 클래스의 생성자에서는 인자 n이 음수이면 raise라는 구문을 사용하여 메시지와 함께 에러를 발생시켰습니다. try에서 객체 생성 시, except에서 NegativeNumberError의 e 객체를 그대로 출력하여 '음수는 담을 수 없습니다'가 출력되었습니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 패키지  (0) 2018.07.07
[Python] 모듈  (0) 2018.07.06
[Python] 에러 처리  (0) 2018.07.04
[Python] 연산자 오버로딩  (0) 2018.07.02
[Python] 언더스코어와 magic method  (0) 2018.07.01

프로그래밍 과정에선 수많은 에러를 마주칩니다. 숫자를 0으로 나누거나, 문자열과 정수를 더하거나, Iterable 자료형의 인덱스에 잘못 접근하는 경우 등이 있습니다. Java의 try-catch-finally처럼 Python도 try-except-else-finally 문법을 통해 에러 처리 구문을 만들 수 있습니다.

try-except

다음은 에러 처리를 위한 try-except문의 기본 구조입니다.

try 내부에서 에러의 종류에 상관없이 에러가 발생하면 except 블럭을 수행합니다. except 블럭에는 처리하고자 하는 에러를 지정할 수 있으며, Iterable 자료형의 인덱스에 잘못 접근하는 경우 발생하는 IndexError를 처리해 보도록 하겠습니다.

에러가 발생했을 경우 except에서 해당 에러에 대한 객체를 생성할 수 있습니다. as라는 키워드를 사용합니다.

IndexError는 __str__이라는 매직 메소드를 구현하고 있기 때문에, print에 그대로 넣으면 에러 메시지를 출력할 수 있습니다.

Python에서 기본적으로 다루는 에러는 IndexError, ZeroDivisionError, TypeError 등 매우 많습니다. Python 공식 레퍼런스에서 확인해볼 수 있습니다.

try-except-else-finally

완전한 에러 처리 구문은 try-except-else-finally입니다. except는 try에서 에러가 발생했을 경우 실행되고, else는 에러가 발생하지 않았을 경우 실행되며, finally는 에러 발생 여부에 상관 없이 실행되는 블럭입니다.


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 모듈  (0) 2018.07.06
[Python] 사용자 정의 에러  (0) 2018.07.05
[Python] 연산자 오버로딩  (0) 2018.07.02
[Python] 언더스코어와 magic method  (0) 2018.07.01
[Python] 클래스의 상속  (0) 2018.06.30

직접 정의한 클래스에 의해 생성된 객체는 기본적으로 연산이 불가능합니다.

연산자 오버로딩

직접 정의한 클래스의 객체에 +, -, *와 같은 일반 연산을 적용하려면, 객체를 연산 가능한(operable) 상태로 만들어야 합니다. 연산자 오버로딩을 통해 이를 구현하며, 이전에 언급했던 매직 메소드를 클래스에 정의하여 연산자 오버로딩을 구현할 수 있습니다.

여기서는 __add__라는 매직 메소드를 구현했습니다. 이는 해당 객체에 대한 + 연산을 오버로딩합니다. 이렇게 되면, 객체에 + 연산이 이루어질 경우 Python 인터프리터가 __add__ 메소드를 호출합니다. 함수 내부에서는 self(객체 자신)other(더하고자 하는 값)을 명시하였습니다.

  • other가 Number 클래스의 객체라면 self.n과 other.n을 더해 Number 객체를 만들어 반환하고
  • other가 int 클래스의 객체(정수)라면 self.n과 other를 더해 Number 객체를 만들어 반환합니다.

연산자 오버로딩 메소드의 종류

Python에서 사용되는 모든 연산자들은 매직 메소드로 정의하여 오버로딩 가능합니다.

  • __add__(self, other) : 이항 + 연산자(A + B, A += B)
  • __sub__(self, other) : 이항 - 연산자(A - B, A -= B)
  • __mul__(self, other) : 이항 * 연산자(A * B, A *= B)
  • __truediv__(self, other) : 이항 / 연산자(A / B, A /= B)
  • __floordiv__(self, other) : 이항 // 연산자(A // B, A //= B)
  • __mod__(self, other) : 이항 % 연산자(A % B, A %= B)
  • __pow__(self, other) : 이항 연산자(A B, pow(A, B))
  • __lshift__(self, other) : 이항 << 연산자(A << B, A <<= B)
  • __rshift__(self, other) : 이항 >> 연산자(A >> B, A >>= B)
  • __and__(self, other) : 이항 & 연산자(A & B, A &= B)
  • __xor__(self, other) : 이항 ^ 연산자(A ^ B, A ^= B)
  • __or__(self, other) : 이항 | 연산자(A | B, A |= B)
  • __not__(self) : 단항 ~ 연산자(~A)
  • __abs__(self) : 단항 절대값 연산자(abs(A))

비교 연산자 오버로딩

비교 연산자들도 오버로딩 가능합니다.

비교 연산자 오버로딩 메소드의 종류

  • __lt__(self, other) : < 연산자 - Less than(self가 other 미만인지)
  • __le__(self, other) : <= 연산자 - Less than or equal to(self가 other 이하인지)
  • __gt__(self, other) : > 연산자 - Greater than(self가 other 초과인지)
  • __ge__(self, other) : >= 연산자 - Greater than or equal to(self가 other 이상인지)
  • __eq__(self, other) : == 연산자 - Equal to(self와 other가 같은지)
  • __ne__(self, other) : != 연산자 - Not equal to(self와 other가 다른지)


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 사용자 정의 에러  (0) 2018.07.05
[Python] 에러 처리  (0) 2018.07.04
[Python] 언더스코어와 magic method  (0) 2018.07.01
[Python] 클래스의 상속  (0) 2018.06.30
[Python] 객체지향  (0) 2018.06.29

Python에서 언더스코어(_)는 중요한 문법적 요소 중 하나이며, 다른 언어들에 비해 조금 특별합니다.

값을 무시하고 싶을 때

Unpacking에서 값을 무시하기 위해 사용 가능합니다.

특별한 의미의 네이밍을 위해 사용하는 경우

Java는 캡슐화(변수나 메소드에 대한 접근을 막는 것)를 지원하기 위해 private과 같은 접근 제한자를 사용합니다. 그러나 Python은 문법적으로 캡슐화를 지원하지 않으며, 암시적으로 private를 나타내기 위해 언더스코어로 시작하는 네이밍(private expression)을 사용합니다. 이는 Java와 같은 방어적 프로그래밍 언어에서 사용하는 진정한 의미의 private은 아니지만, Python의 Trust a developer라는 원칙에 의해 생긴 '책임 있는 개발자'를 지향하기 위한 문화입니다.

하나의 언더스코어로 시작하는 single leading underscored naming은 암시적으로 private을 표현하기 위해 사용합니다. 따라서 이와 같은 네이밍 룰에 의해 정의된 요소는 '외부에서 접근하지 말라'라는 권고의 의미를 지닙니다. 그러나 개발자를 믿는 문화를 기반으로 하고 있기 때문에, 이렇게 private 처리된 요소에 접근하더라도 warning 정도만 발생합니다.

두 개의 언더스코어로 시작하는 double leading underscored naming은 컨벤션이라기 보단 문법적인 요소입니다. 이는 해당 요소를 맹글링(컴파일러나 인터프리터가 요소의 이름을 그대로 사용하지 않고 변형시키는 것)하여 충돌을 방지하는 용도로 쓰입니다. 이와 같은 네이밍을 private를 구현하기 위해 사용하는 코드도 있지만, 이는 맹글링에 의해 private처럼 보여질 뿐이므로 이상적인 구현은 아닙니다.

매직 메소드

클래스에 대해 설명할 때, 생성자와 소멸자 메소드의 이름이 각각 __init____del__이었습니다. 이것도 특별한 방식의 네이밍에 속하며, 이와 같은 double leading, double trailing underscored naming 방식의 메소드를 magic method라고 부릅니다. 이러한 매직 메소드들은 Python 인터프리터에 의해 호출되는 메타 메소드(__init__ : 생성자, __del__ : 소멸자 등), 빌트인 함수에 의해 호출되는 메소드(__int__, __str__, __float__ 등)연산자 오버로딩 메소드 등을 정의할 때 사용합니다. 예를 들어, 빌트인 함수 len()의 경우 내부적으로 전달된 객체의 매직 메소드인 __len__을 호출하며, reversed()의 경우 매직 메소드 __reversed__를 호출합니다.


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 에러 처리  (0) 2018.07.04
[Python] 연산자 오버로딩  (0) 2018.07.02
[Python] 클래스의 상속  (0) 2018.06.30
[Python] 객체지향  (0) 2018.06.29
[Python] 빌트인 함수  (0) 2018.06.28

다른 객체지향 프로그래밍 언어들처럼, Python도 클래스의 상속을 지원합니다. 객체지향 프로그래밍이라는 패러다임은 현실 세계를 객체로 표현하기 위해 만들어졌으므로, 객체의 상속이라는 개념은 더 효율적으로 객체지향을 표현할 수 있도록 합니다. 예를 들어 토끼(Rabbit)와 고양이(Cat)가 동물(Animal)에 속하는 것을 상속 구조로 표현할 수 있습니다.

Animal이라는 클래스에 run 메소드를 정의했고, 메소드에 하나의 출력문을 두었습니다. 그리고 이 클래스를 상속받는 CatRabbit 클래스를 정의하였습니다. Cat 클래스는 상위 클래스가 가지고 있는 run 메소드를 재정의(오버라이딩)했고, Rabbit 클래스는 해당 메소드를 별도로 정의하지 않았습니다.

이후 Cat 클래스의 인스턴스에서 run 호출 시 해당 클래스의 오버라이딩된 run 메소드가 호출되며 'Cat catty is running'이 출력되었고, Cat 클래스만 가지고 있는 cry 메소드를 호출하며 'Cat catty is crying'이 출력되었습니다. Rabbit 클래스의 인스턴스에서 run 호출 시 상위 클래스인 Animal.run 메소드가 호출되며 'carrot is running'이 출력되었습니다.

상속은 보통 기존 클래스를 변경하지 않고 기능을 확장하기 위해 사용합니다. 상위 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황이라면 상속을 이용해야만 할 것입니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 연산자 오버로딩  (0) 2018.07.02
[Python] 언더스코어와 magic method  (0) 2018.07.01
[Python] 객체지향  (0) 2018.06.29
[Python] 빌트인 함수  (0) 2018.06.28
[Python] 값으로서의 함수와 lambda  (0) 2018.06.27

객체 지향 프로그래밍은 실제 세계의 모습을 프로그램 코드에 그대로 반영하기 위하여 만들어졌습니다. 과거에 시뮬레이션 프로그램을 구현하기 위해 현실 세계의 것들을 프로그래밍 언어로 표현할 필요가 있었고, 이를 위하여 객체지향 프로그래밍 패러다임이 등장하게 됩니다.

객체지향 프로그래밍 패러다임에서는 현실 세계의 물건들을 객체(object)로 표현하고, 객체를 만들기 위한 틀을 클래스(class)로 정의합니다. 기능과 특성의 모음을 변수와 메소드로서 클래스(class)라는 캡슐에 넣고, 클래스를 실체화(instance)하면 객체(object)를 만들 수 있는 것입니다.

사실 객체지향은 프로그래밍 입문자들이 종종 힘들어하는 장벽 중 하나입니다. '클래스가 어려워요'라는 생각은, 실제로 프로그래밍 언어의 요소로서 어려운 게 아니라 객체지향 프로그래밍 패러다임에 대한 이해가 부족하기 때문이므로 패러다임 자체에 대한 학습을 조금 더 하는 것이 좋습니다.

Python의 객체지향

Python은 객체지향을 지원하는 멀티 패러다임 언어입니다. 아래는 Python 클래스의 가장 간단한 예입니다.

아무런 기능도 갖고 있지 않고, 객체지향으로 따지면 정말 '틀'만을 가지고 있는 클래스입니다. 그러나 객체를 생성하는 기능은 가지고 있습니다.

FishBread()의 결과값을 돌려받은 fish_breadfish_bread_2가 객체입니다. 붕어빵을 의미하는 'FishBread'라는 틀(클래스)을 이용해 붕어빵 객체 두 개가 생성되었네요.

생성자와 소멸자, 필드와 메소드

생성자는 객체가 생성될 때 호출되는 메소드이며, 소멸자는 객체가 삭제될 때 호출되는 메소드입니다. 그리고 클래스 필드모든 객체가 공유하는 필드이고, 인스턴스 필드각각의 객체가 별도로 가지는 필드입니다. 생성자와 소멸자, 클래스 필드와 인스턴스 필드, 메소드를 모두 가지고 있는 클래스를 정의해 보겠습니다.

생성자는 __init__ 메소드로 정의(7번 라인)하고, 소멸자는 __del__ 메소드로 정의(17번 라인)합니다. 각각 객체가 생성(27, 28번 라인)되고, 삭제(43번 라인)될 때 호출됩니다. 인스턴스 필드는 self.[variable]처럼 접근(8, 9번 라인)하고, 클래스 필드는 [Class].[variable]처럼 접근(13번 라인)합니다.

한가지 특별한 점은 self 변수인데, 이는 객체 자신을 나타내며 Java의 this와 같습니다. Java와는 다르게 메소드에 self를 명시한다는 점이 조금 특별합니다. 객체에서 메소드 호출 시 자동으로 해당 객체가 self에 채워지며, 이는 Class level로 메소드를 호출하며 객체를 넘겨줄 수도 있다는 것(40번 라인)입니다.

어렵지만, 객체지향이라는 패러다임에 익숙해지길 바라는 수밖에 없습니다.

Python의 모든 요소는 객체

Python의 모든 요소(int, str, bytes, list, tuple과 같은 데이터 타입, 함수 등)는 객체입니다. 따라서 저마다의 인스턴스 필드와 메소드들이 존재합니다.


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 언더스코어와 magic method  (0) 2018.07.01
[Python] 클래스의 상속  (0) 2018.06.30
[Python] 빌트인 함수  (0) 2018.06.28
[Python] 값으로서의 함수와 lambda  (0) 2018.06.27
[Python] docstring  (0) 2018.06.26

빌트인 함수(Built-in function)은 언어 설계 과정에서 미리 만들어진 함수를 말합니다. Hello World에서 봤던 print라는 함수도 빌트인 함수에 속합니다. 이와 같은 빌트인 함수와 혼동하지 않기 위해, 개발자가 직접 만든 함수는 사용자 정의 함수라고도 부릅니다. Python 3.6.4 기준 빌트인 함수는 총 68개가 있습니다. 여기서는 쓸만한 여러가지 빌트인 함수들을 소개합니다. Iterable 자료형을 위한 빌트인들을 설명하기 위해 sequence, iterator 등을 언급할텐데, 둘 다 그냥 iterable이라 생각하고 이해는 나중으로 넘기도록 합시다.

콘솔 출력을 위한 함수입니다.

1번 라인의 print문은 end 인자가 기본값인 \n으로 처리되어 'Hello'라는 문자열 출력과 함께 개행이 이루어지고, 2번 라인의 print문은 end를 직접 ''로 지정하였으므로 출력 이후 개행 처리되지 않습니다.

type(obj)

전달된 객체의 타입을 확인하기 위해 사용합니다. 클래스를 배우기 전이라 객체가 어떤 건지 정확히 알기 힘들다면, 그냥 변수의 타입을 반환한다고 생각해도 좋습니다.

isinstance(object, class-or-type-or-tuple)

타입 검증을 위해 사용합니다. 전달된 객체(obj)가 전달된 타입(type)에 속하는지를 boolean 타입으로 반환합니다.

sum(a, b)

두 수의 합을 반환합니다.

divmod(x, y)

a를 b로 floor div한 몫나머지를 반환합니다.

max(arg1, arg2, *args)

전달된 2개 이상의 인자 중 최댓값을 반환합니다. int와 str 등 서로 값을 비교할 수 없는 인자를 함께 전달할 경우 오류가 발생합니다.

Iterable 자료형을 위한 빌트인

len(object)

전달된 iterable 객체의 길이를 반환합니다. 정확히는 해당 객체가 가지고 있는 매직 메소드인 __len__을 호출합니다. 따라서 __len__ 메소드만 가지고 있으면 iterable 자료형이 아니더라도 오류가 발생하지 않습니다. 매직 메소드에 대한 설명은 여기에 나와 있습니다.

reversed(sequence)

전달된 sequence 객체를 역방향으로 뒤집은 iterator를 반환합니다.

sorted(iterable, cmp=None, key=None, reverse=False)

전달된 iterable 객체를 정렬하여 반환합니다.

enumerate(iterable)

전달된 iterable 객체를 이용해 0으로 시작하는 카운트와 값으로 이루어진 튜플로 구성된 iterable로 리턴합니다.

Python 공식 레퍼런스 에서 기타 빌트인 함수들을 더 알아볼 수 있습니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

[Python] 클래스의 상속  (0) 2018.06.30
[Python] 객체지향  (0) 2018.06.29
[Python] 값으로서의 함수와 lambda  (0) 2018.06.27
[Python] docstring  (0) 2018.06.26
[Python] 함수  (0) 2018.06.25

+ Recent posts