10 错误和异常 - 《Python 核心编程》

􀁺 什么是异常?
􀁺 Python 中的异常
􀁺 探测和处理异常
􀁺 上下文管理
􀁺 引发异常
􀁺 断言
􀁺 标准异常
􀁺 创建异常
􀁺 相关模块
10.1 什么是异常人们需要一个"柔和"的处理错误的方法, 而不是终止程序.
错误
    从软件方面来说, 错误是语法或是逻辑上的.

    当 Python 检测到一个错误时, 解释器就会指出当前流已经无法继续执行下去. 这时候就出现了异常.

    语法错误
        语法错误指示软件的结构上有错误, 导致不能被解释器解释或编译器无法编译.
    逻辑错误(域错误和范围错误)
       逻辑错误可能是由不完整或是不合法的输入所致; 在其他情况下, 还可能是逻辑无法生成, 计算, 或是输出结果需要的过程无法执行.
异常
    当 Python 检测到一个错误时, 解释器就会指出当前流已经无法继续执行下去. 这时候就出现了异常.
    异常是因为程序出现了错误而在正常控制流以外采取的行为.
    两个阶段: 
        引起异常发生的错误阶段
        检测(和采取可能的措施)阶段
程序员不仅仅有了检测错误的能力, 还可以在它们发生时采取更可靠的补救措 施. 
由于有了运行时管理错误的能力, 应用程序的健壮性有了很大的提高.
和其他支 持异常处理的语言类似, Python 采用了 "try/尝试" 块和 "catching/捕获" 块的概念.
10.2 Python 中的异常
所有错误, 无论是语意上的还是逻辑上的,都是由于和 Python 解释器不相容导致的, 其后果就是引发异常.
traceback/跟踪返回消息
    traceback/跟踪返回消息提供了一个一致的错误接口,包括错误的名称, 原因, 以及发生错误的行号.
几个异常的例子
    NameError: 尝试访问一个未申明的变量
        >>> foo
        Traceback (innermost last): File "<stdin>", line 1, in ?
        NameError: name 'foo' is not defined
    ZeroDivisionError: 除数为零
        >>> 1 / 0
        Traceback (innermost last): File "<stdin>", line 1, in ?
        ZeroDivisionError: integer division or modulo by zero
    SyntaxError: Python 解释器语法错误(SyntaxError 异常是唯一不是在运行时发生的异常)
        >>> for
        File "<string>", line 1
        for
        ^
        SyntaxError: invalid syntax
    IndexError:请求的索引超出序列范围
        >>> list()[0] 
        Traceback (most recent call last): 
          File "<stdin>", line 1, in <module> 
        IndexError: list index out of range 
        >>>
    KeyError:请求一个不存在的字典关键字
        >>> aDict = {'host': 'earth', 'port': 80}
        >>> print aDict['server'] Traceback (innermost last):
        File "<stdin>", line 1, in ? KeyError: server
    IOError: 输入/输出错误
        >>> f = open("blah")
         Traceback (innermost last):
        File "<stdin>", line 1, in ?
        IOError: [Errno 2] No such file or directory: 'blah'
    AttributeError: 尝试访问未知的对象属性
        >>> class myClass(object):
        ...     pass
        ...
        >>> myInst = myClass()
        >>> myInst.bar = 'spam'
        >>> myInst.bar
        'spam'
        >>> myInst.foo
        Traceback (innermost last): File "<stdin>", line 1, in ?
        AttributeError: foo
10.3 检测和处理异常
    异常可以通过 try 语句来检测. 
    任何在 try 语句块里的代码都会被监测, 检查有无异常发生.
    try 语句有两种主要形式: try-except 和 try-finally .
    一个 try 语句可以对应一个或多个 except 子句, 但只能对应一个finally 子句, 或是一个 try-except-finally 复合语句.
    一个可选的 else 子句可以处理没 有探测到异常的时执行的代码. 
try-except 语句
    try-except 语句(以及其更复杂的形式)定义了进行异常监控的一段代码, 并且提供了处理异 常的机制.
        try:
            try_suite # watch for exceptions here 监控这里的异常
        except Exception[, reason]:
            except_suite # exception-handling code 异常处理代码
        在程序运行时, 解释器尝试执行 try 块里的所有代码, 如果代码块完成后没有异常发生, 执行流就会忽略 except 语句继续执行. 
        而当 except 语句所指定的异常发生后, 我们保存了错误的原因, 控制流立即跳转到对应的处理器( try 子句的剩余语句将被忽略).
    核心笔记: 忽略代码, 继续执行, 和向上移交
        try 语句块中异常发生点后的剩余语句永远不会到达(所以也永远不会执行). 一旦一个异常被
    引发, 就必须决定控制流下一步到达的位置. 剩余代码将被忽略, 解释器将搜索处理器, 一旦找到,
    就开始执行处理器中的代码.
        如果没有找到合适的处理器, 那么异常就向上移交给调用者去处理, 这意味着堆栈框架立即回
    到之前的那个. 如果在上层调用者也没找到对应处理器, 该异常会继续被向上移交, 直到找到合适
    处理器. 如果到达最顶层仍然没有找到对应处理器, 那么就认为这个异常是未处理的, Python 解释
    器会显示出跟踪返回消息, 然后退出.
封装内建函数
    float() 内建函数的基本作用是把任意一个数值类型转换为一个浮点数. 
    从 Python 1.5 开始, float() 增加了把字符串表示的数值转换为浮点数的功能, 没必要使用 string 模块中的 atof() 函数. 
    如果你使用的老版本的 Python , 请使用 string.atof() 替换这里的 float() .
        >>> float('foo') 
        Traceback (most recent call last): 
          File "<stdin>", line 1, in <module> 
        ValueError: could not convert string to float: foo 
        >>> float(('foo','bar')) 
        Traceback (most recent call last): 
          File "<stdin>", line 1, in <module> 
        TypeError: float() argument must be a string or a number
        >>> def safe_float(obj): 
        ... try: 
        ... retval = float(obj) 
        ... except ValueError: 
        ... retval = 'could not convert non-number to float' 
        ... except TypeError: 
        ... retval = 'obj must be a string or a number' 
        ... return retval 
        ... 
        >>> safe_float(1) 
        1.0 
        >>> safe_float('foo') 
        'could not convert non-number to float' 
        >>> safe_float(('foo','bar')) 
        'obj must be a string or a number' 
        >>>
带有多个 except 的 try 语句
        except Exception1[, reason1]:
            suite_for_exception_Exception1
        except Exception2[, reason2]:
            suite_for_exception_Exception2
处理多个异常的 except 语句
        except (Exc1[, Exc2[, ... ExcN]])[, reason]:    #元组: except 语句在处理多个异常时要求异常被放 在一个元组
            suite_for_exceptions_Exc1_to_ExcN
捕获所有异常
    关于捕获所有异常, 你应当知道有些异常不是由于错误条件引起的, 它们是 SystemExit 和 KeyboardInterupt . 
    SystemExit 是由于当前 Python 应用程序需要退出, KeyboardInterupt 代表用户按下了 CTRL-C (^C) , 想要关闭 Python .
        - BaseException 
            |- KeyboardInterrupt 
            |- SystemExit 
            |- Exception 
                |- (all other current built-in exceptions) 所有当前内建异常
    BaseException 所有异常的基类
    如果你确实需要捕获所有异常, 那么你就得使用新的 BaseException, 当然, 也可以使用不被推荐的裸except 语句.
    try:
         :
      except BaseException, e:
          # handle all errors
    Exception常规错误的基类
      try:
          :
     except Exception, e:
         # handle real errors
    核心风格: 不要处理并忽略所有错误 
        Python 提供给程序员的 try-except 语句是为了更好地跟踪潜在的错误并在代码里准备好处 
    理异常的逻辑. 这样的机制在其他语言(例如 C ) 是很难实现的. 它的目的是减少程序出错的次数 
    并在出错后仍能保证程序正常执行. 作为一种工具而言, 只有正确得当地使用它, 才能使其发挥作 
    用. 
        一个不正确的使用方法就是把它作为一个大绷带"绑定"到一大片代码上. 也就是说把一大段程 
    序(如果还不是整个程序源代码的话)放入一个 try 块中, 再用一个通用的 except 语句 "过滤" 
    掉任何致命的错误, 忽略它们. 
        # this is really bad code 
        try: 
            large_block_of_code # bandage of large piece of code 
        except Exception: # same as except: 
            pass # blind eye ignoring all errors 
            很明显, 错误无法避免, try-except 的作用是提供一个可以提示错误或处理错误的机制, 而不 
    是一个错误过滤器. 上边这样的结构会忽略许多错误, 这样的用法是缺乏工程实践的表现, 我们 
    不赞同这样做. 
        底线: 避免把大片的代码装入 try-except 中然后使用 pass 忽略掉错误. 你可以捕获特定 
    的异常并忽略它们, 或是捕获所有异常并采取特定的动作. 不要捕获所有异常,然后忽略掉它们.
异常参数
    异常也可以有参数, 异常引发后它会被传递给异常处理器. 
    当异常被引发后参数是作为附加帮助信息传递给异常处理器的.
    异常原因是可选的, 但标准内建异常提供至少一个参数, 指示异常原因的一个字符串.
        <type 'exceptions.TypeError'> 
        >>> try: 
        ... float({}) 
        ... except TypeError,diag: 
        ... pass 
        ... 
        >>> str(diag) 
        'float() argument must be a string or a number' 
        >>> type(diag) 
        <type 'exceptions.TypeError'> 
        >>> print diag 
        float() argument must be a string or a number 
        >>> diag.__class__ 
        <type 'exceptions.TypeError'> 
        >>> diag.__doc__ 
        'Inappropriate argument type.' 
        >>>
    核心风格: 遵循异常参数规范
        当你在自己的代码中引发内建(built-in)的异常时, 尽量遵循规范, 用和已有 Python 代码 
    一致错误信息作为传给异常的参数元组的一部分. 简单地说, 如果你引发一个 ValueError , 那么 
    最好提供和解释器引发 ValueError 时一致的参数信息, 如此类推. 这样可以在保证代码一致性, 
    同时也能避免其他应用程序在使用你的模块时发生错误.
else 子句
    在try 范围中没有异常被检测到时,执行else 子句.
    在else 范围中的任何代码运行前,try 范围中的所有代码必须完全成功(也就是,结束前没有引发异常).
        import 3rd_party_module
        log = open('logfile.txt', 'w')
        try:
            3rd_party_module.function()
        except:
            log.write("*** caught exception in module\n")
        else:
            log.write("*** no exceptions caught\n")
        log.close()
finally 子句
    finally 子句是无论异常是否发生,是否捕捉都会执行的一段代码.
    可以将finally 仅仅配合try 一起使用,也可以和try-except(else 也是可选的)一起使用.
try-finally 语句
    try:
        try_suite
    finally:
        finally_suite #无论如何都执行
当在try 范围中产生一个异常时,(这里)会立即跳转到finally 语句段.当finally 中的所有代码都执行完毕后,会继续向上一层引发异常.
如果finally 中的代码引发了另一个异常或由于return,break,continue 语 法而终止,原来的异常将丢失而且无法重新引发.
try-except-else-finally:厨房一锅端
    所有不同的可以处理异常的语法样式:
    try: 
        try_suite 
    except Exception1: 
        suite_for_Exception1 
    except (Exception2, Exception3, Exception4): 
        suite_for_Exceptions_2_3_and_4 
    except Exception5, Argument5: 
        suite_for_Exception5_plus_argument 
    except (Exception6, Exception7), Argument67: 
        suite_for_Exceptions6_and_7_plus_argument 
    except: 
        suite_for_all_other_exceptions 
    else: 
        no_exceptions_detected_suite 
    finally: 
        always_execute_suite
10.4 上下文管理
with 语句 (C# using(IDisposalbe)
    try-except 和try-finally 的一种特定的配合用法是保证共享的资源的唯一分配,并在任务结束的时候释放它.
    比如文件(数据,日志,数据库等等),线程资源,简单同步,数据库连接,等等. with 语句的目标就是应用在这种场景.
    with context_expr [as var]:
        with_suite
    with 语句仅能工作于支持上下文管理协议(context managementprotocol)的对象.
    支持 上下文管理协议 的对象简短列表:
        􀁺 file
        􀁺 decimal.Context
        􀁺 thread.LockType
        􀁺 threading.Lock
        􀁺 threading.RLock
        􀁺 threading.Condition
        􀁺 threading.Semaphore
        􀁺 threading.BoundedSemaphore
*上下文管理协议
    上下文表达式(context_expr),上下文管理器
        __context__()方法
    上下文对象,with 语句块
        __enter__()方法
        __exit__()方法
        contextlib 模块
        上下文管理器主要作用于共享资源
10.5 *字符串作为异常
    早在Python 1.5 前,标准的异常是基于字符串实现的.
    到1.5 为止,所有的标准异常都是类了.
    程序员还是可以用字符串作为自己的异常的,但是我们建议从现在起使用异常类.
    你可能用到仍然使用着字符串异常的外部或第三方的模块.
10.6 触发异常
    Python 提供了一种机制让程序员明确的触发异 常:这就是raise 语句.
raise 语句
    语法与惯用法
        rasie 一般的用法是:
            raise [SomeException [, args [, traceback]]]
            第一个参数,SomeExcpetion,是触发异常的名字.如果有,它必须是一个字符串,类或实例
            第二个符号为可选的args(比如参数,值),来传给异常.
                这可以是一个单独的对象也可以是一个对象的元组.
                当异常发生时,异常的参数总是作为一个元组传入.
                如果args 原本就是元组,那么就将其传给异常去处理;
                如果args 是一个单独的对象,就生成只有一个元素的元组(就是单元素元组).
                大多数情况下,单一的字符串用来指示错误的原因.
                如果传的是元组,通常的组成是一个错误字符串,一个错误编号,可能还有一个错误的地址,比如文件,等等.
            最后一项参数,traceback,同样是可选的(实际上很少用它),
                如果有的话,则是当异常触发时新生成的一个用于异常-正常化(exception—normally)的追踪(traceback)对象.
                当你想重新引发异常时,第三个参数很有用(可以用来区分先前和当前的位置).
                如果没有这个参数,就填写None.
        最常见的用法为SomeException 是一个类.
            不需要其他的参数,但如果有的话,可以是一个单一对象参数,一个参数的元组,或一个异常类的实例.
            如果参数是一个实例,可以由给出的类及其派生类实例化(已存在异常类的子集).若参数为实例,则不能有更多的其他参数.
    更多的特殊/少见的惯用法
           
 
            
10.7 断言
    断言是一句必须等价于布尔真的判定;此外,发生异常也意味着表达式为假.
    断言可以简简单单的想象为raise-if 语句(更准确的说是raise-if-not 语句).测试一个表达式,如果返回值是假,触发异常.
    断言通过assert 语句实现.
断言语句
    断言语句等价于这样的Python 表达式,如果断言成功不采取任何措施(类似语句),否则触发AssertionError(断言错误)的异常.
    assert 的语法如下:
        assert expression[, arguments]
    AssertionError 异常和其他的异常一样可以用try-except 语句块捕捉,但是如果没有捕捉,它将终止程序运行而且提供一个traceback.
        >>> assert True 
        >>> assert False,'Hello' 
        Traceback (most recent call last): 
          File "<stdin>", line 1, in <module> 
        AssertionError: Hello 
        >>>
    想象断言语句在Python 中的函数实现.可以像下面这样:
        def assert(expr, args=None):
            if __debug__ and not expr:
                raise AssertionError, args
        内建的变量__debug__在通常情况下为True,如果开启优化后为False(命令行选项-O).
10.8 标准异常
    所有的异常都是内建的. 所以它们在脚本启动 前或在互交命令行提示符出现时已经是可用的了.
 
    
 
    
 
     
 
    
 
所有的标准/内建异常都是从根异常派生的.
    目前,有3 个直接从BaseException 派生的异常子类:SystemExit,KeyboardInterrupt 和Exception.
    其他的所有的内建异常都是Exception 的子类.
    SystemExit 和KeyboardInterrupt 从Exception 的继承中移到BaseException 的继 承下, 这样可以允许如except Exception 的语句捕获所有非控制程序退出的异常.
    从Python2.5 开始,不再支持构建基于字符串的异常并且被正式的弃用,也就是说你不能再触发一个字符串异常了.
10.9 *创建异常 
  1  #!/usr/bin/env python
  2 
  3 import os, socket, errno, types, tempfile
  4 
  5 class NetworkError(IOError):
  6     pass
  7 
  8 class FileError(IOError):
  9     pass
 10 
 11 def updArgs(args, newarg=None):
 12     if isinstance(args, IOError):
 13         myargs = []
 14         myargs.extend([arg for arg in args])
 15     else:
 16         myargs = list(args)
 17 
 18     if newarg:
 19         myargs.append(newarg)
 20     return tuple(myargs)
 21 
 22 def fileArgs(file, mode, args):
 23     if args[0] == errno.EACCES and \
 24  'access' in dir(os):
 25         perms = ''
 26         permd = { 'r': os.R_OK, 'w': os.W_OK, 'x': os.X_OK}
 27         pkeys = permd.keys()
 28         pkeys.sort()
 29         pkeys.reverse()
 30 
 31         for eachPerm in 'rwx':
 32             if os.access(file, permd[eachPerm]):
 33                 perms += eachPerm
 34             else:
 35                 perms += '-'
 36 
 37         if isinstance(args, IOError):
 38             myargs = []
 39             myargs.extend([arg for arg in args])
 40         else:
 41             myargs = list(args)
 42 
 43         myargs[1] = "'%s' %s (perms: '%s')" % \
 44  (mode, myargs[1], perms)
 45 
 46         myargs.append(args.filename)
 47 
 48     else:
 49         myargs = args
 50 
 51     return tuple(myargs)
 52 
 53 def myconnect(sock, host, port):
 54     try:
 55         sock.connect((host, port))
 56         
 57     except socket.error, args:
 58         myargs = updArgs(args) # conv inst2tuple
 59         if len(myargs) == 1: # no #s on some errs
 60             myargs = (errno.ENXIO, myargs[0])
 61 
 62         raise NetworkError, \
 63 updArgs(myargs, host + ':' + str(port))
 64 
 65 def myopen(file, mode='r'):
 66     try:
 67         fo = open(file, mode)
 68     except IOError, args:
 69         raise FileError, fileArgs(file, mode, args)
 70 
 71     return fo
 72 
 73 def testfile():
 74 
 75     file = mktemp()
 76     f = open(file, 'w')
 77     f.close()
 78 
 79     for eachTest in ((0, 'r'), (0100, 'r'),
 80  (0400, 'w'), (0500, 'w')):
 81         try:
 82             os.chmod(file, eachTest[0])
 83             f = myopen(file, eachTest[1])
 84 
 85         except FileError, args:
 86             print "%s: %s" % \
 87  (args.__class__.__name__, args)
 88         else:
 89             print file, "opened ok... perm ignored"
 90             f.close()
 91 
 92     os.chmod(file, 0777)# enable all perms
 93     os.unlink(file)
 94 
 95 def testnet():
 96     s = socket.socket(socket.AF_INET,
 97 socket.SOCK_STREAM)
 98  
 99     for eachHost in ('deli', 'www'):
100         try:
101             myconnect(s, 'deli', 8080)
102         except NetworkError, args:
103             print "%s: %s" % \
104  (args.__class__.__name__, args)
105 
106 if __name__ == '__main__':
107     testfile()
108     testnet()
myexec.py
10.10 为什么用异常(现在)?
    毫无疑问,错误的存在会伴随着软件的存在.区别在于当今快节奏的计算世界, 我们的执行环境已经改变, 所以我们需要改变错误处理, 以准确反映我们软件的开发环境. 就现今应用来说, 普遍的是自洽(self-contained)的图形用户界面(GUIs)或是客户机/服务器体系, 例如Web.在应用层处理错误的能力近来变得更为重要, 用户已不再是应用程序的的唯一的直接运行者.随着互联网和网上电子商业应用越来越普及, web 服务器将成为应用软件的主要客户. 这意味着应用程序再也不能只是直接的失败或崩溃, 因为如果这样, 系统错误导致浏览器的错误, 这反过来又会让用户沮丧. 失去眼球意味着失去广告收入和和潜在的大量无可挽回的生意.如果错误的确发生了, 它们一般都归因于用户输入的数据无效. 运行环境必须足够强健,来处理应用级别的错误,并提供用户级别的错误信息.就服务器而言,这必须转化为一个"非错误" . 因为应用必须要成功的完成, 即使所做的不过是返回一个错误的信息, 向用户是提供一个有效的超文本标记语言(HTML)的网页指明错误.
    如果你不清楚我在说什么, 那个一个简单的网页浏览器窗口,用大而黑的字体写到"内部服务器错误"是否更耳熟?用一个弹出式窗口宣告"文件中没有数据"的致命错误如何?作为一个用户, 这些词语对你有意义吗?没有, 当然没有(除非你是一个互联网软件工程师), 至于对普通用户来说,这些是无休止的混乱和挫折感的来源. 这些错误导致在执行的程序时的失败. 应用不论是返回无效的超文本传输协议( http)数据还是致命地终止, 都会导致Web 服务器举手投降, 说: "我放弃" !这种类型的执行错误不应该被允许, 无论情况如何. 随着系统变得更加复杂, 又牵涉到更多的新手用户, 要采取额外的措施, 确保用户平滑的学到应用经验. 即使面对一个错误, 应用应该成功的中止, 不至于灾难性的影响其执行环境. Python 异常处理促使成熟和正确的编程.
10.11 到底为什么要异常?
    如果上文的动机不够充分, 试想Python 编程没有程序级的异常处理. 第一件事需要担心的是客户端程序员在自己的代码中遗忘控制. 举例来说, 如果你创造了一个交互的应用程序分配并使用了大量的资源, 如果一个用户击中Ctrl+C 或其他键盘中断, 应用程序将不会有机会执行清理工作, 可能导致数据丢失或数据损坏. 此外, 也没有机制来给出可选的行为, 诸如提示用户, 以确认他们真的是想退出或是他们意外的按下了Ctrl 键.另一个缺点就是函数必须重写来为错误的情形返回一个"特殊"的值, 如:None. 程序员要负责检查每一个函数调用的返回值. 这可能是个麻烦, 因为你可能不得不检查返回值, 这和没有发生错误时你期待结果也许不是同一类型的对象. 什么,你的函数要把None 作为一个有效的数值返回?那么, 你将不得不拿出另一个返回值, 也许是负数.我们也许并不需要提醒你, 在Python 的环境下负数下可能是有效的, 比如作为一个序列的索引. 作为一个写应用程序接口( API )的程序员, 你不得不为每个一个用户输入可能遇到的返回错误写文档. 同时, 我们难以(而且乏味)在多层次的代码中以传播错误(和原因).
    没有一个简单的传播方法像异常一样做到这一点. 因为错误的数据需要在调用层次中向上转发,但在前进的道路上可能被曲解. 一个不相干的错误可能会被宣布为起因,而实际上它与原始问题完全无关.在一层一层的传递中,我们失去了对原始错误封装和保管的能力,更不用说完全地失去我们原本关心的数据的踪影!异常不仅简化代码, 而且简化整个错误管理体系 --- 它不该在应用开发中如此重要角色;而有了Python 的异常处理能力, 也的确没有必要了.
10.12 异常和sys 模块
另一种获取异常信息的途径是通过sys 模块中exc_info()函数.
    >>> try:
    ...     float('abc123')
    ... except:
    ...     import sys
    ...     exc_tuple = sys.exc_info()
    ...
    >>> print(exc_tuple)
    (<class 'ValueError'>, ValueError("could not convert string to float: 'abc123'",), <traceback object at 0x000000000296EE88>)
    >>> for eachItem in exc_tuple:
    ...     print(eachItem)
    ...
    <class 'ValueError'>
    could not convert string to float: 'abc123'
    <traceback object at 0x000000000296EE88>
    >>>
    sys.exc_info()元组
        􀁺 exc_type: 异常类 
        􀁺 exc_value: 异常类的实例 
        􀁺 exc_traceback: 追踪(traceback)对象 
        前两项:实际的异常类, 和这个异常类的实例 . 
        第三项, 是一个新增的追踪(traceback)对象.
            这一对象提供了的发生异常的上下文. 
            它包含诸如代码的执行帧,异常发生时的行号等信息.
    在旧版本中的Python 中, 这三个值分别存在于sys 模块, 为sys.exc_type , sys.exc_value ,sys.exc_traceback . 
    不幸的是, 这三者是全局变量而不是线程安全的. 我们建议亡羊补牢, 用sys.exc_info()来代替. 
    在未来版本Python 中,所有这三个变量都将被逐步停用,并最终移除.
10.13 相关模块
异常相关的标准库
模块                   描述
exceptions        内建异常(永远不用导入这个模块)
contextlib        为使用with 语句的上下文对象工具
sys                    包含各种异常相关的对象和函数(见sys.ex*)
posted @ 2015-08-16 21:25  小麦粉  阅读(924)  评论(0编辑  收藏  举报