Как заставить файловый класс работать с isinstance(cls, io.IOBase)?

Кажется, что проверка 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'

Как я могу заставить его работать?


person kiri    schedule 27.12.2013    source источник


Ответы (2)


Проверка isinstance(something, io.IOBase) проверяет только, является ли something экземпляром io.IOBase или производным от него классом, поэтому я не понимаю, откуда у вас ошибочное представление о том, что это «правильный» способ определить, является ли объект «файлоподобным».

Другой способ сделать это — использовать Абстрактный базовый класс. Python имеет ряд встроенных, но в настоящее время нет файла для файлов, который можно было бы использовать с isinstance(). Однако вы можете определить свой собственный с помощью модуля abc, как описано в PEP 3119< /а>.

На веб-сайте Модуль недели Python есть хорошее объяснение использования модуля 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, если будут предприняты какие-либо попытки создать его экземпляр.)

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
person martineau    schedule 27.12.2013

isinstance(obj, some_class) просто повторяет цепочку наследования obj в поисках some_class. Таким образом, isinstance(file_like, io.IOBase) будет ложным, так как ваш класс file_like не имеет io.IOBase в своем происхождении. file_like не указывает явного родителя, поэтому он неявно наследуется только от object. Это единственный класс, кроме самого file_like, который даст положительный результат на экземпляр file_like с isinstance().

То, что вы делаете в file_like, определяет методы, ожидаемые для файлового объекта, не наследуя при этом от какого-либо конкретного "файлоподобного" класса. Этот подход называется утиным вводом, и он имеет много достоинств в динамических языках, хотя он более популярен. в других (например, Ruby), чем Python. Тем не менее, если что-то, что вы предоставляете своему экземпляру file_like, следует за утиным вводом, это должно работать, при условии, что ваш file_like на самом деле «крякает, как файл», т.е. ведет себя достаточно как файл, чтобы не вызывать ошибок при использовании на принимающей стороне. .

Конечно, если принимающая сторона не следует методу утиного ввода, например, пытается проверить типы по isinstance(), как вы делаете здесь, этот подход потерпит неудачу.

Наконец, небольшая стилистическая ниточка: не помещайте пустые скобки в класс, если он ничего не наследует явно. Они избыточны.

person Dun Peal    schedule 27.12.2013
comment
Спасибо за Ваш ответ. Это сняло многие мои вопросы. Также спасибо за примечание о стиле, я считаю, что это перенос функций, для которых требуются скобки. я возьму это на заметку - person kiri; 27.12.2013