美文网首页python3
Head First Python 读书笔记(四)

Head First Python 读书笔记(四)

作者: icessun | 来源:发表于2018-07-10 22:21 被阅读2次

    第六章:存储和管理数据

    打开,处理,关闭文件
    • 打开一个文件
      todos = open('todos.txt','a')a表示以追加的模式打开一个文件,返回一个文件流(对象)赋给变量todos
    • 写入数据
      print('this is cat',file=todos),后面这个参数file是用来指定要写的文件流;写完数据之后,记得关闭文件,防止丢失数据:todos.close()
    • 读取数据
      task=open('todos.txt'),不带第二个模式参数,默认是读取文件,变量task会接收返回的文件流,可以使用for ch in task去循环输出里面的数据,最后最好使用task.close()去关闭文件
    模式参数
    • r
      默认模式,读取数据,并且读取的文件要存在
    • a
      追加数据,保留文件的内容,向文件末尾增加新数据
    • w
      写数据,写之前会清空原文件的内容;写入的文件不存在的时候,会自动创建该文件;只能将字符串写入文本文件,需要使用str()去把数值转为字符串
    with open('py.txt', 'w') as file_object:
        file_object.write('I love programming.')
    
    • x
      打开一个新文件来写数据;如果文件存在则失败
    with语句
    tasks = open('todos.txt')
    for ch in tasks:
        print(ch, end='')
    tasks.close()
    
    # 使用with语句
    with open('todos.txt') as tasks:
        for ch in tasks:
            print(ch, end='')
    

    使用了with语句,可以不用考虑调用close()方法,充当着管理上下文的作用。符合上下文管理协议的约定。

    • 单独打印文件流对象
    print(file_object)
    
    # 读取的文件名称  模式为:read  编码为 cp936 GBK编码
    <_io.TextIOWrapper name='py.txt' mode='r' encoding='cp936'>
    
    • open()返回的文件流对象只在with代码块内可用
    with open('py.txt') as file_object:
        contents = file_object.read()
        print(contents)
    
    print(file_object.read())
    
    with语句外,访问文件流对象
    • 解决办法:先把文件内容存储为列表,然后在with语句外访问
    with open('py.txt') as file_object:
        lines = file_object.readlines()   # readlines() 返回一个列表
        print(type(lines))  # list
    
    for line in lines:
        print(line.rstrip()) # 清除右边空格
    
    • json数据格式存储数据,保存数据的原有格式

      • dump(存储的数据,存储数据的文件对象)方法,存储
    import json
    numbers = [2, 3, 5, 7, 11, 13]
    filename = 'numbers.json'
    with open(filename, 'w') as file_object:
        json.dump(numbers, file_object)    # 以原有的格式保存数据
    
    • load()方法,读取
    import json
    filename = 'numbers.json'
    with open(filename) as file_object:
        numbers = json.load(file_object)
    
    print(numbers)   # 读取原有的格式数据
    

    这是一种程序之间共享数据的方法

    import json
    
    filename = 'username.json'
    try:
        with open(filename) as file_object:      # 如果文件存在就读取到内存里面
            username = json.load(file_object)
    except FileNotFoundError:                 #  否则就等待用户的输入,创建文件
        username = input('What is your name?')
        with open(filename, 'w') as file_object:
            json.dump(username, file_object)
            print('we`ll remember you when you come back, '+username+'!')
    else:
        print('welcome back,'+username+'!')     # 最后都会执行这条语句
    

    上面的代码重构一下

    
    import json
    
    # 获取存储在username.json文件里面的姓名
    def get_stored_username():
        filename = 'username.json'
        try:
            with open(filename) as file_object:
                username = json.load(file_object)
        except FileNotFoundError:
            return None
        else:
            return username
    
    # 存入新名字到username.json
    def get_new_username():
        username = input('what is your name?')
        filename = 'username.json'
        with open(filename, 'w') as file_object:
            json.dump(username, file_object)
        return username
    
    # 问候用户
    def greet_user():
        username = get_stored_username()
        if username:
            print('Welcome back, '+username+'!')
        else:
            username = get_new_username()
            print('we`ll remember you when you come back,'+username+'!')
    
    
    greet_user()
    
    
    日志记录

    结合笔记三做的WEB应用,现在需要记录其web请求的详细信息和结果。

    • 增加一个函数,把日志记录写入文件里面,在do_search函数里面去调用;传入当前的请求对象,和执行结果:log_request(request, results)
    # req:是当前Flask请求对象  res:查询结果
    def log_request(req: 'flask_request', res: str)->None:
        with open('vsearch.log', 'a') as log:
            print(req, res, file=log)
    
    日志记录结果
    • 当然可以在网页中去查看,这就要增加一个URL:/viewlog ,去读取日志文件里面保存的数据显示在浏览器中
    @app.route('/viewlog')
    def view_log()->str:
        with open('vsearch.log') as log:
            contents = log.read()  # 一次性读取整个文件
        return contents
    
    网页访问结果
    转义数据

    根据上面的结果,发现:网页显示的和日志文件不一样;那是因为浏览器会自动把一些标记隐藏。可以使用escape函数去转义,对从日志文件读取出来的内容转义

    >>> from flask import escape
    >>> escape('this is a request')
    Markup('this is a request')
    >>> escape('this is a <html>')
    Markup('this is a &lt;html&gt;')
    
    @app.route('/viewlog')
    def view_log()->str:
        with open('vsearch.log') as log:
            contents = log.read()
        return escape(contents)
    

    就算在网页上面显示了web请求对象,但是目前来说,这些请求对象都是一样的,要查看请求对象的内部,可以使用dir内置方法,查看它的方法和属性列表。

    def log_request(req: 'flask_request', res: str)->None:
        with open('vsearch.log', 'a') as log:
            print(str(dir(req)), res, file=log)
    
    网页显示的详细web请求
    记录特定的web请求属性

    一般来说,有三个属性是日志记录比较重要的:

    • req.form:从web应用的html表单提交的数据
    • req.remote_addr:运行web浏览器的ip地址
    • req.user_agent:提交数据的浏览器标识

    修改我们的日志记录函数:

    def log_request(req: 'flask_request', res: str)->None:
        with open('vsearch.log', 'a') as log:
            print(req.form, file=log)
            print(req.remote_addr,  file=log)
            print(req.user_agent, file=log)
            print(res, file=log)
    

    最后日志记录的结果是这样的:

    ImmutableMultiDict([('phrase', 'this is my first python'), ('letters', 'aeiou')]) # 提交的数据
    127.0.0.1 # ip地址
    Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 # 浏览器版本
    {'o', 'i'} # 查询结果
    

    这样虽然记录了一次web请求的比较关键的信息,但是却要从日志文件里面读取4次,应该想能不能一次web请求,记录一行数据,然后读取一次呢?

    def log_request(req: 'flask_request', res: str)->None:
        with open('vsearch.log', 'a') as log:
            print(req.form, file=log, end='|')
            print(req.remote_addr,  file=log, end='|')
            print(req.user_agent, file=log, end='|')
            print(res, file=log)
    
    可读的输出
    • 字符串列表转为字符串join
    >>> names=['terry','john','michael']
    >>> pythons = '|'.join(names)
    >>> pythons
    'terry|john|michael'
    
    >>> s =pythons.split('|')       
    >>> s       
    ['terry', 'john', 'michael']
    
    • 修改view_log函数
    # 列表的嵌套  contents  就数据读入嵌套列表里面
    @app.route('/viewlog')
    def view_log()->str:
        contents = []  # 空列表
        with open('vsearch.log') as log:
            for line in log:
                contents.append([])  # 追加一个新的空列表
                for item in line.split('|'):  # 根据 | 分解一行数据
                    contents[-1].append(escape(item)) # 把分解的数据放在列表的末尾
        return str(contents)
    
    • 修改后的输出


      修改后的输出
    • Jinja2模板修改输出log数据,格式化输出
      {{ }}和{% block %}是模板标记:可以使用html变量和块作为模板的参数;循环变量模板如下,还可以嵌套:
    # 希望再 the_row_titles  变量中查找数据
    <tr>
     {% for row_title in the_row_titles %}
            <th>{{ row_title }}</th>
     {{% endfor %}}   # 循环结尾
    </tr>
    

    使用HTML的表格标记<table><tr><th><td>,格式化输出日志数据,接着修改view_log函数,使得其向浏览器返回一个html而不是字符串

    @app.route('/viewlog')
    def view_log()->'html':
        contents = []
        with open('vsearch.log') as log:
            for line in log:
                contents.append([])
                for item in line.split('|'):
                    contents[-1].append(escape(item))
        titles=('Form Data','Remote_addr','User_agent','Results')
        return render_template('viewlog.html',the_title='View Log',the_row_titles=titles,the_data=contents,)
    

    修改后的view_loghtml代码:

    {% extends 'base.html' %} {% block body %}
    
    <h2>{{ the_title }}</h2>
    
    <table>
        <tr>
            {% for row_title in the_row_titles %}
            <th>{{ row_title }}</th>
            {% endfor %}
        </tr>
        {% for log_row in the_data %}
        <tr>
            {% for item in log_row %}
            <td>{{item}}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </table>
    
    {% endblock %}
    

    格式化输出效果:


    格式化输出

    相关文章

      网友评论

        本文标题:Head First Python 读书笔记(四)

        本文链接:https://www.haomeiwen.com/subject/ifhnuftx.html