프랭클린 자서전 독후감

구성

1.프랭클린 자서전을 읽은 후 느낌 (인상 깊었던부분 발췌 및 전체리뷰)

2.독서일기 (그날그날 독서하면서 읽었던 느낌)


1.프랭클린 자서전을 읽은 후 느낌

프랭클린 자서전을 읽고...

프랭클린 자서전은 크게 3부로 나누어진다. 1부는 프랭클린이 자서전을 쓰게 된 이유와 프랭클린 삶의 초반부에 대한 내용이 있다. 1부에서는 도입부부터 나는 아주 신선한 충격을 받았는데 그 이유는 바로 벤저민 프랭클린이라는 사람의 솔직함과 그리고 자신이 성공했었던 이유와 습관들을 후손에게 알려주기 위해 썼다는 내용인데 자기자랑만 쓰는 게 대부분의 성공한 사람들의 글인데 이 사람은 정말 고치고 싶었었던 실수에 대해서 솔직하게 공개했고 그것을 반복하지 않기 위해 노력했던 점이 보이는 것이다.

여기서 내가 인상 깊었던 내용을 발췌(책, 글 따위에서 필요하거나 중요한 부분을 가려 뽑아내다)한다고 하면 아래의 14쪽 내용이다.

14쪽

"누군가가 나에게 똑같은 삶을 다시 살 수 있는 기회가 주어지면 그렇게 하겠느냐고 물어 보면 나는 주저 없이 그럴 거라고 대답했다. 돌이켜 보면 내가 누려 온 행복 때문인 것 같다. 하지만 작가가 개정판에서 초판의 오류를 수정하듯이 나도 내 삶에서 고치고 싶은 부분이 있기는 하다. 실수한 일을 고치는 것은 물론이고 불행한 사고나 사건들을 좀더 좋은 일들로 바꿀 수만 있다면 더 바랄 것이 없겠지. 설사 이것이 불가능하다 하더라도 다시 살아 보고 싶은 마음은 변함이 없다. 하지만 다시 산다는 건 있을 수 없는 법. 그러니 그에 버금가는 일은 그 삶을 재조명하고 글로 써두어서 영원한 것으로 만드는 것이리라."

이글을 보고 정말 이 사람은 먼 미래까지 생각하는 시야를 가졌다는 것을 알게 되었다. 그리고 후손들이 자신의 인생 전체를 낭비 없이 알차게 보냈으면 하는 바람으로 썼다는 것을 알 수 있는데 이글에서 그가 인생에 대해 항상 반성하는 성격이 있다는 것을 알 수 있다. 그리고 처음 프랭클린의 조상 얘기와 집안 얘기를 지나 자신의 10대 견습공 얘기가 나오고 아버지와 형제들과의 얘기가 나온다. 그리고 그런 얘기를 지나 인상 깊었던 부분은 대화에 대한 그의 자세인데 대화를 할 때 상대방의 감정을 배려하면서 쓰는 표현을 적었다. 현대사회에서 소통할 때 정말 중요하다고 생각했던 자세이다. 아래는 해당 부분을 발췌한 39쪽과 40쪽 내용이다.

39쪽

"크세노폰이 쓴 <소크라테스의 회고록>을 손에 넣었는데 그 안에 소크라테스식 논쟁법의 예들이 많이 나와있었다. 나는 거기에 홀딱 반해서 그 방법을 사용하기로 했다. 남의 의견을 뚝 잘라 반대하거나 독단적으로 내 의견을 밀어붙이기보다는 겸손하게 남의 의견을 묻고 의문을 던지는 것이다. "


40쪽
"나 자신을 겸손하게 표현하는 습관만은 그대로 지니기로 했다. 이를테면 논박의 여지가 있는 어떤 의견을 낼 때 '확실히', '의심할 여지없이' 같은 독단적인 분위기를 풍기는 말은 사용하지 않는 것이다. 그 대신 이런 식으로 말했다. '제 생각에는 이러이러한 것 같은데요', '저는 여차여차한 이유로 이렇게 생각하는데요', '그럴 거라고 짐작이 갑니다만', '내가 틀리지 않았다면 그건 이럴 겁니다.' 이런 습관은 내게 아주 이득이 되었다고 믿고 있는데 특히 내 의견을 관찰시키거나 내가 추진하고 있는 일에 사람들을 납득시킬 때 큰 효과가 있었다. 우리가 대화를 하는 주된 목적은 서로간에 정보를 주고받거나 ,서로를 즐겁게 하거나, 설득하는 데에 있다. 아무리 똑똑하고 선의를 가지고 있는 사람이라도 거만하고 독단적인 태도로 나오면 그가 하는 선한 일은 그만큼 힘을 발휘하지 못하게 되는 법이니 그런일이 없기를 바란다."

위의 글을 읽으면서 내가 실생활에서 하는 토론이나 회사에서 했던 토론에서 나는 저런 표현을 쓰지 않았는지 독단적인 태도로 대화를 해서 좋은 의도로 한 말이 물거품이 되지는 않았는지 뒤돌아볼 수 있게 한 좋은 내용의 글이었다. 그래서 토론을 할 때 서로 기분이 나빠지면 논리는 없어지고 감정싸움만 하게 되기 때문에 그런 점을 항상 경계하고 겸손하게 대화하고자 하는 태도가 중요하다는 것을 느꼈던 부분이었다. 또 이걸 보면서 생각났던 점은 TV에 나오는 대선후보 토론이나 시장 후보 토론에서 나오는 각 후보의 토론 자세를 보면 겸손하게 경청하거나 겸손한 태도로 대화하는 것을 자주 보기 힘든데 지도자나 리더는 그런 대화와 토론을 하는 것에 있어서 겸손한 태도를 많이 보였으면 좋겠다고 생각하면서 읽기도 했다.

그리고 1부에서 프랭클린이 여러 실수를 하는 과정이 나오고 그런 것이 어떻게 해결되는지, 그리고 방치된 실수는 나중에 어떤 결과를 가져오는지, 2부와 3부에서 내용이 나온다. 이 1부에서 마지막쯤 벤저민 프랭클린의 자서전은 세상에 꼭 나와야 한다고 말하는 벤저민 보건씨라는 사람이 쓴 편지도 있는데 내용이 재밌고 사실적이어서 인상 깊었다.


2부에 대한 내용 중 가장 머리에 남는 부분은 벤저민 프랭클린이 완벽한 인격체의 삶을 살기 위해, 나쁜 습관들을 정복하고자 하는 내용이다. 시행착오를 겪으면서 그는 신념으로는 이 나쁜 습관들을 해결할 수 없어서 13가지 덕목을 정하고 덕목표를 만들어서 자신의 하루하루 삶을 관찰하고 반성하는 습관을 만드는데 이 습관으로 그는 스스로 생각해서 자신의 삶을 그 이전보다 더 주도적으로 살기 시작한다. 여기서 인상 깊었던 내용은 다음의 155쪽이다.

" 한 가지 잘못을 저지르지 않으려고 그것에만 온통 신경 쓰고 있는 사이에 불쑥 다른 잘못을 저질러 버리는 것이었다. 소흘한 틈을 타서 나쁜 습관이 나타났고 성향은 이성으로 이기기에는 너무 강했다. 그렇게 얼마를 보낸 뒤 완벽하게 덕스러운 사람이 되어야지 하는 마음속의 신념만으로는 실수를 막을 수 없다는 결론에 도달했다. 늘 정확하고 일관성 있는 행동을 하려면 반대되는 습관들은 깨부수고 좋은 습관을 익혀야 한다. "

여기서 자신도 신념으로는 자신이 기존에 가지고 있던 나쁜 습관들을 고칠 수 없게 되자 꼭 지켜야 할 덕목들을 스스로 정의하고 덕목표를 만들어서 하루와 일주일 단위로 반성하는 표를 만들어서 나쁜 습관들을 정복하고자 정말 많은 노력을 했다. 그 중 13가지 덕목은 다음의 156쪽과 157쪽에서 발췌한 내용에 있다.

1.  절제 : 배부르도록 먹지말라. 취하도록 마시지 말라.
2.  침묵 : 자신이나 남에게 유익하지 않은 말은 하지 말라. 쓸데없는 말은 피하라.
3.  질서 : 모든 물건을 제자리에 정돈하라. 모든 일은 시간을 정해 놓고 해라.
4.  결단 : 해야 할 일은 하기로 결심하라. 결심한 것은 꼭 이행하라.
5.  절약 : 자신과 다른 이들에게 유익한 일 외에는 돈을 쓰지 말라. 즉, 아무것도 낭비하지 말라.
6.  근면 : 시간을 허비하지 말라. 언제나 유용한 일을 하라. 안 해도 될 행동은 끊어 버려라.
7.  진실 : 남을 일부러 속이려 하지 말라. 순수하고 정당하게 생각하라. 말과 행동이 일치하게 하라.
8.  정의 : 남에게 피해를 주거나 응당 돌아갈 이익을 주지 않거나 하지말라.
9.  중용 : 극단을 피하라. 상대방이 나쁘다고 생각되더라도 홧김에 상처를 주는 일을 삼가라.
10. 청결 : 몸과 의복, 습관 상의 모든 것을 불결하게 하지 말라.
11. 평정 : 사소한 일, 일상적인 일이나 불가피한 일에 흔들리지 말라.
12. 순결 : 건강이나 자손 때문이 아니라면 성 관계를 피하라. 감가이 둔해지거나 몸이 약해지거나, 자신과 다른 이의 평화와 평판에 해가 될정도까지 하지 말라.
13. 겸손 : 예수와 소크라테스를 본받으라. 

여기서 절제인 1번은 지키기 가장 어렵기도 하고 1번 때문에 망한 사람이 책에도 많이 나온다. 대부분 실패한 사람들은 술을 절제하지 않고 먹어서 업무 태도가 해이해지고 인생이 망한다. 나도 술을 좋아하고 많이 마셨던 적이 있기 때문에 지금부터 죽을 때까지 조심하고 경계해야 한다. 주저리 주저리 얘기했지만 여기서 내가 배워야 할 핵심은 프랭클린은 저런 13가지 덕목을 만들고 표까지 만들어서 매일, 매주 스스로 점검하고 반성하면서 발전했다는 점이다.


 

프랭클린 자서전 3부에서는 젊었을 때는 개인사업을 하던 프랭클린이 나이가 들어서는 공익사업에 더 신경을 쓰고 많은 일을 하게 된다. 방위군 조직과 대학설립 그리고 공직에 선출되기까지 여러 일을 하면서 종교적인 내용과 정치적인 내용이 많이 나온다. 나는 현재 이런 배경지식이 없어서 이해하는 것이 어려웠다. 거의 궁금한 점은 구글링으로 찾아봤으나 그것으로도 완벽하게 이해가 되지 않았다. 기회가 되면 현재 읽고 있는 책을 다 읽고 나면 한국사와 미국사 중국사, 유럽사에 관련된 책을 많이 읽어봐야 더 명확하게 이해가 될 것 같다.

이 3부에서는 거의 프랭클린의 지혜로 일을 처리하는 부분이 많이 나오는데 기억에 남는 부분은 대학설립을 할 때 건물 문제가 있었던 것을 프랭클린의 지혜로 해결한 부분이나 소방대를 만든 부분, 도로를 정비하고 청소하는 시스템을 만든 점 등 공익사업에 여러 업적을 남긴 것이 인상 깊었다.

이렇게 프랭클린 책을 읽으면서 여기에 나온 책들도 궁금하고 더 많은 책을 읽고 싶은 마음이 생겼다. 그리고 정말 솔직하게 자신이 행동했던 실수나 시행착오를 오픈하기가 쉽지 않은 일인데 그것을 오픈하고 자손들에게 전달한 점과 그 스스로 완벽하지 않지만 계속해서 완벽을 추구하고 노력한 점은 많은 젊은 사람들 뿐만 아니라 사업가나 정치가 등 다양한 사람들에게 좋은 영향을 줄 만한 책인 것 같다.

이제 이 책을 읽고 여기서 얻은 좋은 내용을 실천하는 것만 남았다.


  • 1부와 2부 구성에 대하여...

여기서 1부와 2부가 내용이 비슷하긴 한데 1부는 프랭클린 자서전을 읽고 느꼈던 전체적인 리뷰와 다시 보고 싶은 내용을 발췌했다. 2부는 책을 읽으면서 그날 그날 느꼈던 점을 일기형식으로 자유롭게 썼던 내용이다. 독서일기를 쓰고 업무회람일지에도 하루 정리부분에 메모를 하기도 했다.


2. 독서일기

독서일기 1장

읽은 부분: 1~23쪽

느낀점: 위인의 자서전에서 자만심에 대해서 솔직하게 쓴 부분이 흥미로웠다. 자만심에 대해서 자신도 가지고 있고 그것의 장단점을 얘기하는데 "어떤 위인이 사람의 솔직한 감정인 자만심에 대해서 말을 하겠는가?" 정말로 솔직하고 진실된다는 것을 이 부분을 읽고 느꼈다. 그리고 "후손들이 보면 좋을것 같다고 이 자서전을 썼다"고 말한 부분을 보면서 정말 노력도 많이하고 후손들까지 생각한 것을 보면 그가 생각한 삶의 태도와 자세 그리고 미래에 대해 멀리 보고 생각한 품격을 알 수 있다.

가문에 대해 조상들이 어떤 삶을 살았는지 궁금해서 알아본 내용도 흥미로웠다. 우리나라만 이런 것에 대해 관심이 많은 줄 알았는데 나라상관 없이 사람들은 이런 성격이나 습성을 전부 가지고 있는 모양이다.

 


독서일기 2장

읽은 부분:24~47쪽

여지없이 프랭클린의 솔직함이 묻어나는 감정을 쓰면서 질서정연하지 않고 사적인 글이니 편하게 쓰겠다는 말도 정말 본인이 말하듯이 쓴 부분이 마음에 들었다. 이런 솔직한 인물은 내가 크면서 봤던 우리나라 인물들과는 많이 다른것 같다. 그러면서 대화에 대해 논쟁에 대해 태도를 말한 부분이 있는데 독단적으로 자신의 주장을 말하는 것은 상대방에게 불쾌함을 줄수도 있으며 대화의 진정한 목적인 정보교환과 재미의 교환에도 맞지 않는다는 것이다. 그리고 그렇게 독단적인 태도는 사리분별이 부족하고 겸손함이 없어서 나오는 문제인데 이런 태도로는 올바른 논쟁도 되지 않으며 내 주장을 남에게 설득할 때 절대 도움되지 않는다는 것이다. 그리고 벤저민프랭클린이 어렸을 때 항해에 관심이 많았지만 아버지가 바다로 가는 것을 반대해서 이런저런 일들을 보여주며 기술공들이 하는 일들도 보고 어깨 너머 보고 배운 기술들로 자신이 작은 기계도 만들었다고 하는데 어렸을 때 이런 경험이 성장한 후에 피뢰침을 만들거나 공학적인 성과를 이루는것에 많은 영향을 끼쳤다고 생각했다. 그러다가 결국에는 책벌레인 벤저민 프랭클린은 형이 하는 인쇄소 견습공으로 들어가는데 여기서도 책을 좋아하고 글을 좋아해서 일에 적응 하며 시도 쓰고 익명으로 글을 써서 형이 쓰는 신문에 글도 제출하는 모습들을 보면서 이런 과정을 통해 생각이 넓어지고 많은 경험을 하게 되서 결국에 자신이 미래에 하게 될 일에 거름으로 쓰였고 이런 역량이 사업과 정치를 하는 것에 많은 도움이 되었을 것이라고 생각했다. 내가 그냥 읽었을 때는 "어린나이에 그런일을 하는 것이 밖에서 보면 재밌어보이지만 안에서는, 정작 본인은 이런 일을 하면서 어려움도 많았을텐데 어린 나이에 대단하다고 느꼈다. "그리고 시간이 지나 형과 다투고 여러 이유로 인해 미국 보스턴에서(형이 일하던 인쇄소가 보스턴에 있다.) 뉴욕행 배를 타는데 이 떄나이가 17살이다 거의 돈 몇푼으로 모험을 시작하는데 다음장도 기대되는 것 같다.


독서일기 3장

읽은 부분:48~61쪽

프랭클린이 새로운 직장을 찾으면서 겪게되는 온갖 고생하는 경험들과 그의 그릇을 알아보고 한 지사가 그의 아버지에게 그가 사업할것을 추천하는데 어린나이에 그런 일을 겪으면서 많은 성장을 했을 것이라고 생각을 했고 그가 또래보다 더 그릇이 커진것은 책을 읽고 많이 생각하고 항상 꺠어있어서 그랬던 것 같다.


독서일기 4장

읽은 부분 : 62~118쪽

오늘 읽은 부분에서는 먼저 프랭클린이 주변 친구들을 사귀면서 겪게 되는 인간관계에 대한 얘기가 많았다. 기억에 남는 부분은 정상적인 사람도 술에 취하고 매일 술을 먹으면 안 좋은 길로 빠지게 될 확률이 높아진다는 것이었다. 그래서 그 부분을 읽으면서 절제에 대해 다시 한번 생각하게 됐다.

프랭클린은 영국을 떠나면서 또 여러 일을 하게 되는데 인상 깊었던 점은 프랭클린과 함께 일했던 사람들은 처음에 그를 신뢰하지 않았고, 따르지도 않았지만, 프랭클린과 같이 일하면서 옆에서 그가 하는 행동과 말, 습관, 품격을 보고 리더로 따르게끔 변했다는 것이다. 그래서 이부분을 보고 "리더는 처음에는 0으로 아무것도 안보이지만 시간이 지나면서 구성원들의 존경을 얻고, 긍정적인 변화를 만들어내는 자리구나" 라고 생각했다.

그리고 또 완벽한 이미지였던 프랭클린도 젊은 시절에 실수를 여러 번 하게 되는데 그런 실수를 하면서 솔직하게 반성하고 또 반성을 그냥 하는 것이 아니라 실수했던 이유와 환경을 생각하면서 기록을 했다는 것이다.

보통 내가 읽었었던 위인전이나 성공했다는 사람들의 자서전에서 다른사람에게 보이기 싫은 실수 같은 것은 적지 않는데 정말 솔직함과 진실함의 대명사 같은 느낌을 받았다. 그리고 부지런함의 미덕에 대해서 자신이 겪은 경험을 보여주는데 나도 처음에는 자기 자랑을 열심히 하는 건가 했지만, 그것도 후손들에게 전달하기 위해서 썼다는 부분이 그의 그릇을 보여주는 것 같았다.


독서일기 5장 읽은 부분 119~132

벤저민 프랭클린이 현재 동업중인 사업체를 혼자 인수하고 결혼을 하게되며 같은 업계에서 일을 하던 사람들이 어떻게 되는지 쓴 부분을 봤는데 대부분 사치하기 좋아하거나 일을 꾸준히 안하고 다른 것에 관심을 둔 사람들은 업계에서 자연스럽게 사라졌다. 꾸준하게 참고 일하던 프랭클린은 좋은 기회를 계속 만들어서 발전하는 부분을 보고 오래 살아남은 사람의 장점 같은 것이 보였다. 그리고 도서관을 만든 일에 대해서 말하는데 확실히 도서관은 인재를 만들고 사람들의 인생을 발전시키는 역활을 크게 하는 것 같다. 미국이 초강대국이 된 이유도 이런 도서관의 역활이 큰 것 같다. 그때 우리나라는 그냥 조선시대에 계급제 생활을 하면서 정체되어있었는데 미국은 벌써 도서관을 만들고 무섭게 성장하고 있었던 것이다. 이렇게 인물이 중요하고 사람이 중요하다는 것을 프랭클린이 도서관을 짓는 부분에서 또 한번 느꼈다.


독서일기 6장

읽은 부분 133~146

오늘 읽은 벤저민 프랭클린 자서전 부분은 조금 웃긴 부분도 있었다. 먼저 프랭클린의 친구 중 한 사람이 프랭클린에게 " 프랭클린의 자서전을 꼭 써서 여러 사람이 읽게 해달라" 고 쓴 부분에 대해서는 나도 공감이 많이 되었고 속으로 응원하는 마음으로 읽었다. 왜냐하면 프랭클린은 정말로 하루하루 솔직하게 자신의 실수와 정면으로 마주쳤고, 그것을 기록하면서 반복하지 않으려고 노력했다. 그게 위대한 것이다. 인간은 실수를 반복하고, 죽을때까지 바뀌기 힘들다. 그러나 이렇게 매일매일 자신의 삶을 돌아보고, 성찰했던 그(프랭클린)의 삶을 솔직하게 자서전에 쓰면 많은 사람들이 자신의 인생에서 실수하는 부분을 줄일것이고, 더 보람찬 시간들을 많이 보낼것이라고 생각했기 때문에다. 이 부분을 읽고 그 다음에는 벤저민 프랭클린을 가상의 인물이라고 생각하고 자서전을 꼭 써야 하는 이유에 대해서 또 글이 나오는데 보면서 재밌기도 하고 글을 정말 잘 썼다는 생각이 들면서 읽었다. 자연스럽게 읽혔지만, 뜻은 명확하게 전달되는 좋은 글이라고 생각이 되었다.


독서일기 7장 읽은 부분 147~166

프랭클린 자서전을 읽고 오늘 읽은 부분 또한 신선했고 이 사람은 삶을 도덕적으로 빈틈 없이 살려고 정 말 노력했다는 생각이 든다. 그냥 목적 없이 노력하며 사는 삶이 아닌 정확하게 자신이 의도한 대로 노력 하면서 살았던 점이 보인다. 그중에서 신념만으로는 자신의 습관을 바꿀 수 없어서 좋은 습관을 만들 덕 목표를 만들고 계획표를 만들고 스스로 점검하면서 지킨 것은 지킨 대로 못 지킨 것은 계속해서 지키려 고 도전하며 관리한 부분이 인간적이라고 생각했다.


독서일기 8장

읽은 부분 167~186

도끼를 사러간 사람에 관한 일화를 말한 부분이 있었다. 이 일화의 핵심은 원래 하고자 한 일에 대해 완벽하게 마무리가 되지 않았음에도 그냥 어중간한 상태에서 일을 마무리한 일화였다. 보면서 뜨금했던 것은 내가 그랬던 적이 많아서 그렇다. 그러면서 완벽함보다 인간미라고 핑계를 대며 했던 그런 습관들이 결과적으로 일을 어렵게 만들었던 것이다. 그래서 이 글을 보면서 좀 기억에 남았고 질서의 아이콘 같이 보였던 벤저민 프랭클린도 사실은 인간이기 때문에 자신이 질서에 대한 덕목을 잘 못지켰다고 밝혔으나 그래도 계속된 노력으로 인해 과거보다 더 나아졌다는 얘기가 나온다. 핵심은 질서라는 덕목을 잘 습득하지 못햇음에도 덕을 얻기 위한 시도를 계속하면서 더 나아졌다는 것이다. 그리고 자만심이라는 것이 얼마나 안 좋고 극복하기 힘든 것인지 말하는 부분도 있는데 이 부분 또한 공감이 많이 되었던 부분이다 왜냐하면 자만심은 사람들끼리 같이 사는 사회에서 소통에 정말 안 좋은 것이기 떄문이다.

그리고 그 다음 부분에서 기억에 남은 부분은 벤저민 프랭클린은 인쇄업을 하면서 쓸모 없거나 재미없는 내용은 신문에 넣지 않는다는 부분이 나오는데 어떻게 현대의 언론들은 그 부분에서 정확하게 반대로 향하고 있는지 보면서 웃긴 생각이 들었다. 그래서 저 시대에 인쇄업을 할 때 쓸모 있고 재미있으며 사람들에게 유익한 글만 신문에 써서 알리려고 한 프랭클린이 존경스러웠고 저 시대의 신문이 궁금했다. 물론 현대에도 저런 분이 있겠지만 내 생각에는 대부분의 신문을 만드는 언론인에게 저런 신념이나 철학이 안 보이는 것 같다고 생각하기 때문이다.


독서일기 9장

읽은 부분 187-243

벤저민 프랭클린 책을 읽으면서 하루를 시작했다. 현재 우리가 일상속에서 누리는 여러 혜택과 편리한 시스템이 프랭클린의 영향을 받은 것이 많다는 것에 놀라면서 읽었고 특히 도로를 정비하는 것과 정치를 하면서 여러 사람과 소통하는 모습에 놀라면서 봤다. 그리고 읽으면서 배경지식으로 미국 역사와 세계사와 관련된 지식이 필요한 것을 알게되서 관련 정보를 찾아보면서 읽었다

도로에 먼지가 있는 것을 알고 도로를 정비하고 그것을 시스템으로 만들어서 계속 순환되게 만든 점 이런 공익 사업은 현재를 살아가는 우리에게도 영향을 끼치고 있다. 그리고 정치를 하면서 반대되는 사람에 대해서 극단으로 주장하지 않고 한번쯤 그사람이 생각할 수 있는 다양한 포인트를 알려주고 생각의 길을 열어주는 지혜까지 굉장히 현명한 사람으로 알게 되었다. 이사람의 자서전을 오대산 자연명상마을에서 읽으면서 이래서 나라에서 사람이 가장 중요하고 1명이라도 대단한 인재가 나올 경우 그 나라에 선한 영향력을 행사하고 발전시키는 원동력이 된다는 것을 느끼면서 오대산 공원을 걷기도 했다. 진부할 정도로 다 아는 얘기들이 나오지만 이것을 실천하고 측정하는 그의 습관은 자연과학에서도 여러 업적을 남겼는데 피뢰침을 발명한 것 부터 다양한 과학적 사실을 밝힌 것 까지, 어떤 성공에 대한 욕심보다는 호기심과 이런것을 해보면 어떨까하는 도전정신으로 이룬것이라고 나는 느꼈다.


독서일기 10장

읽은부분 244-307

이부분을 읽으면서는 군대와 정치에 대한 얘기가 나온다. 전쟁에는 장군들의 전략과 군사기술만 중요한 것이 아니라 장군 개인의 인성과 덕도 전쟁에 큰 영향을 끼친다는것을 느끼면서 읽었고 프랭클린이 군대까지 이끌면서 요새를 만드는 일을 보는 것은 흥미로웠다. 전혀 다른 분야에 도전해서 흥미롭다기 보다는 따를만한 사람이 있으면 그사람이 군인출신이 아니어도 믿고 모이는 사람이 많다는 것이고 그런 사람이 하는 일에는 결국 여러 사람이 일을 돕기 때문에 자만하지 않는다면 좋은 방향으로 일이 진행된다는 것이다.

정치에 대한 부분도 그의 신념을 굽히지 않고 끝까지 영주에 대한 면세조항을 찬성하지 않는 부분이 있는데 아무리 주변에서 유혹을 많이해도 그에게는 공익에 해가 된다면 절대 하지 않는다는 소신도 보였다. 이 책에서 나온 내용들은 대부분 지혜에 관련된 내용이고 그 지혜마다 각각 자신이 경험한 것을 예로 들면서 사람들에게 알리는 목적으로 쓰였다는 것이 그의 성격과 인생을 말해주는 것 같았다. 이 책을 읽으면서 다시 한번 곱씹을만한 내용은 이번주 주말에 독후감을 쓰면서 정리를 제대로 할 생각이다.


"끝."

'기타 > 독서' 카테고리의 다른 글

카라마조프가의 형제들  (0) 2022.01.08
제로투원을 읽고나서..  (0) 2022.01.08
사장을 위한 회계  (0) 2022.01.08
연세대 필독도서 추천  (0) 2021.03.17
정약용 그얼마나 좋을까 (1)  (0) 2021.03.17

6.파이썬 고급주제

멀티프로세스와 멀티 스레드

좋은 습관

단위테스트

6.1.1 subprocess 모듈

import subprocess


subprocess.run(["echo","이것은 subprocess입니다."])
subprocess.run(["sleep","10"])
root@DESKTOP-JPGB0S5:/mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject# python3.6 0_sub_pro.py
이것은 subprocess입니다.

6.1.2 threading 모듈

스레드가 여러 개로 분리되면, 스레드 간 데이터 공유의 복잡성이 증가한다. 또한 락(lock)과 데드락(deadlock)을 회피하는 데 주의를 기울여야 한다. 파이썬 프로그램에는 단 하나의 메인 스레드만 존재한다. 멀티 스레드를 사용하려면 threading 모듈을 사용한다.

내부적으로 락을 관리하려면 queue 모듈을 사용한다. 큐에 의존하면 자원의 접근을 직렬화할 수 있고, 이는 곧 한 번에 하나의 스레드만 데이터에 접근할 수 있게 한다는 뜻이다(FIFO first in, first out 방식으로). 실행 중인 스레드가 있는 동안에는 프로그램은 종료되지 않는다.

워커 스레드(worker thread)가 작업을 완료했는데도, 프로그램이 종료되지 않고 계속 실행되는 경우 문제가 될 수 있다. 스레드를 데몬(daemon)으로 변환하면 데몬 스레드가 실행되지 않는 즉시 프로그램이 종료된다. queue.join() 메서드는 큐가 빌 때까지 (큐의 모든 항목이 처리될 때까지) 기다린다. queue 모듈의 공식 문서 예쩨를 조금 수정한 다음 코드를 살펴보자.

import queue
import threading

q = queue.Queue()

def worker(num):
    while True:
        item = q.get()
        if item is None:
            break
            # 작업을 처리한다. 
        print("스레드 {0} : 처리 완료 {1}".format(num+1, item))
        q.task_done()
if __name__ == "__main__":
    num_worker_threads = 5
    threads  = []
    for i in range(num_worker_threads):
        t = threading.Thread(target=worker, args=(i,))
        t.start()
        threads.append(t)
    for item in range(20):
        q.put(item)
    
    #모든 작업이 끝날 떄까지 기다린다.(block).
    q.join()

결과

PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python .\1_threading_with_queue.py
스레드 1 : 처리 완료 0
스레드 2 : 처리 완료 1
스레드 5 : 처리 완료 3
스레드 1 : 처리 완료 5
스레드 1 : 처리 완료 8
스레드 5 : 처리 완료 7
스레드 2 : 처리 완료 6
스레드 1 : 처리 완료 9
스레드 5 : 처리 완료 10
스레드 5 : 처리 완료 13
스레드 1 : 처리 완료 12
스레드 2 : 처리 완료 11
스레드 5 : 처리 완료 14
스레드 1 : 처리 완료 15
스레드 1 : 처리 완료 18
스레드 1 : 처리 완료 19
스레드 3 : 처리 완료 4
스레드 5 : 처리 완료 17
스레드 2 : 처리 완료 16
스레드 4 : 처리 완료 2

6.1.3 뮤텍스와 세마포어

**뮤텍스**는 락과 같다. 뮤텍스는 공유 리소스에 한 번에 하나의 스레드만 접근 할 수 있도록 하는 상호 배제(mutual exclusion) 동시성 제어정책을 강제하기 위해 설계되었다. 예를 들어 한 스레드가 배열을 수정하고 있다고 가정해보자. 배열 작업을 절반 이상 수행했을 떄, 프로세서가 다른 스레드로 전환했다고 하자, 여기에서 뮤텍스를 사용하지 않는다면, 두 스레드가 동시에 배열을 수정하는 일이 벌어질 것이다. 개념적으로. 뮤텍스는 1부터 시작하는 정수다. 스레드는 배열을 변경해야 할 때마다 뮤텍스를 '잠근다.' 즉, 스레드는 뮤텍스가 양수가 될 때까지 대기한 다음 숫자를 1 감소시킨다.(이것이 곧 락이다.) 배열 수정을 마치면 뮤텍스가 잠금 해제되어 숫자가 1 증가한다.(언락). 배열을 수정하기 전에 뮤텍스를 잠근 후 수정작업이 끝나고 잠금을 해제하면, 두 스레드가 배열을 동시에 수정하는 일은 일어나지 않는다. 다음 뮤텍스 예제를 살펴보자. 예제파일을 다운로드 했다면 thread_safe 변수가 True로 작성되어 있을 텐데, 비교를 위해 다음과 같이 False로 지정하여 실행해보자.

from threading import Thread, Lock
import threading

def worker(mutex, data, thread_safe):
    if thread_safe:
        mutex.acquire()
    try:
        print("스레드 {0}: {1}\n".format(threading.get_ident(),data))
    finally:
        if thread_safe:
            mutex.release()

if __name__ == "__main__":
    threads = []
    thread_safe = False
    mutex =Lock()
    for i in range(20):
        t = Thread(target=worker, args=(mutex, i,thread_safe))
        t.start()
        threads.append(t)
    for i in threads:
        t.join()
        

실행할 때마다 결과가 다르게 나올 것이다. 이제 뮤텍스를 사용하기 위해 thread_safe 변수를 True로 설정한 후 다시 코드를 실행해보자.

한편, **세마포어**는 뮤텍스보다 더 일반적으로 사용되는 개념이다. 세마포어는 1보다 큰 수로 시작할 수 있다. 세마포어 값은 곧 한 번에 자원에 접근할 수 있는 스레드의 수다. 세마포어는 뮤텍스의 락 및 언락 작업과 유사한 대기(wait) 및 신호(signal) 작업을 지원한다. 파이썬의 뮤텍스(락)와 세마포어에 관한 내용은 threading 모듈의 공식 문서를 참조한다. 다음 세마포어 예제를 살펴보자.

import threading
import time

class ThreadPool(object):
    def __init__(self):
        self.active = []
        self.lock = threading.Lock()

    def acquire(self,name):
        with self.lock:
            self.active.append(name)
            print("획득: {0} | 스레드 풀: {1}".format(name,self.active))
    
    def release(self,name):
        with self.lock:
            self.active.remove(name)
            print("반환: {0} | 스레드 풀: {1}".format(name,self.active))
    
def worker(semaphore,pool):
    with semaphore:
        name = threading.currentThread().getName()
        pool.acquire(name)
        time.sleep(1)
        pool.release(name)

if __name__ == "__main__":
    threads=[]
    pool = ThreadPool()
    semaphore = threading.Semaphore(3)
    for i in range(10):
        t = threading.Thread(target=worker, name="스레드 "+ str(i), args=(semaphore,pool))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

결과

PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python .\3_threading_semaphore.py
획득: 스레드 0 | 스레드 풀: ['스레드 0']
획득: 스레드 1 | 스레드 풀: ['스레드 0', '스레드 1']
획득: 스레드 2 | 스레드 풀: ['스레드 0', '스레드 1', '스레드 2']
반환: 스레드 2 | 스레드 풀: ['스레드 0', '스레드 1']
반환: 스레드 1 | 스레드 풀: ['스레드 0']
반환: 스레드 0 | 스레드 풀: []
획득: 스레드 3 | 스레드 풀: ['스레드 3']
획득: 스레드 4 | 스레드 풀: ['스레드 3', '스레드 4']
획득: 스레드 5 | 스레드 풀: ['스레드 3', '스레드 4', '스레드 5']
반환: 스레드 5 | 스레드 풀: ['스레드 3', '스레드 4']
반환: 스레드 4 | 스레드 풀: ['스레드 3']
반환: 스레드 3 | 스레드 풀: []
획득: 스레드 6 | 스레드 풀: ['스레드 6']
획득: 스레드 7 | 스레드 풀: ['스레드 6', '스레드 7']
획득: 스레드 8 | 스레드 풀: ['스레드 6', '스레드 7', '스레드 8']
반환: 스레드 8 | 스레드 풀: ['스레드 6', '스레드 7']
반환: 스레드 6 | 스레드 풀: ['스레드 7']
반환: 스레드 7 | 스레드 풀: []
획득: 스레드 9 | 스레드 풀: ['스레드 9']
반환: 스레드 9 | 스레드 풀: []

6.1.4 데드락과 스핀락

**데드락**(교착상태)은 두 개 이상의 프로세스나 스레드가 서로 상대방의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태다. 프로그램에서 락을 할당하고, 락을 순서대로 획득한다면, 교착 상태를 막을 수 있다.(이는 일반적인 접근법일뿐 정교한 것은 아니다.)

다음 네 가지 조건을 모두 충족하면 데드락이 발생한다. 네 가지 조건 중 하나라도 막을 수 있다면, 데드락을 해결할 수 있다.

  • **상호배제**(mutual exclusion): 자원은 한 번에 한 프로세스(혹은 스레드)만 사용할 수 있다.
  • **점유와 대기**(hold and wait) : 한 프로세스가 자원을 가지고 있는 상태에서, 다른 프로세스가 쓰는 자원의 반납을 기다린다.
  • **비선점**(no preemtion) : 다른 프로세스가 이미 점유한 자원을 강제로 뻇어오지 못한다.
  • **순환대기**(circular wait) : 프로세스 A,B,C가 있다고 가정할 떄 A는 B가 점유한 자원을, B는 C가 점유한 자원을, C는 A가 점유한 자원을 대기하는 상태다.

**스핀락**은 (전체 시스템이 단일 애플리케이션 전용이고, 코어당 하나의 스레드만 사용하는 ) 고성능 컴퓨팅 상황에 유용한 바쁜 대기(busy wating )의 한 형태다. 스핀락은 임계 구역에 진입이 불가능할 때, 진입이 가능할 때까지 반복문을 돌면서 재시도하는 방식으로 구현된 락이다.

6.1.5 스레딩에 대한 구글 파이썬 스타일 가이드

내장 타입의 원자성(atomicity)에 의존하지 않는다. 딕셔너리 같은 파이썬 기본 데이터 타입은 원자적 연산을 수행하는 반면, 내장 타입이 원자적이지 않은 경우가 있어서 &#95;&#95;hash&#95;&#95;() 또는 &#95;&#95;eq&#95;&#95;() 메서드가 구현된 경우), 내장 타입의 원자성에 의존해선 안 된다. 또한 원자적 변수 할당에 의존하지 않아야한다. (이것은 결국 딕셔너리에 의존하기 때문이다.)

queue 모듈의 Queue 데이터 타입을 스레드 간 데이터를 전달하는 기본 방식으로 사용한다. 그렇지 않으면, threading 모듈의 락을 사용한다. 저수준의 락 대신, threading.Condition을 사용할 수 있도록 조건 변수를 적절하게 사용하는 방법을 숙지한다. 생산자-소비자 모델의 간단한 예제를 살펴보자.

import threading

def consumer(cond):
    name = threading.currentThread().getName()
    print("{0} 시작".format(name))
    with cond:
        print("{0} 대기".format(name))
        cond.wait()
        print("{0} 자원 소비".format(name))

def producer(cond):
    name = threading.currentThread().getName()
    print("{0} 시작".format(name))
    with cond:
        print("{0} 자원 생산 후 모든 소비자에게 알림".format(name))
        cond.notifyAll

if __name__ == "__main__":
    condition = threading.Condition()
    consumer1 = threading.Thread(
        name="소비자1", target=consumer,args=(condition,))
    consumer2 = threading.Thread(
        name="소비자2", target=consumer, args=(condition,))
    producer = threading.Thread(name="생산자", target=producer, args=(condition,))

    consumer1.start()
    consumer2.start()
    producer.start()
    

결과

소비자1 시작
소비자1 대기
소비자2 시작
생산자 시작
소비자2 대기
생산자 자원 생산 후 모든 소비자에게 알림
소비자2 자원 소비
소비자1 자원 소비

-기타

&#95; = "_"

6.2. 좋은 습관


6.2.1 가상 환경

프로젝트 경험이 많아질수록, 다양한 버전의 파이썬이나 라이브러리로 작업하는 일이 생길 것이다. 별도의 파이썬 가상환경을 만들기 위한 라이브러리는 많다. 이 책에서는 virtualenv와 virtualenvwrapper로 파이썬 가상환경을 간단하게 만들어보겠다.

**virtualenv**는 파이썬 프로젝트에 필요한 패키지를 사용하기 위해 필요한 모든 실행파일을 포함하는 폴더를 생성한다. 공식문서(https://docs.python-guide.org/dev/virtualenvs/#lower-level-virtualenv)에서 예제를 볼 수 있다.

#virtualenv를 설치한다.
$pip install virtualenv


#설치된 버전을 확인한다.
$virtualenv --version

#가상 환경 프로젝트를 생성한다.
$cd my_project_folder
$virtualenv my_project

#가상 환경 프로젝트를 활성화한다.
$source my_project/bin/activate

# 파이썬 외부 패키지 모듈을 설치한다. (다음 예제에서는 request 라이브러리를 설치한다.).
$(my_project)pip install requests

#가상 환경에서 설치된 외부 패키지 목록을 확인한다.
$(my_project) pip freeze

# 가상 환경 프로젝트를 비활성화한다.
$(my_project) deactivate

이렇게 설정한 로컬 가상환경을 삭제하려면, 생성한 폴더(my_project)를 삭제하면 된다.

**virtualenvwrapper**는 virtualenv를 사용하여 모든 가상 환경을 한곳에 배치한다.(https://docs.python-guide.org/dev/virtualenvs/#virtualenvwrapper). 윈도우 사용자를 위한 문서도 있으니 참조한다.

#virtualenvwrapper를 설치한다.
$pip install virtualenvwrapper

#가상 환경 폴더를 생성한다.
$export WORKON_HOME=~/Envs
$mkdir -p $WORKON_HOME
$source /usr/local/bin/virtualenvwrapper.sh

#가상 환경 프로젝트를 생성한다.
$mkvirtualenv env1

#requests 라이브러리를 설치한다.
(env1)$pip install requests

#설치된 패키지를 확인한다.
(env1)$ pip freeze

#가상 환경 프로젝트를 활성화한다.
$workon env1

$가상 환경 프로젝트를 비활성화한다.
(env1)$ deactivate

# 가상 환경 프로젝트를 삭제한다.
(env1)$ rmvirtualenv env1

6.2.2 디버깅


파이썬 디버거 **pdb**를 이용하면 디버깅을 할 수 있다.(http://pymotw.com/3/pdb) 자세한 사용법은 파이썬 공식 문서를 참조한다.

파이썬 스크립트 파일을 대화식 인터프리터를 사용해 살펴보고 싶다면 -i 뒤에 파일명을 적거나 -m pdb 뒤에 파일명을 적어서 실행하면 된다. 스크립트에 있는 변수와 함수 등을 사용할 수 있다. 먼저 -i 실행 예이다.

root@DESKTOP-JPGB0S5:/mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject# python3.6 -i 1_threading_with_queue.py
스레드 1 : 처리 완료 0
스레드 1 : 처리 완료 5
스레드 4 : 처리 완료 3
스레드 2 : 처리 완료 1
스레드 1 : 처리 완료 6
스레드 4 : 처리 완료 7
스레드 2 : 처리 완료 8
스레드 1 : 처리 완료 9
스레드 4 : 처리 완료 10
스레드 2 : 처리 완료 11
스레드 1 : 처리 완료 12
스레드 4 : 처리 완료 13
스레드 2 : 처리 완료 14
스레드 1 : 처리 완료 15
스레드 4 : 처리 완료 16
스레드 2 : 처리 완료 17
스레드 1 : 처리 완료 18
스레드 4 : 처리 완료 19
스레드 5 : 처리 완료 4
스레드 3 : 처리 완료 2
>>> q
<queue.Queue object at 0x7f7c52c5cda0>
>>> 5

다음은 -m pdb 실행 예이다.

root@DESKTOP-JPGB0S5:/mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject# python3.6 -m pdb 1_threading_with_queue.py
> /mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject/1_threading_with_queue.py(1)<module>()
-> import queue
(Pdb) help

Documented commands (type help <topic>):
========================================
EOF    c          d        h         list      q        rv       undisplay
a      cl         debug    help      ll        quit     s        unt
alias  clear      disable  ignore    longlist  r        source   until
args   commands   display  interact  n         restart  step     up
b      condition  down     j         next      return   tbreak   w
break  cont       enable   jump      p         retval   u        whatis
bt     continue   exit     l         pp        run      unalias  where

Miscellaneous help topics:
==========================
exec  pdb

(Pdb) help n
n(ext)
        Continue execution until the next line in the current function
        is reached or it returns.
(Pdb) n
> /mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject/1_threading_with_queue.py(2)<module>()
-> import threading
(Pdb) n
> /mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject/1_threading_with_queue.py(4)<module>()
-> q = queue.Queue()
(Pdb) n
> /mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject/1_threading_with_queue.py(6)<module>()
-> def worker(num):
(Pdb) n
> /mnt/d/Mastering-Python-Design-Patterns-Second-Edition/algo/advanced_subject/1_threading_with_queue.py(14)<module>()
-> if __name__ == "__main__":

pdb의 명령어를 몇 가지 살펴보겠다. c(continue)를 입력하면 프로그램을 끝까지 실행하고, s(step)은 코드 다음 줄로 넘어간다.(한 단계 씩 코드 실행step into). n(next)도 코드 다음줄로 넘어가되 프로시저 단위 실행(step over)으로서, s와 다른 점은 어떤 함수를 만날 경우 함수 전체를 실행한 뒤 다음 줄로 넘어간다는 점이다. p(point)는 표현식의 값을 출력한다. l(line)은 다음 실행할 코드를 몇 줄 보여준다. h(help)는 도움말이다.

스크립트에서 디버깅하고 싶은 위치에 pdb.set_trace() 함수를 삽입하는 방법도 있다.

import pdbpdb.set_trace()

6.2.3 프로파일링

프로그램이 매우 느리게 실행되거나 예상보다 많은 메모리가 소비된다면, 자료구조나 알고리즘을 잘못 선택했거나 비효율적으로 구현했기 때문인 경우가 많다. 이 경우 다음과 같이 성능 항목을 검토한다.

  • 읽기 전용 데이터는 리스트 대신 튜플을 사용한다.
  • 반복문에서 항목이 많은 리스트나 튜플 대신 **제너레이터**를 사용하여 순회한다.
  • 문자열을 연결할 떄 + 연산자로 문자열을 연결(concatenate)하는 대신, 리스트에 문자열을 추가(append)한 후, 마지막에 리스트의 항목을 모두 하나로 연결(join)한다. 다음 구글 파이썬 스타일 가이드의 예제 코드를 살펴보자.
#좋은 예items=['<table>']for last_name, first_name in employee_list:    items.append('<tr><td>%s,%s</td></tr>' %(last_name,first_name))    items.append('</table>')    employee_table=''.join(items)# 나쁜 예employee_table = '<table>'for last_name, first_name in employee_list:    employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)    employee_table += '</table>'

cProfile 모듈

cProfile 모듈은 호출 시간에 대한 세부 분석을 제공하며,병목 현상(bottleneck)을 찾는 데 사용된다. 흔히 다음과 같은 형태로 사용한다.

import cProfilecProfile.run('main()')

조금 더 실제적인 예는 다음과 같다.

import cProfileimport timedef sleep():    time.sleep(5)def hello_world():    print("Hello World!")def main():    sleep()    hello_world()cProfile.run('main()')

실행 결과

PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python .\test.pyHello World!         8 function calls in 5.014 seconds   Ordered by: standard name   ncalls  tottime  percall  cumtime  percall filename:lineno(function)        1    0.000    0.000    5.014    5.014 <string>:1(<module>)        1    0.000    0.000    5.013    5.013 test.py:17(sleep)        1    0.000    0.000    0.001    0.001 test.py:20(hello_world)        1    0.000    0.000    5.014    5.014 test.py:23(main)        1    0.000    0.000    5.014    5.014 {built-in method builtins.exec}        1    0.001    0.001    0.001    0.001 {built-in method builtins.print}        1    5.013    5.013    5.013    5.013 {built-in method time.sleep}        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

혹은 다음과 같이 스크립트 파일에 대해 실행할 수도 있다.

python -m cProfile -o profile.dat 1_threading_with_queue.pyPS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python -m cProfile -o profile.dat .\1_threading_with_queue.py스레드 2 : 처리 완료 0스레드 1 : 처리 완료 1스레드 2 : 처리 완료 3스레드 5 : 처리 완료 5스레드 5 : 처리 완료 8스레드 2 : 처리 완료 7스레드 2 : 처리 완료 10스레드 4 : 처리 완료 2스레드 4 : 처리 완료 12스레드 4 : 처리 완료 13스레드 4 : 처리 완료 14스레드 2 : 처리 완료 11스레드 1 : 처리 완료 6스레드 5 : 처리 완료 9스레드 2 : 처리 완료 16스레드 2 : 처리 완료 19스레드 5 : 처리 완료 18스레드 4 : 처리 완료 15스레드 3 : 처리 완료 4스레드 1 : 처리 완료 17$python -m pstats profile.dat

이 밖에 프로파일링에 대한 자세한 내용은 파이썬 공식 문서를 참조한다.

timeit 모듈

코드 일부분의 실행 시간을 확인하는 데 사용한다. 다음예제를 살펴보자.

import timeitprint(timeit.timeit("x = 2+2"))print(timeit.timeit("x = sum(range(10))"))

결과

PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python .\test.py0.0251481999999999960.9078216

다음과 같이 스크립트로 실행할수도 있다.

PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python -m timeit "d={}"5000000 loops, best of 5: 49.3 nsec per loopPS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python -m timeit "import collections" "d = collections.OrderedDict()"1000000 loops, best of 5: 371 nsec per loop

time 모듈의 time()함수를 사용한 아주 간단한 예제를 살펴보자.

import time


def sumOfN2(n):
    start = time.time()
    theSum = 0
    for i in range(1,n+1):
        theSum = theSum + i
    end = time.time()
    return theSum, end-start

if __name__ == "__main__":
    n = 5
    print("총 합계: %d\t 시간: %10.7f초" % sumOfN2(n))
    n = 200
    print("총 합계: %d\t 시간: %10.7f초" % sumOfN2(n))
PS D:\Mastering-Python-Design-Patterns-Second-Edition> cd .\algo\advanced_subject\
PS D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject> python .\5_using_time_module.py
총 합계: 15      시간:  0.0000000초
총 합계: 20100   시간:  0.0000000초

6.3. 단위 테스트

개별 함수 및 클래스의 메서드에 대한 테스트 코드를 작성하여, 예상한 값이 맞게 나오는지 확인하는 것이 좋은 습관이다. 파이썬 표준 라이브러리는 이러한 단위 테스트(unit test)를 위해 doctest와 unittest 모듈을 제공한다. 또한 외부 라이브러리인 pytest 모듈도 있다.

6.3.1 용어

  • 테스트 픽스처(test fixture) : 테스트 설정을 위한 코드( 예: 테스트용 입력 파일을 만들었다 삭제하는 코드)
  • 테스트 케이스(test case):테스트의 기본 단위
  • 테스트 스위트(test suitte): unittest.TestCase의 하위 클래스에 의해 생성된 테스트 케이스 집합. 각 테스트 케이스의 메서드 이름은 test로 시작한다.
  • 테스트 러너(test runner): 하나 이상의 테스트 스위트를 실행하는 객체

6.3.2 doctest

먼저 **doctest** 모듈은 모듈과 함수의 독스트링(docstring) 안에 테스트 코드를 작성할 때 사용한다. 테스트를 작성한 후 , 다음 코드 세 줄만 추가하면된다.

if __name__ == "__main__"   import doctest   doctest.testmod()

doctest 모듈이 포함된 프로그램은 두 가지 방법으로 실행할 수 있다. 먼저 -v 옵션으로 파이썬을 실행하는 방법이다. 파이썬 공식문서의 예제를 살펴보자.

"""This is the "example" module.the example module supplies one function, factorial(). For example>>> factorial(5)120"""def factorial(n):    """Return the factorial of n, an exact integer >= 0        >>> [factorial(n) for n in range(6)]      [1,1,2,6,24,120]    >>> factorial(30)    >>> factorial(-1)     >>> factorial(30.0)     >>> factorial(1e100)    """    import math    if not n >= 0:        raise ValueError("n must be >=0")    if math.floor(n) != n:        raise ValueError("n must be exact integer")    if n+1 == n: #catch a value like 1e300        raise OverflowError("n too large")    result =1    factor =2    while factor <= n:        result *= factor        factor += 1    return result    if __name__ == "__main__":        import doctest        doctest.testmod()        

다음과 같이 unittest 모듈과 함께 실행할 수도 있다.

>>> import doctest>>> import unittest>>> import doctest_factorial>>>>>> suite unittest.TestSuite()>>> suite.addTest(doctest.DocTestSuite(doctest_factorial))>>> runner = unittest.TextTestRunner()>>> print(runner.run(suite))

6.3.3 pytest

외부 라이브러리인 **pytest**는 사용법이 매우 쉽다. test로 시작하는 파일에서 test로 시작하는 함수를 작성하면 된다. 간단한 예를 살펴보겠다.

먼저 pytest 라이브러리를 설치한다.

$ pip install pytest

다음 코드를 간단하게 테스트해보자.

def func(x):
    return x + 1

def test_answer():
    assert func(3) == 51

    

터미널의 현재 위치에서 다음 명령을 실행하면, 파일명이 test로 시작하는 파이썬 스크립트가 실행된다.

결과

================================================================================= test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: D:\Mastering-Python-Design-Patterns-Second-Edition\algo\advanced_subject
collected 1 item / 1 error

========================================================================================= ERRORS =========================================================================================
_____________________________________________________________________________ ERROR collecting study_test.py _____________________________________________________________________________
study_test.py:4: in <module>
    for last_name, first_name in employee_list:
E   NameError: name 'employee_list' is not defined
================================================================================ short test summary info =================================================================================
ERROR study_test.py - NameError: name 'employee_list' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

다음과 같이 파일을 지정하여 실행할 수 있다.

$ py.test test_pytest.py

파이썬 디버거 pdb와 같이 실행할 수 있다.

$ py.test --pdb

+ Recent posts