오랜만에 Python과 매우 친해지기 글을 포스팅한다. 사실 지난 Decorator로 어지간한 Python사용법은 거의 포스팅했다고 생각했는데, 몇 가지 빼먹은 게 있더라, 차츰차츰 올리는 걸로 하고, 이번에는 다시 올리는 Python과 매우 친해지기 파라미터 편이다.


파라미터의 사용

 

파라미터는 어디에 사용될까? 아래의 예시를 보자

##코드
class my_class :

    def __init__(self, name) :
        self.my_name = name

    def print_name(self) :
        print(self.my_name)
    
if __name__=="__main__" :
    chulsu = my_class("철수")		## my_class에 파라미터로 "철수" str 전달
    chulsu.print_name()
    
    
    
## 출력결과
철수

 

 

위와같이 클래스(def__init__에) 나 함수 블록에 전달하는 "인자"를 우리는 파라미터라고 부른다.

이런 파라미터들은 함수나 클래스에 정의된 대로 넘겨줄 수 있다. 다음의 예시를 보자

## 코드
def salute(name,job) :
    print("my name is "+name+" and my job is "+job)


if __name__=="__main__":
    salute("chulsu","java programmer")
   
   
   
## 출력결과
my name is chulsu and my job is java programmer

파라미터에 여러가지값을 넘길 수 있는 방안이 있을까?? 가령 학생이 10명 있는 반의 학생관리 프로그램에서 학생정보를 출력하는 함수를 정의한다고 생각하자. 기본값으로 ""를 주어 여러 학생을 받는다는 형식으로 본다면 다음과 같이 길고 더럽고 추잡한 코드가 나올 것이다.

## 코드
def print_student(stu1="", stu2="", stu3="", stu4="", stu5="", stu6="",
                      stu7="",stu8="",stu9="",stu10="") :
    print("stu1 : "+stu1)
    print("stu2 : "+stu2)
    print("stu3 : "+stu3)
    print("stu4 : "+stu4)
    print("stu5 : "+stu5)
    print("stu6 : "+stu6)
    print("stu7 : "+stu7)
    print("stu8 : "+stu8)
    print("stu9 : "+stu9)
    print("stu10 : "+stu10)

if __name__=="__main__":
    print_student("chulsu","yonghee","minsu","jinsu","binsu","osu",stu9="pengsu")
    
    
    
## 출력결과
stu1 : chulsu
stu2 : yonghee
stu3 : minsu
stu4 : jinsu
stu5 : binsu
stu6 : osu
stu7 : 
stu8 : 
stu9 : pengsu
stu10 :

 

이렇게 된다. 10명이라면 다행이지만, 100명 200명이면 어떻할까?(리스트로 받으면 된다고? 엄만 갈 거야 리스트는 여기 있어 그럼) 이와 같은 문제를 멋지게 해결할 수 있는 방법이 가변 파라미터이다.


args, kwargs

args의 약어는 arguments, kwargs의 약어는 Keyword Arguments이다. 각각 튜플과 딕셔너리형태로 다수의 파라미터를 제한 없이 받아들일 수 있는 특징이 있다. 마치 java의...이나 Kotlin의 vararg 같은 특성을 가진다. args의 앞에는 *, kwargs앞에는 **로 이 파라미터들이 가변이라는 것을 표현하는데, 다음의 예시를 보자 

### args의 예시, 코드
def print_student(*stu) :
    
    print(type(stu))        ## 가변인자 stu의 자료구조는 tuple
    
    index = 0
    for s in stu :
        print("stu"+str(index)+" : "+s)
        index += 1
    

if __name__=="__main__":
    print_student("chulsu","yonghee","minsu","jinsu","binsu","osu")
    
    
### args의 예시, 출력결과
<class 'tuple'>
stu0 : chulsu
stu1 : yonghee
stu2 : minsu
stu3 : jinsu
stu4 : binsu
stu5 : osu

출력 결과

다음은 kwargs의 예시이다.

### kargs의 예시, 코드
def print_student(**stu) :
    
    print(type(stu))        ## 가변인자 stu의 자료구조는 dictionary
    
    for k,v in stu.items() :
        print(k + " : " + v)
    

if __name__=="__main__":
    print_student(stu1 = "chulsu",stu2 = "yonghee",stu3 = "minsu")
    
    
## kargs의 예시, 출력결과
<class 'dict'>
stu1 : chulsu
stu2 : yonghee
stu3 : minsu

 

 

흔히 사용하는 우리의 python내장함수 print도 가변인자로 구성되어 있다. print는 다음과 같이 정의되어있다.

 

  ★ print(*objects, sep=' ', end='\n,  file=sys.stdout, flush=False)

 

맨앞 *objests가 가변인자 *args로 지정되어 있는 것을 알 수 있다.

 

Python Challenge 30의 Url은 다음과 같다

Python challenge 30 : http://www.pythonchallenge.com/pc/ring/yankeedoodle.html


구성

문제 페이지에 들어가면 휴식이 있다. 아래 영어는 다음의 의미를 가지고 있다.

 

"이 그림은 오직 당신의 휴식을 위함"

 

(X치고 다음 문제로 보내주면 좋겠다. ) 주석을 한번 보도록 하자

주석은 csv(comma separated values)을 확인하라고 되어있다. yankeedoodle.csv를 다운로드 하자.

확인 결과는 다음과 같다. 

으흠; 어떤 자료인걸까.... 0~1 사이 소수 값들이다.


해결 아이디어

이미지 모드에 'F'모드라는 것이 있다.

https://ko.wikipedia.org/wiki/TIFF

 

TIFF - 위키백과, 우리 모두의 백과사전

TIFF 위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

F 모드는 (32-bit floating point pixels) 그니까 32bit 정확도의 소수 값을 가지는 이미지 모드이다. 딱0~1 사이 값들을 보유하고, 우리 상황이랑 정확하게 틀어맞는다. 일단 Image의 가로세로를 확인하기 위해 w, h를 구하는 내포를 하나 만들고 이미지를 만들어 보자

### 30_1.py

import re
from PIL import Image

if __name__=="__main__" :
    with open("yankeedoodle.csv","r") as f:
        data = f.read()
        data = re.findall("\d[.]\d*",data)

    w,h = (x for x in range(2,len(data)//2) if len(data)%x==0)

    img = Image.new("F",(w,h))
    img.putdata([float(x) for x in data], 256)
    img.show()

 

* img.save("뭐. tiff")가 잘 안된다.

* putdata 2번째 파라미터는 비중치이다. 높으면 높을수록 선명해진다. 어차피 흑백이긴 하지만

 

다음과 같은 사진을 구할 수 있다.

응? 뭔가 Python 식 같은 표현이 나왔는데, 사진이 돌아가 있다. 조금 보기 편하게 돌려보자

### 30_2.py

import re
from PIL import Image

if __name__=="__main__" :
    with open("yankeedoodle.csv","r") as f:
        data = f.read()
        data = re.findall("\d[.]\d*",data)

    w,h = (x for x in range(2,len(data)//2) if len(data)%x==0)

    img = Image.new("F",(w,h))
    img.putdata([float(x) for x in data], 256)
    
    img = img.transpose(Image.FLIP_LEFT_RIGHT)
    img = img.transpose(Image.ROTATE_90)
    img.show()

다음과 같은 사진을 구할 수 있다.

일단 그렇다. 식은

n = str(x [i])[5] + str(x [i+1])[5] + str(x [i+2])[6]

 

이고, 식을 조금 해석해 보자면, 첫 번째, 두 번째, 세 번째의 offset5, offset5, offset6을 더한 값을 2ke2ke 해보라는 의미이다.

코드를 짜 보자

### 30_3.py

import re
from PIL import Image

if __name__=="__main__" :
    with open("yankeedoodle.csv","r") as f:
        data = f.read()
        data = re.findall("\d[.]\d*",data)

    first,second,third = data[0::3],data[1::3],data[2::3]

    ans = list()
  
    for d in zip(first,second,third) :
        ans.append(chr(int(d[0][5] + d[1][5] + d[2][6])))

    print(''.join(ans))

코드의 출력 결과는 다음과 같다.

So, you found the hidden message.
There is lots of room here for a long message, but we only need very little space to say 
"look at grandpa", so the rest is just garbage. 
VTZ.l'tf*Om@I"p]#R`cWEBZ40ofSC>OZFkRP0\)+b?Ir)S%Jt3f{ei%n2<FErFx~IzVm JTh =xdx++'de8C5'
|>2\/We;ib(b%d$N<2u(o$*d@.*6Fd'nW5#J!}a]T"1Q-7Y~bOF]T+^9d]e^J^=&I&<x|EEgdQ$$pX'f!_n>F0(
[j%Y'XjwWu,4w/q;1Hd#1H{{Nf~BQ6f![m#fb^a;{Ei%$2fEyN[*4KhK[-7({jh5k0n kwZyx|x=xvFC
....

 

"grandpa" 찾았다.

Answer Url : http://www.pythonchallenge.com/pc/ring/grandpa.html

Python Challenge 29의 Url은 다음과 같다

Python challenge 29 : http://www.pythonchallenge.com/pc/ring/guido.html


구성

문제 페이지에 들어가면 귀여운 안경 쓴 물병이 우리를 반긴다. 반갑다고 인사한 후 주석을 보도록 하자.

주석의 특별한 점이라면 빈줄이 어마어마하게 많다는 점이다. 

 


해결 아이디어

빈줄이 많고 공백으로 이루어진 암호화라...  CTF를 많이 접한 사람이라면 당연히 snow코드를 생각했겠지만, 아쉽게도 이번 문제는 오히려 더 간단하다. 공백으로 이루어진 줄들이 [공백]*+\n으로 이루어져 있기 때문에 공백들의 길이를 chr화 시키면 bz2 문자열이 나온다. decompress 하면 해결이다. 일단 저장하고

코드는 다음과 같다.

### 28.py

import bz2

if __name__=="__main__" :
    with open("guido.html","r") as f:
        html = f.read().split("\n")

        cand = list()

        for l in html :
            if(l.strip() == "") :
                cand.append(len(l))

        print(bz2.decompress(bytes(cand)))

깔끔. 어째 갈수록 문제들이 쉬워진다는 느낌이 든다.

출력은 다음과 같다.

b"Isn't it clear? I am yankeedoodle!"

Answer Url : http://www.pythonchallenge.com/pc/ring/yankeedoodle.html

pyautogui는 외장모듈이다. 모듈명 그대로 다운로드 가능하다.

pip3 install pyautogui

역할은 마우스, 키보드의 입력 / 이동을 Python으로 조절하기 위한 모듈이다. 흔히 마우스 마음대로 움직이거나, 키보드가 맘대로 쳐지면 이와 비슷한 모듈이 들어가 있다고 보면 된다.


주요 함수 설명

  • pyautogui.size() : 현재 모니터의 해상도를 (x,y) tuple로 반환한다.
  • pyautogui.onScreen(x,y) : 좌표 x,y의 위치가 모니터 안에서 유효한 좌표인지 bool값으로 반환한다.
  • pyautogui.position() : 현재 마우스의 위치를 반환한다.
  • pyautogui.moveTo(x,y,duration=num_seconds) : duration에 적힌 기간동안 x,y의 좌표로 마우스를 이동한다.
  • pyautogui.moveRel(xoffset,yoffset,duration=num_seconds) : duration에 적힌 기간동안 xoffset, yoffset 만큼 마우스를 이동한다.
  • pyautogui.dragTo(x,y,duration=num_seconds) : duration에 적힌 기간동안 x,y로 마우스를 드래그(클릭 후 이동)한다.
  • pyautogui.dragRel(xoffset,yoffset,duration_num_seconds) : duration에 적힌 기간동안 xoffset, yoffset 만큼 마우스를 드래그(클릭 후 이동)한다.
  • pyautogui.click(x=moveToX,y=moveToY,clickes=num, interval=secs_betweens_clicks,button="left") : 현재위치에서 클릭한다. parameter로는 옆과 같은것을 줄 수 있다.
  • pyautogui.rightClick(x=moveToX,moveToY) : 우클릭
  • pyautogui.middleClick(x=moveToX,moveToY) : 휠클릭
  • pyautogui.doubleClick(x=moveToX,moveToY) : 더블클릭
  • pyautogui.tripleClick(x=moveToX,moveToY) : 세번클릭
  • pyautogui.scroll(amount_to_scroll,x=moveToX,y=moveToY) : 마우스 스크롤 한다. 양수면 위로, 음수면 아래로
  • pyautogui.mouseDown(x=moveToX, y=moveToY, button='left') : 마우스버튼을 "누른다"
  • pyautogui.mouseUp(x=moveToX, y=moveToY, button='left') : 마우스버튼을 "뗀다"
  • pyautogui.typewrite(type_string, interval=secs_between_keys) : type_string을 친다.
  • pyautogui.hotkey(param1, param2...) : 단축키로 쓰인다. 보통 param에 'ctrl', 'shift'등이 쓰인다.
  • pyautogui.keyDown(key_name) : key_name을 '누른다'
  • pyautogui.keyUp(key_name) : key_name을 '누른다'
  • pyautogui.alert(message) : 경고 메시지 박스
  • pyautogui.confirm(message) : 확인 메시지 박스
  • pyautogui.prompt(message) : user Input 처리를 위한 메시지 박스
  • pyautogui.screenshot(region=(x1,y1,x2,y2)) : 스크린샷을 찍는다. PIL.Image 객체를 반환한다.
  • pyautogui.locateOnScreen(Image_name) : 모니터화면에서 Image_name에 해당하는 Image를 찾아서 위치를 반환한다.(웹 개체는 안된다. 로컬개체만)

이거말고도 pixelMatchsColor등 특정 규칙에 맞게 화면의 마우스를 이동시키는 방법도 있다.

 

출처 : https://pyautogui.readthedocs.io/en/latest/index.html

 

Welcome to PyAutoGUI’s documentation! — PyAutoGUI documentation

Welcome to PyAutoGUI’s documentation! PyAutoGUI lets your Python scripts control the mouse and keyboard to automate interactions with other applications. The API is designed to be as simple. PyAutoGUI works on Windows, macOS, and Linux, and runs on Pytho

pyautogui.readthedocs.io

 

* pyautogui.FAILSAFE라는 모듈 설정이 있다. 기본 규칙은 TRUE로 되어있다. 화면끝에 마우스가 부딫히면 잠깐동안 프로그램을 멈추는 기능이다.

Python Challenge 28의 Url은 다음과 같다

Python challenge 28 : http://www.pythonchallenge.com/pc/ring/bell.html


구성

들어가면 폭포사진을 볼 수 있다. 아래 번역은 다음과 같다.

"RING-RING-RING 크게 말해보세요"

G발음에 조금 강세를 두고 반복하면 "GREEN"이다. green.html로 이동하면 다음과 같은 페이지를 볼 수 있다.

어허; 다른 태그도 안보인다. 주석도 유의미하지 않고, 무슨 html이 헤더바디 다무시하고 저거 한줄 써있다.


해결 아이디어

일단 사진이 RGB픽셀이니, G만 다 빼보자

### 28_1.py from PIL import Image if __name__ == "__main__" : with Image.open("bell.png") as img : green = list(img.split()[1].getdata())

green을 조금만 볼까?

>>> green[:100] [55, 97, 73, 115, 120, 78, 60, 102, 76, 118, 114, 72, 59, 101, 119, 77, 92, 50, 30, 72, 53, 95, 133, 91, 93, 51, 110, 68, 112, 70, 66, 108, 140, 182, 163, 121, 66, 24, 59, 17, 25, 67, 62, 20, 26, 68, 67, 25, 85, 43, 60, 18, 53, 11, 3, 45, 3, 45, 0, 42, 5, 47, 9, 51, 22, 64, 37, 79, 35, 77, 66, 24, 25, 67, 36, 78, 84, 42, 70, 28, 69, 111, 72, 30, 62, 20, 27, 69, 34, 76, 27, 69, 181, 223, 194, 152, 108, 66, 73, 31]

겉보기에는 그냥 배열처럼 보일 수도 있지만, 홀짝을 짝지어서 보면, 42차이나 나거낭 -42의 차이를 볼 수 있다. 절댓값이 42가 아닌 배열을 빼보면 유의미한 결과를 알 수 있을 것이다.

### 28_2.py from PIL import Image if __name__ == "__main__" : with Image.open("bell.png") as img : green = list(img.split()[1].getdata()) first = green[0::2] second = green[1::2] ans_list = list() for f,s in zip(first,second) : if(abs(f-s) != 42) : val = abs(f-s) ans_list.append(chr(val)) print(''.join(ans_list))

출력결과는 다음과 같다.

whodunnit().split()[0] ?

whodunnit은 영미권의 수수께끼종류이다.(모듈이 있기는 한데, 우리 문제와 관련없다.) 범죄에서 가해자를 추적하는 추리 수수께끼인데, 여기서 가해자는 누가되는 걸까? 누가 우리를 이렇게 힘들게 하는 걸까??

안녕하세요 guido씨

Answer Url : http://www.pythonchallenge.com/pc/ring/guido.html

Python Challenge 27의 Url은 다음과 같다

Python challenge 27 : http://www.pythonchallenge.com/pc/hex/speedboat.html


구성

speedboat.html이라는 페이지 위치에 맞게 보트로 노를 젓고 있는 사진을 한장 확인할 수 있다. 그림의 img태그에는 src로 링크가 걸려있다. 이동하면 자격증명을 물어보는 화면이 나오나, 우리는 아직 모르니 넘어가자

다른 힌트는 보이지 않으니 주석을 확인해 보자

유의미한 주석은 2개가 보인다. 

<!--gif를 말씀하신건가요?>

<!-- 오, 14번문제의 반복은 아닙니다!(도얏)>

이렇게인데, 14번문제는 italy.html로 나선형으로 픽셀을 재배치 하는 문제였다. 아래참고

2021.05.21 - [Python/Python Challenge] - [Python Challenge 14] 빙글빙글

 

[Python Challenge 14] 빙글빙글

Python Challenge 14의 Url은 다음과 같다 Python challenge 14 : http://www.pythonchallenge.com/pc/return/italy.html 구성 페이지에는 사진이 2개 있다. 위에 보이는 나선빵은 italy라는 이름을 가지고 있고,..

tutoreducto.tistory.com

일단 zigzag.gif로 들어가면 14번문제와 유사한 사진을 구할 수 있다. 

<14번 문제>                  <27번문제>

* 실제 14번 문제의 사진은 일렬로 정렬되어있었다.

 

27번문제도 픽셀의 재배치 인걸로 보이나, 방법을 조금 다르게 해야할 거 같다.


해결 아이디어

zigzag가 문제를 풀 수 있는 크나큰 힌트이다. 문제 많이 어렵고, 직관적인 판단을 요구한다. 이미지 픽셀을 재배치하는 것은 분명 어렵지 않은 일이 여야하는데, 지그재그로 재배치해서 사각형을 어떻게 만들 수 있을까? 우리 기본적인 걸로 돌아가보자

"이미지는 무엇을 이루어져 있는가?"

맞다. 픽셀이다.(벡터형은 나가있어) 그러면

"픽셀은 무엇으로 이루어져 있는가?"

맞다. 모드에 따라 다르지만, RGB의 3bytes의 조합으로 이루어져 있다. 우리의 zigzag.gif를 확대해서 보면, 흰색(0,0,0)~검은색(255,255,255)으로 이루어져 있다는 것을 알 수 있다. 이걸 어떻게 지그재그화 할까?

 

팔래트

팔레트(palette)는 컴퓨터 그래픽스에서 디지털 이미지 관리를 위해 존재하는 색
의 유한 집합이다. 컴퓨팅 이외에 쓰는 색상표는 컬러차트라고 한다.

- 출처 위키백과 : https://ko.wikipedia.org/wiki/%ED%8C%94%EB%A0%88%ED%8A%B8_(%EC%BB%B4%ED%93%A8%ED%8C%85) -

이번에 문제에 사용할 중요한 Keyword이다. 이미지중 "P"모드로 작성된 이미지는 모두 이 색상 유한집합(팔레트)를 가진다. 팔래트는 이미지에 사용된 색들을 포함한다. 당연하게도

>>> img = Image.open("zigzag.gif")
>>> img.mode
'P'

우리의 이미지는 'P'모드로 작성되어 있다. 팔래트를 따보자

>>> pal = img.getpalette()
>>> pal
[37, 37, 37, 229, 229, 229, 162, 162, 162, 136, 136, 136, 59, 
59, 59, 212, 212, 212, 9, 9, 9, 41, 41, 41, 24, 24, 24, 156,
156, 156, 148, 148, 148, 112, 112, 112, 254, 254, 254, 91, 91,
91, 106, 106, 106, 49, 49, 49, 248, 248, 248, 213, 213, 213,
220, 220, 220, 15, 15, 15, 85, 85, 85, 159, 159, 159, 62, 62,
62, 78, 78, 78, 76, 76, 76, 111, 111, 111, 103, 103, 103, 150,
150, 150, 154, 154, 154, 68, 68, 68, 25, 25, 25, 169, 169, 169,
126, 126, 126, 185, 185, 185, 140, 140, 140, 234, 234, 234, 244,

...

역시 보이던 대로 흰색(0,0,0) ~ 검정색(255,255,255) 사이의 색들이라 같은 바이트가 3번 반복된다. 팔래트의 최대 길이는 256으로 지정되어 있으니, 3개씩 끊어 읽으면 256의 길이가 나올 것이다.

>>> pal = img.getpalette()[::3]
>>> len(pal)
256

정확했다. 신기한건 이렇게 구한 pal에 중복되는 수가 없다는 것이다. 256이라는 숫자는 또한 8비트로 나타낼 수 있는 최대 수의 개수이다.(0~255) 따라서 0~255에 매칭되게 pal에 변환 테이블을 작성해 주고 그 바이트를 다시 이미지로 바꾸면 유의미한 결과를 기대할 수 있을 것이다.

### 27_1.py

from PIL import Image

if __name__=="__main__" :

    img = Image.open("zigzag.gif")
    
    pal = img.getpalette()[::3]
    trans = bytes.maketrans(bytes(list(range(256))),bytes(pal))
    raw = img.tobytes()
    trans = raw.translate(trans)

    img2 = Image.new("RGB",img.size)
    col = list()
    for i in trans :
        col.append((i,i,i))
    img2.putdata(col)
    img2.save("false.gif")

그러나 아쉽게도 얻은 false.gif은 아름다운 그림은 아니다.

<false.gif>

생각해보자. 이번 문제의 TItle은 zigzag이다. 두개를 교차해야되는 거라면??? 어쩌면 두 그림을 교차할때 같은 픽셀값을 가지면 검정색으로 색칠하면 유의미한 그림이 나오지 않을까?

from PIL import Image


### 27_2.py

if __name__=="__main__" :

    img = Image.open("zigzag.gif")
    
    pal = img.getpalette()[::3]
    trans = bytes.maketrans(bytes(list(range(256))),bytes(pal))
    raw = img.tobytes()
    trans = raw.translate(trans)
    
    col = list(zip(raw,trans))
    
    conv = [i for i,val in enumerate(col) if val[0]!=val[1]]
    
    img3 = Image.new("RGB",img.size)
    colors = [(255,255,255)]*len(raw)
    
    for i in conv :
        colors[i] = (0,0,0)
        
    img3.putdata(colors)
    img3.save("answer_false.gif")

그러나.... 결과로 나온 answer_false도 유의미하진 못하다. 잘 생각해보자

<answer_flase.gif>

 

끝까지 힌트는 zigzag이다. speedboat사진에서 본 zigzag모양을 기억하는가

그렇다. 둘은 같은 선상에 있지 않다. 먼저나온 raw는 인덱스 1부터 가야되고, 나중에 나온 trans는 마지막 인덱스를 포함하면 안된다.

from PIL import Image


### 27_3.py

if __name__=="__main__" :

    img = Image.open("zigzag.gif")
    
    pal = img.getpalette()[::3]
    trans = bytes.maketrans(bytes(list(range(256))),bytes(pal))
    raw = img.tobytes()
    trans = raw.translate(trans)
    
    col = list(zip(raw[1:],trans[:-1]))
    
    conv = [i for i,val in enumerate(col) if val[0]!=val[1]]
    
    img3 = Image.new("RGB",img.size)
    colors = [(255,255,255)]*len(raw)
    
    for i in conv :
        colors[i] = (0,0,0)
        
    img3.putdata(colors)
    img3.save("real_answer.gif")

길었다 참

<real_answer.gif>

아쉽게도 안끝났다. 사진을 보면 keyword가 아니라고 한다. 아래에는 busy?라는 자주보던 문자열이 보인다.

우리가 사용한 이 bytes열. 다시 decompress하면 어떨까?

### 27_4.py

from PIL import Image
import bz2


if __name__=="__main__" :

    img = Image.open("zigzag.gif")
    
    pal = img.getpalette()[::3]
    trans = bytes.maketrans(bytes(list(range(256))),bytes(pal))
    raw = img.tobytes()
    trans = raw.translate(trans)
    
    col = list(zip(raw[1:],trans[:-1]))
    
    conv = [val[0] for i,val in enumerate(col) if val[0]!=val[1]]
    cand = bz2.decompress(bytes(conv))
>>> print(cand[:140])
b'../ring/bell.html del assert repeat raise or class is exec return 
except print return switch from exec repeat else not while assert or class'
>>> len(cand)
70644

결과로 나온 cand는 엄청 많은 조합의 keyword로 되어있다. 사진에는 keyword가 아니라고 하니까 모듈 keyword의 힘을 빌려 필터링 해보자

>>> nk = set([s for s in cand.split() if not keyword.iskeyword(s.decode())])
>>> print(nk)
{b'print', b'switch', b'exec', b'../ring/bell.html', b'repeat'}

중복이 엄청많아 set자료형으로 사용했다. 문제가 만들어 질때는 모르겠는데, exec와 print는 현재 키워드가 아니다.

남은건 switch와 repeat이다. 찾았다.

Answer Url :  http://www.pythonchallenge.com/pc/ring/bell.html

Username : repeat

Password : switch

+ Recent posts