Подтвердить что ты не робот

Присвоение функции атрибуту объекта

Основываясь на моем понимании модели данных 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, если это имеет значение.

4b9b3361

Ответ 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")