如何区分像文件的对象和像obj这样的文件路径

2024-05-16 11:21:24 发布

您现在位置:Python中文网/ 问答频道 /正文

小结:

对于各种各样的函数,能够传入两种类型的对象是非常有用的:表示路径的对象(通常是字符串)和表示某种类型流的对象(通常是从IOBase派生的对象,但并不总是这样)。这种不同的函数如何区分这两类对象,以便它们能够得到适当的处理?在


假设我有一个函数打算从某种对象文件生成器方法中写入文件:

spiff = MySpiffy()

def spiffy_file_makerA(spiffy_obj, file):
    file_str = '\n'.join(spiffy_obj.gen_file()) 
    file.write(file_str)

with open('spiff.out', 'x') as f:
    spiffy_file_makerA(spiff, f)
    ...do other stuff with f...

这很管用。是啊。但我不想担心首先打开文件或传递流,至少有时。。。因此,我可以使用类似文件路径的对象而不是类似文件的对象进行重构,并使用return语句:

^{pr2}$

但是现在我有了一个想法,那就是使用第三个函数来组合其他两个版本,这取决于file是文件类型还是文件路径类型,但是将f目标文件类对象返回给上下文管理器。这样我就可以写这样的代码:

with  spiffy_file_makerAB(spiffy_obj, file_path_like, mode = 'x') as f:
    ...do other stuff with f...

…但也像这样:

file_like_obj = get_some_socket_or_stream()

with spiffy_file_makerAB(spiffy_obj, file_like_obj, mode = 'x'):
    ...do other stuff with file_like_obj...
    # file_like_obj stream closes when context manager exits 
    # unless `closefd=False` 

请注意,这将需要与上面提供的简化版本稍有不同的内容。在

尽管我可能会尝试,但我还没有找到一个明显的方法来做到这一点,而且我发现的方法似乎很做作,只是以后可能出现问题。例如:

def spiffy_file_makerAB(spiffy_obj, file, mode, *, closefd=True):
    try: 
        # file-like (use the file descriptor to open)
        result_f = open(file.fileno(), mode, closefd=closefd)
    except TypeError: 
        # file-path-like
        result_f = open(file, mode)
    finally: 
        file_str = '\n'.join(spiffy_obj.gen_file()) 
        result_f.write(file_str)
        return result_f

有什么更好的方法吗?我是不是太离谱了,需要用完全不同的方式来处理这个问题?在


Tags: 文件对象方法函数obj类型modewith
3条回答

我的建议是把^{} objects传来传去。您可以简单地将^{}^{}添加到这些对象。在

另外,您必须检查file变量的类型(这是python中多态性的实现方式):

from io import IOBase

def some_function(file)
    if isinstance(file, IOBase):
        file.write(...)
    else:
        with open(file, 'w') as file_handler:
            file_handler.write(...)

(我希望^{}是要检查的最基本的类…)。你必须抓住可能的例外。在

对我来说,这是一个固执己见的答案,检查文件类对象的属性以执行所需的操作是一种确定对象类型的python方法,因为这是pythonic的本质duck tests/duck-typing

Duck typing is heavily used in Python, with the canonical example being file-like classes (for example, cStringIO allows a Python string to be treated as a file).

Or from the python docs’ definition of duck-typing

A programming style which does not look at an object’s type to determine if it has the right interface; instead, the method or attribute is simply called or used (“If it looks like a duck and quacks like a duck, it must be a duck.”) By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.

如果您非常强烈地认为仅仅检查接口的适用性是不够的,那么您可以对basestring或{}进行反向测试,以测试所提供的对象是否是类似路径的。根据您的python版本,测试会有所不同。在

is_file_like = not isinstance(fp, basestring) # python 2
is_file_like = not isinstance(fp, str) # python 3

在任何情况下,对于您的上下文管理器,我会继续创建一个完整的对象,如下面所示,以便包装您所寻找的功能。在

^{2}$

然后像这样使用它:

with SpiffyContextGuard(obj, 'hamlet.txt', 'w', True) as f:
    f.write('Oh that this too too sullied flesh\n')

fp = open('hamlet.txt', 'a')
with SpiffyContextGuard(obj, fp, 'a', False) as f:
    f.write('Would melt, thaw, resolve itself into a dew\n')

with SpiffyContextGuard(obj, fp, 'a', True) as f:
    f.write('Or that the everlasting had not fixed his canon\n')

如果要使用try/catch语义检查类型适合性,还可以将要公开的文件操作包装在上下文保护器上:

class SpiffyContextGuard(object):
    def __init__(self, spiffy_obj, file, mode, closefd=True):
        self.spiffy_obj = spiffy_obj
        self.fp = self.file_or_path = file 
        self.mode = mode
        self.closefd = closefd

    def seek(self, offset, *args):
        try:
            self.fp.seek(offset, *args)
        except AttributeError:
            self.fp = open(self.file_or_path, mode)
            self.fp.seek(offset, *args)

    # define wrappers for write, read, etc., as well

    def __enter__(self):
        return self

    def __exit__(self, type_, value, traceback):
        generated = '\n'.join(self.spiffy_obj.gen_file())
        self.write(generated)
        if self.closefd:
            self.fp.__exit__()

可能不是你要找的答案,但从口味的角度来看,我认为最好有只做一件事的函数。用这种方法来推理比较容易。在

我只有两个函数:spiffy_file_makerA(spiffy_obj, file),它处理第一个情况;一个方便的函数,它包装spiffy_file_makerA并为您创建一个文件。在

相关问题 更多 >