“ 점프 투 파이썬” 위키독스를 공부하며 요약한 글입니다.
“점프 투 파이썬” 도서는 기본 문법을 정립하는데 매우 큰 도움이 되었습니다.
누군가 파이썬을 처음 공부한다고 하면, 과감히 이 책을 추천해주고 싶습니다.
Python 기본 문법이 조금 더 기억에 남았으면 하는 바람에서 이 블로그에 기록을 하였습니다.
또한, 필요할 때 CheatSheet로써 사용할 수 도 있겠네요.
📗 자료형#
f 문자열 Formatting 표현식 지원#
>>> count = 3
>>> f'I have {count+3} apples.'
'I have 6 apples.'
딕셔너리 사용#
>>> x = {'name':'Ruff', 'year':1990}
>>> f'My name is {x["name"]}. I was born in {x["year"]}.'
'My name is Ruff. I was born in 1996.'
왼쪽 공백 지우기: lstrip#
>>> hey = " hello "
>>> hey.lstrip()
'hello '
오른쪽 공백 지우기: rstrip#
>>> hey = " hello "
>>> hey.rstrip()
' hello'
양쪽 공백 지우기: strip#
>>> a = " hi "
>>> a.strip()
'hi'
리스트 요소 삭제: del#
>>> test = [4, 5, 6]
>>> del test[2]
>>> test
[4, 5]
>>> test = [4, 5, 6, 7, 8, 9, 10, 11]
>>> del test[1:4]
>>> test
[4, 8, 9, 10, 11]
리스트에 요소 삽입: insert()#
>>> test = [4, 5, 6]
>>> test.insert(0, 10)
>>> test
[10, 4, 5, 6]
리스트 요소 제거: remove()#
>>> test = [1, 2, 3, 4, 5, 6]
>>> test.remove(2)
>>> test
[1, 3, 4, 5, 6]
리스트 요소 꺼내기: pop()#
>>> test = [1, 2, 3, 4, 5, 6]
>>> test.pop()
6
>>> test
[1, 2, 3, 4, 5]
리스트 확장: extend()#
>>> test = [3, 4, 5]
>>> test.extend([6, 7])
>>> test
[3, 4, 5, 6, 7]
>>> test222 = [6, 7]
>>> test.extend(test222)
>>> test
[3, 4, 5, 6, 7, 6, 7]
튜플#
t1 = ()
t2 = (1,)
t3 = (1, 2, 3)
t4 = 1, 2, 3
t5 = ('a', 'b', ('ab', 'cd'))
딕셔너리 Key 리스트 조회: keys()#
>>> a = {'name': 'ruff', 'phone': '010-1111-2222', 'birth': '0102'}
>>> a.keys()
dict_keys(['name', 'phone', 'birth'])
>>> for k in a.keys():
... print(k)
...
name
phone
birth
Value 리스트 만들기: values()#
>>> a.values()
dict_values(['ruff', '010-1111-2222', '0102'])
Key, Value 쌍 얻기: items()#
>>> a.items()
dict_items([('name', 'pey'), ('phone', '010-0000-1111'), ('birth', '0406')])
특정 Key의 딕셔너리 내 존재 여부 확인: in#
>>> c = {'name':'pey', 'phone':'010-0000-1111', 'birth': '0406'}
>>> 'name' in c
True
>>> 'email' in c
False
집합#
>>> set1 = set(['a', 1, 3])
>>> set1
{1, 3, 'a'}
>>> set2 = set("name")
>>> set2
{'m', 'e', 'a', 'n'}
교집합, 합집합, 차집합#
>>> x1 = set([1, 2, 3, 4, 5, 6])
>>> x2 = set([4, 5, 6, 7, 8, 9])
# 교집합
>>> x1 & x2
{4, 5, 6}
>>> x1.intersection(x2)
{4, 5, 6}
# 합집합
>>> x1 | x2
{1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> x1.union(x2)
{1, 2, 3, 4, 5, 6, 7, 8, 9}
# 차집합
>>> x1 - x2
{1, 2, 3}
>>> x2 - x1
{8, 9, 7}
>>> x1.difference(x2)
{1, 2, 3}
>>> x2.difference(x1)
{8, 9, 7}
📗 변수#
리스트 얕은 복사#
=
이용하여 대입 시 얕은 복사가 되어 동일한 리스트(메모리)를 가리킴
>>> a = [1,2,3]
>>> b = a
>>> a[1] = 4
>>> a
[1, 4, 3]
>>> b
[1, 4, 3]
리스트 깊은 복사#
데이터를 통째로 복사, 별도의 메로리 차지
1. [:] 이용하기#
리스트 전체를 가리키는 [:]을 사용해서 복사하는 방법이다. a 리스트 값을 바꾸더라도 b 리스트에는 영향이 없다.
>>> a = [1, 2, 3]
>>> b = a[:]
>>> a[1] = 4
>>> a
[1, 4, 3]
>>> b
[1, 2, 3]
2. copy 모듈 이용하기#
b = copy(a)는 b = a[:]과 동일하다. 두 변수의 값은 같지만, 서로 다른 객체를 가리키고 있는지 확인해 보자.
>>> from copy import copy
>>> a = [1, 2, 3]
>>> b = copy(a)
>>> b is a
False
리스트 자료형의 자체 함수인 copy 함수를 사용해도 동일한 결과를 얻을 수 있다.
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> b is a
False
튜플 생성#
다음과 같이 튜플로 a, b에 값을 대입할 수 있다.
>>> a, b = ('python', 'life')
>>> (a, b) = 'python', 'life'
>>> [a, b] = ['python', 'life']
>>> a = b = 'python'
Swap 방법
>>> a = 3
>>> b = 5
>>> a, b = b, a
>>> a
5
>>> b
3
📗 반복문 : 리스트 컴프리헨션#
리스트 안에 for 문을 포함하여 직관적으로 표현합니다.
[표현식 for 항목 in 반복_가능_객체 if 조건문]
>>> a = [1, 2, 3, 4]
>>> result = [num * 3 for num in a]
>>> print(result)
[3, 6, 9, 12]
짝수에만 3을 곱하기
>>> a = [1, 2, 3, 4]
>>> result = [num * 3 for num in a if num % 2 == 0]
>>> print(result)
[6, 12]
복잡한 예제 - 구구단의 모든 결과를 리스트에 담기:
>>> result = [x * y for x in range(2, 10) for y in range(1, 10)]
>>> print(result)
[2, 4, 6, 8, 10, 12, 14, 16, 18, 3, 6, 9, 12, 15, 18, 21, 24, 27, 4, 8, 12, 16,
20, 24, 28, 32, 36, 5, 10, 15, 20, 25, 30, 35, 40, 45, 6, 12, 18, 24, 30, 36, 42
, 48, 54, 7, 14, 21, 28, 35, 42, 49, 56, 63, 8, 16, 24, 32, 40, 48, 56, 64, 72,
9, 18, 27, 36, 45, 54, 63, 72, 81]
📗 함수#
여러 개의 입력값#
매개변수 앞에 *
을 붙여서 모든 입력 값을 튜플로 받습니다.
def add_all(*args):
result = 0
for i in args:
result += i
return result
>>> result = add_all(1, 2, 3)
>>> print(result)
6
>>> result = add_all(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(result)
55
매개변수 앞에 *
을 붙이면 입력값을 튜플로 만들어 줍니다.
def add_mul(choice, *args):
if choice == "add":
result = 0
for i in args:
result += i
elif choice == "mul":
result = 1
for i in args:
result *= i
return result
>>> result = add_mul('add', 1, 2, 3, 4, 5)
>>> print(result)
15
>>> result = add_mul('mul', 1, 2, 3, 4, 5)
>>> print(result)
120
가변 인자 *args
#
여러 개의 인자를 받을 수 있습니다.
def sum_nums(*args):
return sum(args)
>> print(sum_nums(1, 2, 3))
# Output: 6
키워드 매개변수 **kwargs
#
매개변수 앞에 **
을 붙여 키워드 매개변수를 받을 수 있습니다.
**kwargs
는 키워드 인수를 딕셔너리로 받아줍니다. (key:value)
def print_kwargs(**kwargs):
print(kwargs)
>>> print_kwargs(a=1)
{'a': 1}
>>> print_kwargs(name='foo', age=3)
{'age': 3, 'name': 'foo'}
함수의 리턴값 항상 하나#
함수는 여러 값을 리턴해도 하나의 튜플로 리턴합니다.
def add_and_mul(a, b):
return a+b, a*b
>>> result = add_and_mul(1, 2)
>>> print(result)
(3, 2)
Default 값이 있는 매개변수#
Default 값이 있는 인자는 없는 인자 뒤에 와야 합니다.
def iam(name, man=True, age=20):
print(f"나의 이름은 {name}입니다.")
print(f"나이는 {age}살입니다.")
if man:
print("남자입니다.")
else:
print("여자입니다.")
global
명령어 사용하기#
함수 내부에서 전역 변수를 수정할 수 있습니다.
a = 1
def vartest():
global a
a += 1
vartest()
>>> print(a)
2
lambda
예약어#
함수를 간결하게 만들 때 사용합니다.
add = lambda a, b: a + b
>>> result = add(3, 4)
>>> print(result)
7
lambda
로 만든 함수는 return
명령어 없이도 표현식의 결괏값을 리턴합니다. 이는 다음과 같은 def
함수와 동일한 역할을 합니다.
def add(a, b):
return a + b
>>> result = add(3, 4)
>>> print(result)
7
📗 입출력#
한 줄에 결과 출력하기#
print
함수의 end
매개변수를 사용하면 출력 후 줄바꿈을 하지 않고 이어서 출력할 수 있습니다.
for i in range(10):
print(i, end=' ')
# Output
# 0 1 2 3 4 5 6 7 8 9
파일 읽기#
with open("hello.txt", "r") as f:
data = f.read()
print(data)
파일 읽기 : readline 함수 이용하기#
모든 줄을 읽기 위해 while
루프를 사용합니다.
with open("hello.txt", "r") as f:
while True:
line = f.readline()
if not line:
break
print(line)
파일 읽기 : readlines 함수 사용하기#
파일의 모든 줄을 읽어 리스트로 리턴합니다.
with open("hello.txt", "r") as f:
for line in lines:
print(line)
파일 읽기 : read 함수 사용하기#
파일의 내용을 모두 읽습니다.
with open("hello.txt", "r") as f:
data = f.read()
print(data)
파일 객체를 for 문과 함께 사용#
with open("hello.txt", "r") as f:
for line in f:
print(line)
파일 쓰기#
with open("foo.txt", "w") as f:
f.write("Life is too short, you need python")
파일 경로와 슬래시#
파이썬 코드에서 파일 경로를 표시할 때 슬래시(/
)나 이스케이프 문자(\\
)를 사용할 수 있습니다.
"C:/ruff/hello.txt"
"C:\\\\ruff\\\\hello.txt"
r"C:\\ruff\\hello.txt"
파일에 새로운 내용 추가하기#
파일을 추가 모드('a'
)로 열어 데이터를 추가합니다.
# add_data.py
f = open("C:/doit/새파일.txt", 'a')
for i in range(11, 20):
data = f"{i}번째 줄입니다.\\n"
f.write(data)
f.close()
with 문과 함께 사용하기#
with
문을 사용하여 파일을 열고 자동으로 닫습니다.
# file_with.py
with open("foo.txt", "w") as f:
f.write("Life is too short, you need python")
sys 모듈 사용하기#
sys
모듈을 사용하여 프로그램에 인수를 전달할 수 있습니다.
import sys
args = sys.argv[1:]
for i in args:
print(i)
실행 시 인수를 전달합니다.
python sys1.py arg1 arg2 arg3
# 출력:
# arg1
# arg2
# arg3
📗 클래스#
메소드의 self
인자#
클래스 메소드의 첫 번째 매개변수는 항상 객체 자신을 가리키는 self
입니다.
a.setdata(4, 2)
를 호출하면 self
에는 객체 a
가 자동으로 전달됩니다.
class FourCal:
def setdata(self, first, second):
self.first = first
self.second = second
# 클래스 인스턴스 생성
>>> a = myFunc()
# 메소드 호출
>>> a.setdata(4, 2)
생성자#
생성자는 객체 생성 시 초깃값을 설정하는 역할을 합니다.
class FourCal:
def __init__(self, first, second):
self.first = first
self.second = second
def add(self):
return self.first + self.second
def mul(self):
return self.first * self.second
def sub(self):
return self.first - self.second
def div(self):
return self.first / self.second
>>> a = FourCal(4, 2)
>>> a.add()
6
>>> a.div()
2.0
클래스 상속#
상속을 통해 기존 클래스의 기능을 확장합니다.
class FourCal:
def setdata(self, first, second):
self.first = first
self.second = second
class myCal(FourCal):
def pow(self):
return self.first ** self.second
>>> a = myCal(4, 2)
>>> a.pow()
16
>>> a.add()
6
메서드 오버라이딩#
부모 클래스의 메서드를 자식 클래스에서 재정의합니다.
class myCal(FourCal):
def div(self):
if self.second == 0:
return 0
return self.first / self.second
>>> a = myCal(4, 0)
>>> a.div()
0
클래스 변수#
클래스 변수는 클래스 내 모든 인스턴스에서 공유됩니다.
class Family:
lastname = "김"
>>> Family.lastname
'김'
>>> a = Family()
>>> b = Family()
>>> a.lastname
'김'
>>> b.lastname
'김'
>>> Family.lastname = "박"
>>> a.lastname
'박'
>>> b.lastname
'박'
클래스 변수와 동일한 이름의 객체 변수를 생성할 수 있습니다. 객체 변수는 클래스 변수와 독립적으로 존재합니다.
>>> a.lastname = "최"
>>> a.lastname
'최'
>>> Family.lastname
'박'
>>> b.lastname
'박'
📗 모듈 & 패키지#
모듈 만들기#
모듈은 파이썬 파일로, 함수와 변수를 포함할 수 있습니다.
이 파일을 C:\\ruff
디렉터리에 저장합니다.
def add(a, b):
return a + b
def sub(a, b):
return a - b
모듈 불러오기#
모듈을 사용하려면 import
명령어를 사용합니다.
C:\\Users\\ruff>cd C:\\ruff
C:\\ruff>python
>>> import mod1
>>> print(mod1.add(3, 4))
7
>>> print(mod1.sub(4, 2))
2
모듈의 함수를 직접 import하여 사용할 수 있습니다. 함수 이름 앞에 모듈 이름을 붙이지 않아도 됩니다. 기존의 함수명과 중복되지 않도록 합니다.
>>> from mod1 import add, sub
>>> add(3, 4)
7
>>> sub(4, 2)
2
모든 함수를 import하려면 *
를 사용합니다.
>>> from mod1 import *
if __name__ == "__main__"
의미#
모듈이 직접 실행될 때만 코드가 실행됩니다. 이렇게 하면 모듈이 import될 때는 실행되지 않습니다.
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))
클래스나 변수 등을 포함한 모듈#
모듈에는 함수 외에도 클래스와 변수를 포함할 수 있습니다.
PI = 3.141592
class Math:
def solv(self, r):
return PI * (r ** 2)
def add(a, b):
return a + b
모듈 불러오기#
import mod2
result = mod2.add(3, 4)
print(result)
다른 디렉터리에 있는 모듈 불러오기#
sys.path.append
사용하기#
import sys
sys.path.append("C:/ruff/modules")
import mymod
print(mymod.add(3, 4))
PYTHONPATH
환경 변수 사용하기#
C:\\ruff>set PYTHONPATH=C:\\ruff\\modules
C:\\ruff>python
>>> import mymod
>>> print(mymod.add(3, 4))
7
패키지 만들기#
파이썬에서 패키지(packages)란 관련 있는 모듈의 집합을 말합니다. 패키지는 파이썬 모듈을 계층적(디렉터리 구조)으로 관리할 수 있게 해줍니다.
패키지 구조#
game/
__init__.py
sound/
__init__.py
echo.py
graphic/
__init__.py
render.py
패키지 만들기#
디렉터리와 파일을 생성합니다.
echo.py
파일에 함수를 작성합니다.# echo.py def echo_test(): print("echo")
render.py
파일에 함수를 작성합니다.# render.py def render_test(): print("render")
PYTHONPATH
를 설정하고 패키지를 사용합니다.C:\\> set PYTHONPATH=C:/ruff C:\\> python >>> from game.sound import echo >>> echo.echo_test() echo
패키지 사용#
이와 같이 패키지를 사용하면 모듈을 체계적으로 관리하고 재사용성을 높일 수 있습니다.
C:\\> set PYTHONPATH=C:/ruff
C:\\> python
>>> import game
Initializing game ...
>>> game.print_version_info()
The version of this game is 3.5.
>>> from game.sound import echo
>>> echo.echo_test()
echo
>>> from game.graphic.render import render_test
>>> render_test()
render
echo
__init__.py
의 용도#
패키지 변수 및 함수 정의
# C:/ruff/game/__init__.py
# 패키지 내 모듈을 미리 Import
from .graphic.render import render_test
# 패키지 변수 및 함수 정의
VERSION = 3.5
def print_version_info():
print(f"The version of this game is {VERSION}.")
# C:/ruff/game/__init__.py
print("Initializing game ...")
# 여기에 초기화 코드 작성
__all__
#
여기에서 __all__
이 의미하는 것은 sound 디렉터리에서 *
를 사용하여 import할 경우, 이곳에 정의된 echo 모듈만 import된다는 의미입니다.
__all__
과 상관없이 무조건 import되는 경우는 from a.b.c import *
에서 from의 마지막 항목인 c
가 모듈인 때이다.
# C:/ruff/game/sound/__init__.py
__all__ = ['echo']
relative 패키지#
다른 디렉터리의 모듈을 relative하게 Import 할 수 있습니다.
# render.py
from ..sound.echo import echo_test
def render_test():
print("render")
echo_test()
📗 유니코드#
파이썬에서 사용하는 문자열은 모두 유니코드 문자열입니다. 파이썬 3부터 모든 문자열을 유니코드로 처리합니다.
인코딩 (Encoding)#
유니코드 문자열을 바이트 문자열로 변환하는 과정을 인코딩이라고 합니다.
>>> a = "Hello Python"
>>> b = a.encode('utf-8')
>>> b
b'Hello Python'
>>> type(b)
<class 'bytes'>
인코딩 방식을 생략하면 기본값인 utf-8로 동작합니다.
한글 문자열을 아스키 방식으로 인코딩하려고 하면 오류가 발생합니다.
>>> a = "한글"
>>> a.encode("ascii")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
다른 인코딩 방식(e.g., euc-kr)을 사용하면 인코딩할 수 있습니다.
>>> a = '한글'
>>> a.encode('euc-kr')
b'\\xc7\\xd1\\xb1\\xdb'
>>> a.encode('utf-8')
b'\\xed\\x95\\x9c\\xea\\xb8\\x80'
디코딩 (Decoding)#
인코딩한 바이트 문자열을 유니코드 문자열로 변환하는 과정을 디코딩이라고 합니다.
>>> a = '안녕'
>>> b = a.encode('euc-kr')
>>> b.decode('euc-kr')
'안녕'
잘못된 인코딩 방식으로 디코딩하려고 하면 오류가 발생합니다.
>>> b.decode('utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc7 in position 0: invalid continuation byte
입출력과 인코딩#
인코딩과 관련된 입출력 작업을 할 때 다음 규칙을 지키면 좋습니다.
- 입력으로 받은 바이트 문자열은 되도록 빨리 유니코드 문자열로 디코딩합니다.
- 함수나 클래스 등에서는 유니코드 문자열만 사용합니다.
- 입력에 대한 결과를 전송하는 마지막 부분에서만 유니코드 문자열을 바이트 문자열로 인코딩하여 반환합니다.
다음은 euc-kr 방식으로 작성한 파일을 읽고 수정하여 저장하는 예제입니다.
# 1. euc-kr로 작성된 파일 읽기
with open('euc_kr.txt', encoding='euc-kr') as f:
data = f.read() # 유니코드 문자열
# 2. unicode 문자열로 프로그램 수행하기
data = data + "\\n" + "추가 문자열"
# 3. euc-kr로 수정된 문자열 저장하기
with open('euc_kr.txt', encoding='euc-kr', mode='w') as f:
f.write(data)
소스 코드의 인코딩#
소스 코드의 인코딩이란 소스 코드 파일이 어떤 방식으로 인코딩되었는지를 뜻합니다. 소스 코드 가장 위에 다음과 같은 문장을 넣어 인코딩 방식을 명시할 수 있습니다.
# -*- coding: utf-8 -*-
파이썬 3.0부터는 기본값이 utf-8이므로 이 문장은 생략할 수 있습니다. 만약 소스 코드를 euc-kr로 인코딩했다면 다음과 같이 작성해야 합니다.
# -*- coding: euc-kr -*-
잘못된 인코딩으로 명시하면 문자열 처리 부분에서 인코딩 관련 오류가 발생할 수 있습니다.
📗 클로저#
클로저는 함수 안에 내부 함수를 구현하고, 그 내부 함수를 리턴하는 함수입니다.
외부 함수는 자신의 변숫값 등을 내부 함수에 전달할 수 있습니다.
클로저 사용#
def multiply(m):
def wrapper(n):
return m * n
return wrapper
if __name__ == "__main__":
multiply3 = multiply(3)
multiply5 = multiply(5)
print(multiply3(10)) # 30 출력
print(multiply5(10)) # 50 출력
위 로직을 클래스로 구현
class Multiply:
def __init__(self, m):
self.m = m
def multiply(self, n):
return self.m * n
if __name__ == "__main__":
multiply3 = Multiply(3)
multiply5 = Multiply(5)
print(multiply3.multiply(10)) # 30 출력
print(multiply5.multiply(10)) # 50 출력
class Multiply:
def __init__(self, m):
self.m = m
def __call__(self, n):
return self.m * n
if __name__ == "__main__":
multiply3 = Multiply(3)
multiply5 = Multiply(5)
print(multiply3.multiply(10)) # 30 출력
print(multiply5.multiply(10)) # 50 출력
📗 데코레이터#
기존 함수에 기능을 추가하는 클로저입니다.
실행 시간 측정#
import time
def execfunc():
start = time.time()
print("EXECUTE A FUNCTION")
end = time.time()
print(f"RUN TIME: {end-start}초")
execfunc()
클로저를 이용한 데코레이터#
import time
def elapsed(original_func):
def wrapper():
start = time.time()
result = original_func()
end = time.time()
print("함수 수행시간: %f 초" % (end - start))
return result
return wrapper
def myfunc():
print("함수가 실행됩니다.")
decorated_myfunc = elapsed(myfunc)
decorated_myfunc()
데코레이터 적용#
# decorator.py
import time
def elapsed(original_func):
def wrapper():
start = time.time()
result = original_func()
end = time.time()
print("함수 수행시간: %f 초" % (end - start))
return result
return wrapper
@elapsed
def myfunc():
print("함수가 실행됩니다.")
myfunc()
매개변수가 있는 함수에 데코레이터 적용#
import time
def elapsed(original_func):
def wrapper(*args, **kwargs):
start = time.time()
result = original_func(*args, **kwargs)
end = time.time()
print("함수 수행시간: %f 초" % (end - start))
return result
return wrapper
@elapsed
def myfunc(msg):
""" 데코레이터 확인 함수 """
print(f"{msg}을 출력합니다.")
myfunc("I need python")
📗 타입 어노테이션#
변수 타입을 명시하여 코드의 가독성과 유지보수성을 높입니다.
def add(a: int, b: int) -> int:
return a + b
📗 이터레이터#
이터레이터는 next 함수 호출 시 계속 그다음 값을 리턴하는 객체입니다.
더 리턴할 값이 없다면 StopIteration 예외가 발생하게 됩니다.
>>> a = ['a', 'b', 'c']
>>> ia = iter(a)
>>> type(ia)
<class 'list_iterator'>
>>> next(ia)
'a'
>>> next(ia)
'b'
>>> next(ia)
'c'
>>> next(ia)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
이터레이터는 for문이나 next로 그 값을 한 번 읽으면 그 값을 다시는 읽을 수 없습니다.
>>> a = [1, 2, 3]
>>> ia = iter(a)
>>> for i in ia:
... print(i)
...
1
2
3
>>> for i in ia:
... print(i)
__iter__
& __next__
#
__iter__
메소드를 구현하면 해당 클래스로 생성한 객체는 반복 가능한 객체가 됩니다.
__iter__
메소드는 반복 가능한 객체를 리턴해야 하며 보통 클래스의 객체를 의미하는 self를 리턴합니다.
__iter__
메서드를 구현할 경우 반드시 __next__
함수를 구현해야 합니다.
__next__
메서드는 반복 가능한 객체의 값을 차례대로 반환하는 역할을 하며 for 문을 수행하거나 next 함수 호출 시 수행됩니다.
아래는 MyIterator 객체를 생성할 때 전달한 data를 하나씩 리턴하고 더는 리턴할 값이 없으면 StopIteration 예외를 발생시킵니다.
class MyIter:
def __init__(self, data):
self.data = data
self.position = 0
def __iter__(self):
return self
def __next__(self):
if self.position >= len(self.data):
raise StopIteration
result = self.data[self.position]
self.position += 1
return result
if __name__ == "__main__":
i = MyIter([6,7,8])
for num in i:
print(num)
📗 제너레이터#
이터레이터를 생성해 주는 함수입니다.
next 함수 호출 시 그 값을 차례대로 얻을 수 있으며, 이러한 결과를 반환하고자 return 대신 yield 키워드를 사용합니다.
차례를 기억하고 있습니다.
>>> def my_generator():
... yield 4
... yield 5
... yield 6
...
>>> a = my_generator()
>>> type(a)
<class 'generator'>
>>> next(a)
4
>>> next(a)
5
>>> next(a)
6
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
제너레이터 표현식#
def my_square():
for i in range(1, 1000):
result = i * i
yield result
sq = my_square()
print(next(sq))
print(next(sq))
print(next(sq))
위 함수는 아래와 같이 간단히 표현 가능합니다.
리스트 컴프리헨션과 비슷하지만, 튜플을 이용한 점이 다릅니다.
sq = (i * i for i in range(1, 1000))
간단한 경우라면 이터레이터 클래스보다는 제너레이터 표현식을 사용하는 것이 훨씬 간편하고 이해하기 쉽습니다.
제너레이터 활용#
lazy evaluation 되어 list_job에는 이터레이터가 담겨지고, 아래 next()로 호출됩니다.
import time
def my_job():
print("job start")
time.sleep(1)
return "done"
list_job = (my_job() for i in range(5))
print(next(list_job))
출력
job start
done
아래 이터레이터는 5번 실행되어 결과가 변수에 담기게 되고, print에서 출력만 하게됩니다.
import time
def longtime_job():
print("job start")
time.sleep(1) # 1초 지연
return "done"
list_job = [longtime_job() for i in range(5)]
print(list_job[0])
출력
job start
job start
job start
job start
job start
done
📗 정규식#
컴파일된 패턴 객체를 사용하여 문자열 검색을 수행할 수 있습니다. 컴파일된 패턴 객체는 다음과 같은 4가지 메서드를 제공합니다.
match()
: 문자열의 처음부터 정규식과 매치되는지 조사합니다.
search()
: 문자열 전체를 검색하여 정규식과 매치되는지 조사합니다.
findall()
: 정규식과 매치되는 모든 문자열(substring)을 리스트로 리턴합니다.
finditer()
: 정규식과 매치되는 모든 문자열(substring)을 반복 가능한 객체로 리턴합니다.
match
와search
는 정규식과 매치될 때match
객체를 리턴하고 매치되지 않을 때는None
을 리턴합니다.
match#
match
메서드는 문자열의 처음부터 정규식과 매치되는지 조사합니다.
>>> import re
>>> pattern = re.compile("[a-z]+")
>>> m = pattern .match("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
>>> m = pattern.match("3 python")
>>> print(m)
None
match
의 결과로 match
객체 또는 None
을 리턴하기 때문에 파이썬 정규식 프로그램은 보통 다음과 같은 흐름으로 작성합니다.
pattern = re.compile('정규표현식')
m = p.match('조사할 문자열')
if m:
print('Match found:', m.group())
else:
print('No match')
search#
search
메서드는 문자열 전체를 검색하여 정규식과 매치되는지 조사합니다.
>>> m = pattern.search("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
>>> m = pattern.search("3 python")
>>> print(m)
<re.Match object; span=(2, 8), match='python'>
findall#
findall
은 정규식과 매치되는 모든 값을 찾아 리스트로 리턴합니다.
>>> import re
>>> pattern = re.compile("[a-z]+")
>>> result = pattern.findall("Hello python hi")
>>> print(result)
['ello', 'python', 'hi']
finditer#
finditer
는 findall
과 동일하지만, 그 결과로 반복 가능한 객체(iterator object)를 리턴합니다.
>>> reresult = pattern.finditer("Hello python hi")
>>> for r in result:
... print(r)
...
<re.Match object; span=(1, 5), match='ello'>
<re.Match object; span=(6, 12), match='python'>
<re.Match object; span=(13, 15), match='hi'>
match 객체의 메서드#
group
: 매치된 문자열을 리턴합니다.
start
: 매치된 문자열의 시작 위치를 리턴합니다.
end
: 매치된 문자열의 끝 위치를 리턴합니다.
span
: 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 리턴합니다.
>>> m = pattern.match("aaabbbccc")
>>> m.group()
'aaabbbccc'
>>> m.start()
0
>>> m.end()
6
>>> m.span()
(0, 6)
모듈 직접 실행#
>>> m = re.match('[a-z]+', "python")
컴파일 옵션#
DOTALL(S)
:.
(dot)이 줄바꿈 문자를 포함해 모든 문자와 매치될 수 있게 합니다.IGNORECASE(I)
: 대소문자에 관계없이 매치될 수 있게 합니다.MULTILINE(M)
: 여러 줄과 매치될 수 있게 합니다.^
,$
메타 문자 사용과 관계 있는 옵션입니다.VERBOSE(X)
: verbose 모드를 사용할 수 있게 합니다. 정규식을 보기 편하게 만들 수 있고 주석 등을 사용할 수 있게 됩니다.
>>> p1 = re.compile('a.b', re.DOTALL)
>>> m = p1.match('a\\nb')
>>> print(m)
<re.Match object; span=(0, 3), match='a\\nb'>
>>> p = re.compile('[a-z]+', re.I)
IGNORECASE(I)#
>>> p = re.compile('[a-z]+', re.I)
>>> m = p.match('aBCd')
>>> print(m)
<re.Match object; span=(0, 4), match='aBCd'>
MULTILINE, M#
각 라인의 처음으로 인식시키고 싶은 경우:
import re
p = re.compile("^python\\s\\w+", re.MULTILINE)
data = """python 12
python 34
I need python
python 56"""
print(p.findall(data))
# Output
# ['python 12', 'python 34', 'python 56']
VERBOSE, X#
문자열에 사용된 화이트스페이스는 컴파일할 때 제거됩니다 (단, [ ] 안에 사용한 화이트스페이스는 제외).
charref = re.compile(r"""
&[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
역슬래시 문제#
\\\\section
정규식을 컴파일하려면 다음과 같이 작성해야 합니다.
>>> p = re.compile('\\\\abc')
결국 정규식 엔진에 \\\\
문자를 전달하려면 파이썬은 \\\\\\\\
처럼 역슬래시를 4개나 사용해야 합니다.
>>> p = re.compile('\\\\\\\\abc')
raw string 표현법을 사용하여 해결할 수 있습니다.
>>> p = re.compile(r'\\\\abc')
문자열 소비가 없는 메타 문자#
|
메타 문자는 or 과 동일한 의미로 사용됩니다.
>>> import re
>>> p = re.compile('Python|Hello')
>>> m = p.match('PythonHi')
>>> print(m)
<re.Match object; span=(0, 6), match='Python'>
\A
는 문자열의 처음과 매치된다는 것을 의미합니다.
^
메타 문자와 동일한 의미지만, re.MULTILINE
옵션을 사용할 경우에는 다르게 해석됩니다.
\Z
는 문자열의 끝과 매치된다는 것을 의미합니다.
\b
는 단어 구분자(word boundary)입니다.
>>> p = re.compile(r'\bvery\b')
>>> print(p.search('she is very cute'))
<re.Match object; span=(7, 11), match='very'>
\B
메타 문자는 \b
메타 문자와 반대의 경우입니다.
>>> p = re.compile(r'\Bvery\B')
>>> print(p.search('she is very cute'))
None
>>> print(p.search('sheisverycute'))
<re.Match object; span=(5, 9), match='very'>
그루핑#
그룹을 만들어 주는 메타 문자는 ()
입니다.
(ABC)+
>>> p = re.compile('(ABC)+')
>>> m = p.search('ABCABCABC GGG?')
>>> print(m)
<re.Match object; span=(0, 9), match='ABCABCABC'>
>>> print(m.group())
ABCABCABC
이름과 전화번호를 찾는 정규식
>>> p = re.compile(r"\w+\s+\d+[-]\d+[-]\d+")
>>> m = p.search("park 010-1234-1234")
이름만 추출
>>> p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
>>> m = p.search("kim 010-1234-1234")
>>> print(m.group(1))
kim
국번 추출
>>> p = re.compile(r"(\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group(3))
010
그룹 인덱스#
group(0)
: 매치된 전체 문자열
group(1)
: 첫 번째 그룹에 해당되는 문자열
group(2)
: 두 번째 그룹에 해당되는 문자열
group(n)
: n 번째 그룹에 해당되는 문자열
그루핑된 문자열 재참조#
>>> p = re.compile(r'(\b\w+)\s+\1')
>>> p.search('he is very very coool').group()
'very very'
그루핑된 문자열에 이름 붙이기#
(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)
그룹 이름을 참조
>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("kim 010-1234-1234")
>>> print(m.group("name"))
kim
재참조
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('he is very very coool').group()
'very very'
전방 탐색#
긍정형 전방 탐색
:
에 해당하는 부분에 긍정형 전방 탐색 기법을 적용하여 (?=:)
으로 변경하였습니다.
:
에 해당하는 문자열이 정규식 엔진에 의해 소비되지 않아(검색에는 포함되지만, 검색 결과에는 제외됨) 검색 결과에서는 :
이 제거된 후 리턴됩니다.
>>> p = re.compile(".+(?=:)")
>>> m = p.search("<http://google.com>")
>>> print(m.group())
http
부정형 전방 탐색
확장자가 bat가 아닌 것 추출
.*[.](?!bat$).*$
예제
.*[.](?!bat$|exe$).*$
문자열 바꾸기#
>>> p = re.compile('(red|black|green)')
>>> p.sub('colour', 'black hat and red shoes')
'colour hat and colour shoes'
바꾸기 횟수 제어
>>> p.sub('colour', 'black hat and red shoes', count=1)
'colour hat and red shoes'
📗 라이브러리#
tempfile#
mkstemp()#
중복되지 않는 임시 파일의 이름을 무작위로 생성하여 리턴합니다.
import tempfile
filename = tempfile.mkstemp()
print(filename)
# Output
# ('C:\\\\WINDOWS\\\\TEMP\\\\tmp1a2b3c', 3)
TemporaryFile()#
임시 저장 공간으로 사용할 파일 객체를 리턴합니다. 기본적으로 바이너리 쓰기 모드(wb)로 열리며, f.close()
가 호출되면 자동으로 삭제됩니다.
import tempfile
f = tempfile.TemporaryFile()
f.close()
traceback#
오류 추적 결과를 문자열로 리턴하는 traceback.format_exc()
를 사용하여 어디에서 오류가 발생했는지 확인할 수 있습니다.
# traceback_test.py
def a():
return 1 / 0
def b():
a()
def main():
try:
b()
except:
print("오류가 발생했습니다.")
print(traceback.format_exc())
main()
json#
파일을 딕셔너리로 읽기
import json
with open('myinfo.json') as f:
data = json.load(f)
print(type(data))
print(data)
# Output
# <class 'dict'>
# {'name': '홍길동', 'birth': '0525', 'age': 30}
딕셔너리를 파일로 저장
import json
data = {'name': '김철수', 'birth': '0203', 'age': 23}
with open('myinfo.json', 'w') as f:
json.dump(data, f)
딕셔너리를 JSON 문자열로 변환
import json
d = {'name': '김철수', 'birth': '0203', 'age': 23}
json_data = json.dumps(d, ensure_ascii=False)
print(json_data)
# Output
# {'name': '김철수', 'birth': '0203', 'age': 23}
JSON 문자열을 딕셔너리로 변환
import json
json_data = '{"name": "김철수", "birth": "0203", "age": 23}'
data = json.loads(json_data)
print(data)
# Output
# {'name': '김철수', 'birth': '0203', 'age': 23}
JSON 문자열 보기 좋게 출력
import json
d = {'name': '김철수', 'birth': '0203', 'age': 23}
print(json.dumps(d, indent=2, ensure_ascii=False))
# Output
# {
# "name": "김철수",
# "birth": "0203",
# "age": 23
# }
urllib#
URL을 읽고 분석하는 모듈입니다.
특정 페이지를 파일로 저장#
import urllib.request
def get_pages(page):
resource = f'<https://test-site.com/{page}>'
with urllib.request.urlopen(resource) as s:
with open(f'skitttles_{page}.html', 'wb') as f:
f.write(s.read())
get_pages("posts/ssrf_curl_url_globbing/")
SSL 인증서 검증 비활성화#
SSL 오류가 발생할 경우 다음과 같이 SSL 인증서 검증을 비활성화할 수 있습니다.
import urllib.request
import ssl
context = ssl._create_unverified_context()
def get_pages(page):
resource = f'<https://test-site.com/{page}>'
with urllib.request.urlopen(resource, context=context) as s:
with open(f'skitttles_{page}.html', 'wb') as f:
f.write(s.read())
get_pages("posts/ssrf_curl_url_globbing/")
threading#
스레드 프로그래밍을 통해 한 프로세스 안에서 여러 작업을 동시에 수행할 수 있습니다.
스레드의 join()
함수를 사용하면 해당 스레드가 종료될 때까지 기다립니다.
import time
import threading
def long_task():
for i in range(5):
time.sleep(1)
print(f"working: {i}")
print("Start")
threads = []
for i in range(5):
t = threading.Thread(target=long_task)
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join() # 모든 스레드가 종료될 때까지 기다림
print("End")