3

似乎检查isinstance(..., io.IOBase)是确定对象是否“类似文件”的“正确”方法。

但是,在定义我自己的类文件类时,它似乎不起作用:

import io

class file_like():

    def __init__(self):
        pass

    def write(self, line):
        print("Written:", line)

    def close(self):
        pass

    def flush(self):
        pass

print(isinstance(file_like(), io.IOBase))
# Prints 'False'

我怎样才能让它工作?

4

2 回答 2

1

isinstance(obj, some_class)只是迭代 obj 的继承链,寻找some_class. 因此isinstance(file_like, io.IOBase), 将是错误的,因为您的班级在其祖先file_like中没有。不指定显式父级,因此它仅隐式继承自. 这是唯一的类 - 除了它本身 - 将对带有.io.IOBasefile_likeobjectfile_likefile_likeisinstance()

您正在做的file_like是定义类文件对象预期的方法,而不是从任何特定的“类文件”类继承。这种方法称为duck-typing,它在动态语言中具有许多优点,尽管它在其他语言(例如Ruby)中比Python 更流行。尽管如此,如果您提供的file_like实例遵循鸭子类型,它应该可以工作,前提是您file_like确实“像文件一样嘎嘎”,即行为足够像文件,不会在接收端使用时导致错误。

当然,如果接收端没有遵循鸭子类型,例如尝试isinstance()像您在此处所做的那样检查类型,则此方法将失败。

最后,一个小的文体问题:如果一个类没有显式继承任何东西,就不要把空括号放在一个类上。它们是多余的。

于 2013-12-27T08:00:48.307 回答
1

检查isinstance(something, io.IOBase)只检查是否something是从它派生的一个io.IOBase或一个类的实例 - 所以我不明白你在哪里得到错误的想法,即它是确定一个对象是否是“类文件”的“正确”方法。

另一种方法是使用Abstract Base Class。Python 有许多内置的,但目前还没有一个可以与isinstance(). 但是,您可以使用PEP 3119abc中概述的模块来定义自己的模块。

The Python Module of the Week webiste很好地解释了如何使用该abc模块来做这样的事情。而这个高度评价的答案正确的方法来检测序列参数?显示了定义您自己的 ABC 的类似方法。

为了说明将其应用于您的案例,您可以定义一个像这样的 ABC 及其所有方法抽象 - 从而强制派生类定义所有它们以便被实例化:

from abc import ABCMeta, abstractmethod

class ABCFileLike(metaclass=ABCMeta):
    @abstractmethod
    def __init__(self): pass

    @abstractmethod
    def write(self, line): pass

    @abstractmethod
    def close(self): pass

    @abstractmethod
    def flush(self): pass

然后,您可以从中派生出您自己的具体类,确保提供所有抽象方法的实现。(如果您没有全部定义它们,那么TypeError如果尝试实例化它,则会引发 a。)

class FileLike(ABCFileLike):
    """ Concrete implementation of a file-like class.
        (Meaning all the abstract methods have an implementation.)
    """
    def __init__(self):
        pass

    def write(self, line):
        print("Written:", line)

    def close(self):
        pass

    def flush(self):
        pass

print(isinstance(FileLike(), ABCFileLike))  # -> True

您甚至可以通过向新元类注册现有类来添加它们:

import io

print(isinstance(io.IOBase(), ABCFileLike))  # -> False

ABCFileLike.register(io.IOBase)
print(isinstance(io.IOBase(), ABCFileLike))  # -> True
于 2013-12-27T11:01:13.690 回答