接收文件对象或文件路径

0 投票
4 回答
735 浏览
提问于 2025-04-18 16:07

我知道在静态语言中,接收一个文件对象总是比接收一个表示路径的字符串要好,这样从软件设计的角度来看更合理。不过在像Python这样的动态语言中,你看不到变量的类型,那么传递文件的“正确”方式是什么呢?

传递函数对象是不是有问题呢?因为你需要记得在之后关闭它(而且你可能不会记得,因为你看不到类型)?

4 个回答

0

你可以在Python中使用文件对象。当这些文件对象被自动清理时,文件会自动关闭。

文件对象是通过C语言的标准输入输出包来实现的,可以使用内置的open()函数来创建。

1

像其他类型一样传递文件对象。

def f(myfile):
    myfile.write('asdf')

ofs = open(filepath, 'w') # ofs is file object
f(ofs) # passes file object
ofs.close()

也可以使用闭包。

def f():
    return open(filepath, 'w') # returns file object

ofs = f()
ofs.write('something')
ofs.close()
1

不过,在像Python这样的动态语言中,你看不到变量的类型,传递文件的“正确”方式是什么呢?

简单来说,就是你不需要特别去考虑这个问题。

在大多数面向对象的编程语言中,有一个对象契约,保证如果一个对象有一个叫做quack的方法,那它就知道怎么“呱呱叫”。有些语言在执行这个契约时非常严格(比如Java),而有些则没有那么严格。

最终,这个问题可以归结为Python的一个原则EAFP

Easier to ask for forgiveness than permission。这种常见的Python编码风格假设存在有效的键或属性,并在假设不成立时捕获异常。这种干净快速的风格通常会有很多try和except语句。这个方法与许多其他语言(如C)常用的LBYL风格形成对比。

LBYL = 先看后跳

这意味着,如果你的方法期待一个“文件”(而你在文档中说明了这一点),那么就假设你接收到的是一个“类似文件的对象”。尝试在这个对象上执行文件操作(比如read()close()),如果出现异常再去捕获它。

EAFP方法的一个主要观点是,你可能接收到的是一个像文件一样工作的对象,换句话说,调用者知道他们在做什么。所以如果你花时间去检查具体的类型,你的代码可能在应该工作的情况下却不工作。现在,责任在于调用者去满足你的“对象契约”;但如果他们处理的不是文件,而是一个内存中的缓冲区(它有与文件相同的方法)呢?或者是一个请求对象(同样,它也有类似文件的方法)。你不可能在代码中检查所有这些变种。

这就是推荐的方法——而不是先进行类型检查的LBYL方法。

所以,如果你的方法文档说明它期待一个文件对象,它应该能与任何“类似文件”的对象一起工作,但当有人传递一个文件路径的字符串时,你的方法应该抛出一个合适的异常。

另外,更重要的是——你应该避免在你的方法中关闭这个对象;因为它可能不是一个“文件”,就像之前解释的那样。不过,如果你确实必须关闭,请确保你的方法文档中明确说明这一点。

下面是一个例子:

def my_method(fobj):
    ''' Writes to fobj, which is any file-like object,
        and returns the object '''
    try:
        fobj.write('The answer is: {}\n'.format(42))
    except (AttributeError, TypeError):
        raise TypeError('Expected file-like object')
    return fobj
2

理想情况下,每次打开文件时都应该使用with语句,这样文件会自动关闭。

with open('filepath', 'r') as f:
    myfunc(f)

otherstuff() # f is now closed

来自文档的说明:

在处理文件对象时,使用with关键字是个好习惯。这样做的好处是,即使在执行过程中出现了错误,文件也会在操作完成后被正确关闭。

撰写回答