• 8、IO


    一、文件读写:

    Python内置了读写文件的函数

    1、打开一个文件对象,使用Python内置的open()函数,如果文件不存在,open()函数就会抛出一个IOError的错误

    >>> f = open('/Users/michael/test.txt', 'r')

    2read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示

    >>> f.read()

    'Hello, world!'

    3close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量是有限的:

    >>> f.close()

    4、由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现:

    try:

        f = open('/path/to/file', 'r')

        print f.read()

    finally:

        if f:

            f.close()

    但是每次都这么写实在太繁琐,所以,Python引入了with语句来自动帮我们调用close()方法:

    with open('/path/to/file', 'r') as f:

        print f.read()

    这和前面的try ... finally是一样的,但是代码更佳简洁,并且不必调用f.close()方法

    5、调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。

    如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便

    6、要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:

    >>> f = open('/Users/michael/test.jpg', 'rb')

    >>> f.read()

    'xffxd8xffxe1x00x18Exifx00x00...' # 十六进制表示的字节

    7、要读取非ASCII编码的文本文件,就必须以二进制模式打开,再解码。比如GBK编码的文件:

    >>> f = open('/Users/michael/gbk.txt', 'rb')

    >>> u = f.read().decode('gbk')

    >>> u

    u'u6d4bu8bd5'

    >>> print u

    测试

    如果每次都这么手动转换编码嫌麻烦(写程序怕麻烦是好事,不怕麻烦就会写出又长又难懂又没法维护的代码),Python还提供了一个codecs模块帮我们在读文件时自动转换编码,直接读出unicode

    import codecs

    with codecs.open('/Users/michael/gbk.txt', 'r', 'gbk') as f:

        f.read() # u'u6d4bu8bd5'

    8、写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:

    >>> f = open('/Users/michael/test.txt', 'w')

    >>> f.write('Hello, world!')

    >>> f.close()

    你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险:

    with open('/Users/michael/test.txt', 'w') as f:

        f.write('Hello, world!')

    要写入特定编码的文本文件,请效仿codecs的示例,写入unicode,由codecs自动转换成指定编码。

    二、操作文件和目录

    1、操作系统提供的命令只是简单地调用了操作系统提供的接口函数,Python内置的os模块也可以直接调用操作系统提供的接口函数。

    >>> import os

    >>> os.name # 操作系统名字

    'posix'

    如果是posix,说明系统是LinuxUnixMac OS X,如果是nt,就是Windows系统。

    要获取详细的系统信息,可以调用uname()函数:

    >>> os.uname()

    ('Darwin', 'iMac.local', '13.3.0', 'Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64', 'x86_64')

    注意uname()函数在Windows上不提供,也就是说,os模块的某些函数是跟操作系统相关的。

    2、在操作系统中定义的环境变量,全部保存在os.environ这个dict中,可以直接查看:

    >>> os.environ

    {'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'TERM_PROGRAM_VERSION': '326', 'LOGNAME': 'michael', 'USER': 'michael', 'PATH': '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin', ...}

    要获取某个环境变量的值,可以调用os.getenv()函数:

    >>> os.getenv('PATH')

    '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin'

    3、操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中

    # 查看当前目录的绝对路径:

    >>> os.path.abspath('.')

    '/Users/michael'

    # 在某个目录下创建一个新目录,

    # 首先把新目录的完整路径表示出来:

    >>> os.path.join('/Users/michael', 'testdir')

    '/Users/michael/testdir'

    # 然后创建一个目录:

    >>> os.mkdir('/Users/michael/testdir')

    # 删掉一个目录:

    >>> os.rmdir('/Users/michael/testdir')

    # 对文件重命名:

    >>> os.rename('test.txt', 'test.py')

    # 删掉文件:

    >>> os.remove('test.py')

    4、复制文件,shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用函数,它们可以看做是os模块的补充。

    5、最后看看如何利用Python的特性来过滤文件。比如我们要列出当前目录下的所有目录,只需要一行代码:

    >>> [x for x in os.listdir('.') if os.path.isdir(x)]

    ['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Adlm', 'Applications', 'Desktop', ...]

    要列出所有的.py文件,也只需一行代码:

    >>> [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']

    三、序列化

    在程序运行的过程中,所有的变量都是在内存中,一旦程序结束,变量所占用的内存就被操作系统全部回收。把变量从内存中变成可存储或传输的过程称之为序列化。序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling

    Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。json模块的dumps()loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。

    1JSON表示的对象就是标准的JavaScript语言的对象,JSONPython内置的数据类型对应如下:

    JSON类型Python类型{}dict[]list"string"'str'u'unicode'1234.56intfloattrue/falseTrue/FalsenullNone

    2Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON

    >>> import json

    >>> d = dict(name='Bob', age=20, score=88)

    >>> json.dumps(d)

    '{"age": 20, "score": 88, "name": "Bob"}'

    dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object

    要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化:

    >>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'

    >>> json.loads(json_str)

    {u'age': 20, u'score': 88, u'name': u'Bob'}

    有一点需要注意,就是反序列化得到的所有字符串对象默认都是unicode而不是str。由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在PythonstrunicodeJSON的字符串之间转换。

    3Pythondict对象可以直接序列化为JSON{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化:

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

    运行代码,毫不留情地得到一个TypeError

    Traceback (most recent call last):

      ...

    TypeError: <__main__.Student object at 0x10aabef50> is not JSON serializable

    错误的原因是Student对象不是一个可序列化为JSON的对象。

    如果连class的实例对象都无法序列化为JSON,这肯定不合理!

    别急,我们仔细看看dumps()方法的参数列表,可以发现,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数:

    https://docs.python.org/2/library/json.html#json.dumps

    这些可选参数就是让我们来定制JSON序列化。前面的代码之所以无法把Student类实例序列化为JSON,是因为默认情况下,dumps()方法不知道如何将Student实例变为一个JSON{}对象。

    可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:

    def student2dict(std):

        return {

            'name': std.name,

            'age': std.age,

            'score': std.score

        }

    print(json.dumps(s, default=student2dict))

    这样,Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON

    不过,下次如果遇到一个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实例对象。

  • 相关阅读:
    PHP-FPM详解
    Nginx与PHP交互过程 + Nginx与PHP通信的两种方式
    cgi,fast-cgi,php-cgi,php-fpm转载详解
    ( 转 ) mysql复合索引、普通索引总结
    快速搭建ELK日志分析系统
    高并发
    多线程
    关于MySQL中查询大数据量的情况下分页limit的性能优化
    电商搜索引擎的架构设计和性能优化
    MYSQL优化之碎片整理
  • 原文地址:https://www.cnblogs.com/quyong/p/6687874.html
Copyright © 2020-2023  润新知