CSV(comma seperated  values, 쉼표로 구분된 값들)파일을 읽어서 처리하는 코드를 작성하고보니 업로드 해야겠다는 생각이 들었다.

python 에서는 csv라는 모듈을 제공해준다. 그래서 import csv를 해서 사용하면 된다.

- csv 파일 읽기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import csv
 
def getCsv(self):
        csvList = []
    
        f = open('./exam.csv','r')    #현재 경로의 exam.csv를 연다.
        csvReader = csv.reader(f)    #reader로 파일을 읽는다.
        
        for i in csvReader:            #한 행씩 돌면서 i[2]값 (3번째 컬럼)을 가져와서 리스트에 저장한다.
            csvList.append(i[2])
        f.close()
    
        return csvList
        
def checker(self):
    csvList = self.getCsv()
    
    if 'ecycle' not in csvList:        #리스트에서 ecycle 문구 존재여부 체크
        print "X"
    else:
        print "O"    
 
if __name__ =='__main__':
    
    print sta.Checker()    
cs

- csv 파일 쓰기

1
2
3
4
5
6
7
8
def setCsv(self):
    
    f = open('./exma2.csv','w')
    csvWriter = csv.writer(f)
        
    csvWriter.writerow(['ecycle','1989','seoul','preprofession'])
    f.close()
        
cs


- 약한 참조 : 레퍼런스 카운트로 고려되지 않는 레퍼런스 객체

% Reference count(참조수)란?
        : 얼마나 많은 부분에서 객체를 참조하고 있는가를 나타내 주는 정수


sys모듈의 getrefcount는 요소의 레퍼런스카운트 갯수를 반환해줌

1. python을 구현한 내부코드에서 3이란 숫자를 29번 참조했다는 의미

2. 한 변수를 참조하는 다른 변수 또한 같은 메모리 공간을 가리키고 있음

3. 서로 같은 값(256이하)을 가지고 있는 다른 변수는 같은 메모리 공간을 가리킴

4. 257 이상 부터는 같은 값을 저장해도 다른 아이디 값은 가진다. 

%아마도 256까지는 singletone으로 구현되었을 것이다.


**python의 Garbage Collecter의 원리**
        : 레퍼런스 카운트가 0인 경우 사용하지 않는 메모리라고 판단하고 GC가 메모리를 반환한다. 


- weakref.ref()
        : 주어진 객체에 대한 약한 참조를 만들어 낸다. 원 객체를 얻을 때에는 약한 참조 자체를 함수처럼 호출.


- weakref.proxy()
        : 원본객체를 대신하는 프록시 객체로 약한 참조를 얻는다. (약한 참조를 객체로 접근 하는 방법)


- 사용 용도

1. 탱글링 방지(cycle reference : 순환 참조)
    : 서로 다른 객체들 사이에 참조 방식이 순환 형태로 연결되는 방식
    독립적으로 존재하지만 순환 참조되는 서로 다른 객체 그룹은 쓰레기 수집이 잘 안됨.
    주기적으로 수집 기능이 있지만 CPU자원 장비가 심함 > 빈도가 낮으면 메모리 불필요한 메로리 점유

2. caching에 사용하면 효과적
    : 대용량의 이미지 데이터들을 가지고 있을 때, 메모리 관리를 편하게 하기 위해 (local에 제한을 둠) 예제 참고 ExpensiveObject.py
        (불필요한 데이터를 끝까지 가지고 있을 필요 없음)

3. 용량이 큰 객체를 자주 사용하거나, 쉽게 만들 수 있을 때 


ExpensiveObject.py (WeakValueDictionary를 사용하여 약한 참조를 만든다. > 원 객체를 참조를 없애고 가비지 컬렉터를 돌려 약한 참조의 생명주기를 확인한다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import gc
from pprint import pprint
import weakref
 
gc.set_debug(gc.DEBUG_LEAK)
 
class ExpensiveObject(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'ExpensiveObject(%s)' % self.name
    def __del__(self):
        print '(Deleting %s)' % self
        
def demo(cache_factory):
    # hold objects so any weak references 
    # are not removed immediately
    all_refs = {}
    # the cache using the factory we're given
    print 'CACHE TYPE:', cache_factory
    cache = cache_factory()
    for name in [ 'one''two''three' ]:
        o = ExpensiveObject(name)
        cache[name] = o
        all_refs[name] = o
        del o # decref
 
    print 'all_refs =',
    pprint(all_refs)
    print 'Before, cache contains:', cache.keys()
    for name, value in cache.items():
        print '  %s = %s' % (name, value)
        del value # decref
        
    # Remove all references to our objects except the cache
    print 'Cleanup:'
    del all_refs
    gc.collect()
 
    print 'After, cache contains:', cache.keys()
    for name, value in cache.items():
        print '  %s = %s' % (name, value)
    print 'demo returning'
    return
 
demo(dict)
print
demo(weakref.WeakValueDictionary)
cs


- 반복자(iterator) 
    : 데이터 타입의 내부 구현을 노출하지 않고 포함하고 있는 요소들을 순회하는 방법

리스트 또는 어떤 컨테이너를 Iterator로 만들려면

def __iter__(self):
    return self

def next(self):
    self.data += 1
    if self.limit and self.limit <= self.data:
       
         raise StopIteration
    return self.data

을 선언해 주면 된다. > iterator가 되었으므로 for문을 돌면서 next 메소드를 호출시켜 준다.

StopIteration을 종료후 던져야 한다.

ex)    


- 발생자 (Generator)
    : return 대신 yield를 사용하는 것

-> 함수 호출된 후에 되돌아갈 때, 메모리가 해제되지 않고 그대로 남아있음
    그리고 다시 그 함수가 호출되면 이전에 수행이 종료되었던 지점 이후를 계속 진행함.

yield : Iterator를 구현할 수 있는 훨씬 간결한 문법을 제공한다.!!


   코드 설명

gen = generate_inits(3) #초기 스택 프레임 발생 / 실행 중단 상태
gen.next()                  #실행 재개
yield                         #실행 중단/ 값 return

- 일반 함수와의 차이점 : 일반 함수는 모든 일을 마친 결과를 넘김, But 정보를 생성하는 중간에 값을 넘길 수 있다.
    > 어떤 작업의 중간 결과를 다른 코드에서 참조해야 할 경우 유용하게 활용됨
    > 병행 프로세스나 멀티쓰레드 처리 업무에도 활용 가능
    > 메모리 효율성에 좋음

1번 글에서 conf의 모양을 봤다면 DB접속 정보라는 것을 알 수 있다.

DBInfo.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[DB] 
host= 1.1.1.1
user=ecycle
passwd=ecycle123123
db=testdb
charset=utf8
use_unicode=true
sql_insert =    insert into user
                (
                CONNTIME,
                UNO,
                UNAME,
                UEMAIL,
                UPASSWORD
                )
                VALUES(
                str_to_date('%s', '%%Y%%m%%d%%H%%i%%s'),
                '%s',
                '%s',
                '%s',
                '%s'
                )
                ;
 
cs

이 conf파일은 DB정보와 insert 구문을 가지고 있다.
이 파일을 이용해서 DB와 연동된 python 코드를 작성해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
#-*- codig: utf-8 -*-
 
import MySQLdb
import sys
import time
import ConfigParser
import signal
from datetime import datetime
 
 
SHUTDOWN = False
 
def shutdown(sigNum , frame):
    global SHUTDOWN
    SHUTDOWN = True
    __LOG__.Trace("SIGNAL NUM:%s" %sigNum)
    return
 
signal.signal(signal.SIGTERM, shutdown)  # Sig Terminate: 15
signal.signal(signal.SIGINT, shutdown)    # Sig Inturrupt: 2
try:
    signal.signal(signal.SIGHUP, shutdown)  # Sig HangUp: 1
except Exception, e : pass
try:
    signal.signal(signal.SIGPIPE, shutdown) # Sig Broken Pipe: 13
except Exception, e : pass
 
def db_connPool(cfg):
    conn = None
    try:
        conn = MySQLdb.connect(
            host = cfg.get('DB''host'),
            user = cfg.get('DB''user'),
            passwd = cfg.get('DB''passwd'),
            db = cfg.get('DB''db'),
            charset = cfg.get('DB''charset'),
            use_unicode = cfg.getboolean('DB''use_unicode')
            )
    except Exception as e:
        __LOG__Trace("DB connection error : %s" %e)
 
 
    return conn
 
 
def db_insert(params,cfg):
    try:
        
        conn = db_connPool(cfg)
        __LOG__.Trace("DB connected")
 
        #cursor = None
 
        #transaction
    
        conn.autocommit = False
        cursor = conn.cursor()
        strSql = cfg.get('DB','sql_insert') % params
        cursor.execute(strSql)
        __LOG__.Trace("Inserted!")
 
    except Exception as e:
        conn.rollback()
        __LOG__Trace("insert Error : %s" % e)
 
    finally:
        conn.commit()
        if cursor:
            cursor.close()
        if conn:
            conn.close()
 
if __name__ == '__main__':
 
    if len(sys.argv) <5:
        __LOG__.Trace('not enough arguments')
        sys.exit()
    
    __LOG__.Trace("*****DB INSERT START*****")
 
    try:
 
        cfg = ConfigParser.ConfigParser()
        cfg.read("./DBInfo.conf")
        
        
        time = datetime.now().strftime('%Y%m%d%H%M%S')
        nparams = (
            time,
            sys.argv[1],
            sys.argv[2],
            sys.argv[3],
            sys.argv[4]
            
            )
        
        db_insert(nparams,cfg)
        
    except:
        __LOG__.Trace("insert error")
        sys.exit()
 
    __LOG__.Trace("*****complete*****")
 

cs

이 코드는 transaction도 구현하였다. 간단하게
signal은 추후에 정리해서 올리겠다.

프로그램을 작성하다보면 서버 접속정보, DB정보 등 변경 가능한 정보들을 소스에 코딩해 놓는 것은 위험하다.

그래서 설정정보를 분리한 .conf나 .ini를 작성하여 외부에서 설정정보를 가지고 오도록 작성하는 것이 좋다.


python의 경우 ConfigParser모듈을 import해서 외부 설정 정보를 가지고 올 수 있다.


예제)
    getConfig.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
#-*- codig: utf-8 -*-
 
import ConfigParser
 
 
def get_config(cfg):
    
    host = cfg.get('DB''host'),
    user = cfg.get('DB''user'),
    passwd = cfg.get('DB''passwd'),
    db = cfg.get('DB''db'),
    charset = cfg.get('DB''charset'),
    use_unicode = cfg.getboolean('DB''use_unicode')
    insert_Sql = cfg.get('DB'.'sql_insert')
    
    
    #cfg값 출력
    print host, user, passwd, db, charset, use_unicode, insert_Sql
    
 
if __name__ == '__main__':
 
    cfg = ConfigParser.ConfigParser()
    cfg.read("./DBInfo.conf")            #절대주소를 적어주는게 좋다.
    get_config(cfg)
cs


DBinfo.conf

[DB] 
host= 1.1.1.1
user=ecycle
passwd=ecycle123123
db=testdb
charset=utf8
use_unicode=true
sql_insert = insert into user
                (
                CONNTIME,
                UNO,
                UNAME,
UEMAIL,
                UPASSWORD
                )
                VALUES(
                 str_to_date('%s', '%%Y%%m%%d%%H%%i%%s'),
                '%s',
                '%s',
                '%s',
                '%s'
                );




+ Recent posts