如何使用“isinstance(cls,io.IOBase)”来创建类文件类?

时间:2013-12-27 03:07:52

标签: python file class isinstance

检查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'

我怎样才能让它发挥作用?

2 个答案:

答案 0 :(得分:1)

isinstance(obj, some_class)只是迭代obj的继承链,寻找some_class。因此,isinstance(file_like, io.IOBase)将是假的,因为您的file_like类在其祖先中没有io.IOBasefile_like未指定显式父级,因此它仅隐式继承自object。除了file_like本身之外,这是唯一一个对file_like实例isinstance()测试为正的课程。

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

当然,如果接收端不遵循鸭子类型,例如尝试按isinstance()检查类型,则此方法将失败。

最后,一个小小的风格:如果它没有明确地继承任何东西,就不要把空的parens放在一个类上。它们是多余的。

答案 1 :(得分:1)

检查isinstance(something, io.IOBase)仅检查somethingio.IOBase的实例还是从中派生的类 - 所以我不明白你在哪里错误地认为它是“纠正“确定对象是否像文件一样”的方法。

另一种方法是使用抽象基类。 Python有一些built-in ones,但目前没有一个可用于isinstance()的“类文件”。但是,您可以使用PEP 3119中列出的abc模块来定义自己的模块。

The Python Module of the Week网站管理员good explanation使用abc模块执行此操作。这个问题answer的高评价Correct way to detect sequence parameter?显示了定义自己的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。)

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