StringIO和BytesIO
StringIO
很多时候,数据读写不一定是文件,也可以在内存中读写。
StringIO顾名思义就是在内存中读写str。
要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5 # 返回的是写入的字符串的长度
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!
getvalue()方法用于获得写入后的str。
要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline() # 按照行的形式 从内存中读取数据
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!
BytesIO
BytesIO
StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。
BytesIO实现了在内存中读写bytes,我们创建一个BytesIO,然后写入一些bytes:
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
请注意,写入的不是str,而是经过UTF-8编码的bytes。
和StringIO类似,可以用一个bytes初始化BytesIO,然后,像读文件一样读取:
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
通过 os 来获取环境变量中的值
os.environ.get(key, default) 从环境变量中获取到需要的值, 如果没有这个值, 就返回 设定的默认的值
使用 os.path 来操作文件的目录
os.path.abspath() 获取项目下的绝对的路径
os.path.split(文件的名称) 返回的结果是一个 列表 列表中的 元素是 文件的名称 和 文件的后缀名称
第一个使用的是 os.path.split()
os.path.split(os.path.join(os.path.abspath(''), "1111.txt"))
返回的内容: ('D:\\test\\练习', '1111.txt')
第二个使用的是 os.path.splitext() 返回的是文件的名称和后缀
os.path.splitext(os.path.join(os.path.abspath(''), "1111.txt"))
返回的内容: ('D:\\test\\练习\\1111', '.txt')
这些合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作。
# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')
copyfile() 函数的使用
幸运的是shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用函数,它们可以看做是os模块的补充
一行代码搞定当前目录下的所有的文件夹
os.list.dir() # 获取到当前目录下面的所有的文件和文件夹
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
列出当前文件下面所有的采用的是以指定文件后缀结尾的文件
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
序列化的操作
pickle
pickle 序列化后的操作 只能使用Python 语言进行使用
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:
和json的 功能是一样的 但是就是 json 是通用的 但是 pickle 只能Python 语言 才能进行使用
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
json 序列化的 进阶操作 使用json 序列化一个对象
使用 json 序列化一个类的对象
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))
这样直接的进行序列化的时候 是会出现错误的
Traceback (most recent call last):
...
TypeError: <__main__.Student object at 0x10603cc50> is not JSON serializable
json中的 可选参数default 可以传入一个函数 来先把累对象 转换成一个 字典
可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
这样,Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON:
>>> print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}
不过,下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class的实例变为dict:
print(json.dumps(s, default=lambda obj: obj.__dict__))
因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class
同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例:
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
运行结果如下:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>
打印出的是反序列化的Student实例对象。
使用json 序列化一个日期的的对象 明天记得完成
dict_ 可以把类中的对象的属性转换成一个字典的形式
class Student():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
demo = Student("xixi", 20, 1).__dict__
print(type(demo))
print(demo)
输出结果:
<class 'dict'>
{'name': 'xixi', 'age': 20, 'gender': 1}
使用 subprocess 来创建子进程
一、subprocess以及常用的封装函数
运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。
subprocess.call()
父进程等待子进程完成
返回退出信息(returncode,相当于Linux exit code)
subprocess.check_call()
父进程等待子进程完成
返回0
检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查
多线程下 使用 lock 的
当多个程序同时修改一个数据的时候 就需要用到锁的概念,
使用 lock = threading.lock() 创建一个锁的对象
获取锁的对象采用的是 lock.acquire() 释放锁 采用的是 lock.release()
常用模块
- datetime 模块
- 2.
获取当前的时间
>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2017, 10, 12, 16, 36, 45, 179103)
>>> now = datetime.now()
>>> print(now)
2017-10-12 16:36:59.643930
>>> type(now)
<class 'datetime.datetime'>
设定指定的时间
>>> time = datetime(2017, 10, 12, 16, 40)
>>> print(time)
2017-10-12 16:40:00
- datetime转换为timestamp
3.
>>> now.timestamp()
1507797419.64393
把timestamp 转换会 datetime的方法
now = datetime.fromtimestamp(t)
要把timestamp转换为datetime,使用datetime提供的fromtimestamp()方法:
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t))
2015-04-19 12:20:00
注意到timestamp是一个浮点数,它没有时区的概念,而datetime是有时区的。上述转换是在timestamp和本地时间做转换。
本地时间是指当前操作系统设定的时区。例如北京时区是东8区,则本地时间:
2015-04-19 12:20:00
实际上就是UTC+8:00时区的时间:
2015-04-19 12:20:00 UTC+8:00
而此刻的格林威治标准时间与北京时间差了8小时,也就是UTC+0:00时区的时间应该是:
2015-04-19 04:20:00 UTC+0:00
timestamp也可以直接被转换到UTC标准时区的时间:
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t)) # 本地时间
2015-04-19 12:20:00
>>> print(datetime.utcfromtimestamp(t)) # UTC时间
2015-04-19 04:20:00
字符串和 时间的转换
str转换为datetime
很多时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是通过datetime.strptime()实现,需要一个日期和时间的格式化字符串:
datetime.strptime(string, "%Y-%m-%d-%H")
>>> from datetime import datetime
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
2015-06-01 18:19:59
字符串’%Y-%m-%d %H:%M:%S’规定了日期和时间部分的格式
把datetime.now() 转换成str 字符串的方法
>>> now.strftime("%Y-%m-%d %H:%M:%S")
'2017-10-12 16:36:59'
datetime 的加减操作
对日期和时间进行加减实际上就是把datetime往后或往前计算,得到新的datetime。加减可以直接用+和-运算符,不过需要导入timedelta这个类:
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
>>> now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
>>> now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)
小结
datetime表示的时间需要时区信息才能确定一个特定的时间,否则只能视为本地时间。
如果要存储datetime,最佳方法是将其转换为timestamp再存储,因为timestamp的值与时区完全无关。
collections的使用
- 使用namedtuple创建一个指定属性的元组
>>> from collections import namedtuple
>>> point = namedtuple("point", ['x', 'y'])
>>> p = point(1, 2)
>>> p.x
1
>>> p.y
2
>>> p
point(x=1, y=2)
同时满足这两种的实例
>>> isinstance(p, point)
True
>>> isinstance(p, tuple)
True
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
可以验证创建的Point对象是tuple的一种子类:
- 使用deque 来构建 双向的列表 list
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
>>> q = deque(['a', 'b', 'c'])
>>> q
deque(['a', 'b', 'c'])
>>> q.append('d')
>>> q
deque(['a', 'b', 'c', 'd'])
>>> q.append(1)
>>> q
deque(['a', 'b', 'c', 'd', 1])
>>> q.appendleft(2)
>>> q
deque([2, 'a', 'b', 'c', 'd', 1])
从左右边删除 但是里面不能带参数 list中的pop 是可以携带参数的 代表的是要删除那个索引下的元素
>>> q.pop()
1
>>> q.popleft()
2
- defaultdict
可以作为字典来使用, 当获取到的key 不存在的时候 返回一个默认的值 给 字典中的键统一的设定
defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为
>>> dd1 = defaultdict(list)
>>> dd1["key"]
[]
>>> dd1["key1"] = 2
>>> dd1["key1"]
2
defaultdict类除了接受类型名称作为初始化函数的参数之外,还可以使用任何不带参数的可调用函数,到时该函数的返回结果作为默认值
>>> dd = defaultdict(lambda:' ')
>>> dd["key"]
''
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。
除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。
在参数中使用 * 或者 ** 是为了让这个指定的变量 进行解包操作
- Orderdict 实现一个有序的字典的方式
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:
其他的操作和 普通的字典是没有什么区别的
- counter 计数器的使用
使用counter 可以完成 统计一个列表中出现重复的次数
from collections import Counter
c = Counter(sequence) 传入的是一个序列对象 可迭代的对象
c.most_common(n) 传入的参数是一个 整型, 是要获取到 前几个数据
源码中的一些简单实例:
'''Dict subclass for counting hashable items. Sometimes called a bag
or multiset. Elements are stored as dictionary keys and their counts
are stored as dictionary values.
>>> c = Counter('abcdeabcdabcaba') # count elements from a string
>>> c.most_common(3) # three most common elements
[('a', 5), ('b', 4), ('c', 3)]
>>> sorted(c) # list all unique elements
['a', 'b', 'c', 'd', 'e']
>>> ''.join(sorted(c.elements())) # list elements with repetitions
'aaaaabbbbcccdde'
>>> sum(c.values()) # total of all counts
15
>>> c['a'] # count of letter 'a'
5
>>> for elem in 'shazam': # update counts from an iterable
... c[elem] += 1 # by adding 1 to each element's count
>>> c['a'] # now there are seven 'a'
7
>>> del c['b'] # remove all 'b'
>>> c['b'] # now there are zero 'b'
0
>>> d = Counter('simsalabim') # make another counter
>>> c.update(d) # add in the second counter
>>> c['a'] # now there are nine 'a'
9
>>> c.clear() # empty the counter
>>> c
Counter()
Note: If a count is set to zero or reduced to zero, it will remain
in the counter until the entry is deleted or the counter is cleared:
>>> c = Counter('aaabbc')
>>> c['b'] -= 2 # reduce the count of 'b' by two
>>> c.most_common() # 'b' is still in, but its count is zero
[('a', 3), ('c', 1), ('b', 0)]
struct 模块的使用
Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。
struct的pack函数把任意数据类型变成bytes:
hashlib 加密的使用
在python的时候 需要把 加密的数据转换成字节的形式 才能都进行加密
如果是 英文的 可以直接使用 b”nihao ’ 把字符串转换成 byte 类型的
如果是中文的 需要经过编码来进行解决 ‘你好’.encode(‘utf-8’)
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
如果要加密的字节很长还可以, 分段进行加密操作, 例如:
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in '.encode('utf-8'))
md5.update('python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。
sha1的使用
使用方法 和md5 是一样的 但是生成的是一个 40为的16进制的字符串来表示的
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
因为 md5 可能会被反推出来 , 存在一定的安全的隐患, 推荐的是使用 加密和 加盐的操作 来进行处理
就是在 进行加密的时候 加入一个字符串, 只要这个字符串不被外泄, 别人就很难破解你的密码
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in '.encode('utf-8'))
md5.update('python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
md5 = hashlib.md5()
>>> def main(string):
... md5.update((string + "hehe").encode("utf-8"))
... print(md5.hexdigest())
itertools 模块的使用
Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数。
1. 无限次的重复
>>> import itertools
>>> natuals = itertools.count(1)
>>> for n in natuals:
... print(n)
...
1
2
3
...
因为count()会创建一个无限的迭代器,所以上述代码会打印出自然数序列
- 无限次的重复
cycle()会把传入的一个序列无限重复下去:请注意 这里是一个序列
>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
... print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...
- repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数:
>>> ns = itertools.repeat('A', 3)
>>> for n in ns:
... print(n)
...
A
A
A
无限序列只有在for迭代时才会无限地迭代下去,如果只是创建了一个迭代对象,它不会事先把无限个元素生成出来,事实上也不可能在内存中创建无限多个元素。
无限序列虽然可以无限迭代下去,但是通常我们会通过takewhile()等函数根据条件判断来截取出一个有限的序列:
>>> natuals = itertools.count(1)
>>> ns = itertools.takewhile(lambda x:x<=10, natuals)
>>> list(ns)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chain()
chain()可以把一组迭代对象串联起来,形成一个更大的迭代器:
>>> for c in itertools.chain('ABC', 'XYZ'):
... print(c)
# 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'
groupby()
groupby()把迭代器中相邻的重复元素挑出来放在一起:
>>> for key, group in itertools.groupby('AAABBBCCAAA'):
... print(key, list(group))
...
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']
实际上挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,这两个元素就被认为是在一组的,而函数返回值作为组的key。如果我们要忽略大小写分组,就可以让元素’A’和’a’都返回相同的key:
>>> for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
... print(key, list(group))
...
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']