Перезапись __class__ вызывает TypeError - PullRequest
0 голосов
/ 02 сентября 2018

Я пытаюсь расширить функциональность класса модуля, написанного на C, путем его извлечения и перезаписи / добавления определенных методов *.

Проблема в том, что его модуль создает экземпляры класса, который я пытаюсь расширить в разных местах. Поэтому мне нужно создать метод приведения, чтобы привести базовый класс, предоставленный модулем, к моему собственному производному классу с дополнительными функциями.

Вот что я пробовал:

class Derived(Base):
    @classmethod
    def cast(cls, obj: Base):
        obj.__class__ = cls

Этот метод работает с иерархиями классов, которые я создал сам - однако он не работает с модулем, который я использую, выдавая следующее исключение:

TypeError: назначение __class__ поддерживается только для типов кучи или подклассов ModuleType

Мне трудно найти официальную информацию об этом исключении. Любая информация помогает, я бы даже принял хакерские решения, если они чистые и полностью имитируют поведение, которое я ищу.

* Класс, который я пытаюсь расширить, - это Surface внутри пакета pygame.

1 Ответ

0 голосов
/ 02 сентября 2018

Атрибут __class__ всегда был ограничен в том, что является приемлемым, и классы Python могут быть намного более гибкими, чем типы, определенные в C.

Например, раздел __slots__ в документации по датамоделю гласит:

__class__ назначение работает, только если оба класса имеют одинаковые __slots__.

и тот же документ вызывает атрибут instance.__class__ только для чтения :

Реализация добавляет несколько специальных атрибутов только для чтения к нескольким типам объектов, где они актуальны.

Таким образом, __class__ фактически не предназначался для записи, но это было в течение длительного времени и является очень полезным свойством.

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

Это было , кратко поддерживаемое в сборках-кандидатах выпуска Python 3.5, чтобы разрешить установку __class__ на модулях , но это вызвало обратный эффект, когда было обнаружено, что можно изменить тип интернированных неизменных значений:

class MyInt(int):
    # lets make ints mutable!

(1).__class__ = MyInt

1 является внутренним значением , поэтому теперь все использование целого числа 1 везде в программе Python было изменено. Не то, что вам нужно, особенно если вы повторно используете память интерпретатора в нескольких процессах, как это делает Google App Engine! См. Issue # 24912 для ознакомления с информацией о низком уровне.

Но именно поэтому экземпляры модуля специально упоминаются в исключении, см. Настройка доступа к атрибуту модуля .

Вам придется найти другой путь для решения вашей проблемы. Например, возможно, ваша конкретная проблема может быть решена вместо этого с помощью обтекания экземпляров во что-то, что использует __getattr__ для передачи атрибутов обернутому экземпляру.

...