Python精确指南——第四章-部署和技巧

举报
lurayvis 发表于 2017/12/08 15:13:36 2017/12/08
【摘要】 4 Python工程打包部署Python程序在提供给用户使用时,要脱离Python开发环境运行,此时,需要对python工程进行打包。常用的Python打包工具有PyInstaller, py2exe等。4.1 PyInstaller特点一条命令即可完成打包。以GPL标准许可发布,但可用于闭源商业性质的打包使用。早先的版本不支持Python 3.x版本,最新的版本已经支持,并且

image.png

4       Python工程打包部署

Python程序在提供给用户使用时,要脱离Python开发环境运行,此时,需要对python工程进行打包。

常用的Python打包工具有PyInstaller, py2exe等。

4.1     PyInstaller

  • 特点

一条命令即可完成打包。

以GPL标准许可发布,但可用于闭源商业性质的打包使用。

早先的版本不支持Python 3.x版本,最新的版本已经支持,并且具有跨平台特性,首选打包工具。

  •   安装

pip install pyinstaller

pyinstaller.exe将安装到Python安装目录的Scripts目录下,和pip工具在相同路径。

  • 打包命令简介

基本命令形式:

pyinstaller yourprogram.py

其中yourprogram.py是程序入口源文件。

-p指令

这个指令后面可以增加pyinstaller搜索模块的路径。因为应用打包涉及的模块很多。这里可以自己添加路径。不过经过笔者测试,site-packages目录下都是可以被识别的,不需要再手动添加。

-F指令

注意指令区分大小写。这里是大写。使用-F指令可以把应用打包成一个独立的exe文件,否则是一个带各种dll和依赖文件的文件夹。

-w指令

直接发布的exe应用带命令行控制台窗口,在指令内加入-w命令可以屏蔽。

--icon指令

为打包的exe指定图标。

-v指令

指定版本信息。

以上是最常用的几个可选参数指令,更多详细功能介绍可以参考PyInstaller官网:

http://www.pyinstaller.org/

如果Python项目工程比较复杂,可以编写一个python脚本辅助执行打包命令。

4.2     py2exe

官网地址:

http://www.py2exe.org/

  •   特点

py2exe也是比较常用的打包工具,py2exe 是在 Distutils 的基础上扩展了一个新的 "命令"。

所以使用py2exe进行打包,需要编写一个带有setup指令的脚本。

  • 安装

pip install py2exe

SourceForge网站提供有历史版本的下载:

http://prdownloads.sourceforge.net/py2exe

安装完成后,lib\site-packages\py2exe\samples目录下含有使用py2exe打包的样例。

  • 使用

可以参考官方的使用教程:

http://www.py2exe.org/index.cgi/Tutorial

  • 注意

使用py2exe打包完成后,包含有一个名为library.zip的压缩包,压缩包内包含了所有python源文件编译后的的pyc或者pyo字节码文件。但是pyc和pyo很容易被反编译回Python源码,这一点需要注意。

5       Python常用编码技巧

5.1     Python编程规范

Python官方PEP8标准编程规范:

https://www.python.org/dev/peps/pep-0008/

python集成开发环境当中介绍的PyCharm,在编码过程中自带有官方PEP8标准的编程规范提示,推荐使用。

实际使用时,可以根据公司或者部门的代码特点进行更新或者扩展。

5.2     时间模块

  • 等待一段时间

import time

time.sleep(5)

  • 获得当前时间,时间差

方法1:

import datatime

import time

start_t = datetime.datetime.now() #get current time

time.sleep(3)

end_t = datetime.datetime.now()

tdiff = (end_t - start_t).seconds

 

方法2:

import time

t1 = time.localtime

time.sleep(3)

t2 = time.localtime

tdiff = time.mktime(t2) - time.mktime(t1)

if int(my_time_diff) > 30:

        print “30 seconds time out”

5.3     序列类型

序列类型包括元组,字典和字符串,以下语法这三种类型全部适用。

5.3.1  序列类型的访问

以常量字符串a = “1234567890”,进行举例说明:

  • 索引访问

>>> a[3]

'4'

>>> a[2:5]

'345'

>>> a[:5]

'12345'

>>> a[5:]

'67890'

>>> a[:]

'1234567890'

>>> a[2:9:2]

'3579'

>>> a[::2]

'13579'

>>> a[-2]

'9'

>>> a[::-1]

'0987654321'

5.3.2  序列类型的操作

长度:len()

连接:+

重复:*

判断元素是否在序列中:in

最大值:max()

最小值:min()

比较是否相同:cmp(elem1, elem2)

5.4     多级字典

多级字典不能一次性赋值,比如

              level2_dict[“lvl1”][“lvl2”] = “result”

              这样就会报错。只能分两步。

              level2_dict[“lvl1”] = {}

              level2_dict[“lvl1”][“lvl2”] = “result”

5.5     字符串

字符串的基本方法这里不做过多介绍,主要介绍一些字符串的实际使用技巧。

5.5.1  字符串的打印

Python的打印是不需要加入’\n’就能换行的,这点要注意,和其他的语言不太一样。

5.5.2  格式化字符串

>>> "my name is %s, I'm %d years old."%("mike", 15)

"my name is mike, I'm 15 years old."

  • 字符串类format方法

按照参数索引:

>>> "my name is {0}, I'm {1} years old.".format("mike", 15)

"my name is mike, I'm 15 years old."

按照参数名称:

>>> "my name is {name}, I'm {age} years old.".format(name="mike", age="15")

"my name is mike, I'm 15 years old."

5.5.3  数字和字符串转换

my_str = str(my_num)

my_num = int(my_str)

  • 通过string类方法

>>> import string

>>> a="12345"

>>> string.atoi(a)

12345

>>> b="123.678"

>>> string.atof(b)

123.678

5.5.4  正则表达式

Python对于字符串的操作十分简单方便,结合内置正则表达式re模块,几乎可以完成所有字符串操作的需求。

>>> import re

>>> tt = "Tina is a good girl, she is cool, clever, and so on..."

>>> rr = re.compile(r'\w*oo\w*')

>>> print(rr.findall(tt))

['good', 'cool']

>>> print(re.match('com\w*.', 'comwww.runcomoob').group())

comwww.

>>> print(re.search('\dcom', 'www.4comrunoob.5com').group())

4com

>>> a = "123abc456"

>>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0)) # 索引0表示返回匹配的整体

123abc456

>>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1))

123

>>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2))

abc

>>> print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(3))

>>> iter = re.finditer(r'\d+', '12 drumm44ers drumming, 11 ... 10 ...')

>>> for i in iter:

      print(i)

      print(i.group())

      print(i.span())

<_sre.SRE_Match object at 0x02517F38>       # 匹配到的Match对象

12                                                                                                                          # 匹配到的子串

(0, 2)                                                                                                                       # 匹配到的字串的索引

<_sre.SRE_Match object at 0x02526250>

44

(8, 10)

<_sre.SRE_Match object at 0x02517F38>

11

(24, 26)

<_sre.SRE_Match object at 0x02526250>

替换匹配到的字串

>>> text = "Mike is a handsome boy, he is cool, clever, and so on..."

>>> print(re.sub(r'\s+', '-', text))

Mike-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...

并获得替换的个数:

>>> print(re.sub("g.t", "have", 'I get A,  I got B ,I gut C'))

I have A,  I have B ,I have C

>>> print(re.subn("g.t", "have", 'I get A,  I got B ,I gut C'))

('I have A,  I have B ,I have C', 3)

>>> print(re.split('\d+', 'one1two2three3four4five5'))

['one', 'two', 'three', 'four', 'five', '']

5.6     断言

Python中的assert是判断表达式是否为True的断言关键字,例如判断实例是否属于一个类:

 

assert isinstance(rsm_info_preview, RsmKeyInfo)

5.7     系统命令

  • 路径的操作

ll_test_path = os.path.join(os.path.dirname(__file__),"win32", "IEDriver.dll")

os.path.dirname(__file__)表示当前file的位置。

Import sys

sys.exit()

注意有的时候在函数和类并不能使整个程序退出,注意尽量在主函数里退出才可以。

os.system(“xxx”)

阻塞命令,需要等待系统命令调用完成才能返回,成功返回0,失败返回1。

os.startfile(“xxx”)

非阻塞命令,单纯启动命令,执行后立即返回,与目标应用不存在从属关系。相当于双击操作。

os.popen(“xxx”)

执行后,启动一个进程,返回文件描述符对象,利用该对象可以捕获该进程的输出。

  • 命令行

commands模块

纯命令行调用模块,可以获得命令的输出或(且)返回值。

  •   进程高级

subprocess模块

更为高级的进程管理模块,提供比os.popen更加高级的接口,可以实现复杂的多进程管理。

5.8     lambda

在Python中,lambda是匿名函数的关键字。

lambda可以定义一句代码覆盖的方法或者函数。大部分情况下,lambda函数当做参数传递给其他方法或者函数,在方法或者函数内部实现动态调整计算实现的过程。类似模板方法设计模式的思路。

5.8.1  基本语法

>>> add = lambda x, y: x + y

>>> add(2,3)

5

如上面例子,lambda表达式中,除了lambda关键字,冒号左边等价于def函数的入参,冒号右边等价于def函数的函数体中的return返回值。

整个lambda表达式可以作为一个函数的引用,赋值给一个变量(即例子中的add)。

5.8.2  常用内建函数

lamda结合内建函数map, reduce, filter可以实现强大的序列类型(元组,列表,字符串)的处理功能。

  •  map

内建函数map可以得到入参序列中逐个元素按照lambda函数处理后返回的序列。省去循环代码实现的过程。

              >>> list1 = range(5)

>>> print list1

[0, 1, 2, 3, 4]

>>> map(lambda x: x*2, list1)

[0, 2, 4, 6, 8]

如上代码实现了列表中每个元素乘2的效果。

  •  reduce

内建函数reduce进行两两元素的迭代计算,每次迭代,将上一次的迭代结果(第一次时为initial的元素,如没有initial则为列表的第一个元素)与下一个元素一同执行一个二元的lambda函数,最终得到一个计算结果。

>>> list1

[0, 1, 2, 3, 4]

>>> reduce(lambda x, y: x + y, list1)

10

>>> reduce(lambda x, y: x + y, list1, 2)

12

如上代码实现了列表求和的效果。

  •   filter

该内建函数的作用相当于一个筛选器。lambda函数是一个布尔函数,filter调用这个布尔函数,将每个列表中的元素依次经过一次筛选,选出使lambda返回值是Ture的元素的序列。

>>> list1

[0, 1, 2, 3, 4]

>>> filter(lambda x: x % 2 == 1, list1)

[1, 3]

如上代码实现了筛选列表中所有奇数的效果。

5.9     字符串也是代码

5.9.1  eval

eval可以执行字符串参数的表达式,并返回表达式执行完成的结果。

>>> eval("((1+2)*3.0/4)**2")

5.0625

>>> eval("'hello '*2")

'hello hello '

>>> list1 = eval("[1,0.5,[3,4], 'hello python', {1:'one'}]")

>>> list1

[1, 0.5, [3, 4], 'hello python', {1: 'one'}]

>>> type(list1)

<type 'list'>

5.9.2  exec

exec可以执行字符串中的代码。

>>> exec("print 'hello python'")

hello python

>>> exec("for i in range(3):print i")

0

1

2

5.9.3  区别和使用

eval需要传入的是一个表达式,执行完成后会返回一个结果;exec则是执行字符串内包含的python代码,并不会返回结果。两者不能混用。

因为eval和exec的存在,大大增加了python代码的灵活性。程序可以在运行时状态动态生成代码去执行,可以留作热更新的接口,免去重新编译和打包的过程。不过也因此,带来编码安全的隐患,恶意的字符串注入,将可以执行任意代码。使用时需谨慎。

5.10   yield

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

如下就生成了一个斐波那契数列的生成器:

>>> def fib(max):

a, b = 1, 1

while a < max:

yield a

a, b = b, a+ b

>>> fib_gen = fib(15)

>>> fib_gen.next()

1

>>> fib_gen.next()

1

>>> fib_gen.next()

2

>>> fib_gen.next()

3

>>> fib_gen.next()

5

>>> fib_gen.next()

8

>>> fib_gen.next()

13

>>> fib_gen.next()

 

Traceback (most recent call last):

File "<pyshell#100>", line 1, in <module>

    fib_gen.next()

StopIteration

   可以看到,在使用next()的时候要注意生成器的生成边界。

在for循环当中,会自动调用 next(),并在触发StopIteration异常时自动捕获终止:

>>> for n in fib(15): 

    print n

1

1

2

3

5

8

13

              yield的实时性更高,只有在调用的时候才真正执行,可以控制对于系统资源的消耗。

5.11   dir, setattr, getattr,hasattr

这四个函数是Python的内建函数,结合起来使用可以获取对象的属性信息,动态调整对象的属性和值。

>>> class BaseClass(object):

    '''

    classdocs

    '''

    def __init__(self, first, second, third=3, forth=4):

        self.first = first

        self.second = second

        self._third = third

        self.__forth = forth

        print "in init"

    def __del__(self):

        print "in del"

    def first_func(self):

        print "in first func"

    def _second_func(self, para1):

        pass

    def __third_func(self, para1, para2=0):

        pass

>>> A = BaseClass(1, 2)

>>> dir(A)

['_BaseClass__forth', '_BaseClass__third_func', '__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_second_func', '_third', 'first', 'first_func', 'second']

 

>>> getattr(A, "first")

1

>>> getattr(A, "first_func")()

in first func

 

>>> hasattr(A, "five")

False

>>> setattr(A, "five", 5)

>>> A.five

5

>>> hasattr(A, "five")

True

这一组内建函数为Python的面向对象特性进行了灵活性扩展,不过完全越过了对于类中成员的保护,在使用时需要注意成员访问的权限问题。

作者|lurayvis 撰写初稿,fhk精美更新

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。