Py002-02-10常用模块九(logging)

logging (日志模块)

logging的日志可以分为5个级别

  • debug()
  • info()
  • warning()
  • error()
  • critical()

最简单用法

1
2
3
4
5
6
7
8
9
10
11
import logging

logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")

输出

WARNING:root:user [alex] attempted wrong password more than 3 times
CRITICAL:root:server is down

# 默认以root执行的,就是你没指定用户的情况下

其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。

如果想把日志写到文件里,也很简单

  • filename 日志写入的文件
  • level 输入指定的级别以及以上级别的信息

当你再次运行的时候,日志会追加而不是覆盖

1
2
3
4
5
6
7
8
9
10
import logging

logging.basicConfig(filename='example.log',level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

# example.log内容如下
INFO:root:So should this
WARNING:root:And this, too

自定义日志格式

日志起码要有时间吧!

1
2
3
4
5
6
import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

#输出
12/12/2010 11:46:36 AM is when this event was logged.

除了加时间,还可以自定义一大堆格式,下表就是所有支持的格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%(name)s	Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息

日志同时输出到屏幕和文件

如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了

Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

  • logger提供了应用程序可以直接使用的接口;
  • handler将(logger创建的)日志记录发送到合适的目的输出;
  • filter提供了细度设备来决定输出哪条日志记录;
  • formatter决定日志记录的最终输出格式。

Py002-02-09常用模块八(subprocess)

subprocess 模块

跟操作系统打交道的模块

  • run
  • call
  • Popen 前两种的底层实现
1
2
3
4
5
6
7
8
import subprocess

a = subprocess.run(['df','-h'])
print(a.args) # 命令的参数
print(a.returncode) # 命令行的返回结果 0代表成功
a.stderr
a.stdout
a.check_returncode() 非 0 报错

进程 word无法访问 qq的内存数据 不然就会把qq搞崩了, 所以每个应用的内存数据是相互独立的

疑问? qq里 别人一段消息 你ctrl + c之后 在word里 ctrl + v 数据就过去了? 因为这是操作系统帮你做的

subprocess 执行时会开启一个进程,那返回的结果 如何获取呢? 答案是通过操作系统的桥梁

  • subprocess.PIPE 就是管道
  • check=True 如果不加 执行命令出错了 整个程序没有出错 加了会有报错信息
1
2
3
s = subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)

print(s.stdout)

如果我想执行 带管道的命令呢? 答案是报错 因为subprocess本身就带管道里 如果命令还带就报错

1
2
3
4
5
6
7
# s = subprocess.run(['df','-h','|','grep','disk1'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)

# 发现还是报错
# s = subprocess.run('df -h |grep disk1',stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)

# 添加shell参数 意思是之前是列表里 拼参数 现在你别管了 把整条命令传给 shell执行
s = subprocess.run('df -h |grep disk1', stderr = subprocess.PIPE, stdout = subprocess.PIPE, shell=True)

call方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

Popen()方法

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • shell:同上
  • cwd:用于设置子进程的当前目录
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

下面这2条语句执行会有什么区别?

1
2
a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE)
a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)

区别是Popen会在发起命令后立刻返回,而不等命令执行结果。这样的好处是什么呢?

如果你调用的命令或脚本 需要执行10分钟,你的主程序不需卡在这里等10分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。

Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法

  • poll()

结论:run方法同步,Popen会新开一个进程不影响主程序,可以通过poll方法获取命令的返回结果

preexex_fn是什么鬼?代表执行命令前先执行的方法

1
2
3
4
5
6
7
8
9
10
import subprocess

def sayHi():
print('hello')

a = subprocess.Popen('sleep 10',stdout=subprocess.PIPE,shell=True,preexec_fn=sayHi)
a.stdout
s = a.stdout.read()

print(s)

cwd 用于设置子进程的当前目录

1
2
3
4
5
a = subprocess.Popen('echo $PWD',cwd='/Users/almost/Desktop/pycode',stdout=subprocess.PIPE,shell=True)
a.stdout
s = a.stdout.read()

print(s) # b'/Users/almost/Desktop/pycode\n'

wait() 等待子进程的结果

terminate()终止所启动的进程Terminate the process with SIGTERM

kill() 杀死所启动的进程 Kill the process with SIGKILL

communicate()与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束

1
2
3
4
>>> a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)

>>> a.communicate(b'22')
(b'your guess:try bigger\n', b'')

send_signal(signal.xxx)发送系统信号

pid 拿到所启动进程的进程号

Py002-02-08常用模块七(hashlib)

加密算法介绍

HASH

一种将任意长度的消息压缩到某一固定长度的消息摘要的函数

不可逆,也就是无法通过hash值反推原数据信息

1
2
3
4
5
6
# 第一次运行
hash('hjx') # 4843833162619343159
# 第二次运行
hash('hjx') # 3179283320655676208
# 第三次运行
hash('hjx') # -1078172669992245685

结论:每次对同一内容进行hash后 值是不同的(大多数情况下)

MD5

基于hash的,一种加密算法。

MD5功能

输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
不同的输入得到的不同的结果(唯一性);

也就是16位字符的信息

MD5算法的特点

  • 压缩性:任意长度的数据,算出的MD5值的长度都是固定的
  • 容易计算:从原数据计算出MD5值很容易
  • 抗修改性:对原数据进行任何改动,修改一个字节生成的MD5值区别也会很大
  • 强抗碰撞:已知原数据和MD5,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

MD5算法是否可逆?

MD5不可逆的原因是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

m = hashlib.md5()

m.update(b'hjx')
a = m.hexdigest()

print(a) # ffd529f6b20aff2291bf876c68433e78

# 长度32的16进制字符 我们知道两个16进制数代表一个字节 所以就是32/2 = 16字节
# 一字节8位 所以 正好是16*8 = 128位

# 而且这个md5值 不管在任何语言任何平台生成的值都是一样的,不变的

生活实例:支付宝密码如 if123456

你肯定会担心,支付宝的后台维护人员知道我的密码,到时候他跑路了,盗取账号咋办?

  1. 你的密码在支付宝数据库里存的不是明文 if123456
  2. 存的是类似 md5 的形式。[ffd529f6b20aff2291bf876c68433e78]
  3. 支付宝维护人员看到这个 md5 值也没有用,因为他拿这个 密码的md5值 [ffd529f6b20aff2291bf876c68433e78] 登录你的支付宝就会又进行一次md5 加密 生成新的值[87a28ad32d3dc1747c2c65c4dcc99e64]
  4. 肯定不是 if123456加密的值
    [ffd529f6b20aff2291bf876c68433e78] != [87a28ad32d3dc1747c2c65c4dcc99e64]

MD5用途

防止被篡改:

  • 比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。

  • 比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。

  • SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

防止直接看到明文:

  • 现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

防止抵赖(数字签名):

  • 这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。

网上的破解md5密码原理

实际上是撞库,它自己的数据库收集了常用密码生成的md5值,如果匹配上了就代表成功了,反之就破解不了

Py002-02-07常用模块六(configparser)

configparser 配置解析模块

此模块用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser。

来看一个好多软件的常见配置文件格式如下

1
2
3
4
5
6
7
8
9
10
11
12
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

解析配置文件

1
2
3
4
5
6
7
8
9
10
11
12
import configparser

conf = configparser.ConfigParser()

conf.read('conf.ini')

print(conf.sections()) # 打印每个section 但是不打印 default里的
print(conf.default_section)

print(conf['bitbucket.ort']['User']) # hg

print(list(conf['bitbucket.ort'].keys()))

其它增删改查语法

1
2
3
4
5
6
[group1] # 支持的两种分隔符“=”, “:”
k1 = v1
k2:v2

[group2]
k1 = v1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import configparser

config = configparser.ConfigParser()
config.read('i.cfg')

# ########## 读 ##########
#secs = config.sections()
#print(secs)
#options = config.options('group2') # 获取指定section的keys
#print(options)

#item_list = config.items('group2') # 获取指定 section 的 keys & values ,key value 以元组的形式
#print(item_list)

#val = config.get('group1','key') # 获取指定的key 的value
#val = config.getint('group1','key')

# ########## 改写 ##########
#sec = config.remove_section('group1') # 删除section 并返回状态(true, false)
#config.write(open('i.cfg', "w")) # 对应的删除操作要写入文件才会生效

#sec = config.has_section('wupeiqi')
#sec = config.add_section('wupeiqi')
#config.write(open('i.cfg', "w")) #


#config.set('group2','k1',11111)
#config.write(open('i.cfg', "w"))

#config.remove_option('group2','age')
#config.write(open('i.cfg', "w"))

Py002-02-06常用模块五(xml)

xml模块

你肯定见过如下文件 1.xml的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year attr_test="yes">2009</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year attr_test="yes">2012</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year attr_test="yes">2012</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>

通过xml模块,获取data节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import xml.etree.ElementTree as ET

tree = ET.parse("1.xml")
root = tree.getroot()

# 第一步 获取 根节点
print(root.tag) # data

# 第二步 遍历子节点
for child in root:
# tag 节点名称 attrib 节点属性(字典形式)
print(child.tag,child.attrib)
for i in child:
# text 节点文本内容
print(i.tag,i.text)

# 只遍历指定节点 如只筛选 year节点
for node in root.iter('year'):
print(node.tag,node.text)

CRUD

  • 修改

还是刚才那个xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import xml.etree.ElementTree as ET

tree = ET.parse("1.xml")
root = tree.getroot()

# 第一步 获取 根节点
print(root.tag) # data

# 第二步 把year里的 文本内容递增 并设置属性 xxx="aaa"
for node in root.iter('year'):
new_year = int(node.text) + 1
node.text = str(new_year)
node.set('xxx','aaa')
# 第三步 写入文件(保存)
tree.write("1bak.xml")
  • 删除
1
2
3
4
5
6
7
8
9
10
import xml.etree.ElementTree as ET

tree = ET.parse("1.xml")
root = tree.getroot()
# findall 代表筛选所有的 xxx节点
for country in root.findall('country'):
rank = int(country.find('rank').text)
if rank > 50:
root.remove(country)
tree.write('1bak2.xml')
  • 创建一个xml文档形如以下格式
1
2
3
4
5
6
7
8
<?xml version='1.0' encoding='utf-8'?>
<namelist>
<userInfo info="3">
<name>hjx</name>
<age ageMax="1000" />
<sex>男</sex>
</userInfo>
</namelist>

创建一个xml的实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import xml.etree.ElementTree as ET

# 根节点
root_node = ET.Element('namelist')

info = ET.SubElement(root_node,"userInfo",attrib={"info":"3"})
name = ET.SubElement(info,"name")
name.text = "hjx"
age = ET.SubElement(info,"age",attrib={"ageMax":"1000"})
sex = ET.SubElement(info,"sex")
sex.text = '男'

# 生成文档对象
et = ET.ElementTree(root_node)
et.write("userInfo.xml",encoding="utf-8",xml_declaration=True)

Py002-02-05常用模块四(序列化相关)

序列化模块

  • 序列化——将内存数据转换为字符串
  • 反序列化——将字符串转换为内存数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 序列化
data = {
"infos":[
{"name":"张飞","life":100,"arm":"丈八蛇矛"},
{"name":"关羽","life":110,"arm":"青龙偃月刀"}
]
}

f = open("info.txt",mode="w",encoding="utf-8")
f.write(str(data))

# ----------------------------------------------------------------

# 反序列化
f = open("info.txt",mode="r",encoding="utf-8")
strinfo = f.read()
info = eval(strinfo)
print(info)

常用模块

  • pickle
  • json

序列化模块知识点总结

json 模块

  • json.dumps(序列化对象:如字典)
  • json.loads(字符串)
  • json.dump(数据类型,文件)
  • json.load(文件)

注意点

1
2
3
4
5
6
f = open('fff',encoding='utf-8')
res = json.load(f)
res = json.load(f)
f.close()

# 不要尝试多次 dump或者 多次load
  • python2中多次dump文件里会帮你自动换行
  • python3里直接禁止了你多次调用dump/load这些方法

结论:无论何时都不要多次调用在一个文件句柄里多次调用 dump和load

pickle 模块

  • pickle.dumps(序列化对象:如字典)
  • pickle.loads(字符串)
  • pickle.dump(数据类型,文件)
  • pickle.load(文件)

它与json方法差不多,唯一区别就是序列化的内容是bytes你看不懂的内容(二进制的)

json 模块和 pickle 模块区别

  • json 仅支持序列化 str / int / tuple / list / dict
  • pickle 支持python里所有数据类型(只能在python里)
  • load dump 都仅能用一次

shelve 模块

跟前俩模块的区别就是:json、pickle只能load /dump 一次,但是shelve支持多次

  • 原理是对pickle进行了封装,python里独有的,并且顺序不会出错
  • 核心就是key/value

序列化到文件里

1
2
3
4
import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'} #直接对文件句柄操作,就可以存入数据
f.close()

反序列化

1
2
3
4
5
6
7
8
9
import shelve
f1 = shelve.open('shelve_file')
existing = f1['key'] #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
# 推荐使用 get()获取字段 这样如果key不存在返回 None
xx = f1['xxx']
print(xx) # None

f1.close()
print(existing)

关于修改你肯定会这样遇到问题?为啥修改不了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import shelve
f1 = shelve.open('shelve_file')
f1['names'] = ["a","b","c","d","e"]
f1['names'][2] = "ccc"
f1.close()

f2 = shelve.open('shelve_file')
print(f2["names"])

--------------------------
正确答案是 整体修改
--------------------------

f1['names'] = ["a","b","c","d","e"]
# f1['names'][2] = "ccc"
f1['names'] = ["a","b","ccc","d","e"]

Py002-02-04常用模块三(shutil)

shutil 模块

复制压缩文件的模块

详细操作参考

复制文件

1
2
3
4
5
6
7
8
# shutil.copyfileobj(fsrc, fdst[, length])

import shutil

f1 = open('1.txt',mode="r")
f2 = open('2.txt',mode="w")

shutil.copyfileobj(f1,f2)

递归的操作目录

1
2
3
4
5
6
7
8
# 递归复制目录及文件
shutil.copytree('aa','cc')

# 递归的删除目录
shutil.rmtree('cc')

# 递归的移动文件 剪切功能
shutil.move('aa','bb')

压缩打包

1
shutil.make_archive(base_name, format,...)
  • base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
    如:www =>保存至当前路径
    如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
  • format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
  • root_dir: 要压缩的文件夹路径(默认当前目录)
  • owner: 用户,默认当前用户
  • group: 组,默认当前组
  • logger: 用于记录日志,通常是logging.Logger对象
1
2
3
4
5
6
7
8
9
# 将 bb 下的文件打包放置当前程序目录

import shutil
ret = shutil.make_archive("bbgz", 'gztar', root_dir='aa')


# 将 bb 下的文件打包放置 aa/aagz 目录
import shutil
ret = shutil.make_archive("aa/aagz", 'gztar', root_dir='aa')

Py002-02-03常用模块二(random,str,sys,os)

random 模块

  • randint(1-100) 左闭右开
  • randrange(1-100) 左闭右闭
  • random() 随机浮点数
  • choise() 从字符串和列表里随机选取一个
  • sample(‘随机内容’,个数) 从字符串里 随机取几位 返回列表
1
2
3
4
5
6
7
8
9
10
11
12
13
# 从 1-100直接取随机数
random.randint(1,100) # 不包含100

random.randrange(1,100) # 包含100

# 随机浮点数
random.random()

# 从 字符串里返回随机的字符 也可以是列表
random.choise('fdsa342423')

#
random.sample('afsd25afa321sf',3) # ['f','a','5']

string 模块

1
2
3
4
5
6
import string 

string.digits # 返回数字
string.hexdigits # 十六进制的 0123456789ABCDEF
string.ascii_letters # 大小写字母返回
string.ascii_lowercase # 小写字母

通常用来生成验证码

1
2
3
4
5
6
import random
import string


s = string.ascii_lowercase + string.digits
''.join(random.sample(s),5)

洗牌模式

将有序的内容打乱

1
2
d = list(range(100))
random.shuffle(d) # 这样 d 就乱序了

os 模块

常用api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#os模块就是对操作系统进行操作,使用该模块必须先导入模块:
import os

#getcwd() 获取当前工作目录(当前工作目录默认都是当前文件所在的文件夹)
result = os.getcwd()
print(result)

#chdir()改变当前工作目录
os.chdir('/home/sy')
result = os.getcwd()
print(result)

open('02.txt','w')

#操作时如果书写完整的路径则不需要考虑默认工作目录的问题,按照实际书写路径操作
open('/home/sy/下载/02.txt','w')

#listdir() 获取指定文件夹中所有内容的名称列表
result = os.listdir('/home/sy')
print(result)

#mkdir() 创建文件夹
#os.mkdir('girls')
#os.mkdir('boys',0o777)

#makedirs() 递归创建文件夹
#os.makedirs('/home/sy/a/b/c/d')

#rmdir() 删除空目录
#os.rmdir('girls')

#removedirs 递归删除文件夹 必须都是空目录
#os.removedirs('/home/sy/a/b/c/d')

#rename() 文件或文件夹重命名
#os.rename('/home/sy/a','/home/sy/alibaba'
#os.rename('02.txt','002.txt')

#stat() 获取文件或者文件夹的信息
#result = os.stat('/home/sy/PycharmProject/Python3/10.27/01.py)
#print(result)

#system() 执行系统命令(危险函数)
#result = os.system('ls -al') #获取隐藏文件
#print(result)

#环境变量
'''
环境变量就是一些命令的集合
操作系统的环境变量就是操作系统在执行系统命令时搜索命令的目录的集合
'''
#getenv() 获取系统的环境变量
result = os.getenv('PATH')
print(result.split(':'))

#putenv() 将一个目录添加到环境变量中(临时增加仅对当前脚本有效)
#os.putenv('PATH','/home/sy/下载')
#os.system('syls')

#exit() 退出终端的命令

#os模块中的常用值
#curdir 表示当前文件夹 .表示当前文件夹 一般情况下可以省略
print(os.curdir)

#pardir 表示上一层文件夹 ..表示上一层文件夹 不可省略!
print(os.pardir)

#os.mkdir('../../../man')#相对路径 从当前目录开始查找
#os.mkdir('/home/sy/man1')#绝对路径 从根目录开始查找

#name 获取代表操作系统的名称字符串
print(os.name) #posix -> linux或者unix系统 nt -> window系统

#sep 获取系统路径间隔符号 window ->\ linux ->/
print(os.sep)

#extsep 获取文件名称和后缀之间的间隔符号 window & linux -> .
print(os.extsep)

#linesep 获取操作系统的换行符号 window -> \r\n linux/unix -> \n
print(repr(os.linesep))



#导入os模块
import os

#以下内容都是os.path子模块中的内容

#abspath() 将相对路径转化为绝对路径
path = './boys'#相对
result = os.path.abspath(path)
print(result)

#dirname() 获取完整路径当中的目录部分 & basename()获取完整路径当中的主体部分
path = '/home/sy/boys'
result = os.path.dirname(path)
print(result)

result = os.path.basename(path)
print(result)

#split() 将一个完整的路径切割成目录部分和主体部分
path = '/home/sy/boys'
result = os.path.split(path)
print(result)

#join() 将2个路径合并成一个
var1 = '/home/sy'
var2 = '000.py'
result = os.path.join(var1,var2)
print(result)

#splitext() 将一个路径切割成文件后缀和其他两个部分,主要用于获取文件的后缀
path = '/home/sy/000.py'
result = os.path.splitext(path)
print(result)

#getsize() 获取文件的大小
#path = '/home/sy/000.py'
#result = os.path.getsize(path)
#print(result)

#isfile() 检测是否是文件
path = '/home/sy/000.py'
result = os.path.isfile(path)
print(result)

#isdir() 检测是否是文件夹
result = os.path.isdir(path)
print(result)

#islink() 检测是否是链接
path = '/initrd.img.old'
result = os.path.islink(path)
print(result)

#getctime() 获取文件的创建时间 get create time
#getmtime() 获取文件的修改时间 get modify time
#getatime() 获取文件的访问时间 get active time

import time

filepath = '/home/sy/下载/chls'

result = os.path.getctime(filepath)
print(time.ctime(result))

result = os.path.getmtime(filepath)
print(time.ctime(result))

result = os.path.getatime(filepath)
print(time.ctime(result))

#exists() 检测某个路径是否真实存在
filepath = '/home/sy/下载/chls'
result = os.path.exists(filepath)
print(result)

#isabs() 检测一个路径是否是绝对路径
path = '/boys'
result = os.path.isabs(path)
print(result)

#samefile() 检测2个路径是否是同一个文件
path1 = '/home/sy/下载/001'
path2 = '../../../下载/001'
result = os.path.samefile(path1,path2)
print(result)


#os.environ 用于获取和设置系统环境变量的内置值
import os
#获取系统环境变量 getenv() 效果
print(os.environ['PATH'])

#设置系统环境变量 putenv()
os.environ['PATH'] += ':/home/sy/下载'
os.system('chls')

# 获取当前终端大小
os.get_terminal_size()

# 杀死进程
import signal # 信号模块
os.kill(10884,signal.SIGKILL)

判断文件是否存在

1
2
import os
os.path.exists(test_file.txt)

sys 模块

1
2
3
4
5
6
7
8
9
10
11
12
sys.argv  # 命令行参数,第一个元素就是程序本身
sys.exit(n) # 退出程序,正常退出时exit(0)
sys.version # python解释器版本信息
sys.maxint # 最大int值
sys.path # 返回模块搜索路径
sys.platform # 操作系统平台名称
sys.stdout.wtite('please') # 标准输出,
val = sys.stdin.readline()[:-1] # 标准输入
sys.getrecursionlimit # 递归最大层数
sys.setrecursionlimit(1200) # 设置递归层数
sys.getdefaultencoding # 解释器编码
sys.getfilesstemencoding # 获取内存数据到文件的默认编码

Py002-02-02常用模块一(时间相关)

time 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import time

time.time() # 从1970年1月1日0时0分0秒 到现在的秒

time.localtime()
# 本地系统时间
# time.struct_time(tm_year=2018, tm_mon=8, tm_mday=20, tm_hour=23, tm_min=1, tm_sec=6, tm_wday=0, tm_yday=232, tm_isdst=0)

你可以自定义格式的时间:
a = time.localtime()
'%s-%s-%s'%(a.tm_year,a.tm_mon,a.tm_mday)

time.gmtime() # 格林时间 UTC时间

# localtime还可以传递时间戳「 秒 」来生成时间
time.localtime(1403232424)

# mktime() 将时间对象反转为 时间戳
a = time.localtime()
b = time.mktime(a) # 1403232424

# sleep() 线程挂起
time.sleep(3) # 线程挂起3秒


# asctime() 老外看见的那种格式 带英文的
time.asctime() # 我们很少用

# ctime() 将时间戳转为 asctime格式

# striftime() 格式化时间字符串
time.strftime('%Y-%m-%d %H:%M:%S') # 2018-08-21 10:11:42

time.strftime('%Y-%m-%d %H:%M:%S',结构化时间对象)

# 将一个日期字符串 转换为 结构化时间对象
a = '2018-08-21 10:11:42'
time.strptime(a,'%Y-%m-%d %H:%M:%S') # 结构化时间对象

datetime 模块

1
2
3
4
5
6
7
8
import datetime

a = datetime.now() # 时间对象
a.year
a.month

# 将时间戳转换为时间对象 立刻获取年月日
d = datetime.date.fromtimestamp(time.time()) # 这样会丢失 时分秒的信息

时间运算

加/减 一小时/分钟/天/。。。。

  • datetime.timedelta()
1
2
3
4
5
6
7
8
9
10
11
datetime.datetime.now() - datetime.timedelta(days=1)

datetime.datetime.now() + datetime.timedelta(days=4)

datetime.datetime.now() - datetime.timedelta(months=1)

datetime.datetime.now() - datetime.timedelta(hours=3)

datetime.datetime.now() - datetime.timedelta(minutes=45)

datetime.datetime.now() - datetime.timedelta(secs=12)

时间替换

1
2
d = datetime.datetime.now()
d.replace(year=2016,month=8) # 有很多参数

Py002_02_01模块和包

模块

一个python文件就是一个模块

随着程序代码越来越多,在一个文件里代码越来越多,越来越难以维护。于是就把代码进行拆分。每个功能进行独立。–模块的诞生

这样,每个文件包含的代码就变少了。维护成本就低了

是不是很像树的结构那样。如某个功能修改仅仅需要修改对应节点的代码。

模块的好处

  • 提高可维护性
  • 可重用
  • 避免函数名和变量名冲突

模块分类

  • 内置模块,执行help(‘modules’) 查看所有python自带模块
  • 第三方模块,通过pip install 的模块
  • 自定义模块

导入一个模块

1
2
3
4
import mod
from mod import xx
from mod.xx.xx import xx as yy
from mod.xx.xx import *

详情链接

通过sys可以查看模块的路径

1
2
3
4
5
import sys

print(sys.path) # 打印一个列表,列表代表导入模块的路径,会从左到右查找,默认先从当前目录找

如果全没找到就报错

sys.path里的目录我们只关心 site-packages目录这是所有的标准库和第三方库位置

开源模块使用

开源模块网址

下载文件(人肉安装)

1
2
3
4
5
1. 下载第三方包
2. 解压
3. 打开命令行输入 python setup.py build
会帮你创建一个build目录
4. python setup.py install 会提示你安装成功

命令行安装

1
2
3
4
5
# 安装
pip3 install 包名

# 卸载
pip3 uninstall 包名

pip安装的一个问题

1
2
3
由于网站是外国网站所以pip安装比较慢

于是类似于js的npm淘宝源的东西出现了,就是国内镜像

一些国内镜像源

1
2
3
4
5
6
pip3 install -i http://pypi.douban.com/simple/ 模块名

但是mac上会报安全问题

可以这样:
pip3 install -i http://pypi.douban.com/simple/ 模块名 --trusted-host pypi.douban.com

包 package

现有如下目录结构

  • 第一步
1
2
3
4
5
6
7
8
9
10
-|my_pro  根路径
---|mod1
------|a.py
------|b.py
------|c.py
---|mod2
------|d.py
------|e.py
------|f.py
---|index.py
  • 第二步
1
2
3
# a.py中有如下内容 
def sayHi():
print('hello')
  • 第三步
1
2
3
4
5
# index.py 中引入a.py功能

from mod1 import a

a.sayHi()
  • 以上在python3中能正常运行,但是在 python2 中会报错找不到这个模块
1
2
3
4
5
6
7
8
实际上 mod1 / mod2 虽然可以导入,但它并不是一个包

你需要这样:

在mod1、和mod2文件夹下分别创建 __init__.py文件(内容什么也没有)

这样在 python2 中就是一个包了
在 python3 中加不加都一样(默认帮你优化了)
  • 模块间相互使用嵌套特别多的时候
1
2
3
4
5
6
7
8
9
10
11
12
13
|——root
|——|——t1
|——|——|——__init__.py
|——|——|——aaa.py
|——|——|——t3
|——|——|——|——__init__.py
|——|——|——|——ccc.py
|——|——t2
|——|——|——__init__.py
|——|——|——bbb.py
|——|——|——t4
|——|——|——|——__init__.py
|——|——|——|——ddd.py

这样的目录结构需要把 root路径引入

1
2
3
4
5
6
7
8
9
如在 t1包下的 aaa为入口时就要 将根目录root加入到sys.path中 这样该目录下的所有包之间都可以引用了
import os ,sys
# os.path.dirname() 获取当前路径的目录名
# print(os.path.abspath(__file__)) # 获取当前目录绝对路径

# 获取根路径
baseDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 添加到sys.path 这个里面是个列表 就是调用方法或模块的查找路径
sys.path.append(baseDir)

结论:

1
2
1. 文件夹中必须有 __init__.py 文件,该文件可以为空,但必须存在
2. 在任意一个py文件里 往sys.path里添加 项目的根路径就可以任意的使用模块了