Динамическое изменение определения модуля и класса без непосредственного изменения кода модуля - PullRequest
1 голос
/ 27 февраля 2020

Я даже не уверен, возможно ли следующее. Мой вопрос легко описывается с помощью следующего кода:

Мой модуль определен как

asdf = 5
class MyClass:
    def myfct(self):
        return asdf

У меня есть модуль, который определяет сложный класс. Он использует определение модуля, в котором он находится. Здесь я называю его asdf. На самом деле, asdf само по себе является определением класса, для простоты у меня здесь просто есть целое число.

Я хочу удалить asdf из модуля в качестве члена, но класс все еще есть Работа. Поэтому перед удалением из модуля я сначала записываю его в определение MyClass динамически после загрузки модуля, затем удаляю его из модуля.

import mymodule

MyClass.asdf = asdf
del sys.modules['mymodule'].asdf

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

myobj = mymodule.MyClass()
myobj.myfct()

но, конечно, это выдает ошибку

NameError: имя 'asdf' не определено

Как сделать класс работать, не изменяя код класса, а именно, не изменяя return asdf на return self.asdf и все же удаляя asdf из модуля (и не добавляя другие атрибуты модуля). Тем не менее, мне разрешено динамически изменять функцию после import mymodule.

Имейте в виду, что определение функции очень сложное, поэтому что-то вроде ее повторной записи с exec ({ ссылка }) - это не вариант.

Одна из моих идей заключается в том, чтобы динамически изменять сигнатуру метода так, чтобы он получал необязательный аргумент asdf. Таким образом, код внутри будет использовать эту переменную, но я не знаю, как это сделать. Возможно, полезными могут быть байтплей / метаклассы:

1 Ответ

0 голосов
/ 27 февраля 2020

Следующий ответ работает, но он полусилен. В основном мы сохраняем оригинальный метод в фиктивной переменной myfct_original. Затем мы переопределяем нашу функцию так, что переменная MyClass.asdf копируется в модуль, применяется оригинальный метод, а переменные удаляются из модуля - поэтому на короткое время она доступна в модуле.

asdf = 5
class MyClass:
    def myfct(self, test=1):
        return asdf

MyClass.asdf = asdf
MyClass.myfct_original = MyClass.myfct

del sys.modules[__name__].asdf
delattr(MyClass, 'myfct')

def newmyfct(self, *args, **kwargs):
    setattr(sys.modules[__name__], 'asdf', MyClass.asdf)
    result = MyClass.myfct_original(self, *args, **kwargs)
    del sys.modules[__name__].asdf
    return result

setattr(MyClass, 'myfct', newmyfct)

myobj = MyClass()
myobj.myfct()

Может быть, кто-то может сделать лучше (см. Конец вопроса).

...