人生苦短系列中级篇 : python 进阶技巧
python --version : 3.7.0
update : 2018/09/24
六层标题备忘 : (一) / 1. / (1) / <1> / (i) /<i>
(一)编码问题
1. 'abc' 和 b'abc'
>>> 'abc'.encode()
b'abc'
>>> b'abc'.decode()
'abc'
python3有两种字符序列类型: "str"和"bytes" ,分别对应字符串的"文本状态"和"字节流状态"。
python3中的字符串使用Unicode字符集进行存储,当其类型为"str"时,直接输出会显示对应的文本状态; 而需要查看其对应的字节流(二进制)时,需要对"str"进行编码(encode),需要选择方案,如utf-8,GBK等(python3中默认编码方案为utf-8); 反之由"bytes"类型向"str"转换(decode)时,需要声明之前的编码方案。
>>> s = '中'
>>> type(s)
<class 'str'>
>>> sb = s.encode() # equal to "sb = s.encode(encoding = "utf-8")"
>>> sb
b'\xe4\xb8\xad'
>>> type(sb)
<class 'bytes'>
>>> sb.decode() # means "sb.decode(encoding = "utf-8")"
'中'
2. base64编码
3 str * 8 bit --> 4 str * 6 bit
>>> import base64
>>> base64.b64encode('Life is short, you need python.'.encode())
b'TGlmZSBpcyBzaG9ydCwgeW91IG5lZWQgcHl0aG9uLg=='
>>> base64.b64decode(b'TGlmZSBpcyBzaG9ydCwgeW91IG5lZWQgcHl0aG9uLg==')
b'Life is short, you need python.'
(二)数制转换
1. python中的整数
python中的整数不设上限,可任意大,处理起来非常方便。
python中的整数有4种常用的表示形式,分别为十进制,二进制,八进制,十六进制,其中二、八、十六进制的前缀分别为'0b'、'0o'、'0x',四种形式输入IDLE,都被解释为十进制整数,是一回事。
>>> 65
65
>>> 0b1000001
65
>>> 0o101
65
>>> 0x41
65
2. 拥有字符形式的整数
设整数的十进制形式为n, 当32<=n<=126时,其也可以表示可见字符的ASCII码,可与对应字符相互转换。
>>> chr(65)
'A'
>>> ord('A')
65
3.内置数制转换函数
可以将上述任意数制转换为二、八、十六进制的函数为bin(x),oct(x),hex(x),输出为str。
>>> bin(65)
'0b1000001'
>>> oct(65) #八进制对应的str无前缀
'0101'
>>> hex(65)
'0x41'
>>> hex(0b1000001)
'0x41'
4.其他数制转换方法
(1) 将任意数制(str)转换为十进制 : int('num', base)
这一函数常用于将str强制类型转换成int。
>>> int('0x41',16)
65
>>> int('41',16)
65
>>> int('41') #默认base = 10
41
(2) 不带前缀的格式化输出 : "{:base}".format(x)
>>> "{:b}".format(65)
'1000001'
>>> "{:x}".format(65)
'41'
>>> "{:o}".format(65)
'101'
5.字符串转字节流
(1) binascii.hexlify()
有时需要将字符串与对应的十六进制字节流相互转换(如rtf文档的构造或解析), 使用自带库binascii的函数可轻松解决。
>>> import binascii
>>> binascii.hexlify(b'abc')
b'616263'
>>> binascii.unhexlify(b'616263')
b'abc'
>>> binascii.b2a_hex(b'abc')
b'616263'
>>> binascii.a2b_hex(b'616263')
b'abc'
hexlify()和b2a_hex()都可将字符串转为16进制字节流,
unhexlify()和a2b_hex()都可将16进制字节流转为字符串。
(2) python2的技巧(python3不适用)
在python2中一行代码即可搞定,注意此方法在python3中会报错("hex"编解码器已被删除)。
>>> "abc".encode("hex")
'616263'
>>> '616263'.decode("hex")
'abc'
(三)输入输出
1. 格式化输出需加括号
print("My name is %s" %("yourDaddy"))
2. 格式化输出字符串的两种方式
(1) %
>>> n = 123
>>> s = "%d" % n # 等价于强制类型转换 s = str(n)
>>> s
'123'
(2) .format
<1> 常规填充
>>> "{} - {} cm".format('sandy',18)
'sandy - 18 cm'
<2> 数制转换
>>> '{:b}'.format(18) # to 2
'10010'
>>> '{:x}'.format(18) # to 16
'12'
2. python3 取消了raw_input, 但还可以用input接收输入
>>>message = input()
123
>>> message
'123'
3. sys.stdin.read()可以实现标准输入, IDLE下"enter + ctrl + d"可终止输入(linux下同样适用),windows cmd下为"enter + ctrl + z + enter"
>>>import sys
>>>s=sys.stdin.read()
123
>>>s
'123\n'
(四)命令行下输入参数的处理技巧
1. 获取命令行下输入的参数 : sys.argv
命令行下执行py脚本,后加一堆参数,则用sys.argv可将这些参数以列表形式存储,每个参数都是"str"类型,且列表首元素sys.argv[0] = "pythonFileName"
(列表长度 = 输入参数 + 1)。
#test.py
import sys
parse = sys.argv
print(parse)
---
shell > test.py 1 2 3
['C:\\Users\\Administrator\\Desktop\\python3\\test.py','1','2','3']
shell >
2. 带选项的输入 : getopt库
(1) 短选项模式
#getoptShortMode.py
import getopt
inputt = '-n cos -e -d 18cm add'.split()
print("Input: %s" % (inputt))
opts, args = getopt.getopt(inputt, 'n:ed:')
print("opts: %s" % (opts))
print("args: %s" % (args))
---
shell > getoptShortMode.py
Input: ['-n', 'cos', '-e', '-d', '18cm', 'add']
opts: [('-n', 'cos'), ('-e', ''), ('-d', '18cm')]
args: ['add']
短选项模式假定py文件后面跟的都是形如"-选项 参数"的格式,其中"选项"为单字母(这就是为什么叫短选项),"参数"可以为空;
短模式函数引用形式为opts,args = getopt(input,shortOpts)
,input
就是输入的"选项-参数"流,上例中的shortOpts = "n:ed:"
, 其首先将所有选项的字母收集进来,选项后如果有参数,则选项后跟冒号(如"n:"),否则不跟(如"e");
getopt依据shortOpts的规则将Input分成tuple,存入opts,Input中没在shortOpts中出现的部分划到args里。
注意shortopts里选项的顺序不一定非要和input里的选项顺序一致,最后opts里tuple的顺序是跟input里选项出现的先后顺序一致;
但没有选项的参数(如'add')一定要放到最后,否则其后的选项不被识别。
(3) 长选项模式
#getoptLongMode.py
import getopt
inputt = '--name=cos --dick=18cm --r unkown'.split()
print("Input: %s" % (inputt))
opts, args = getopt.getopt(inputt, '', ['name=', 'dick=', 'r'])
print("opts: %s" % (opts))
print("args: %s" % (args))
---
shell > getoptLongMode.py
Input: ['--name=cos', '--dick=18cm', '--r', 'unkown']
opts: [('--name', 'cos'), ('--dick', '18cm'), ('--r', '')]
args: ['unkown']
长选项假定后面跟的都是形如"--选项=参数"或"--选项"的格式,注意shortOpt=''
。
(3) 混合模式
#mixMode.py
import getopt
inputt = '--name=cos -f handsome --rich --dick=18cm -h 185 -r add unkown'.split()
opts, args = getopt.getopt(inputt, 'f:rh:', ['name=', 'rich', 'dick='])
print("input: %s " % (inputt))
print("opts: %s " % (opts))
print("args: %s " % (args))
---
shell > mixMode.py
input: ['--name=cos', '-f', 'handsome', '--rich', '--dick=18cm', '-h', '185', '-r', 'add', 'unkown']
opts: [('--name', 'cos'), ('-f', 'handsome'), ('--rich', ''), ('--dick', '18cm'), ('-h', '185'), ('-r', '')]
args: ['add', 'unkown']
shell >
3. 自动生成使用帮助 : argparse库
python 2.7以后弃用optparse模块,改用argparse替代。
(1) 用法
#test2.py
import argparse
parser = argparse.ArgumentParser(description = "argparse test")
parser.add_argument("-t", help="target IP")
cmdInput = parser.parse_args()
---
shell> test2.py -h
usage: test2.py [-h] [-t T]
argparse test
optional arguments:
-h, --help show this help message and exit
-t T target IP
命令行下输入".py文件" + "-h"自动生成用法帮助信息;
关键函数是add_args,其向命令行添加输入选项,帮助信息存放于help参数中(当然也可以不要)。
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument("-t")
_StoreAction(option_strings=['-t'], dest='t', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
(2) add_args函数参数详解
<1> 可选参数 : option_strings = "-t" 或 option_strings = "-p", "--port"
用法: shell> test2.py -p 4444
或 shell>test2.py --port 4444
#test2.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-t")
parser.add_argument("-p","--port")
cmdInput = parser.parse_args()
print("target input: %s %s" % (cmdInput.t,type(cmdInput.t)))
print("port input: %s" % (cmdInput.port)
shell>test2.py -t 127.0.0.1 --port 4444
target input: 127.0.0.1 <class 'str'>
port input: 4444
当指定选项名为形如"-t"这样的短选项时,后面跟的具体参数会以字符串类型保存于"parse_args().t"中;
而当选项名为"-p"+"--port"长短结合的形式,参数会保存于"parse_args().port"中,当然长短形式的选项都可以使用。
<2> 位置参数 : option_strings = "target"
用法: shell> test2.py 127.0.0.1
#test2.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target")
cmdInput = parser.parse_args()
print("target: %s " % (cmdInput.target))
---
shell> test2.py 127.0.0.1
target: 127.0.0.1
使用位置参数时,无需输入选项,直接输入参数,参数会保存在"parse_args().target"中。
<3> 不需要参数的选项 : action = "store_true"
用法: shell> test2.py -t
#test2.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-t", action="store_true")
cmdInput = parser.parse_args()
print(cmdInput.t)
print(type(cmdInput.t))
---
shell> test2.py -t
True
<class 'bool'>
参数action是用来处理当捕获到输入参数后下一步的动作,默认为 action = "store",即将参数保存到指定变量;
action = "store_true"表示选项后无需输入参数,默认参数为bool型的"True",将其存入变量中。
<4> 其他参数
- type = int : 函数默认输入参数保存为字符类型,使用type可将输入参数保存为其他类型。
- required = True : 默认该参数为"False",赋值后选项变为必须输入项,不输入则报错
(五)进程处理技巧
1. 进程中开启多线程 : threading库
- 进程(process)就是一个应用程序的一次执行过程,线程(thread)是进程中执行运算的最小单位。
- 操作系统把资源分配给进程,而线程可以在其中独立运行和调度。
- 一个进程可以有多个线程,一个线程必须属于某个进程。
- 若一个进程是单线程的,若此进程要执行三个任务,则只能串行执行(即单线程按任务顺序依次执行);多线程是指若一进程有三个线程,要执行三个任务,则三个线程可同时并行处理(CPU在同一时间只能处理一条线程,之所以可以多线程并行处理CPU在多条线程间可以快速切换,造成了同时进行的假象)。
(1) 用法
#Usage:
import threading
threadd = threading.Thread(target = function, args = (funcParameter1,funcParameter1)) #create thread,execute target(function) with/without arguments
threadd.start()
(2) 简单示例
#multiThread.py
import threading
def printNum(Num,Counts):
for i in range(Counts):
print(str(Num)*Counts)
counts = 5
thread1 = threading.Thread(target = printNum, args = (1,counts))
thread2 = threading.Thread(target = printNum, args = (0,counts))
thread1.start()
thread2.start()
---
========== RESTART: C:\Users\Administrator\Desktop\python3\multiThread.py ==========
11111
11111
1111100000
>>>
11111
11111
00000
00000
00000
00000
2. 由窗口到进程 : ctypes.windll.user32.GetWindowThreadProcessId()
- PID : 进程在被创建时系统内核会为其分配一个固定不变的序列号(PID),供函数或其他进程调用,进程终止后会被回收进行二次分配。
- 进程句柄(号) : 是指针,动态变化,当访问该进程时得到,用完必须释放。
- 窗口 : 一个进程可以有多个窗口(想象firefox.exe开两个窗口上网),窗口也有对应的窗口句柄(号)供系统调用,同样是指针,每次调用也动态变化。
#tryCtypes.py
import ctypes
#get window handle
handle = ctypes.windll.user32.GetForegroundWindow()
#get window title
windowTitle = ctypes.create_string_buffer(("\x00" * 512).encode())
length = ctypes.windll.user32.GetWindowTextA(handle, ctypes.byref(windowTitle), 512)
#get PID
pid = ctypes.c_ulong(0)
ctypes.windll.user32.GetWindowThreadProcessId(handle, ctypes.byref(pid))
#get process name
peName = ctypes.create_string_buffer(("\x00" * 512).encode())
hProcess = ctypes.windll.kernel32.OpenProcess(0x400 | 0x10, False, pid)
ctypes.windll.psapi.GetModuleBaseNameA(hProcess,None, ctypes.byref(peName), 512)
kernel32.CloseHandle(handle)
kernel32.CloseHandle(hProcess)
print("[*] current window:")
print("[*] window handle: %s" % handle)
print("[*] pid(process ID): %s" % pid.value)
print("[*] process name: %s" % peName.value.decode())
print("[*] window title: %s" % windowTitle.value.decode())
---
>>>
========== RESTART: C:\Users\Administrator\Desktop\python3\tryCtypes.py ==========
[*] current window:
[*] window handle: 2360406
[*] pid(process ID): 4172
[*] process name: pythonw.exe
[*] window title: *Python 3.7.0 Shell*
>>>
(六) windows进阶
1. windows消息机制和钩子 : pyHook库
(1) windows的窗口程序基于消息机制,并由事件驱动。
- 事件(event) : 事件是由用户通过外部输入设备触发的动作,如点击鼠标左键,敲击键盘都会产生一个事件。
(除此之外事件还可能由诸如button控件、file菜单触发,也有来自windows内部的事件) - 消息(message) : 事件产生消息。当一个事件被触发后,会被windows翻译(量化)成一个消息。
消息是一个结构体数据,里面包含窗口句柄(消息所属窗口),消息编号,和两个DWORD消息参数。 - windows消息机制
<1> 当用户运行一个应用程序时,通过点击鼠标或者敲键盘,产生了一些事件;
<2> 事件被windows监控到,量化为消息,消息被windows捕获到之后放到windows的消息队列;
<3> windows在分析该消息之后得知它应该属于哪个应用程序,就把它存到该程序自己的消息队列中;
<4> 每个程序都有一个消息循环,不断从自己的消息队列中读取消息,并将其分配给相应的线程或窗口,交由窗口处理函数处置。
(2) 钩子(函数)
- 前文提到,当一个消息被windows分析之后要发往相应程序的消息队列。
钩子就是(在消息被发往程序的消息队列之前)截获消息的函数。 - windows的原生函数SetWindowsEx允许我们自定义钩子,从而去截获特定事件生成的消息。
pyHook库就是基于这一函数进行封装编写的。 - pyHook是第三方库,但无法pip快捷安装。
[pyHook下载地址]
下载完成后输入pip install xxx.whl
安装即可。
注意64位windows7下安装的python有可能是32位的(使用platform.architecture()
查看python架构)。
(3) 应用 : 键盘/鼠标记录
#example.py
import pyHook
def OnMouseEvent(event):
print('MessageName:',event.MessageName)
print('Message:',event.Message)
print('Time:',event.Time)
print('Window:',event.Window)
print('WindowName:',event.WindowName)
print('Position:',event.Position)
print('Wheel:',event.Wheel)
print('Injected:',event.Injected)
print('---')
# return True to pass the event to other handlers
# return False to stop the event from propagating
return True
def OnKeyboardEvent(event):
print('MessageName:',event.MessageName)
print('Message:',event.Message)
print('Time:',event.Time)
print('Window:',event.Window)
print('WindowName:',event.WindowName)
print('Ascii:', event.Ascii, chr(event.Ascii))
print('Key:', event.Key)
print('KeyID:', event.KeyID)
print('ScanCode:', event.ScanCode)
print('Extended:', event.Extended)
print('Injected:', event.Injected)
print('Alt', event.Alt)
print('Transition', event.Transition)
print('---')
# return True to pass the event to other handlers
# return False to stop the event from propagating
return True
# create the hook mananger
hm = pyHook.HookManager()
# register two callbacks
hm.MouseAllButtonsDown = OnMouseEvent
hm.KeyDown = OnKeyboardEvent
# hook into the mouse and keyboard events
hm.HookMouse()
hm.HookKeyboard()
if __name__ == '__main__':
import pythoncom
pythoncom.PumpMessages()
---
>>>
RESTART: C:\Program Files (x86)\Python37-32\Lib\site-packages\pyHook\example.py
MessageName: mouse left down
Message: 513
Time: 179313024
Window: 65680
WindowName: Running applications
Position: (278, 1015)
Wheel: 0
Injected: 0
---
MessageName: key down
Message: 256
Time: 179317158
Window: 1312710
WindowName: Administrator: Command Prompt
Ascii: 73 I
Key: I
KeyID: 73
ScanCode: 23
Extended: 0
Injected: 0
Alt 0
Transition 0
2. 设备描述表(未完待续)
-
windows显示原理 : 用户操作 --> 设备描述表 --> 显卡驱动程序 --> 显卡硬件 --> 显示器
-
设备描述符(Device Context, DC) : 又称设备上下文,用以实现应用程序与硬件之间的交互。
-
DC是一种数据结构,其结构的核心是位图(bitmap)。
-
GDI(Graphics Device Interface, 图形设备接口) ?
DC是GDI函数内部保留的数据结构
- 应用 : 屏幕快照
#pywin32 library
import win32gui
import win32ui
import win32con
import win32api
#1.获取当前桌面的句柄和像素尺寸
hdesktop = win32gui.GetDesktopWindow()
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
#2.获取当前桌面的设备描述表,用以创建截图用的设备描述表
desktopDC = win32gui.GetWindowDC(hdesktop)
imgDC = win32ui.CreateDCFromHandle(desktopDC)
memDC = imgDC.CreateCompatibleDC()
screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(imgDC, width, height)
memDC.SelectObject(screenshot)
memDC.BitBlt((0, 0), (width, height), imgDC, (left, top), win32con.SRCCOPY)
screenshot.SaveBitmapFile(memDC, 'C:\\Users\\Administrator\\Desktop\\screenshot.bmp')
memDC.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())
python实现全屏截图(两个程序对比着看,继续学习这部分内容)
import time
import os, win32gui, win32ui, win32con, win32api
def window_capture():
hwnd = 0
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC=win32ui.CreateDCFromHandle(hwndDC)
saveDC=mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
MoniterDev=win32api.EnumDisplayMonitors(None,None)
w = MoniterDev[0][2][2]
h = MoniterDev[0][2][3]
print w,h
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0,0),(w, h) , mfcDC, (0,0), win32con.SRCCOPY)
bmpname=win32api.GetTempFileName(".","")[0]+'.bmp'
saveBitMap.SaveBitmapFile(saveDC, bmpname)
return bmpname
os.system(window_capture())
3. pywin32库(pip)
import pythoncom
import win32clipboard
4. ctypes库
5. 命令行下与shell交互的处理技巧
(1) 如何与命令行交互(脚本命令放入命令行执行,结果返回)
(2) subprocess库
(七)导入模块的三种方法的区别
1. import module
方法型引用
>>> import math
>>> math.pi
3.141592653589793
2. from module import *
将模块下所有名字引入当前名称空间,直接引用。
>>> from math import *
>>> pi
3.141592653589793
3. import module as xxx
用xxx来代替module进行引用
>>> import math as m
>>> m.pi
3.141592653589793
(八)待研究问题
1.深拷贝与浅拷贝
- !!!!!!!!!!!! 类 class !!!!!
- py文件封装成exe : XXX库
- 爬虫
- GUI编程 : PyQt
- json
- 正则表达式