2.3 函数和类
C、C++、Java、Ruby、Perl、Lisp……在笔者所知的编程语言之中,所有程序都是由函数(有的编程语言叫做过程、方法什么的)和类组成的。可以说任何程序里面包含的不是函数就是类,Python当然也不例外。
2.3.1 函数
在学习UNIX时,曾经有一句非常出名的话是In UNIX Everything Is A File,在UNIX中所有的一切都是文件。在这里可以借鉴一下,In Python Everything Is A Function,在Python程序中,所有的一切都是函数。这是典型的C语言写法,把所需的功能都写成一个个的函数,然后由函数调用函数。以此类推,最终完成整个程序的功能。
还记得前面提过的暴力破解吗?不管用什么工具,暴力破解都少不了一个合适的字典文件(此字典非彼字典,这里的字典指的是一个包含密码的文件,也就是一个密码集,而不是Python的变量类型)。当然网上有很多的密码字典可供下载,但它们要么太大,遍历一次需要太多的时间,要么没有针对性,根本就不包含所需的密码。如果已知了一些可能是密码的字符串,完全可以根据已知条件用程序编写有针对性的字典出来,这样会节省很多时间。
【示例2-13】现在来编写一个简单的程序mkPassFileFunction.py。
mkPassFileFunction.py,创建一个有针对性的专用密码字典。打开Putty连接到Linux,执行命令:
cd code/crawler vi mkPassFileFunction.py
mkPassFileFunction.py的代码如下:
1 #!/usr/bin/env python3 2 #-*- coding: utf-8 -*- 3 __author__ = 'hstking hst_king@hotmail.com' 4 5 import os 6 import platform 7 import itertools 8 import time 9 10 def main(): 11 '''主程序 ''' 12 global rawList #原始数据列表 13 rawList = [] 14 global denyList #非法单词列表 15 denyList = [' ','','@'] 16 global pwList #最终的密码列表 17 pwList = [] 18 global minLen #密码的最小长度 19 minLen = 6 20 global maxLen #密码的最大长度 21 maxLen = 16 22 global timeout 23 timeout = 3 24 global flag 25 flag = 0 26 run = { 27 '0':exit, 28 '1':getRawList, 29 '2':addDenyList, 30 '3':clearRawList, 31 '4':setRawList, 32 '5':modifyPasswordLen, 33 '6':createPasswordList, 34 '7':showPassword, 35 '8':createPasswordFile 36 } 37 38 while True: 39 mainMenu() 40 op = input('输入选项:') 41 if op in map(str,range(len(run))): 42 run.get(op)() 43 else: 44 tipMainMenuInputError() 45 continue 46 47 def mainMenu(): 48 '''主菜单 ''' 49 global denyList 50 global rawList 51 global pwList 52 global flag 53 clear() 54 print('||'), 55 print('='*40), 56 print('||') 57 print('|| 0:退出程序') 58 print('|| 1:输入密码原始字符串') 59 print('|| 2:添加非法字符到列表') 60 print('|| 3:清空原始密码列表') 61 print('|| 4:整理原始密码列表') 62 print('|| 5:改变默认密码长度(%d-%d)' %(minLen,maxLen)) 63 print('|| 6:创建密码列表') 64 print('|| 7:显示所有密码') 65 print('|| 8:创建字典文件') 66 print('||'), 67 print('='*40), 68 print('||') 69 print('当前非法字符为:%s' %denyList) 70 print('当前原始密码元素为:%s' %rawList) 71 print('共有密码%d个' %len(pwList)) 72 if flag: 73 print("已在当前目录创建密码文件dic.txt") 74 else: 75 print("尚未创建密码文件") 76 77 def clear(): 78 '''清屏函数 ''' 79 OS = platform.system() 80 if (OS == u'Windows'): 81 os.system('cls') 82 else: 83 os.system('clear') 84 85 def tipMainMenuInputError(): 86 '''错误提示 ''' 87 clear() 88 print("只能输入0-7的整数,等待%d秒后重新输入" %timeout) 89 time.sleep(timeout) 90 91 def getRawList(): 92 '''获取原始数据列表 ''' 93 clear() 94 global denyList 95 global rawList 96 print("输入回车后直接退出") 97 print("当前原始密码列表为:%s" %rawList) 98 st = None 99 while not st == '': 100 st = input("请输入密码元素字符串:") 101 if st in denyList: 102 print("这个字符串是预先设定的非法字符串") 103 continue 104 else: 105 rawList.append(st) 106 clear() 107 print("输入回车后直接退出") 108 print("当前原始密码列表为:%s" %rawList) 109 110 def addDenyList(): 111 '''添加非法词 ''' 112 clear() 113 global denyList 114 print("输入回车后直接退出") 115 print("当前非法字符为:%s" %denyList) 116 st = None 117 while not st == '': 118 st = input("请输入需要添加的非法字符串:") 119 denyList.append(st) 120 clear() 121 print("输入回车后直接退出") 122 print("当前非法字符列表为:%s" %denyList) 123 124 def clearRawList(): 125 '''清空原始数据列表 ''' 126 global rawList 127 rawList = [] 128 129 def setRawList(): 130 '''整理原始数据列表 ''' 131 global rawList 132 global denyList 133 a = set(rawList) 134 b = set(denyList) 135 rawList = [] 136 for str in set(a - b): 137 rawList.append(str) 138 139 def modifyPasswordLen(): 140 '''修改默认密码的长度 ''' 141 clear() 142 global maxLen 143 global minLen 144 while True: 145 print("当前密码长度为%d-%d" %(minLen,maxLen)) 146 min = input("请输入密码最小长度:") 147 max = input("请输入密码最大长度:") 148 try: 149 minLen = int(min) 150 maxLen = int(max) 151 except ValueError: 152 print("密码长度只能输入数字[6-18]") 153 break 154 if minLen not in range(6,19) or maxLen not in range(6,19): 155 print("密码长度只能输入数字[6-18]") 156 minLen = 6 157 maxLen = 16 158 continue 159 if minLen == maxLen: 160 res = input("确定将密码长度设定为%d吗?(Yy/Nn)" %minLen) 161 if res not in list('yYnN'): 162 print("输入错误,请重新输入") 163 continue 164 elif res in list('yY'): 165 print("好吧,你确定就好") 166 break 167 else: 168 print("给个机会,改一下吧") 169 continue 170 elif minLen > maxLen: 171 print("最小长度比最大长度还大,可能吗?请重新输入") 172 minLen = 6 173 maxLen = 16 174 continue 175 else: 176 print("设置完毕,等待%d秒后回主菜单" %timeout) 177 time.sleep(timeout) 178 break 179 180 def createPasswordList(): 181 '''创建密码列表 ''' 182 global rawList 183 global pwList 184 global maxLen 185 global minLen 186 titleList = [] 187 swapcaseList = [] 188 for st in rawList: 189 swapcaseList.append(st.swapcase()) 190 titleList.append(st.title()) 191 sub1 = [] 192 sub2 = [] 193 for st in set(rawList + titleList + swapcaseList): 194 sub1.append(st) 195 for i in range(2,len(sub1) + 1): 196 sub2 += list(itertools.permutations(sub1,i)) 197 for tup in sub2: 198 PW = '' 199 for subPW in tup: 200 PW += subPW 201 if len(PW) in range(minLen,maxLen + 1): 202 pwList.append(PW) 203 else: 204 pass 205 206 def showPassword(): 207 '''显示创建的密码 ''' 208 global pwList 209 global timeout 210 for i in range(len(pwList)): 211 if i%4 == 0: 212 print("%s\n" %pwList[i]) 213 else: 214 print("%s\t" %pwList[i]), 215 print('\n') 216 print("显示%d秒,回到主菜单" %timeout) 217 time.sleep(timeout) 218 219 def createPasswordFile(): 220 '''创建密码字典文件 ''' 221 global flag 222 global pwList 223 print("当前目录下创建字典文件:dic.txt") 224 time.sleep(timeout) 225 with open('./dic.txt','w+') as fp: 226 for PW in pwList: 227 fp.write(PW) 228 fp.write('\n') 229 flag = 1 230 231 232 if __name__ == '__main__': 233 main()
按Esc键,进入命令模式后输入:wq,保存mkPassFileFunction.py。mkPassFileFunction.py稍微复杂一点点,它的作用就是根据用户输入的“密码元素”来创建一个字典列表。该脚本将输入的元素根据一定的规则修改、添加后当作新元素添加到元素列表中去。最后将元素列表排列组合得到字典列表。执行命令:
python mkPassFileFunction.py
得到的结果如图2-19所示。
图2-19 运行mkPassFileFunction.py
纯C语言的写法好处就是关系简单明了,函数调用一目了然。如果调用的函数过多,就难免有些混乱了。简单功能的程序还无妨,稍大一点项目就有些吃力了。
提示
不要添加太多的“密码元素”,这个程序只是利用了Python 3的模块,没有优化算法。如果输入的“密码元素”超过了20个,那么创建密码字典的时间会非常长。
2.3.2 类
既然有了In Python Everything Is A Function,当然会有In Python Everything Is A Class。这种C++的写法就是把所有相似的功能都封装到一个类里。最理想的情况是一个程序只有一个主程序,然后在主程序里实例化类。
【示例2-14】还是以编写密码字典为例,将mkPassFileFunction.py改编成mkPassFileClass.py。打开Putty连接到Linux,执行命令:
cd code/crawler vi mkPassFileClass.py
mkPassFileClass.py的代码如下:
1 #!/usr/bin/env python3 2 #-*- coding: utf-8 -*- 3 __author__ = 'hstking hst_king@hotmail.com' 4 5 import os 6 import platform 7 import itertools 8 import time 9 10 class MakePassword(object): 11 def __init__(self): 12 self.rawList = [] 13 self.denyList = ['',' ','@'] 14 self.pwList = [] 15 self.minLen = 6 16 self.maxLen = 16 17 self.timeout = 3 18 self.flag = 0 19 self.run = { 20 '0':exit, 21 '1':self.getRawList, 22 '2':self.addDenyList, 23 '3':self.clearRawList, 24 '4':self.setRawList, 25 '5':self.modifyPasswordLen, 26 '6':self.createPasswordList, 27 '7':self.showPassword, 28 '8':self.createPasswordFile 29 } 30 self.main() 31 32 def main(self): 33 while True: 34 self.mainMenu() 35 op = input('输入选项:') 36 if op in map(str,range(len(self.run))): 37 self.run.get(op)() 38 else: 39 self.tipMainMenuInputError() 40 continue 41 42 def mainMenu(self): 43 self.clear() 44 print('||'), 45 print('='*40), 46 print('||') 47 print('|| 0:退出程序') 48 print('|| 1:输入密码原始字符串') 49 print('|| 2:添加非法字符到列表') 50 print('|| 3:清空原始密码列表') 51 print('|| 4:整理原始密码列表') 52 print('|| 5:改变默认密码长度(%d-%d)' %(self.minLen,self.maxLen)) 53 print('|| 6:创建密码列表') 54 print('|| 7:显示所有密码') 55 print('|| 8:创建字典文件') 56 print('||'), 57 print('='*40), 58 print('||') 59 print('当前非法字符为:%s' %self.denyList) 60 print('当前原始密码元素为:%s' %self.rawList) 61 print('共有密码%d个' %len(self.pwList)) 62 if self.flag: 63 print("已在当前目录创建密码文件dic.txt") 64 else: 65 print("尚未创建密码文件") 66 67 def clear(self): 68 OS = platform.system() 69 if (OS == u'Windows'): 70 os.system('cls') 71 else: 72 os.system('clear') 73 74 def tipMainMenuInputError(self): 75 self.clear() 76 print("只能输入0-7的整数,等待%d秒后重新输入" %timeout) 77 time.sleep(timeout) 78 79 def getRawList(self): 80 self.clear() 81 print("输入回车后直接退出") 82 print("当前原始密码列表为:%s" %self.rawList) 83 st = None 84 while not st == '': 85 st = input("请输入密码元素字符串:") 86 if st in self.denyList: 87 print("这个字符串是预先设定的非法字符串") 88 continue 89 else: 90 self.rawList.append(st) 91 self.clear() 92 print("输入回车后直接退出") 93 print("当前原始密码列表为:%s" %self.rawList) 94 95 def addDenyList(self): 96 self.clear() 97 print("输入回车后直接退出") 98 print("当前非法字符为:%s" %self.denyList) 99 st = None 100 while not st == '': 101 st = input("请输入需要添加的非法字符串:") 102 self.denyList.append(st) 103 self.clear() 104 print("输入回车后直接退出") 105 print("当前非法字符列表为:%s" %self.denyList) 106 107 def clearRawList(self): 108 self.rawList = [] 109 110 def setRawList(self): 111 a = set(self.rawList) 112 b = set(self.denyList) 113 self.rawList = [] 114 for str in set(a - b): 115 self.rawList.append(str) 116 117 def modifyPasswordLen(self): 118 self.clear() 119 while True: 120 print("当前密码长度为%d-%d" %(self.minLen,self.maxLen)) 121 min = input("请输入密码最小长度:") 122 max = input("请输入密码最大长度:") 123 try: 124 self.minLen = int(min) 125 self.maxLen = int(max) 126 except ValueError: 127 print("密码长度只能输入数字[6-18]") 128 break 129 if self.minLen not in range(6,19) or self.maxLen not in range(6,19): 130 print("密码长度只能输入数字[6-18]") 131 self.minLen = 6 132 self.maxLen = 16 133 continue 134 if self.minLen == self.maxLen: 135 res = input("确定将密码长度设定为%d吗?(Yy/Nn)" %self.minLen) 136 if res not in list('yYnN'): 137 print("输入错误,请重新输入") 138 continue 139 elif res in list('yY'): 140 print("好吧,你确定就好") 141 break 142 else: 143 print("给个机会,改一下吧") 144 continue 145 elif self.minLen > self.maxLen: 146 print("最小长度比最大长度还大,可能吗?请重新输入") 147 self.minLen = 6 148 self.maxLen = 16 149 continue 150 else: 151 print("设置完毕,等待%d秒后回主菜单" %self.timeout) 152 time.sleep(self.timeout) 153 break 154 155 def createPasswordList(self): 156 titleList = [] 157 swapcaseList = [] 158 for st in self.rawList: 159 swapcaseList.append(st.swapcase()) 160 titleList.append(st.title()) 161 sub1 = [] 162 sub2 = [] 163 for st in set(self.rawList + titleList + swapcaseList): 164 sub1.append(st) 165 for i in range(2,len(sub1) + 1): 166 sub2 += list(itertools.permutations(sub1,i)) 167 for tup in sub2: 168 PW = '' 169 for subPW in tup: 170 PW += subPW 171 if len(PW) in range(self.minLen,self.maxLen + 1): 172 self.pwList.append(PW) 173 else: 174 pass 175 176 def showPassword(self): 177 for i in range(len(self.pwList)): 178 if i%4 == 0: 179 print("%s\n" %self.pwList[i]) 180 else: 181 print("%s\t" %self.pwList[i]), 182 print('\n') 183 print("显示%d秒,回到主菜单" %self.timeout) 184 time.sleep(self.timeout) 185 186 def createPasswordFile(self): 187 print("当前目录下创建字典文件:dic.txt") 188 time.sleep(self.timeout) 189 with open('./dic.txt','w+') as fp: 190 for PW in self.pwList: 191 fp.write(PW) 192 fp.write('\n') 193 self.flag = 1 194 195 196 if __name__ == '__main__': 197 mp = MakePassword()
按Esc键,进入命令模式后输入:wq,保存mkPassFileClass.py。mkPassFileClass.py和mkPassFileFunction.py实质上没有什么区别,只是一个使用的是C语言风格的函数调用,一个使用的是C++风格的类实例化。执行命令:
python3 mkPassFileClass.py
得到的结果如图2-20所示。
图2-20 运行mkPassFileClass.py
执行结果完全一样。这种C++的写法好处就是调用过程简单,不再关心类具体的实现过程,只需要调用其功能即可;但随之而来就是类的继承、函数重载等麻烦。这种写法在写大项目时可能非常有用,写小程序也行,只是没有那么多优势了。
提示
这个程序还有一个问题,就是在创建密码文件前并没有估算磁盘剩余空间是否足够。一般的解决办法是先估算密码文件的大小,然后创建一个大小相同的空文件,能创建成功就继续运行程序,不能则抛出异常。