파이선에서 날짜에 대한 연산을 도와주는 calendar라는 모듈이다. 현실세계의 날짜 연산에서 사용될 수 있는 계산 함수들을 제공한다. 하나하나 알아보자


Calendar 객체

모든 날짜 연산은 캘린더 객체에서 이루어 진다. 객체는 다음과 같이 생성한다.

>>> my_cal = calendar.Calendar()
### Calendar(firstweekday=0)로 인자지정이 가능하다. firstweekday는 언제가 그 주의 시작요일인지 정하는 것이다)

Calendar객체가 제공하는 연산은 다음과 같다.

  • iterweekdays() : 0~6까지의 요일 인덱스를 담고 있는 iterator를 반환한다. firstweekday가 지정되어있으면 시작 순서가 바뀐다.
  • itermonthdays(year,month) : year년도의 month동안의 iterator를 반환한다. 그니까 itermonthdays(2020,12)면 2020년 12월의 일을 포함한 iterator를 반환하는 건데, 달력에 표기하기 위한 만큼의 넘어가는주와 넘어오는주를 포함해서 반환한다. 그때 값은 0이다.
  • itermonthdays2(year,month) : itermonthdays와 같은 범위이나, (일, 요일)의 튜플형태의 iterator를 반환한다.
  • itermonthdays3(year,month) : itermonthdays와 같은 범위이나, (년, 월, 일)의 튜플형태의 iterator를 반환한다.
  • itermonthdays4(year,month) : itermonthdays와 같은 범위이나, (년, 월, 일, 요일)의 튜플형태의 iterator를 반환한다.
  • monthdatescalendar(year,month) : itermonthdays와 같은 범위이나, 매일매일에 대한 datetime.date객체를 리스트로 반환한다.
  • monthdays2calendar(year,month) : itermonthdays와 같은 범위이나, 하루하루는 (일, 요일)의 튜플로, 주 단위로 리스트로 반환한다.
  • monthdayscalendar(year,month) : itermonthdays와 같은 범위이나, 주 단위 리스트로 반환한다
  • yeardatescalendar(year,width=3) : 연도에 대한 매일매일을 datetime.date객체로 월 단위 리스트로 반환한다. 이때 한 리스트원소에 얼만큼의 월이 들어갈지가 width이다.
  • yeardays2calendar(year,width=3) : 위와 유사하나 datetime.date객체는 아니고 요일이 포함된 튜플을 반환한다.
  • yeardayscalendar(year.width=3) : 위와 유사하나 datetime.date객체는 아니고 날짜만 해당하는 리스트를 반환한다.

 

 

TextCalendar 객체

Calendar는 숫자와 객체로 이루어져 있어 가시성이 떨어진다. 조금 보기 쉬운 표현을 위해서 TextCalendar라는 객체가 존재한다.

>>> my_cal = calendar.TextCalendar()
### TextCalendar(firstweekday=0)로 인자지정가능하다. firstweekday는 어디가 요일의 시작일지 이다.

TextCalendar 객체가 제공하는 연산은 다음과 같다.

  • formatmonth(theyear,themonth,w=0,l=0) : 해당 월의 달력을 출력한다. w, l로 달력 크기 지정을 한다.
  • prmonth(theyear,themonth,w=0,l=0) : formatmonth의 결과를 인쇄한다.
  • formatyear(theyear,w=2,l=1,c=6,m=3) : 해당 년도의 달력을 출력한다. w,l로 달력의 크기를, c로 월 사이사이의 공간의 크기를, m으로 몇 열로 출력할지를 정한다.
  • pryear(theyear,w=2,l=1,c=6,m=3) : formatyear의 결과를 인쇄한다.

 

 

HTMLCalendar 객체

HTMLCalendar은 HTML에서 사용할 수 있게끔 HTML 태그를 붙여준다. 

>>> my_cal = calendar.HTMLCalendar()
### HTMLCalendar(firstweekday=0)로 인자지정가능하다. firstweekday는 어디가 요일의 시작일지 이다.

TextCalendar 객체가 제공하는 연산은 다음과 같다.

  • formatmonth(theyear,themonth,withyear=True) : 월의 달력을 html Table로 반환한다. withyear의 설정으로 연도를 헤더에 표현할지를 선택할 수 있다.
  • formatyear(theyear,width=3) : 연도의 달력을 html Table로 반환한다. width는 행에 얼만큼의 월이 들어 갈지 이다.
  • formatyearpage(theyear,width=3,css='calendar.css',encoding=True) : 연도의 달력을 완전한 html로 생성한다. css를 지정할 수 있다.

* TextCalendar에는 css를 재정의 할 수 있도록 여러가지 Attrib도 있으니 사용이 필요하다면 포스팅 끝의 공식 문서를 참고바란다.

 

 

 

정적 매서드

다음은 calendar에서 제공하는 정적 매서드이다. Calendar.매서드명 으로 객체 생성 없이 이용 가능하다.

  • setfirstweekday(weekday) : 현재 import된 모듈의 calendar weekday를 weeday로 고정한다 주의 첫요일이 바뀐다. calendar에는 리터럴로 calendar.MONDAY등을 제공하니 사용 바란다.
  • firstweekday() : 요일의 현재 설정을 반환한다.
  • leapdays(y1,y2) : y1년과 y2년 사이의 윤년의 수를 반환한다.
  • weekday(year,month,day) : year년 month월 day일의 요일을 반환한다.
  • weekheader(n) : 요일의 헤더를 반환한다. n은 헤더의 길이이다(1 : "F", 3 : "Fri" 등)
  • monthrange(year,month) : year년 month월의 첫날의 요일과 월의 일수를 반환한다.
  • monthcalendar(year,month) : year년 month의 행렬을 반환한다.
  • month(theyear,themonth,w=0,l=0) : TextCalendar의 formatmonth를 바로 이용 가능하게 한다.
  • calendar(year,w=0,l=0,c=6,m=3) : TextCalendar의 formatyear를 바로 이용 가능하게 한다.

 

 

어트리뷰트

다음은 Calendar가 가진 클래스 어트리뷰트이다.

  • calendar.day_name : 현재 로케일의 요일을 나타내는 배열이다.
  • calendar.day_abbr : 현재 로케일의 약식 요일을 나타내는 배열이다.
  • calendar.month_name : 현재 로케일의 연중 월을 나타낸다.
  • calendar.mont_abbr : 현재 로케일의 연중 월을 약식으로 나타낸다.

 

* 출처 : https://docs.python.org/ko/3/library/calendar.html

 

calendar — 일반 달력 관련 함수 — Python 3.9.5 문서

calendar — 일반 달력 관련 함수 소스 코드: Lib/calendar.py 이 모듈을 사용하면 유닉스 cal 프로그램과 같은 달력을 출력할 수 있으며, 달력과 관련된 유용한 추가 함수를 제공합니다. 기본적으로, 이

docs.python.org


정적 매서드의 프린팅 함수는 제외하고 기술했다. 참고하도록 하자

Python Challenge 14의 Url은 다음과 같다

Python challenge 14 : http://www.pythonchallenge.com/pc/return/italy.html

 


구성

페이지에는 사진이 2개 있다. 위에 보이는 나선빵은 italy라는 이름을 가지고 있고, 아래 바코드 같은 사진은 wire.png라는 이름을 가지고 있다. 별다른 상호작용을 보이지 않으니 주석을 보자

가운데 주석으로 100*100 = (100+99+99+98) 이라는 글이 쓰여있다. 


해결 아이디어

가운데 식으로 부터 다음을 생각할 수 있다.

즉 빙글빙글 돌면서 그림을 채우는 것이다. 채울 대상은 wire.png이다. 다운로드하여보면 10000*1의 크기로 된 사진이다.

### 14.py

from PIL import Image

if __name__ == "__main__" :

    with Image.open("wire.png") as img :
        tar = Image.new('RGB',(101,101))
        length = img.width

        dx = [1,0,-1,0]
        dy = [0,1,0,-1]
        direction = 0
        x = 100
        y = 99
        cur_x = 0
        cur_y = 0
        index = 0

        while(x+y>0) :
            if(direction%2==0) :
                for i in range(x) :
                    tar.putpixel((cur_x,cur_y),img.getpixel((index,0)))
                    cur_x += dx[direction]
                    index += 1
                x -= 1
            else :
                for i in range(y) :
                    tar.putpixel((cur_x,cur_y),img.getpixel((index,0)))
                    cur_y += dy[direction]
                    index += 1
                y -= 1
            direction = (direction + 1)%4

        tar.save("new_wire.png")

출력된 사진 new_wire.png는 다음과 같다.

 

허어... 커여어.... 찾았다.

Answer Url : http://www.pythonchallenge.com/pc/return/cat.html

 

Python Challenge 13의 url은 다음과 같다.

Python challenge 13 : http://www.pythonchallenge.com/pc/return/disproportional.html


구성

 

가운데 영어는 다음과 같은 의미이다.

 

"나쁜 놈에게 전화할 것"

 

상호작용은 5번 다이얼에 href로 phonebook.php로 이동할 수 있다. 주석 소스를 확인하자

저번 꿀벌 문제처럼 area태그에 걸려있는 coords와 href로 이동을 시켜주는구나 싶다.

phonebook.php의 페이지는 다음과 같다.

응? XML Error Code 105로 오류 페이지가 확인된다. 어허... 어디서부터 접근을 해야 되는 것일까


해결 아이디어

모르면 검색이다. 에러에 출력된 대로 XML faultCode 105를 검색하자. 검색에 많이 올라오는 것이 XML-RPC라는 keyword가 압도적으로 많이 보인다. 이게 무엇인지부터 알아보자

 

XML-RPC

RPC프로토콜을 아는가? 원격 프로시저 호출이라는 프로토콜이다. 분산 프로그램에서 많이 이용하는 방법으로 다른 주소 공간에서 함수나 프로시저를 실행시킬 수 있게 해주는 프로토콜로 JSON을 이용한 방법과 XML을 이용한 방법이 있다고 한다. 그럼 Python에서 XML-RPC를 사용할 수 있는 방법을 알아보자

 

Python에서 xmlrpc의 사용

일단 다음의 Doc에서 자세한 사용법을 설명한다.

https://docs.python.org/ko/3/library/xmlrpc.html

 

xmlrpc — XMLRPC 서버와 클라이언트 모듈 — Python 3.9.5 문서

xmlrpc — XMLRPC 서버와 클라이언트 모듈 XML-RPC는 HTTP를 트랜스포트로 사용해서 전달되는 XML을 사용하는 원격 프로시저 호출 방법입니다. 이를 통해, 클라이언트는 원격 서버(서버는 URI로 지정됩니

docs.python.org

 

서버는 Python Challenge에 있으니 우리는 client를 이용해서 붙으면 된다.

필자는 문제에 필요한 함수만을 사용할 것이며 그 항목은 다음과 같다.

  • xmlrpc.client.ServerProxy(url) : 서버에 붙을 수 있도록 client객체를 리턴한다.
  • ServerProxy.system.listMethods() : 서버에서 사용할 수 있는 매머드의 종류를 list로 리턴한다.
  • ServerProxy.system.methodSignature(method) : method의 인자 값의 종류와 리턴 값을 보여준다.
  • ServerProxy.system.methodHelp(method) : method의 사용법을 리턴한다.

다음은 phonebook.php에서 사용할 수 있는 함수를 찾는 코드이다.

### 13.py

import xmlrpc.client as xc

if __name__ == "__main__" :
    url = "http://www.pythonchallenge.com/pc/phonebook.php"
    serv = xc.ServerProxy(url)
    print(serv.system.listMethods())

결과는 다음과 같다.

['phone', 'system.listMethods', 'system.methodHelp', 
'system.methodSignature', 'system.multicall', 'system.getCapabilities']

phone이라는 매더스가 보인다. 사용법을 확인하기 위해 다음과 같은 코드를 작성하자

### 13_2.py

import xmlrpc.client as xc

if __name__ == "__main__" :
    url = "http://www.pythonchallenge.com/pc/phonebook.php"
    serv = xc.ServerProxy(url)

    ### phone메서드의 사용법을 알아낸다.
    print("Usage :",serv.system.methodHelp("phone"))

    ### phone메서드의 인자와 리턴값을 확인한다.
    ### 첫 인덱스는 입력, 두 번째 인덱스는 출력이다.
    sig = serv.system.methodSignature("phone")
    print("In :",sig[0][0]," / out :",sig[0][1])

결과는 다음과 같다.

Usage : Returns the phone of a person
In : string  / out : string

으흠 즉 phone매서드는 string을 받아들여서 string을 반환하는 매서드이다. 예시로 다음을 입력해보자

>>> serv.phone("REDUCTO")
'He is not the evil'

그렇구먼 이제 나쁜 놈을 찾으면 된다. 그런데 나쁜 놈이 누구지? 정답은 저번 글에 있다.

2021.05.19 - [Python/Python Challenge] - [Python Challenge 12] 나쁜놈은 누구인가

 

[Python Challenge 12] 나쁜놈은 누구인가

Python Challenge 12의 url은 다음과 같다. Python challenge 12 : http://www.pythonchallenge.com/pc/return/evil.html 구성 위와 같은 사진이 있다. 사진의 파일명은 evil1.jpg(응? 왜 1이지? 점 심나 가서 먹..

tutoreducto.tistory.com

Bert 네이놈!

>>> serv.phone("Bert")
'555-ITALY'

정답은 ITALY이다. 옛날 *023#을 알고 있는가? 장난전화할 때 많이 쓰던 번호인데(-틀-).... 되었다 하여튼 555는 미국식으로 없는 번호를 의미할 때 뭐 저렇게 한다고 한다. 정답 "ITALY" 찾았다.

Answer Url? : http://www.pythonchallenge.com/pc/return/ITALY.html

더럽게 깐깐하게 구네

Answer Url : http://www.pythonchallenge.com/pc/return/italy.html

bzip2이라는 확장자가 있다. gzip과 비슷하게 하나의 파일만을 압축할 수 있으며, bz2라는 확장자명을 가진다. (오 모듈 이름이랑 똑같다.) 압축 과정은 다른 정보보안 이론과정에 자세하게 소개하는 것으로 하고 Python에서 bz2의 사용법을 알아보자


파일 압축/해제관련

  • bz2.open(filename, mode="rb"...) : 파일 혹은 데이터를 바이트 혹은 텍스트로 오픈하는 모드이다. 리턴 값은 File 객체이다. 
  • bz2.BZ2FILE(filename, mode="r") : 위에 객체 반환과 같으나, 무조건 binary로 open 한다.

증분 압축/해제관련

  • bz2.BZ2Compressor(compresslevel=9) : 증분 압축기 객체를 반환한다.  compress(data)와 flush()를 사용하고 이 객체는 사라진다.
  • bz2.BZ2Decompressor() : 증분 압축 해체기 객체를 반환한다. decompress(data)를 통해서 압축 해제된 data를 바이트 열로 가져올 수 있다.

일괄 압축/해제관련

  • bz2.compress(data,xompresslevel=9) : 일괄 압축을 수행한다.
  • bz2.decompress(data) : 압축된 data를 압축 해제해서 바이트 열을 반환한다.

사용 예제

import bz2

### 일괄 압축으로 압축하여 target.bz2에 작성
raw = b"I Love Python"
with open("target.bz2","wb") as f:
    f.write(bz2.compress(raw))

### 압축된 target.bz2 에서 문자열 읽기
with open("target.bz2","rb") as f:
    data = f.read()
    print(bz2.decompress(data).decode())

 

* 일괄 압축은 데이터를 인수로 주면서 바로 압축이 수행되고, 증분 압축방식은 압축기 개체가 생성되고 나서 데이터가 추가되는 방식이다.

 

출처 : https://docs.python.org/ko/3/library/bz2.html#incremental-de-compression

 

bz2 — bzip2 압축 지원 — Python 3.9.5 문서

bz2 — bzip2 압축 지원 소스 코드: Lib/bz2.py 이 모듈은 bzip2 압축 알고리즘을 사용하여 데이터 압축과 압축 해제를 위한 포괄적인 인터페이스를 제공합니다. bz2 모듈에는 다음이 포함됩니다: 이 모

docs.python.org

 

Python Challenge 12의 url은 다음과 같다.

Python challenge 12 : http://www.pythonchallenge.com/pc/return/evil.html


구성

위와 같은 사진이 있다. 사진의 파일명은 evil1.jpg(응? 왜 1이지? 점 심나 가서 먹을 거 같아) 웹상에서 상호작용은 특별히 없는 것 같으니 주석을 확인해 보자

title이 dealing evil이다. 돌린다는 것에 이번문제를 해결할 key가 있는 거 같다.


해결 아이디어

사진이름에 이상함을 느끼자 evil1.jpg가 있다는 것은 evil2.jpg도 있다는 것이다. 아래는 evil2.jpg를 요청한 결과이다.

gfx로 다운로드하라고 한다. evil2.gfx를 요청하면 파일을 다운로드할 수 있다. gfx라는 확장자는 애니메이션 파일이지만 문제에서는 이를 표현하고자 한 것은 아닐 것이다. 파일을 hxd로 뜯자

잘 보면 헤더에 간격이 띄워진 상태로 JFIF 등의 문자열이 있는 것을 확인할 수 있다. evil1.jpg에서 5등분이 난 카드처럼 우리도 바이트를 5개로 나누어보자 아래는 구현 코드이다.

### 12.py

if __name__=="__main__" :
    with open("evil2.gfx","rb") as img :
        data = img.read()
        for i in range(5) :
            with open(str(i)+".dat","wb") as f:
                f.write(data[0+i::5])

오호 잘 쪼개졌다. 헤더 확인 결과 각각의 폴더는 다음의 확장자를 가진다.

  • 0.dat : jpg
  • 1.dat : png
  • 2.dat : gif
  • 3.dat : png
  • 4.dat : jpg

사진을 첨부하고 싶은데 알올라간다. 삭선 되어있는 4.jpg를 제외하면 글자는 disproportional이다. 찾았다.

Answer Url : http://www.pythonchallenge.com/pc/return/disproportional.html


참고사항이다. evilN.jpg를 계속 탐색하다 보면 evil4.jpg까지 도달한다. 이미지처럼 보이지 않아도 다운로드하여서 파일을 헥스로 보면 다음의 글이 써져있다.

그렇다. 바트가 나쁜놈이다. 기억하자

Python Challenge 11의 url은 다음과 같다.

Python challenge 11 : http://www.pythonchallenge.com/pc/return/5808.html


구성

그림 한 장이 있다. 파일명은 cave.jpg이고 hxd로 확인해보고 웹에서 클릭을 해봐도 상호작용은 없다. 페이지 소스를 보도록 하자

골 때린다. 아무것도 없다. 


해결 아이디어

문제 이름이 odd, even이다. 그럼 그림의 홀수번째 픽셀과 와 짝수번째 픽셀을 나누면?? 이거 가능하겠다는 생각이 들었다. 아래는 구현 코드이다.

### 11.py

from PIL import Image

if __name__=="__main__" :
    with Image.open("cave.jpg") as img :
        odd_img = Image.new('RGB',img.size)
        even_img = Image.new('RGB',img.size)
        for y in range(img.height) :
            for x in range(img.width) :
                if((x+y)%2==0) :
                    even_img.putpixel((x,y),img.getpixel((x,y)))                
                else :
                    odd_img.putpixel((x,y),img.getpixel((x,y)))

        odd_img.save("odd_image.png")
        even_img.save("even_image.png")

다음과 같은 두개의 그림을 가질 수 있다.

<odd_image.png  ---  even_image.png>

even Image의 우측 상단을 자세하게 보면 evil이라는 글자 열이 쓰여있다. 찾았다.

Answer Url : http://www.pythonchallenge.com/pc/return/evil.html

+ Recent posts