python知识小结(2)

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()

常用模块

  1. datetime 模块
  2. 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
  1. 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的使用

  1. 使用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的一种子类:

  1. 使用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
  1. defaultdict
    可以作为字典来使用, 当获取到的key 不存在的时候 返回一个默认的值 给 字典中的键统一的设定

defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为

>>> dd1 = defaultdict(list)
>>> dd1["key"]
[]
>>> dd1["key1"] = 2
>>> dd1["key1"]
2

defaultdict类除了接受类型名称作为初始化函数的参数之外,还可以使用任何不带参数的可调用函数,到时该函数的返回结果作为默认值

>>> dd = defaultdict(lambda:' ')
>>> dd["key"]
''

注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。

除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。

在参数中使用 * 或者 ** 是为了让这个指定的变量 进行解包操作

  1. 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本身排序:

其他的操作和 普通的字典是没有什么区别的
  1. 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()会创建一个无限的迭代器,所以上述代码会打印出自然数序列
  1. 无限次的重复
    cycle()会把传入的一个序列无限重复下去:请注意 这里是一个序列
>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
...     print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...
  1. 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']

你可能感兴趣的:(python笔记)