1.1 整体
1.1.1 概述
1.1.2 文件开头
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
第3行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第4行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
1.1.3 文件编码
•ASCII占1个字节,只支持英文
•GB2312占2个字节,支持6700+汉字
•GBKGB2312的升级版,支持21000+汉字
•Shift-JIS日本字符
•ks_c_5601-1987韩国编码
•TIS-620泰国编码
由于每个国家的编码只涵盖了自己国家的字符,所以应运而生出现了万国码,他涵盖了全球所有的文字和二进制的对应关系,
•Unicode2-4字节已经收录136690个字符,并还在一直不断扩张中...
Unicode起到了2个作用:
1.直接支持全球所有语言,每个国家都可以不用再使用自己之前的旧编码了,用unicode就可以了。(就跟英语是全球统一语言一样)
2.unicode包含了跟全球所有国家编码的映射关系。
Unicode解决了字符和二进制的对应关系,但是使用unicode表示一个字符,太浪费空间。例如:利用unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。
由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!!
为了解决存储和网络传输的问题,出现了UnicodeTransformationFormat,学术名UTF,即:对unicode中的进行转换,以便于在存储和网络传输时可以节省空间!
•UTF-8:使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个
•UTF-16:使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
•UTF-32:使用4个字节表示所有字符;
总结:UTF是为unicode编码设计的一种在存储和传输时节省空间的编码方案。
1.1.4 模块和包
每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。
__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块。
模块的概念:一个.py文件就称为一个模块
导入模块中函数的方式:
方式一:import 模块名
使用时:模块名.函数名()
方式二:from 模块名 import 函数名
使用时:函数名()
方式三:from 模块名 import *
使用时:函数名(),另外可以在模块当中定义一个变量__all__=[f1,f2],用于限定导入的函数
方式四:from 模块名 import 函数名 as xx
使用时:xx()
包的概念:包含了几个py文件的文件夹里面再创建一个__init__文件就是包
导入包中函数,以及模块的方式:
方式一:from 包名 import 模块名
使用时:模块名.函数名()
方式二:from 包名.模块名 import 函数名
使用时:函数名()
方式三:import 包名.模块名
使用时:包名.模块名.函数名()
方式四:from 包名 import *
前提是:将__init__.py文件中写入__all__变量(写入方式同模块导入的写入方式),变量当中写入哪个模块则导入哪个模块,不写则什么都不导入
使用时:模块名.函数名()
方式五:import 包名
前提是:在包里面的__init__.py文件里写入from . import 模块名
使用时:模块名.函数名()
导入同级模块(在同一个文件夹中的py文件)直接导入即可
导入下级目录模块也很容易,需在下级目录中新建一个空白的__init__.py文件再导入
导入上级目录下模块,可以使用sys.path:
import sys
sys.path.append("..")
导入隔壁文件夹下的模块,这其实是前面两个操作的组合,其思路本质上是将上级目录加到sys.path里,再按照对下级目录模块的方式导入
注意,要以运行脚本的位置为中心来计算其他模块的导入
.pyc文件的介绍:
导入时会产生一个.pyc的字节码文件,此文件是当第一次导入时python解释器会将被导入的模块预解释成字节码的文件,下次再导入时python解释器则不会做预解释而是直接拿.pyc文件使用,这样就不会每次导入时做解释的操作,节省时间,当修改模块文件的内容时,python解释器会根据.pyc文件和模块的修改时间判断有没有对模块做修改,如果模块的修改时间比.pyc文件的晚说明模块已经被修改,Python解释器会将模块重新解释成.pyc文件。
from .A import b和from A import b区别:前者是从当前目录的包A下导入b,是相对路径,后者是从包A下导入b,此时包A不一定在当前目录下,是绝对路径的。
1.1.5 命名规范
类
总是使用首字母大写单词串。如MyClass。内部类可以使用额外的前导下划线。
函数&方法
小写+下划线
*注意*:混合大小写仅被允许用于这种风格已经占据优势的时候,以便保持向后兼容。
函数和方法的参数
如果一个函数的参数名称和保留的关键字冲突,通常使用一个后缀下划线
全局变量
对于from M import *导入语句,如果想阻止导入模块内的全局变量可以使用旧有的规范,在全局变量上加一个前导的下划线。
*注意*:应避免使用全局变量
变量
小写,由下划线连接各个单词。如color=WHITE,this_is_a_variable=1
*注意*:
1.不论是类成员变量还是全局变量,均不使用m或g前缀。
2.私有类成员使用单一下划线前缀标识。
3.变量名不应带有类型信息,因为Python是动态类型语言。如iValue、names_list、dict_obj等都是不好的命名。
常量
常量名所有字母大写,由下划线连接各个单词如MAX_OVERFLOW,TOTAL。
异常
以“Error”作为后缀。
文件名
全小写,可使用下划线
包
应该是简短的、小写的名字。如果下划线可以改善可读性可以加入。如mypackage。
模块
与包的规范同。如mymodule。
缩写
命名应当尽量使用全拼写的单词,缩写的情况有如下两种:
1.常用的缩写,如XML、ID等,在命名时也应只大写首字母,如XmlParser。
2.命名中含有长单词,对某个单词进行缩写。这时应使用约定成俗的缩写方式。
例如:
function 缩写为 fn
text 缩写为 txt
object 缩写为 obj
count 缩写为 cnt
number 缩写为 num,等。
前导后缀下划线
一个前导下划线:表示非公有。
一个后缀下划线:避免关键字冲突。
两个前导下划线:当命名一个类属性引起名称冲突时使用。
两个前导和后缀下划线:“魔”(有特殊用图)对象或者属性,例如__init__或者__file__。绝对不要创造这样的名字,而只是使用它们。
*注意*:关于下划线的使用存在一些争议。
特定命名方式
主要是指__xxx__形式的系统保留字命名法。项目中也可以使用这种命名,它的意义在于这种形式的变量是只读的,这种形式的类成员函数尽量不要重载。如
class Base(object):
def __init__(self, id,parent=None):
self.__id__=id
self.__parent__=parent
def __message__(self,msgid):
其中__id__、__parent__和__message__都采用了系统保留字命名法。
1.1.6 下划线
1. 单前导下划线 _var:下划线前缀的含义是告知其他程序员:以单个下划线开头的变量或方法仅供内部使用。
2. 单末尾下划线 var_:有时候,一个变量的最合适的名称已经被一个关键字所占用。 因此,像class或def这样的名称不能用作Python中的变量名称。 在这种情况下,你可以附加一个下划线来解决命名冲突
3. 双前导下划线 __var:双下划线前缀会导致Python解释器重写属性名称为_类名__var,以避免子类中的命名冲突。
4. 双前导和双末尾下划线 __var__:如果一个名字同时以双下划线开始和结束,则不会应用名称修饰,由双下划线前缀和后缀包围的变量不会被Python解释器修改
5. 单下划线 _:按照习惯,有时候单个独立下划线是用作一个名字,来表示某个变量是临时的或无关紧要的。
例如,在下面的循环中,我们不需要访问正在运行的索引,我们可以使用“_”来表示它只是一个临时值:
for _ in range(32):
print('Hello, World.')
也可以在一个解释器会话中表示访问先前计算的结果
1.1.7 分号
Python也支持分号,同样用于一条语句的结束标识。但在Python中分号的作用已经不像C、Java中那么重要了,Python中的分号可以省略,主要通过换行来识别语句的结束。
如果要在一行中书写多条句,就必须使用分号分隔每个语句,否则Python无法识别语句之间的间隔
Python同样支持多行写一条语句,Python使用“\\”作为换行符。在实践中,一条语句写在多行也是非常常见的。
1.1.8 帮助文档
sys.__doc__
print(sys.__doc__)
help(sys)
def fun_single_doc(num):
" 单行函数文档 "
# pycharm会提示:使用 """ 代替 "
return num + 1
def fun_multiple_doc():
"""
多行的函数文档
单行文档不好,推荐使用多行的函数文档。
pycharm有快捷键ctrl+q,可以快捷查看函数的说明信息。
"""
def main():
print(fun_single_doc.__doc__)
print(fun_multiple_doc.__doc__)
if __name__ == '__main__':
main()
1.1.9 打印数据
print([object1, object2, object3, …] [,sep=’ ‘] [end=’\n’] [file = sys.stdout])
对于print所有的参数都是可选的,如果没有参数,print会打印一个换行符到标准输出
参数解释:
object是要打印的对象,多个打印对象用逗号隔开
sep ->对于多个打印对象,之间用sep隔开,默认值一个空格
end ->打印末尾自动添加的,默认值是换行符
file ->打印的输出位置,默认值是标准输出,还可以是一个文件句柄。这样打印直接就进了文件中
print和sys.stdout的关系:
sys.stdout.write(‘hello world’)这个也会在标准输出实现,其实print就是python为你封装了一个sys.stdout.write的更好用的版本
例子:
print(1,2) ==>sys.stdout.write(str(1) + ' ' + str(2) + '\n')
也就是说,print会自动调用str内置函数,将打印字符字符串化(sys.stdout.write()只能打印字符串)
也就是相对于sys.stdout.write()来说,print对(1)自动将打印对象字符串化,(2)在多个对象之间添加分隔符,(3)在末尾添加结束符
print()和pprint()都是python的打印模块,功能基本一样,唯一的区别就是pprint()模块打印出来的数据结构更加完整,每行为一个数据结构,更加方便阅读打印输出结果。特别是对于特别长的数据打印,print()输出结果都在一行,不方便查看,而pprint()采用分行打印输出,所以对于数据结构比较复杂、数据长度较长的数据,适合采用pprint()打印方式。当然,一般情况多数采用print()。
以下为代码示例:
import pprint
data = ("test", [1, 2, 3,'test', 4, 5], "This is a string!",
{'age':23, 'gender':'F'})
print(data)
pprint.pprint(data)
输出结果:
print 同一行打印显示进度条效果
print 中的 end = '\r' 是一个转义符,作用是让光标重新回到首行,默认是'\n'换行符
import sys,time
total = 153
for i in range(total):
if i+1 == total:
percent = 100.0
print(f'当前核算进度 : {percent}% [{i+1}/{total}]',end='\n')
else:
percent = round(1.0 * i / total * 100,2)
print(f'当前核算进度 : {percent}% [{i+1}/{total}]',end='\r')
time.sleep(0.1)
实现效果:
1.1.10 输入数据
s = input('birth: ')
birth = int(s)
if birth < 2000:
print('00前')
else:
print('00后')
1.1.11 作用域
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问
1.1.12 运行字符串代码
eval() 和 exec() 函数都属于 Python 的内置函数,由于这两个函数在功能和用法方面都有相似之处。eval() 和 exec() 函数的功能是相似的,都可以执行一个字符串形式的 Python 代码(代码以字符串的形式提供),相当于一个 Python 的解释器。二者不同之处在于,eval() 执行完要返回结果,而 exec() 执行完不返回结果。
eval() 函数的语法格式为:eval(source, globals=None, locals=None, /);而 exec() 函数的语法格式如下:exec(source, globals=None, locals=None, /)。
expression:这个参数是一个字符串,代表要执行的语句 。该语句受后面两个字典类型参数 globals 和 locals 的限制,只有在 globals 字典和 locals 字典作用域内的函数和变量才能被执行。
globals:这个参数管控的是一个全局的命名空间,即 expression 可以使用全局命名空间中的函数。如果只是提供了 globals 参数,而没有提供自定义的 __builtins__,则系统会将当前环境中的 __builtins__ 复制到自己提供的 globals 中,然后才会进行计算;如果连 globals 这个参数都没有被提供,则使用 Python 的全局命名空间。
locals:这个参数管控的是一个局部的命名空间,和 globals 类似,当它和 globals 中有重复或冲突时,以 locals 的为准。如果 locals 没有被提供,则默认为 globals。
注意,__builtins__ 是 Python 的内建模块,平时使用的 int、str、abs 都在这个模块中。通过 print(dic["__builtins__"]) 语句可以查看 __builtins__ 所对应的 value。
在使用 Python 开发服务端程序时,这两个函数应用得非常广泛。例如,客户端向服务端发送一段字符串代码,服务端无需关心具体的内容,直接跳过 eval() 或 exec() 来执行,这样的设计会使服务端与客户端的耦合度更低,系统更易扩展。
报错:NameError: name 'f' is not defined。
这是因为exec函数的作用域的问题,这个函数现在有两个参量globals() 和locals()。默认情况下,exec是作用于局部范围的,因为我是在自己定义的子函数里使用exec函数,所以就会报错。
解决方法是修改为下图,在子函数中使用时,在后面增加globals()。
1.1.13 打包
window系统下发布:
一、安装pyinstaller(PyInstaller 3.3.1)
cmd安装命令:
pip install pyinstaller
二、python程序打包(不带参数)
文件命名规范:不支持中文命令的python文件;
路径:建议路径中不带中文目录;
命令:pyinstaller -F 文件名(带后缀py)
#命令语法:pyinstaller -F 文件名(带后缀py)
#常用参数说明:
#–icon=图标路径
#-F 打包成一个exe文件
#-w 使用窗口,无控制台
#-c 使用控制台,无窗口
#-D 创建一个目录,里面包含exe以及其他一些依赖性文件
#pyinstaller -h 来查看参数
#将cmd的目录切换至(命令:cd 文件路径(注意空格))需要打包的py文件目录下:
#有命令窗口弹出
pyinstaller -F shjys_rjjqk.py
#无命令窗口弹出
pyinstaller -F -w shjys_rjjqk.py
#或者
pyinstaller -F shjys_rjjqk.py --noconsole
三、查看生成文件
1,返回目标文件目录,发现该目录下生成了.spec文件shjys_rjjqk.spec:
2,打包好的exe文件,在同目录的dist文件中(一共生成三个文件夹):
四、pyinstaller 改变生成exe程序的图标
# my.ico 是一个图标名,和当前的shjys_rjjqk.py文件在同一个目录下
pyinstaller -F --icon=my.ico shjys_rjjqk.py
五,生成exe(带参数)
py中获取外界参数:
方法1:args 是运行前输入参数(不能在exe黑框中输入,可以用cmd窗口执行:shrjj.py 20180119);
方法2:input是运行时输入参数(可以在exe黑框中输入);
建议用input获取;
补充:方法一中的args参数(运行前输入参数)打包成exe,利用bat批处理来调用,传递参数;
注意:在有调用到外界配置文件的情况下,需要使用绝对路径;不然打包后,会出现找不到配置文件;