一些基本概念
Python语言简洁明了,但对于不少从其他编程语言转过来的人来说,相信有不少兄弟是奔着机器学习来的,经常被Python里的模块冲突,语法错误搞到疯魔,这里就对Python中的一些基本概念做一些基本的阐述,希望能对大家有所帮助(PS:本文不是Python教程,只是对于一些比较常用而又容易忽略的知识点做一些补充)。
解释器
Linux/Unix系统上,一般默认的python版本为2.x,我们可以将python3.x安装在/usr/local/python3目录中,安装完成后,我们可以将路径/usr/local/python/bin添加到Linux/Unix操作系统的环境变量中。
Python解释器不止一种,有CPython、IPython、JPython、PyPy等,顾名思义,CPython使用C语言开发的,官方标准实现,是目前使用最广泛的;IPython是在CPython基础上在交互式方面得到增强的解释器;JPython是转为Java平台设计的解释器,他把Python代码编译成Java字节码执行,PyPy是Python语言(2.7.13和3.5.3)的一种快速、兼容的替代实现,以速度快著称。
头部注释
在python2的py文件写中文,则必须添加一行声明文件编码的注释,否则python2会默认使用ascii编码,具体请参考PEP-0263。用户可以通过使用 #coding=<编码名称> 或 #-*-编码:<编码名称>-*- 来声明编码。
关于 #!/usr/bin/env python 和 #!/usr/bin/python ,在Windows系统下,可以直接通过扩展名来判断文件类型,所以只要是.py的后缀的可以直接关联到python去执行,而Linux系统,则是根据文件开头(首行)的标记来判断文件类型,通过文件指定的程序来运行,如 #!/usr/bin/python 是告诉操作系统调用/usr/bin下的python解释器来执行这个脚本, #!/usr/bin/env python 则是为了防止没有python装在默认的/usr/bin路径里,当系统看到这一行时,会先到env设置里查找python的安装路径,在调用对应的解释器来执行,所以推荐这种写法。
每回需要手动添加头部注释是很麻烦的一件事儿,可不可以通过快捷键直接插入呢?我本人使用的VSCode来撸代码,这里给出VSCode的设置方法:选择File->Preferences->User Snippets选择python.json添加如下:
"HEADER": {
"prefix": "header",
"body": [
"#!/usr/bin/env python",
"# -*- encoding: utf-8 -*-",
"'''",
"@文件 :$TM_FILENAME",
"@说明 :",
"@时间 :$CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND",
"@作者 :姜戈",
"@版本 :1.0",
"'''",
"",
"$0"
],
}
在新建**.py文件后,输入header按tab键即可快速生成头部文件,当然你也可以修改自定义快捷键。除了头部注释以外,我们还会用到单行注释(一个#号),多行注释(3个单引号或双引号),下面我们以Engineer类为例,给大家展示注释的用法:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@文件 :helloworld.py
@说明 :
@时间 :2020/12/30 13:48:21
@作者 :姜戈
@版本 :1.0
'''
class Engineer:
'''
这是我的多行注释:
工程师类,当其写在类下或者方法下时,会被当作文档注释 。
'''
id = '' # 单行注释:标识
name = '' # 单行注释:姓名
age = 24 # 单行注释:年龄
def __init__(self, id, name, age):
'''
构造函数
'''
self.id = id
self.name = name
self.age = age
def work(self):
print(self.name + "正在工作。")
def __str__(self):
'''
等同于java和.net中的tostring()方法
'''
return 'id:{},name:{},age:{}'.format(self.id, self.name, self.age)
if __name__ == '__main__':
'''
这段代码是定式,如果被声明,则当前文件被直接运行时,该代码块会被直接运行;当.py文件是以模块的形式导入是,该代码块则不会执行。
'''
xiaowang = Engineer(1, '小王', 25)
xiaowang.work()
模块和包
模块
有时候,为了提高代码的可维护性,我们会对编写的函数进行分类,放到不同的Python文件里,这样的Python文件我们称之为模块(Moudle),当模块编写完成后,就可以被其他地方引用,我们在编程的时候也会经常引用其他模块,包括Python的内置模块和第三方模块。
包
使用模块还有一个好处就是避免了变量名的冲突,相同名字的函数和变量存在不同的模块中(不过还是要尽量避免命名与内置函数冲突),就像这个世界上有很多重名的人一样,模块命名总有可能冲突,为解决这个问题,Python又引入按目录来组织模块的方法,称之为包(Package)。
举个例子:
/factory
├─/hr
│ ├─ __init__.py
│ └─ work.py
├─/engineer
│ ├─ __init__.py
│ └─ work.py
文件work.py就是一个名字叫做work的模块,但是,hr和enginner都有work的模块,但由于其目录不同,所以在引用时并不会发生冲突,比如我们想让hr去工作,通过 factory.hr. work 来引入,想让 enginner 去工作,就通过 factory.enginner.work 来引入。这里需要注意的是,我们在每一层目录下都有一个 _init_.py的文件,它可以为空,但是是必须的,否则,Python就会把这个目录当成一个普通目录,而不是一个包。
下面我们通过os包中的getcwd方法来获取:
import os
def show():
print(os.getcwd())
if __name__ == '__main__':
show()
如上面的代码所示,当我们需要使用os中的getcwd方法时,先通过import将其导入进来,就可以直接调用了。每个模块都包含一些对象的定义和一些语句,当模块第一次被加载时这些语句会被执行(多次import只会执行一次),当引入一个模块时,Python会按照以下路径来查找模块:
- 当前目录
- 环境变量PYTHONPATH所制定的目录
- Python包的安装目录
一般来说,正常函数和变量名是公开的,可以被直接引用,类似 __xxx__这样的变量就属于特殊变量,会有一些特殊用途,比如_author_,_name_,__version__就属于特殊变量,而类似_xxx和__xxx这种变量就是非公开的,不应该被直接引用,当然这种不应该只是习惯上的,并不是不能,Python并没有强制性的做Public和Private的限制。
当然,每个模块中除了你自己自定义的变量之外,还会有一些默认的attribute,我们通过dir()可以查看到结果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
第三方模块
除了自己编写的模块,大多数时候,我们还需要引入第三方模块,如常用的numpy,那么该如何引入呢? 与nuget和maven类似,我们在使用模块之前,我们先要使用命令“pip install numpy”将numpy安装到本地,通常安装在对应解释器的 .../site-packages 目录下。如果仍不清楚,可以使用 pip show numpy
来查看 numpy 安装目录,如果仍不清楚,可以通过巧用 pip uninstall numpy
来查看其安装目录,只需要在确认时输入n即可。
模块的引入
一个模块的导入方式可以有多种,这里我们依旧以numpy 为例:
- 直接引入:import numpy
- 直接引入并指定别名:import numpy as np
- 直接引入所有:from numpy import *
- 直接引入模块中的指定内容:from numpy import random
- 直接引入模块中的指定内容并指定别名:from numpy import random as rn
其中,我们使用第三种方式来引入包时,就不需要在调用时,指定numpy作为前缀,但这种方式并不推荐,因为这种引用方式很有可能导致其和你自定义或其他模块的变量冲突。
常用工具
pip
Python从2.7.9版本后,引入了pip工具,用于安装第三方包,这里我们依旧以numpy为例:
- 安装指定的Package:pip install numpy
- 安装指定版本的Package:pip install numpy=1.19.2
- 安装最小版本的Package:pip install numpy>=1.19.2
- 升级指定的Package:pip install --upgrade numpy
pydoc
有时候引入了包之后,我们想快速了解包中有哪些功能,这个时候我们可以通过pydoc来生成文档来快速查看api说明,在终端输入命令
python -m pydoc -w -p 9527
然后再浏览器中直接输入:http://localhost:9527/ 就可以直接快速浏览相关文档啦。
import os
print("PYTHONPATH:", os.environ.get('PYTHONPATH'))
print("PATH:", os.environ.get('PATH'))
格式化输出
格式化输出也是程序当中经常用到的功能,尤其是在记录日志的时候,我们工作中主要用到的是字符传的格式化输出和日期格式化输出。
字符串格式化
占位符
其实很简单,如上文的Enginner中的work方法,我们需要打印出张三的工作内容,可以使用占位符直接输出:
print('%s is working,his age is %d'%(name,age))
占位符会自动进行替换,%s是针对所有的数据类型,%d则仅仅针对数字类型,当然也有其他诸如%g、%e的占位符,有兴趣的可以自行了解,下面是一部分示例代码:
print('%d' % (0b10)) # 转换为带符号十进制整数
print('%o' % (0x10)) # 转换为带符号八进制整数
print('%x' % (18)) # 转换为带符号十六进制整数
pi = 3.1415926
print('%f' % pi) # 转化为十进制浮点数
print('%.3f' % pi) # 转化为十进制浮点数,保留小数点后三位
print('%08.3f' % pi) # 转化为十进制浮点数,保留小数点后三位并指定宽度为8,不够的左边补0
print('%.03f' % 3.1) # 转化为十进制浮点数,小数点后补0
format 格式化
# 按顺序的输出
print('{} is working,his age is {}'.format(self.name, self.age))
# 按索引输出
print('{1} is working,his age is {0}'.format(self.age,self.name))
f-String格式化
name='张三'
age=24
print(f'{name} is working,his age is {age}')
# 输出的简单运算,如age的乘2
print(F'{name} is working,his age is {age*2}')
print(F"{pi:.2f}") # 保留小数点后2位
print(F"{pi:0>20}") # 指定宽度不够的左侧以0补齐
print(F"{pi:0<20}") # 指定宽度不够的右侧以0补齐
日期格式化
时间转换是工作中经常遇见的问题,Python格式化日期的函数为datetime.datetime.strftime()
,由字符串转换日期的函数为datetime.datetime.strptime()
,下面将简单介绍一下日期的格式化输出:
from datetime import datetime
now = datetime.now()
print(now) # 输出格式为:2021-01-05 14:10:27.149326
print('年:{},月:{},日:{},小时:{},分钟:{},秒:{}'.format(now.year, now.month, now.day,
now.hour, now.minute,
now.second))
print(now.strftime('%a')) # 输出星期几英文简称,全称请用大写 A Tue
print(now.strftime('%b')) # 输出月份英文简称,全称请用大写 B Jan
print(now.strftime('%c')) # 以本地时间输出 Tue Jan 5 14:16:29 2021
print(now.strftime('%p')) # 显示上午还是下午以AM/PM显示
print(now.strftime('%j')) # 显示一年中的第几天 005
print(now.strftime('%w')) # 显示一周中的第几天 2
print(now.strftime('%W')) # 显示一年中的第几周 01
print(now.strftime('%Y')) # 显示年月日中的年 2021
print(now.strftime('%m')) # 显示年月日中的月 01
print(now.strftime('%d')) # 显示年月日中的日 05
print(now.strftime('%H')) # 显示年月日中的小时(24小时制) 14
print(now.strftime('%I')) # 显示年月日中的小时(12小时制) 02
print(now.strftime('%M')) # 显示分钟数 39
print(now.strftime('%S')) # 显示秒数
# 组合使用
print(now.strftime('%Y-%m-%d %H:%M:%S')) # 2021-01-05 14:44:57
日期的反向格式化直接使用strptime()
函数即可:
now = '2021/01/05 14:44:57'
print(datetime.strptime(now, '%Y/%m/%d %H:%M:%S').day)
了解了这些格式化输出,已经基本上可以满足正常的工作使用否,最后稍稍的介绍一下日期的加减法,工作中也会经常用到:
import datetime
from dateutil.relativedelta import relativedelta
now = datetime.datetime.now()
# 添加日
addday = datetime.timedelta(days=1)
print(now + addday)
# 添加分钟
addmin = datetime.timedelta(minutes=5)
print(now + addmin)
# 加减年
addyear = relativedelta(years=1)
print(now + addyear)