代码之家  ›  专栏  ›  技术社区  ›  fmark

python“with”语句的设计目的是什么?

  •  358
  • fmark  · 技术社区  · 14 年前

    我遇到了蟒蛇 with 今天第一次发表声明。我已经轻而易举地使用了几个月,甚至不知道它的存在!考虑到它的地位有些模糊,我想应该问一下:

    1. 什么是蟒蛇 具有 陈述 设计用于?
    2. 做什么 你用它吗?
    3. 有没有 明白了,我需要知道,或者 常见的反模式与 它的用途?在任何情况下,最好使用 try..finally 具有 ?
    4. 为什么使用得不更广泛?
    5. 哪些标准库类与它兼容?
    10 回复  |  直到 7 年前
        1
  •  354
  •   Tamás    14 年前
    1. 我相信我之前的其他用户已经回答了这个问题,所以我添加它只是为了完整性: with 语句通过在所谓的 context managers . 有关更多详细信息,请参见 PEP 343 . 例如, open 语句本身就是一个上下文管理器,它允许您打开一个文件,只要执行在 具有 语句的使用位置,并在离开上下文后立即关闭它,无论是由于异常还是在常规控制流期间离开它。这个 具有 因此,语句的使用方式类似于 RAII pattern 在C++中:一些资源是由 具有 声明并在离开时发布 具有 语境。

    2. 例如:打开文件时使用 with open(filename) as fp: ,获取锁使用 with lock: (何处) lock 是的实例 threading.Lock )您还可以使用 contextmanager 装饰者 contextlib . 例如,当我必须临时更改当前目录,然后返回到我所在的位置时,我经常使用此选项:

      from contextlib import contextmanager
      import os
      
      @contextmanager
      def working_directory(path):
          current_dir = os.getcwd()
          os.chdir(path)
          try:
              yield
          finally:
              os.chdir(current_dir)
      
      with working_directory("data/stuff"):
          # do something within data/stuff
      # here I am back again in the original working directory
      

      下面是另一个临时重定向的示例 sys.stdin , sys.stdout sys.stderr 到另一个文件句柄,稍后将其还原:

      from contextlib import contextmanager
      import sys
      
      @contextmanager
      def redirected(**kwds):
          stream_names = ["stdin", "stdout", "stderr"]
          old_streams = {}
          try:
              for sname in stream_names:
                  stream = kwds.get(sname, None)
                  if stream is not None and stream != getattr(sys, sname):
                      old_streams[sname] = getattr(sys, sname)
                      setattr(sys, sname, stream)
              yield
          finally:
              for sname, stream in old_streams.iteritems():
                  setattr(sys, sname, stream)
      
      with redirected(stdout=open("/tmp/log.txt", "w")):
           # these print statements will go to /tmp/log.txt
           print "Test entry 1"
           print "Test entry 2"
      # back to the normal stdout
      print "Back to normal stdout again"
      

      最后,另一个创建临时文件夹并在离开上下文时将其清除的示例:

      from tempfile import mkdtemp
      from shutil import rmtree
      
      @contextmanager
      def temporary_dir(*args, **kwds):
          name = mkdtemp(*args, **kwds)
          try:
              yield name
          finally:
              shutil.rmtree(name)
      
      with temporary_dir() as dirname:
          # do whatever you want
      
        2
  •  83
  •   systempuntoout    14 年前

    我建议两个有趣的讲座:

    1。 这个 with 语句用于用上下文管理器定义的方法包装块的执行。这使得 try...except...finally 要封装的使用模式以方便重用。

    2。 你可以这样做:

    with open("foo.txt") as foo_file:
        data = foo_file.read()
    

    from contextlib import nested
    with nested(A(), B(), C()) as (X, Y, Z):
       do_something()
    

    或(Python 3.1)

    with open('data') as input_file, open('result', 'w') as output_file:
       for line in input_file:
         output_file.write(parse(line))
    

    lock = threading.Lock()
    with lock:
        # Critical section of code
    

    三。 我在这里看不到任何反模式。
    引用 Dive into Python :

    试试……终于好了。用得更好。

    4。 我想这和程序员的习惯有关 try..catch..finally 其他语言的声明。

        3
  •  33
  •   Jesper    7 年前

    蟒蛇 with 语句是的内置语言支持 Resource Acquisition Is Initialization 常用于C++中的习语。它旨在允许安全获取和释放操作系统资源。

    这个 具有 语句在作用域/块内创建资源。使用块中的资源编写代码。当块退出时,不管块中代码的结果如何(即块是正常退出还是由于异常退出),都将干净地释放资源。

    python库中的许多资源都遵守 具有 语句,因此可以与它一起使用。但是,任何人都可以通过实施有良好文档记录的协议来生成可在WITH语句中使用的资源: PEP 0343

    每当您获取应用程序中必须明确放弃的资源(如文件、网络连接、锁等)时,都可以使用它。

        4
  •  24
  •   John La Rooy    14 年前

    反模式的一个例子可能是使用 with 在一个循环中,当 具有 在环路外

    例如

    for row in lines:
        with open("outfile","a") as f:
            f.write(row)
    

    VS

    with open("outfile","a") as f:
        for row in lines:
            f.write(row)
    

    第一种方法是打开和关闭每个 row 与第二种打开和关闭文件的方式相比,这可能会导致性能问题。

        5
  •  23
  •   JudoWill    14 年前

    同样,为了完整性,我将添加我最有用的用例 with 声明。

    我做了很多科学计算,对于一些活动,我需要 Decimal 用于任意精度计算的库。我的代码的某些部分需要高精度,而对于大多数其他部分,我需要较少的精度。

    我将默认精度设置为一个较低的数字,然后使用 具有 要对某些部分获得更精确的答案,请执行以下操作:

    from decimal import localcontext
    
    with localcontext() as ctx:
        ctx.prec = 42   # Perform a high precision calculation
        s = calculate_something()
    s = +s  # Round the final result back to the default precision
    

    我经常在超几何测试中使用这个方法,它要求将产生阶乘的大数除法。当你做基因组尺度计算时,你必须小心舍入和溢出错误。

        6
  •  9
  •   stefanB    14 年前

    PEP 343 - The 'with' statement ,结尾有一个示例部分。

    …python的新语句“with” 要制作的语言 可以考虑Try/Finally语句的标准用法。

        7
  •  3
  •   zefciu    14 年前

    WITH语句与所谓的上下文管理器一起工作:

    http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

    其思想是在离开“with”块后进行必要的清理,从而简化异常处理。一些python内置模块已经作为上下文管理器工作。

        8
  •  3
  •   cobbal    14 年前

    第1点、第2点和第3点被合理地涵盖:

    4: 它是相对较新的,仅在python2.6+中可用(或python2.5使用 from __future__ import with_statement )

        9
  •  2
  •   bgse    7 年前

    另一个例子是开箱即用的支持,当您习惯了内置的方式时,可能会有点困惑。 open() 行为,是 connection 常用数据库模块的对象,如:

    这个 连接 对象是上下文管理器,因此可以在 with-statement 但是,当使用上述内容时,请注意:

    with-block 完成,无论是否有例外, 连接未关闭 . 万一 带块 异常结束后,事务将回滚,否则将提交事务。

    这意味着程序员必须注意自己关闭连接,但允许获取一个连接,并在多个连接中使用它。 with-statements ,如中所示 psycopg2 docs :

    conn = psycopg2.connect(DSN)
    
    with conn:
        with conn.cursor() as curs:
            curs.execute(SQL1)
    
    with conn:
        with conn.cursor() as curs:
            curs.execute(SQL2)
    
    conn.close()
    

    在上面的示例中,您将注意到 cursor 对象 psycopg2 还有上下文管理器。从有关行为的文档中:

    当A 光标 退出 带块 它被关闭,释放最终与之关联的任何资源。事务的状态不受影响。

        10
  •  2
  •   Tushar Patil    7 年前

    在python中通常是__ 具有 _157;语句用于打开文件、处理文件中存在的数据,以及在不调用close()方法的情况下关闭文件。_156;with_157;语句通过提供清理活动简化了异常处理。

    一般形式:

    with open(“file name”, “mode”) as file-var:
        processing statements
    

    注: 不需要通过对file-var.close()调用close()来关闭文件