美文网首页
人生苦短系列中级篇 : python 进阶技巧

人生苦短系列中级篇 : python 进阶技巧

作者: siuLimTau | 来源:发表于2018-09-24 13:57 被阅读0次

    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 4444shell>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.深拷贝与浅拷贝

    1. !!!!!!!!!!!! 类 class !!!!!
    2. py文件封装成exe : XXX库
    3. 爬虫
    4. GUI编程 : PyQt
    5. json
    6. 正则表达式

    相关文章

      网友评论

          本文标题:人生苦短系列中级篇 : python 进阶技巧

          本文链接:https://www.haomeiwen.com/subject/wnbcoftx.html