【问题标题】:Python function that accepts file object or path接受文件对象或路径的 Python 函数
【发布时间】:2015-08-27 21:37:09
【问题描述】:

我想编写一个函数,它可以接受路径作为字符串或文件对象。到目前为止,我有:

def awesome_parse(path_or_file):
    if isinstance(path_or_file, basestring):
        f = open(path_or_file, 'rb')
    else:
        f = path_or_file
    with f as f:
        return do_stuff(f)

do_stuff 接受一个打开的文件对象。

有没有更好的方法来做到这一点? with f as f:有影响吗?

谢谢!

【问题讨论】:

    标签: python file-io


    【解决方案1】:

    您的代码的奇怪之处在于,如果传递一个打开的文件,它将关闭它。这不好。无论打开文件的代码应该负责关闭它。这使得函数有点复杂:

    def awesome_parse(path_or_file):
        if isinstance(path_or_file, basestring):
            f = file_to_close = open(path_or_file, 'rb')
        else:
            f = path_or_file
            file_to_close = None
        try:
            return do_stuff(f)
        finally:
            if file_to_close:
                file_to_close.close()
    

    您可以通过编写自己的上下文管理器将其抽象出来:

    @contextlib.contextmanager
    def awesome_open(path_or_file):
        if isinstance(path_or_file, basestring):
            f = file_to_close = open(path_or_file, 'rb')
        else:
            f = path_or_file
            file_to_close = None
    
        try:
            yield f
        finally:
            if file_to_close:
                file_to_close.close()
    
    def awesome_parse(path_or_file):
        with awesome_open(path_or_file) as f:
            return do_stuff(f)
    

    【讨论】:

    • 哦...在不应该关闭文件时很好地抓住它!我绝对不希望这种情况发生。谢谢!
    • 不应该是yieldyield f
    • @contextlib.contextmanager 到底是做什么的?为什么没有它我会得到AttributeError: __exit__?谢谢!
    【解决方案2】:

    你可以这样做:

    def awesome_parse(do_stuff):
        """Decorator to open a filename passed to a function
           that requires an open file object"""
        def parse_path_or_file(path_or_file):
            """Use a ternary expression to either open the file from the filename
               or just pass the extant file object on through"""
            with (open(path_or_file, 'rb') 
                   if isinstance(path_or_file, basestring) 
                    else path_or_file) as f:
                return do_stuff(f)
        return parse_path_or_file
    

    然后,当您声明任何对打开的文件对象执行操作的函数时:

    @awesome_parse
    def do_things(open_file_object):
        """This will always get an open file object even if passed a string"""
        pass
    
    @awesome_parse
    def do_stuff(open_file_object):
        """So will this"""
        pass
    

    编辑 2:有关装饰器的更多详细信息。

    【讨论】:

    • 啊,是的,对不起。我只是将其更改为 awesome_parse,以免与 super 混淆。
    • 您的答案似乎落入了@Ned Batchelder 的答案所涉及的同一个陷阱(过早关闭文件)。不过我喜欢装饰器的想法,所以我也会使用你的一些想法!
    • 是的,确实如此。在parse_path_or_file() 中使用他的@contextlib.contextmanager 结构就可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 2022-09-24
    • 2021-06-14
    相关资源
    最近更新 更多