Сделать мой пользовательский список Класс возвращает «список» как тип - PullRequest
1 голос
/ 23 января 2020

Я пишу собственный класс, который расширяет списки python по умолчанию, добавляя некоторые новые функции, такие как перемешивание, добавление, умножение и т. Д. c. Код выглядит примерно так:

class xlist(list):
    def __init__(self, original_list: list):
        self._olist = original_list

    def sumall(self) -> int:
        sum = 0
        for e in self._olist:
            sum += e
        return sum
...

Но при выполнении некоторых вычислений мне нужно было получить тип экземпляра xlist. Я хочу сделать что-то вроде этого:

>>> from xlist import xlist
>>> x = xlist([1, 2, 3])
>>> type(x)

Когда я делаю это, я получаю <class 'xlist.xlist'>, но я хочу, чтобы он возвратил list.

Я немного озадачен метаклассами, которые, кажется, способны решить проблему.

Любая помощь?

Ответы [ 2 ]

2 голосов
/ 23 января 2020

Для Python есть два способа проверить класс объекта - один вызывает type, а другой проверяет слот __class__. В большинстве случаев оба возвращают одно и то же, но можно изменить класс (например, путем настройки доступа к атрибутам в метаклассе), чтобы __class__ получило "l ie", а Python код с использованием myobject.__class__ получит " ложная информация.

Однако под слотом "true" __class__ в объекте type всегда будет содержаться ссылка на реальный тип - и это не может быть подделано. Любое расширение C и, возможно, даже несколько расширений Python, а возврат к type(myobject) сам увидит реальный класс.

Изменение содержимого этого слота фактически изменяет класс вашего экземпляра. Это возможно из чистого Python с простой атрибуцией = - но в этом назначении предусмотрены средства защиты, чтобы гарантировать, что оно выполняется только для типов, имеющих совместимый макет памяти. Принудительное изменение его на несовместимый тип (через расширение или ctypes) приведет к тому, что ваша Python среда выполнения перейдет в segfault.

Все это говорит о том, что у пользователя * ie нет никаких оснований относиться к вашему классу. вашего класса - они должны быть в состоянии «увидеть», что объект, который они содержат, является xlist, а не list, и что xlist s также являются list объектами из-за наследования. Фальсификация этой информации была бы довольно плохой практикой. С другой стороны, в самом Python stdlib есть несколько вызовов, которые требуют, чтобы базовый объект действительно был list и не принимал подтипы (как известно, сериализация Python json.dumps). Этот вызов имеет собственный путь к коду и не будет обманут, настроив доступ к __class__. Однако тот же вызов также имеет только кодовый путь Python, который запускается установкой некоторых необязательных аргументов (например, передачей идент = 4 в вызове). Если это то, чего вы пытаетесь достичь (обмануть какой-то код, требующий строгого списка), вам придется это проверить, и если это код Python, это выполнимо. В указанном c случае json.dump вам лучше установить кодировщик, чтобы использовать менее строгую проверку, чем фальсифицировать ваш объект - потому что я думаю, что код использует type для проверки.

Итак, с учетом всего вышесказанного, «трюк метакласса» для фальсификации возврата .__class__ может быть таким простым:



class xlist(list, metaclass=Meta):
    def __init__(self, original_list: list):
        self._olist = original_list
    def sumall(self) -> int:
        sum = 0
        for e in self._olist:
            sum += e
        return sum
    @property
    def __class__(self):
         return list

2 голосов
/ 23 января 2020

Почему вы ожидаете, что type(x) вернет list, если вы действительно создаете xlist? Ваш xlist наследуется от list, поэтому каждый xlist объект является экземпляром list, поскольку он наследует от него все свое поведение (и расширяется за счет добавления некоторой новой функциональности).

Обратите внимание:

x = xlist([1, 2, 3])
isinstance(x, list)

возвращает True. Вы также можете взглянуть на разницу между type() и isinstance()

...