Introducing Python(처음 시작하는 파이썬)
Chapter 8. 흘러가는 데이터
* Python Reference (URL)
* Python File Methods (URL)
8.1 FIle Input/Output (파일 입출력)
* 파일 열기
- open() Function
* 파일 읽기
- read() Function
- readline() Function
- readlines() Function
- seek() Function
- tell() Function
* 파일 쓰기
- write() Function
- print() Function
* 파일 닫기
- close() Function
\(\texttt{open()}\) Function (\(\texttt{open()}\) 함수)
open( filename, mode )
Return:
- 파일 객체를 리턴한다.
Arguments:
1) \(\texttt{filename}\)
- \(\texttt{filename}\)은 파일 이름을 의미하는 String이다.
2) \(\texttt{mode}\)
\(\texttt{mode}\)의 첫 번째 글자 | Description | \(\texttt{mode}\)의 두 번째 글자 | Description |
\(\texttt{r}\) | 파일 읽기 | \(\texttt{t}\) (Default) |
Text Type |
\(\texttt{w}\) | 파일 쓰기 (파일이 존재하지 않으면 파일을 생성한다.) (파일이 이미 존재하면, 덮어쓴다.) |
\(\texttt{b}\) | Binary Type |
\(\texttt{x}\) | 파일 쓰기 (파일이 존재하지 않을 경우에만 해당된다.) * 중요한 파일이라 덮어쓰기에 유의해야 하는 경우 사용한다. |
||
\(\texttt{a}\) | 파일 추가하기 (파일이 존재하면 파일의 끝에서부터 쓴다.) |
ex) \(\texttt{wt}\) : 파일이 존재하지 않는다면, 새로 생성하여 텍스트 데이터를 쓰고, 이미 존재하는 파일이라면 덮어쓴다.
ex) \(\texttt{w}\) : \(\texttt{wt}\)와 동일하다. \(\texttt{t}\)는 Default이다.
ex) \(\texttt{xt}\) : 파일을 덮어쓰지 않게, 해당 파일이 존재하지 않을 경우 새로 생성하여 텍스트 데이터를 쓴다.
Example. 중요한 파일에 대한 덮어쓰기를 방지하기 위한 예외 처리 예시
>>> try:
... fout = open('relativity', 'xt')] # 중요한 파일 'relativity'
... fout.write('stomp stomp stomp')
... except FileExistsError:
... print('relativity already exists!. That was a close one.')
...
relativity already exists!. That was a close one.
* Context Manager (\(\texttt{with ~ as}\) 구문)
with <expr> as <fileobj>:
<file_operation1>
<file_operation2>
...
- \(\texttt{open()}\)을 통해 열린 파일은 해당 Scope가 종료되면, \(\texttt{close()}\)를 명시적으로 호출하지 않아도 자동으로 닫힌다.
- 또한, \(\texttt{with ~ as}\) 블록내에 파일 연산을 위치시켜서 \(\texttt{with ~ as}\) 블록이 종료되면 파일을 자동으로 닫게 할 수도 있다.
※ 즉, \(\texttt{with ~ as}\) 구문과 같은 Context Manager를 사용함으로써 원하는 타이밍에 정확하게 리소스를 할당하고 회수할 수 있다.
Example. Context Manager 사용 예시
>>> with open('relativity', 'wt') as fout:
... fout.write(poem)
\(\texttt{read()}\) Function (\(\texttt{read()}\) 함수)
fileobj.read( [size=-1] )
Return:
- 읽어들인 데이터의 Byte수를 리턴한다.
- EOF에 도달할 경우, Empty String('')를 리턴한다. (''는 False값으로 해석된다.)
Arguments:
1) size (Optional)
- 읽어들일 데이터의 최대 Byte수를 지정한다.
- Default값인 -1은 해당 파일의 전체를 의미한다.
※ 크기가 매우 큰 파일을 한꺼번에 읽어들일 때의 메모리 부족에 유의해야 한다.
Example. 크기가 큰 파일을 Chunk로 나누어 read하는 예시
>>> poem = ''
>>> fin = open('relativity', 'rt' )
>>> chunk = 100
>>> while True:
... fragment = fin.read(chunk)
... if not fragment: # read()가 EOF에 도달하면 Empty String(False)를 리턴한다.
... break
... poem += fragment
...
>>> fin.close()
readline() Function (readline() 함수)
fileobj.readline( [size=-1] )
- \(\texttt{fileobj}\) 파일의 데이터를 라인 단위로 읽어들인다.
※ 텍스트 파일의 Empty Line의 Length값은 1이고 이는 True값으로 해석된다. (Empty Line = '\n')
Return:
- 읽어들인 하나의 라인에 해당하는 데이터의 Byte수를 리턴한다.
- EOF에 도달할 경우, Empty String('')를 리턴한다. (''는 False값으로 해석된다.)
Arguments:
1) size (Optional)
- 읽어들일 한 라인의 최대 Byte수를 지정한다.
- Default값인 -1은 해당 라인의 전체를 의미한다.
Example. readline()을 사용하여 파일을 읽는 예시
>>> poem = ''
>>> fin = open('relativity', 'rt' )
>>> while True:
... line = fin.readline()
... if not line:
... break
... poem += line
...
>>> fin.close()
Example. iterator를 이용한 텍스트 파일 읽기 (readline()과 유사)
>>> poem = ''
>>> fin = open('relativity', 'rt' )
>>> for line in fin: # using iterator
... poem += line
...
>>> fin.close()
>>> len(poem)
150
- iterator는 readline()과 유사하게, 한 번에 한 라인씩 리턴한다.
\(\texttt{readlines()}\) Function (\(\texttt{readlines()}\) 함수)
fileobj.readlines( [size=-1] )
Return:
- 한 번의 호출로 fileobj 파일의 모든 라인을 읽어들여 한 라인으로 된 String을 리턴한다.
Arguments:
1) size (Optional)
- 읽어들일 데이터의 최대 Byte수를 지정한다.
- Default값인 -1은 해당 파일의 데이터 전체를 의미한다.
seek() Function (seek() 함수)
fileobj.seek( offset [, whence = os.SEEK_SET] )
- fileobj 파일에서 offset 바이트만큼 Read/Write Pointer의 위치를 이동시킨다.
- Binary File에서 Offset을 이동시키기에 적합한 함수이다.
- Text File에도 사용할 수는 있으나 ASCII Code와 같이 한 문자의 크기가 1Byte로 고정되어 있지 않다면,
Offset을 계산하기가 어려워 Logical Error를 발생시킬 확률이 높다.
Return:
- None을 리턴한다. (별다른 값을 리턴하지 않는다.)
Arguments:
1) \(\texttt{offset}\)
- Read/Write Pointer를 이동
(즉, Read/Write Pointer이다.)
- Byte단위이다.
2) \(\texttt{whence}\) (Optional)
- Standard Module os에 포함된 3개의 상수 중 하나로 옵션을 지정할 수 있다.
a) \(\texttt{os.SEEK_SET}\) (Default)
- 파일의 시작 지점에서 offset 바이트만큼 이동시킨다.
- 상수 0에 해당되는 값이다.
b) \(\texttt{os.SEEK_CUR}\)
- 현재 Read/Write Pointer가 가리키고 있는 위치에서 offset 바이트만큼 이동시킨다.
- 상수 1에 해당되는 값이다.
c) \(\texttt{os.SEEK_END}\)
- 파일의 끝 지점에서 offset 바이트만큼 이동시킨다. (offset값을 음수로 설정하면 뒤로 이동한다.)
- 상수 2에 해당되는 값이다.
tell() Function (tell() 함수)
fileobj.tell()
Return:
- fileobj 파일의 현재 Read/Write Pointer(File Stream)의 위치를 리턴한다.
\(\texttt{write()}\) Function (\(\texttt{write()}\) 함수)
fileobj.write( data )
- 파일 객체(fileobj)에 data를 쓴다.
- print() Function과 달리, write() Function은 스페이스, 줄바꿈을 자동으로 추가하지 않는다.
Return:
- 쓴 데이터의 Byte수를 리턴한다.
Example. 크기가 큰 String을 Chunk로 나누어 write하는 예시
>>> fout = open('relativity', 'wt')
>>> size = len(poem)
>>> offset = 0
>>> chunk = 100
>>> while True:
... if offset > size:
... break
... fout.write(poem[offset:offset+chunk])
... offset += chunk
...
100
50
>>> fout.close()
\(\texttt{print()}\) Function (\(\texttt{print()}\) 함수)
print( object(s) [, sep=' '] [, end='\n'] [, file=sys] [, flush=false] )
- object(들)을 String Type으로 변환하여, 출력한다.
Return:
- \(\texttt{print()}\)는 어떤 값도 리턴하지 않는다.
Arguments:
1) object(s)
2) sep (Optional)
- Seperator를 지정한다.
- Default는 ' '이다.
3) end (Optional)
- 출력의 끝을 지정한다.
- Default는 '\n'(Line Feed)이다.
4) file (Optional)
- 출력할 위치를 지정한다.
- Default는 sys(Console)이다.
5) flush (Optional)
- 출력에 대한 Flush 여부를 지정하는 Boolean 값이다.
- Default는 false이다. 즉, Flush하지 않는 것이 기본값이다.
\(\texttt{close()}\) Function (\(\texttt{close()}\) 함수)
fileobj.close()
- fileobj 파일을 닫는다.
Return:
- close() 함수는 별다른 값을 리턴하지 않는다.
8.2 Structured Text Files (구조화된 텍스트 파일)
- CSV (Comma-Separated Value)
- XML (Extensible Markup Language)
- HTML (HyperText Markup Language)
- JSON (JavaScript Object Notation)
- YAML (YAML Ain't Markup Language)
* XML, YAML보다 빠른 Binary Data Interchange Format
(Binary Format이기 때문에 Text Editor로 쉽게 편집할 수 없어 아래 Format들을 이용할 것이 권장된다.)
- Msgpack (URL)
- Protocol Buffers (URL)
- Avro (URL)
- Thrift (URL)
CSV (Comma-Separated Value)
- Separator(Delimiter)로 '\t'(Tab), ','(Comma), '|'(Vertical Bar)를 사용한다.
- 일반적으로, Standard Module csv를 사용하여 처리한다.
- OS마다, 개행문자가 다른데, UNIX와 Apple은 '\n', Microsoft는 '\r\n'을 사용한다.
- Column Name이 첫 Line에 위치할 수도 있다.
- csv Module에 포함된 reader(), writer() 함수를 사용하여 열은 콤마로 나누고, 행은 개행 문자로 나누어 읽고쓸 수 있다.
- csv Module에 포함된 DictReader(), DictWriter() 함수를 통해 열의 이름을 지정하여 읽고쓸 수 있다.
Example. csv.reader() 함수 활용 예시
>>> import csv
>>> with open('villains', 'rt') as fin: # context manager
... cin = csv.reader(fin)
... villains = [row for row in cin] # This uses a list comprehension
...
>>> print(villains)
[['Doctor', 'No'], ['Rosa', 'Klebb'], ['Mister', 'Big'],
['Auric', 'Goldfinger'], ['Ernst', 'Blofeld']]
Example. csv.writer() 함수 활용 예시
>>> import csv
>>> villains = [
... ['Doctor', 'No'],
... ['Rosa', 'Klebb'],
... ['Mister', 'Big'],
... ['Auric', 'Goldfinger'],
... ['Ernst', 'Blofeld'],
... ]
>>> with open('villains', 'wt') as fout: # a context manager
... csvout = csv.writer(fout)
... csvout.writerows(villains)
# 위 코드는 아래 내용이 저장된 'villains'파일을 생성한다.
Doctor,No
Rosa,Klebb
Mister,Big
Auric,Goldfinger
Ernst,Blofeld
Example. csv.DictReader() 함수 활용 예시
>>> import csv
>>> with open('villains', 'rt') as fin:
... cin = csv.DictReader(fin, fieldnames=['first', 'last'])
... villains = [row for row in cin]
...
>>> print(villains)
[{'last': 'No', 'first': 'Doctor'},
{'last': 'Klebb', 'first': 'Rosa'},
{'last': 'Big', 'first': 'Mister'},
{'last': 'Goldfinger', 'first': 'Auric'},
{'last': 'Blofeld', 'first': 'Ernst'}]
- csv.DictReader() 함수의 두 번째 Argument(fieldnames)를 생략하면,
csv파일의 첫 번째 Line의 값을 Dictionary의 Key로 사용한다.
Example. csv.DictWriter() 함수 활용 예시
import csv
villains = [
{'first': 'Doctor', 'last': 'No'},
{'first': 'Rosa', 'last': 'Klebb'},
{'first': 'Mister', 'last': 'Big'},
{'first': 'Auric', 'last': 'Goldfinger'},
{'first': 'Ernst', 'last': 'Blofeld'},
]
with open('villains', 'wt') as fout:
cout = csv.DictWriter(fout, ['first', 'last'])
cout.writeheader()
cout.writerows(villains)
# 위 코드가 실행되어, 아래와 같은 내용을 담은 csv 파일이 생성된다.
first,last # writeheader() 함수에 의해 쓰여진 Column Header
Doctor,No
Rosa,Klebb
Mister,Big
Auric,Goldfinger
Ernst,Blofeld
XML (Extensible Markup Language)
- 잘 알려진 Markup Format이다.
- Data Feed와 Message에 이용된다.
- XML의 Subformat으로는 RSS, Atom 등이 있다.
* RSS (Rich Site Summary, Really Simple Syndication, RDF Site Summary)
- 업데이트가 빈번한 웹사이트의 정보를 User에게 쉽게 제공하기 위해 만들어진 XML 기반 컨텐츠 배급 포맷이다.
* Atom
- Web Log나 최신 소식과 같은 Web Contents의 Syndication을 위한 SML 기반의 문서 포맷이자,
Web Log 편집을 위한 HTTP 기반의 Protocol이다.
* XML Syntax
- 데이터를 구분하기 위해 Tag를 사용하며, Tag는 홑화살괄호(<>)로 표현한다.
- 임의의 Start Tag(<example>)는 End Tag(</example>)로 마무리된다.
- Start Tag와 End Tag 사이에 들어가는 내용을 Value(값)라 한다.
- Tag가 값을 갖지 않는 경우, End Tag 하나로 표현할 수도 있다.
(<exam></exam> 대신, </exam>으로 표현하는 것이 가능하다.)
- Start Tag에는 여러 Attribute값을 지정할 수 있다.
- Tag 내부에 Tag를 중첩시킬 수 있다.
- Whitespace(공백)은 무시된다. (즉, Syntax에서 공백은 고려되는 요소가 아니다.)
Example. menu.xml
<?xml version="1.0"?>
<menu>
<breakfast hours="7-11">
<item price="$6.00">breakfast burritos</item>
<item price="$4.00">pancakes</item>
</breakfast>
<lunch hours="11-3">
<item price="$5.00">hamburger</item>
</lunch>
<dinner hours="3-10">
<item price="8.00">spaghetti</item>
</dinner>
</menu>
* XML Parsing in Python
- Module \(\texttt{ElementTree}\)을 사용하여 XML 파일을 파싱(해석)할 수 있다. (URL)
Example. menu.xml 파일을 Python에서 파싱하는 예시
>>> import xml.etree.ElementTree as et
>>> tree = et.ElementTree(file='menu.xml')
>>> root = tree.getroot()
>>> root.tag
'menu'
>>> for child in root:
... print('tag:', child.tag, 'attributes:', child.attrib)
... for grandchild in child:
... print('\ttag:', grandchild.tag, 'attributes:', grandchild.attrib)
...
# Output
tag: breakfast attributes: {'hours': '7-11'}
tag: item attributes: {'price': '$6.00'}
tag: item attributes: {'price': '$4.00'}
tag: lunch attributes: {'hours': '11-3'}
tag: item attributes: {'price': '$5.00'}
tag: dinner attributes: {'hours': '3-10'}
tag: item attributes: {'price': '8.00'}
>>> len(root) # number of menu sections
3
>>> len(root[0]) # number of breakfast items
2
* Standard Python XML Library
1) xml.dom
- 전체 XML 파일을 메모리에 로딩하여 XML의 모든 항목에 접근할 수 있게 한다.
2) xml.sax
- SAX(Simple API for XML)는 즉석에서 XML 파일을 Parsing하기 때문에 전체 XML 파일을 메모리에 로딩하지는 않는다.
- 매우 큰 XML Stream을 처리하는 경우, xml.dom 모듈보다 xml.sax 모듈이 더 적합하다.
HTML (Hypertext Mark-up Language) (URL)
JSON (JavaScript Object Notation)
- JavaScript의 Subset이며, 데이터를 교환하는데 쓰이는 Format이다.
- Python에는 JSON을 처리하기 위한 하나의 Main Module \(\texttt{json}\)이 있다.
- json 모듈은 데이터를 JSON String으로 Dump(인코딩)하고, JSON String을 데이터로 Load(디코딩)할 수도 있다.
Example. menu
>>> menu = \
... {
... "breakfast": {
... "hours": "7-11",
... "items": {
... "breakfast burritos": "$6.00",
... "pancakes": "$4.00"
... }
... },
... "lunch" : {
... "hours": "11-3",
... "items": {
... "hamburger": "$5.00"
... }
... },
... "dinner": {
... "hours": "3-10",
... "items": {
... "spaghetti": "$8.00"
... }
... }
... }
Example. menu를 JSON String으로 Dump(인코딩)하고 Load(디코딩)하는 예시
>>> import json
>>> menu_json = json.dumps(menu)
>>> menu_json
'{"dinner": {"items": {"spaghetti": "$8.00"}, "hours": "3-10"},
"lunch": {"items": {"hamburger": "$5.00"}, "hours": "11-3"},
"breakfast": {"items": {"breakfast burritos": "$6.00", "pancakes":
"$4.00"}, "hours": "7-11"}}'
>>> menu2 = json.loads(menu_json)
>>> menu2
{'breakfast': {'items': {'breakfast burritos': '$6.00', 'pancakes':
'$4.00'}, 'hours': '7-11'}, 'lunch': {'items': {'hamburger': '$5.00'},
'hours': '11-3'}, 'dinner': {'items': {'spaghetti': '$8.00'}, 'hours': '3-10'}}
Example. JSON String으로 Dump(인코딩)가 가능하도록 datetime 값을 수정하는 Class 선언하는 예시
>>> import datetime
>>> now = datetime.datetime.utcnow()
>>> now
datetime.datetime(2013, 2, 22, 3, 49, 27, 483336)
>>> json.dumps(now)
Traceback (most recent call last):
# ... (deleted stack trace to save trees)
TypeError: datetime.datetime(2013, 2, 22, 3, 49, 27, 483336) is not JSON serializable
# datetime 객체는 곧바로 JSON String으로 Dump할 수 없다.
>>> class DTEncoder(json.JSONEncoder): # 사용자 정의 클래스 DTEncoder는 JSONEncoder의 Subclass이다.
... def default(self, obj): # JSONEncoder의 default() 메서드만 오버라이드한다.
... # isinstance()는 obj의 Type을 확인한다.
... if isinstance(obj, datetime.datetime):
... return int(mktime(obj.timetuple()))
... # obj가 datetime Type이 아니라면, 기본 JSON String으로 인코딩한다.
... return json.JSONEncoder.default(self, obj)
...
>>> json.dumps(now, cls=DTEncoder)
'1361526567'
YAML (YAML Ain't Markup Language) (URL)
- JSON과 같이 Key와 Value로 이루어져 있지만, JSON보다 더 다양한 Type의 날짜와 시간을 처리할 수 있다.
- YAML은 Python Standard Library로 지원되지 않아, Third-Party Library인 yaml을 설치한 후 사용해야 한다.
- load() 함수로 YAML String을 Python Data로,
dump() 함수로 Python Data를 YAML String으로 변환할 수 있다.
* Python Standard Library (URL)
Example. mcintyre.yaml
name:
first: James
last: McIntyre
dates:
birth: 1828-05-25
death: 1906-03-31
details:
bearded: true
themes: [cheese, Canada]
books:
url: http://www.gutenberg.org/files/36068/36068-h/36068-h.htm
poems:
- title: 'Motto'
text: |
Politeness, perseverance and pluck,
To their possessor will bring good luck.
- title: 'Canadian Charms'
text: |
Here industry is not in vain,
For we have bounteous crops of grain,
And you behold on every field
Of grass and roots abundant yield,
But after all the greatest charm
Is the snug home upon the farm,
And stone walls now keep cattle warm.
Example. mcintyre.yaml 파일을 Module yaml을 이용하여 처리하는 예시
>>> import yaml
>>> with open('mcintyre.yaml', 'rt') as fin:
>>> text = fin.read()
>>> data = yaml.load(text)
>>> data['details']
{'themes': ['cheese', 'Canada'], 'bearded': True}
>>> len(data['poems'])
2
>>> data['poems'][1]['title']
'Canadian Charms'
- 신뢰할 수 없는 YAML 파일을 불러오는 경우, load() 대신, safe_load()를 사용할 것이 권장된다.
Security Note
- Structured Text Files을 불러오는 과정에서 Billion laughs attack과 같은 DoS 공격에 노출될 수 있다.
- Defused XML (URL)에서는 Python Library의 취약점과 Dos 공격 이외의 공격에 대한 대비책이 설명되어 있다.
- \(\texttt{defusexml}\) Library에서는 Library에 대한 Front-End Security를 제공하고 있다.
* Billion laughs.xml
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
- 10개의 Entity는 각각 하위 레벨로 10배씩 확장되어, 총 10억개로 확장된다.
Example. 보안을 고려한 Parsing과 그렇지 않은 Parsing 예시
>>> # insecure:
>>> from xml.etree.ElementTree import parse
>>> et = parse(xmlfile)
>>> # protected:
>>> from defusedxml.ElementTree import parse
>>> et = parse(xmlfile)
Configuration Files
* Standard Module \(\texttt{configparser}\) (URL)
- Windows에서의 .ini 파일을 처리하는 표준 모듈이다.
- Key=Value 형식의 Section으로 구성된다.
Example. settings.cfg
[english]
greeting = Hello
[french]
greeting = Bonjour
[files]
home = /usr/local
# simple interpolation:
bin = %(home)s/bin
Example. settings.cfg를 읽어 Python Data Structure로 변환하는 예시
>>> import configparser
>>> cfg = configparser.ConfigParser()
>>> cfg.read('settings.cfg')
['settings.cfg']
>>> cfg
<configparser.ConfigParser object at 0x1006be4d0>
>>> cfg['french']
<Section: french>
>>> cfg['french']['greeting']
'Bonjour'
>>> cfg['files']['bin']
'/usr/local/bin'
Serialization
- 객체를 파일로 저장하는 작업을 의미한다.
- Python에서는 Binary Format 객체를 저장/복원하기 위해 Standard Module \(\texttt{pickle}\) (URL)을 제공하고 있다.
Example. pickle 모듈을 통해 datetime 객체를 곧바로 JSON 타입으로 인코딩하는 예시
>>> import pickle
>>> import datetime
>>> now1 = datetime.datetime.utcnow()
>>> pickled = pickle.dumps(now1)
>>> now2 = pickle.loads(pickled)
>>> now1
datetime.datetime(2014, 6, 22, 23, 24, 19, 195722)
>>> now2
datetime.datetime(2014, 6, 22, 23, 24, 19, 195722)
8.3 Structured Binary Files (구조화된 이진 파일)
- Spreadsheets
- HDF5
Spreadsheets
- Microsoft Excel과 같은 스프레드시트는 Binary Data Format이다.
- 스프레드시트를 CSV 파일로 저장하면, Standard Module \(\texttt{csv}\)를 사용하여 이 파일을 읽을 수 있다.
- \(\texttt{xls}\) Binary File의 경우, Third-Party Package \(\texttt{xlrd}\)를 설치하여 사용하면 된다.
* Third-Party Package
- xlrd (URL)
- XlsxWriter (URL)
- OpenPyXL (URL)
- PyExcelerate (URL)
HDF5 (Hierarchical Data Format)
- Multidimensional Numeric Data, Hierarchical Numeric Data를 위한 Binary Data Format이다.
- 주로, 아주 큰 데이터 집합(GB - TB)에 대한 빠른 접근이 필요한 과학 분야에서 사용된다.
- WORM(Write Once/Read Many) Application에 적합하다.
(WORM의 경우, Write Conflict에 대한 보호가 필요하지 않다.)
* h5py (URL)
- 완전한 기능을 갖춘 Low-Level Interface를 제공한다.
* PyTables (URL)
- DB와 같은 기능을 갖춘 High-Level Interface를 제공한다.
8.4 Relational Databases (관계형 데이터베이스)
- DB-API
- SQLite
- MySQL
- PostgreSQL
- SQLAlchemy
DB-API
- RDB에 접근하기위해 Python에서 제공하고 있는 Standard API이다.
- DB API를 통해 여러 종류의 DB를 동작하게 하는 하나의 프로그램을 작성할 수 있다.
(Java의 JDBC, Perl의 dbi와 유사하다.)
DB API | Description |
connect() | - DB와의 Connection을 설정한다. - 사용자 이름, 암호, 서버 주소 등을 Argument로 갖는다. |
cursor() | - Query 결과에 보다 쉽게 접근하기 위한 Cursor 객체를 만든다. |
execute() executemany() |
- DB에 하나 이상의 SQL 명령을 실행한다. |
fetchone() fetchmany() fetchall() |
- Query 실행 결과를 얻는다. |
SQLite
- 보다 가벼운 Open Source RDB이다.
(MySQL, PostgreSQL처럼 완전하진 않지만, 가벼우며 호환성이 뛰어나다.)
- Python에서는 Standard Library로 SQLite를 지원하고 있으며, 일반 파일처럼 DB를 저장한다.
(이 파일은 서로다른 컴퓨터, OS에서 호환된다.)
- Web Browser, Smartphone, 기타 Application에서 SQLite를 Embedded DB처럼 사용한다.
* \(\texttt{':memory:'}\) 문자열을 사용하여 DB를 D-RAM에 생성시킬 수 있다.
Example. 동물원 사업을 위한 zoo DB
>>> import sqlite3
>>> conn = sqlite3.connect('enterprise.db')
>>> curs = conn.cursor()
>>> curs.execute('''CREATE TABLE zoo
(critter VARCHAR(20) PRIMARY KEY,
count INT,
damages FLOAT)''')
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.execute('INSERT INTO zoo VALUES("duck", 5, 0.0)')
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.execute('INSERT INTO zoo VALUES("bear", 2, 1000.0)')
<sqlite3.Cursor object at 0x1006a22d0>
# Placeholder를 사용한 방법
>>> ins = 'INSERT INTO zoo (critter, count, damages) VALUES(?, ?, ?)'
>>> curs.execute(ins, ('weasel', 1, 2000.0))
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.execute('SELECT * FROM zoo')
<sqlite3.Cursor object at 0x1006a22d0>
>>> rows = curs.fetchall()
>>> print(rows)
[('duck', 5, 0.0), ('bear', 2, 1000.0), ('weasel', 1, 2000.0)]
>>> curs.execute('SELECT * from zoo ORDER BY count')
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.fetchall()
[('weasel', 1, 2000.0), ('bear', 2, 1000.0), ('duck', 5, 0.0)]
>>> curs.execute('SELECT * from zoo ORDER BY count DESC')
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.fetchall()
[('duck', 5, 0.0), ('bear', 2, 1000.0), ('weasel', 1, 2000.0)]
>>> curs.execute('''SELECT * FROM zoo WHERE
... damages = (SELECT MAX(damages) FROM zoo)''') # damages는 동물의 사육 비용값이라 가정한다.
<sqlite3.Cursor object at 0x1006a22d0>
>>> curs.fetchall()
[('weasel', 1, 2000.0)]
>>> curs.close() # cursor는 닫는다.
>>> conn.close() # 연결을 해제한다.
※ Placeholder를 사용함으로써 SQL Injection(Web에서의 악의적인 SQL 명령을 통한 공격)을 방지할 수 있다.
MySQL
- Open Source RDB이며, 실제로 서버로 사용되고 있다.
- MysqlDB는 인기있는 MySQL 드라이버이지만, Python 3 Porting되지는 않았다.
* MySQL Driver
Name | Link | Pypi Package | Import as | Notes |
MySQL Connector | bit.ly/mysql-cpdg | mysql-connectorpython | \(\texttt{mysql.connector}\) | |
PYMySQL | github.com/petehunt/ | pymysql | \(\texttt{pymysql}\) | |
oursql | pythonhosted.org/oursql/ | oursql | \(\texttt{oursql}\) | Requires the MySQL C client libraries. |
PostgreSQL
- Open Source RDB이며, 실제로 서버로 사용되고 있다.
* PostgreSQL Driver
Name | Link | Pypi Package | Import as | Notes |
psycopg2 | initd.org/psycopg/ | psycopg2 | psycopg2 | Needs pg_config from PostgreSQL client tools |
py-postgresql | python.projects.pgfoundry.org/ | py-postgresql | postgresql |
SQLAlchemy (URL)
- Cross-DB Python Library 중 하나이다.
(다양한 DB들간의 차이를 해소하는 API이다.)
- Low-Level에서 DB Connection Pools을 처리할 수 있다.
(SQL 명령을 실행하고 결과를 리턴한다. DB-API에게 기대되는 전형적인 기능을 수행한다.)
- 보다 Pythonic하게, SQL Expression Language를 지원한다.
- High-Level에서 SQL Expression Language를 사용하고,
RDB 자료구조와 Application 코드를 Binding하는 ORM(Object Relational Model)의 역할을 한다.
Example. SQLAlchemy 설치 명령어
$ pip install sqlalchemy
Example. SQLAlchemy Connection String Format
dialect + driver :// user : password @ host : port / dbname
- dialect: DB 타입
- driver: 사용하고자 하는 DB의 특정 드라이버
DB | Driver |
sqlite | pysqlite (또는 생략) |
mysql | mysqlconnector |
mysql | pymysql |
mysql | oursql |
postgresql | psycopg2 |
postgresql | pypostgresql |
- user, password: DB 인증 문자열, 사용자와 비밀번호
- host, port: DB 서버의 위치 (포트번호는 서버의 DB 포트가 기본 설정이 아닌 경우에만 입력한다.)
- dbname: 서버에 연결할 DB 이름
Example. Engine Layer (Low-Level) Handling
>>> import sqlalchemy as sa
>>> conn = sa.create_engine('sqlite:///:memory:')
# DB에 연결하고, D-RAM에 Storage를 생성한다.
>>> conn.execute('''CREATE TABLE zoo
... (critter VARCHAR(20) PRIMARY KEY,
... count INT,
... damages FLOAT)''')
<sqlalchemy.engine.result.ResultProxy object at 0x1017efb10>
# SQL을 실행한다.
# conn.execute()는 ResultProxy(SQLAlchemy 객체) 타입의 객체를 리턴한다.
>>> ins = 'INSERT INTO zoo (critter, count, damages) VALUES (?, ?, ?)'
>>> conn.execute(ins, 'duck', 10, 0.0)
<sqlalchemy.engine.result.ResultProxy object at 0x1017efb50>
>>> conn.execute(ins, 'bear', 2, 1000.0)
<sqlalchemy.engine.result.ResultProxy object at 0x1017ef090>
>>> conn.execute(ins, 'weasel', 1, 2000.0)
<sqlalchemy.engine.result.ResultProxy object at 0x1017ef450>
>>> rows = conn.execute('SELECT * FROM zoo')
>>> print(rows)
<sqlalchemy.engine.result.ResultProxy object at 0x1017ef9d0>
# conn.execute()가 리턴하는 ResultProxy(SQLAlchemy 객체) 타입의 객체는 리스트가 아니며,
# 출력해서 곧바로 확인할 수 없는 형태이다. 따라서, 아래처럼 Iteration을 통해 SQL 질의 결과를 확인해야 한다.
>>> for row in rows:
... print(row)
...
('duck', 10, 0.0)
('bear', 2, 1000.0)
('weasel', 1, 2000.0)
Example. SQL Expression Language
>>> import sqlalchemy as sa
>>> conn = sa.create_engine('sqlite://')
>>> meta = sa.MetaData()
>>> zoo = sa.Table('zoo', meta,
... sa.Column('critter', sa.String, primary_key=True),
... sa.Column('count', sa.Integer),
... sa.Column('damages', sa.Float)
... )
>>> meta.create_all(conn)
# SQL대신, Expression Language를 사용하여 zoo 테이블을 생성한다.
>>> conn.execute(zoo.insert(('bear', 2, 1000.0)))
<sqlalchemy.engine.result.ResultProxy object at 0x1017ea910>
>>> conn.execute(zoo.insert(('weasel', 1, 2000.0)))
<sqlalchemy.engine.result.ResultProxy object at 0x1017eab10>
>>> conn.execute(zoo.insert(('duck', 10, 0)))
<sqlalchemy.engine.result.ResultProxy object at 0x1017eac50>
>>> result = conn.execute(zoo.select())
# Expression Lauguage에서의 "SELECT * FROM zoo" 문이다.
>>> rows = result.fetchall()
>>> print(rows)
[('bear', 2, 1000.0), ('weasel', 1, 2000.0), ('duck', 10, 0.0)]
- Expression Language는 Low-Level의 Engine Layer보다 더 다양한 SQL문을 처리할 수 있고,
중간에 위치해서 RDB Application에 쉽게 접근할 수 있도록 한다.
* ORM (Object-Relational Mapper)
- 내부는 SQL Expression Language를 사용하지만, 이를 Encapsulatiln함으로써 단순한 Interface를 제공한다.
(보다 High-Level적인 개념이다.)
- 즉, ORM은 SQL을 보다 더 추상화 한 개념이다.
Example. ORM Usage
>>> import sqlalchemy as sa
>>> from sqlalchemy.ext.declarative import declarative_base
>>> conn = sa.create_engine('sqlite:///zoo.db')
# DB에 연결한다.
>>> Base = declarative_base()
>>> class Zoo(Base):
... __tablename__ = 'zoo'
... critter = sa.Column('critter', sa.String, primary_key=True)
... count = sa.Column('count', sa.Integer)
... damages = sa.Column('damages', sa.Float)
... def __init__(self, critter, count, damages):
... self.critter = critter
... self.count = count
... self.damages = damages
... def __repr__(self):
... return "<Zoo({}, {}, {})>".format(self.critter, self.count, self.damages)
# SQLAlchemy의 ORM을 통해 테이블을 정의한다. (DDL을 보다 Pythonic하게 작성하는 것이다.)
>>> Base.metadata.create_all(conn)
# DB와 Table을 생성한다.
>>> first = Zoo('duck', 10, 0.0)
>>> second = Zoo('bear', 2, 1000.0)
>>> third = Zoo('weasel', 1, 2000.0)
>>> first
<Zoo(duck, 10, 0.0)>
# SQL의 INSERT문과 같다. 하지만 보다 Pythonic하다.
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=conn)
>>> session = Session()
# ORM을 전송하기 위해 DB와의 Session을 생성한다.
>>> session.add(first) # add() 함수는 Session에 하나의 객체를 추가시킨다.
>>> session.add_all([second, third]) # add_all() 함수는 Session에 객체가 들어있는 리스트를 통채로 추가시킨다.
# 생성한 Session을 통해 DB에 생성할 객체를 추가한다.
>>> session.commit()
# 모든 작업을 완료한다.
8.5 NoSQL Data Stores (NoSQL 데이터 스토어)
- NoSQL은 매우 큰 데이터 집합을 처리하고, 데이터 정의에 대해 더 유연하거나, 커스텀 데이터 연산을 지원하기 위해 만들어졌다.
- RDB가 아니며, SQL 또한 지원되지 않는다.
The dbm Family
- Key-Value Store 형식으로, 다양한 설정을 유지하기 위해 Application에 포함된다.
Example. dbm Usage
>>> import dbm
>>> db = dbm.open('definitions', 'c')
# open() 함수의 'c'는 읽기/쓰기 모드를 의미한다. (파일이 없는 경우, 생성한다.)
>>> db['mustard'] = 'yellow'
>>> db['ketchup'] = 'red'
>>> db['pesto'] = 'green'
# Key에 Value를 할당하여 값을 생성한다.
>>> len(db)
3
>>> db['pesto']
b'green'
>>> db.close()
>>> db = dbm.open('definitions', 'r')
>>> db['mustard']
b'yellow'
- Key와 Value는 Byte로 저장되기 때문에, 리턴결과에 Prefix \(\texttt{b}\)가 붙는다.
Memcached (URL)
- 빠른 In-Memory Key-Value Cache Server이다.
- 주로 DB의 앞단에 쓰이거나, Web Server의 Session Data를 저장하는 데 사용된다.
- 데이터는 지속되지 않아, 이전에 쓴 데이터가 사라질 수 있다.
(이는 Cache Server의 특징이다.)
Example. Memcached Python Driver 설치 명령어
$ pip install python-memcached
Example. Memcached Usage
>>> import memcache
>>> db = memcache.Client(['127.0.0.1:11211'])
>>> db.set('marco', 'polo')
True
>>> db.get('marco')
'polo'
>>> db.set('ducks', 0)
True
>>> db.get('ducks')
0
>>> db.incr('ducks', 2)
2
>>> db.get('ducks')
2
Redis (URL)
- Data Structure Server중 하나로, Redis 서버에 있는 모든 데이터는 메모리에 Fit해야 한다.
- 서버의 재시작과 신뢰성을 위해 데이터를 디스크에 저장한다.
- 기존 데이터가 유지된다.
- 간단한 문자열 이상의 Data Structure를 지원한다.
- Redis의 데이터 타입과 Python의 데이터 타입 간에는 유사점이 많아
Redis 서버는 여러 Python Application에서 데이터를 공유하기 위한 중개 역할을 하는 플랫폼으로 응용하기에 유리하다.
Example. Redis 설치 명령어
$ pip install redis
Example. String Handling in Redis
>>> import redis
>>> conn = redis.Redis('localhost', 6379)
# 기본값 localhost, 포트번호 6379에 연결을 시도한다.
>>> conn.keys('*')
[]
# 모든 Key를 나열한다. (아직까진 데이터가 없어 Empty List가 리턴된다.
>>> conn.set('secret', 'ni!')
True
>>> conn.set('carats', 24)
True
>>> conn.set('fever', '101.5')
True
# Key-Value 데이터 3개를 설정한다.
>>> conn.get('secret')
b'ni!'
>>> conn.get('carats')
b'24'
>>> conn.get('fever')
b'101.5'
# Key를 통해 Value를 리턴받는다.
>>> conn.setnx('secret', 'icky-icky-icky-ptang-zoop-boing!')
False
# setnx() 메서드는 Key가 존재하지 않는 경우에만 Value를 설정한다.
>>> conn.getset('secret', 'icky-icky-icky-ptang-zoop-boing!')
b'ni!'
# getset() 메서드는 이전 Value을 리턴하고, 동시에 새로운 Value을 설정한다.
>>> conn.getrange('secret', -6, -1)
b'boing!'
# getrange() 메서드를 사용하여 Substring을 리턴받는다.
>>> conn.setrange('secret', 0, 'ICKY')
32
>>> conn.get('secret')
b'ICKY-icky-icky-ptang-zoop-boing!'
# setrange() 메서드를 사용하여 Substring을 교체한다.
>>> conn.mset({'pie': 'cherry', 'cordial': 'sherry'})
True
# mset() 메서드를 사용하여 한 번에 여러 Key를 설정한다.
>>> conn.mget(['fever', 'carats'])
[b'101.5', b'24']
# mget() 메서드를 사용하여 한 번에 여러 Value를 리턴받는다.
>>> conn.delete('fever')
True
# delete() 메서드를 사용하여 Key를 제거한다.
>>> conn.incr('carats')
25
>>> conn.incr('carats', 10)
35
>>> conn.decr('carats')
34
>>> conn.decr('carats', 15)
19
>>> conn.set('fever', '101.5')
True
>>> conn.incrbyfloat('fever')
102.5
>>> conn.incrbyfloat('fever', 0.5)
103.0
# incr(), incrbyfloat() 메서드를 사용하여 Value를 증가시키고,
# decr() 메서드를 사용하여 Value를 감소시킨다.
>>> conn.incrbyfloat('fever', -2.0)
101.0
# decrbtfloat() 메서드는 따로 정의되어 있지 않으므로, 음수값을 증가시키는 방법을 사용한다.
Example. List Handling in Redis
>>> conn.lpush('zoo', 'bear')
1
>>> conn.lpush('zoo', 'alligator', 'duck')
3
# lupsh() 메서드를 사용하여 하나 이상의 값을 삽입할 수 있다.
>>> conn.linsert('zoo', 'before', 'bear', 'beaver')
4
>>> conn.linsert('zoo', 'after', 'bear', 'cassowary')
5
# linsert() 메서드를 사용하여 특정값(bear) 이전, 이후에 값을 삽입할 수 있다.
>>> conn.lset('zoo', 2, 'marmoset')
True
# lset() 메서드를 사용하여 Offset을 통해 삽입할 수 있는데, 이 경우 List가 존재해있어야 한다.
>>> conn.rpush('zoo', 'yak')
6
# rpush() 메서드를 사용하여 값을 리스트의 마지막에 삽입할 수 있다.
>>> conn.lindex('zoo', 3)
b'bear'
# lindex() 메서드를 사용하여 Offset에 해당하는 값을 리턴받을 수 있다.
>>> conn.lrange('zoo', 0, 2)
[b'duck', b'alligator', b'marmoset']
>>> conn.lrange('zoo', 0, -1)
[b'alligator', b'marmoset', b'bear', b'cassowary']
# lrange() 메서드를 사용하여 Offset 범위에 있는 값들을 리턴받을 수 있다.
# Offset을 0, -1로 지정하면 리스트의 모든 값을 리턴받게된다.
>>> conn.ltrim('zoo', 1, 4)
True
# ltrim() 메서드를 사용하여 지정한 Offset 범위에 해당되는 값들을 제외한 모든 값들을 제거할 수 있다.
Example. Hash Handling in Redis
>>> conn.hmset('song', {'do': 'a deer', 're': 'about a deer'})
True
# hmset() 메서드를 사용하여 하나 이상의 필드를 설정할 수 있다.
>>> conn.hset('song', 'mi', 'a note to follow re')
1
# hset() 메서드를 사용하여 해시에서 하나의 필드값을 설정할 수 있다.
>>> conn.hget('song', 'mi')
b'a note to follow re'
# hget() 메서드를 사용하여 하나의 필드값을 리턴받을 수 있다.
>>> conn.hmget('song', 're', 'do')
[b'about a deer', b'a deer']
# hmget() 메서드를 사용하여 하나 이상의 필드값을 리턴받을 수 있다.
>>> conn.hkeys('song')
[b'do', b're', b'mi']
# hkeys() 메서드를 사용하여 해시의 모든 필드키를 리턴받을 수 있다.
>>> conn.hvals('song')
[b'a deer', b'about a deer', b'a note to follow re']
# hvals() 메서드를 사용하여 해시의 모든 필드값을 리턴받을 수 있다.
>>> conn.hlen('song')
3
# hlen() 메서드를 사용하여 해시 필드의 개수를 리턴받을 수 있다.
>>> conn.hgetall('song')
{b'do': b'a deer', b're': b'about a deer', b'mi': b'a note to follow re'}
# hgetall() 메서드를 사용하여 해시 필드의 모든 키와 값을 리턴받을 수 있다.
>>> conn.hsetnx('song', 'fa', 'a note that rhymes with la')
1
# hsetnx() 메서드를 사용하여 키가 존재하지 않는 경우에 필드를 설정할 수 있다.
- Redis의 Hash는 Python과 비슷하지만, String만 포함할 수 있다.
Example. Set Handling in Redis
>>> conn.sadd('zoo', 'duck', 'goat', 'turkey')
3
# sadd() 메서드를 사용하여 Set에 하나 이상의 값을 추가할 수 있다.
>>> conn.scard('zoo')
3
# scard() 메서드를 사용하여 Set에 있는 값의 개수를 리턴받을 수 있다.
>>> conn.smembers('zoo')
{b'duck', b'goat', b'turkey'}
# smembers() 메서드를 사용하여 Set의 모든 값들을 리턴받을 수 있다.
>>> conn.srem('zoo', 'turkey')
True
# srem() 메서드를 사용하여 Set에서 값을 삭제할 수 있다.
>>> conn.sadd('better_zoo', 'tiger', 'wolf', 'duck')
0
>>> conn.sinter('zoo', 'better_zoo')
{b'duck'}
# sinter() 메서드를 사용하여 Set들간의 Intersection 연산을 수행할 수 있다.
>>> conn.sinterstore('fowl_zoo', 'zoo', 'better_zoo')
1
# sintersotre() 메서드를 사용하여 Set들간의 Intersection 연산결과를 또 다른 Set에 저장할 수 있다.
>>> conn.sunion('zoo', 'better_zoo')
{b'duck', b'goat', b'wolf', b'tiger'}
# sunion() 메서드를 사용하여 Set들간의 Union 연산을 수행할 수 있다.
>>> conn.sunionstore('fabulous_zoo', 'zoo', 'better_zoo')
4
# sunionstore() 메서드를 사용하여 Set들간의 Union 연산결과를 또 다른 Set에 저장할 수 있다.
>>> conn.sdiff('zoo', 'better_zoo')
{b'goat'}
# sdiff() 메서드를 사용하여 Set 사이의 Difference 연산을 수행할 수 있다.
>>> conn.sdiffstore('zoo_sale', 'zoo', 'better_zoo')
1
# sdiffstore() 메서드를 사용하여 Set들간의 Difference 연산결과를 또 다른 Set에 저장할 수 있다.
Example. Sorted Set(zest) Handling in Redis
>>> import time
>>> now = time.time()
>>> now
1361857057.576483
>>> conn.zadd('logins', 'smeagol', now)
1
>>> conn.zadd('logins', 'sauron', now+(5*60))
1
>>> conn.zadd('logins', 'bilbo', now+(2*60*60))
1
>>> conn.zadd('logins', 'treebeard', now+(24*60*60))
1
>>> conn.zrank('logins', 'bilbo')
2
>>> conn.zscore('logins', 'bilbo')
1361864257.576483
>>> conn.zrange('logins', 0, -1)
[b'smeagol', b'sauron', b'bilbo', b'treebeard']
>>> conn.zrange('logins', 0, -1, withscores=True)
[(b'smeagol', 1361857057.576483), (b'sauron', 1361857357.576483),
(b'bilbo', 1361864257.576483), (b'treebeard', 1361943457.576483)]
- zest(Sorted Set)는 유일한 값의 Set이며, 각 값들은 고유의 Score값을 가지며, Score는 부동소수점수이다.
- 각 값들은 Value 혹은 Score로 접근될 수 있다.
- zest는 Leader Board, Secondary Index, Timeseries에 이용된다.
Example. Bit Handling in Redis
>>> days = ['2013-02-25', '2013-02-26', '2013-02-27']
>>> big_spender = 1089
>>> tire_kicker = 40459
>>> late_joiner = 550212
>>> conn.setbit(days[0], big_spender, 1)
0
>>> conn.setbit(days[0], tire_kicker, 1)
0
>>> conn.setbit(days[1], big_spender, 1)
0
>>> conn.setbit(days[2], big_spender, 1)
0
>>> conn.setbit(days[2], late_joiner, 1)
0
>>> for day in days:
... conn.bitcount(day)
...
2
1
2
>>> conn.getbit(days[1], tire_kicker)
0
>>> conn.bitop('and', 'everyday', *days)
68777
>>> conn.bitcount('everyday')
1
>>> conn.getbit('everyday', big_spender)
1
>>> conn.bitop('or', 'alldays', *days)
68777
>>> conn.bitcount('alldays')
3
* 모든 Redis의 Key는 TTL(Time-To-Live)를 가진다. (즉, 일정 시간이 지나면 만료된다.)
>>> import time
>>> key = 'now you see it'
>>> conn.set(key, 'but not for long')
True
>>> conn.expire(key, 5)
True
>>> conn.ttl(key)
5
>>> conn.get(key)
b'but not for long'
>>> time.sleep(6)
>>> conn.get(key)
- 키가 유지되는 시간을 expire() 메서드를 통해 설정할 수 있다.
- expireat() 메서드는 주어진 에포치 시간에 키를 만료시키게 한다.
- Key Expiration은 Cache를 적정수준으로 유지하고 로그인 세션을 제한하는 데 유용하다.
기타 NoSQL
Site | Python API |
Cassandra | pycassa |
CouchDB | couchdb-python |
HBase | happybase |
Kyoto Cabinet | kyotocabinet |
MongoDB | mongodb |
Riak | riak-python-client |
8.6 Full-Text Databases (풀텍스트 데이터베이스)
Site | Python API |
Lucene | pylucene |
Solr | SolPython |
ElasticSearch | pyes |
Sphinx | sphinxapi |
Xapian | xappy |
Whoosh | (written in Python, includes an API) |
Reference: Introducing Python(처음 시작하는 파이썬) (Bill Lubanovic 저, O'Reilly, 2015)