Присвоение функции атрибуту объекта
Основываясь на моем понимании модели данных Python и, в частности, подразделе "Методы экземпляров", всякий раз, когда вы читаете атрибут, значение которого имеет тип "пользовательская функция", некоторые магические удары, и вы получаете метод связанного экземпляра вместо фактической, оригинальной функции. Эта магия - это то, почему вы не передаете явно параметр self
при вызове метода.
Но тогда я ожидал бы, что сможет заменить метод объекта на функцию с той же сигнатурой:
class Scriptable:
def __init__(self, script = None):
if script is not None:
self.script = script # replace the method
def script(self):
print("greetings from the default script")
>>> scriptable = Scriptable()
>>> scriptable.script()
greetings from the default script
>>> def my_script(self):
... print("greetings from my custom script")
...
>>> scriptable = Scriptable(my_script)
>>> scriptable.script()
Traceback (most recent call last):
...
TypeError: script() takes exactly 1 positional argument (0 given)
Я создаю экземпляр Scriptable
и устанавливаю его атрибут script
для пользовательской функции с единственным параметром, точно так же, как в классе. Поэтому, когда я читаю атрибут scriptable.script
, я ожидал бы, что магия начнется и даст мне связанный метод экземпляра, который не будет иметь никаких параметров (как и я, когда я не заменил script
). Вместо этого он, кажется, возвращает ту же самую функцию, в которую я прошел, параметр self
и все. Магия, связывающая метод, не происходит.
Почему маска, связывающая метод, работает, когда я определяю метод внутри объявления класса, но не когда я назначаю атрибут? Что заставляет Python относиться к этим ситуациям по-разному?
Я использую Python3, если это имеет значение.
Ответы
Ответ 1
Вот как вы это делаете:
import types
class Scriptable:
def __init__(self, script = None):
if script is not None:
self.script = types.MethodType(script, self) # replace the method
def script(self):
print("greetings from the default script")
Как отмечалось в комментариях ba__friend, методы сохраняются в объекте class
. Дескриптор объекта класса возвращает функции как связанные методы при доступе к атрибуту из экземпляра.
Когда вы назначаете функцию instance
, ничего не происходит, происходит специальное событие, поэтому вы должны сами обернуть функцию.
Ответ 2
Благодаря Alex Martelli answer вот еще одна версия:
class Scriptable:
def script(self):
print(self)
print("greetings from the default script")
def another_script(self):
print(self)
print("greetings from the another script")
s = Scriptable()
s.script()
# monkey patching:
s.script = another_script.__get__(s, Scriptable)
s.script()
Ответ 3
Посмотрите на это:
>>> scriptable = Scriptable()
>>> scriptable.script
<bound method Scriptable.script of <__main__.Scriptable instance at 0x01209DA0>>
>>> scriptable = Scriptable(my_script)
>>> scriptable.script
<function my_script at 0x00CF9730>
Заявление self.script = script
создает только атрибут объекта класса без каких-либо "магии" с ним.
Утверждение def script(self):
внутри определения класса создает дескриптор - специальный объект, который фактически управляет всем содержимым с параметром self
.
Подробнее о дескрипторах в Python вы можете прочитать в упомянутой ссылке на модель данных: implementing-descriptors.
Еще одна замечательная статья о дескрипторах на Python от Raymond Hettinger:
Руководство для дескрипторов.
Ответ 4
Я не могу ответить на ваш вопрос , почему он работает так, вам нужно спросить Guido van Rossum, но я могу дать вам возможное обходное решение:
class Scriptable:
def __init__(self, script = None):
self._script = script # replace the method
def script(self):
if self._script: return self._script(self)
return self._defaultscript()
def _defaultscript(self):
print("greetings from the default script")