Как работает автоматический карринг с self при назначении метода в var в Python 3? - PullRequest
2 голосов
/ 01 декабря 2011

Я пишу контекстный менеджер, чтобы обернуть функцию builtins.print.И это прекрасно работает.Однако я столкнулся с поведением Python, которое не могу обернуть головой:

Всякий раз, когда метод класса назначается в переменную для последующего вызова, первый аргумент «self» также автоматически сохраняется ииспользуется для всех последующих вызовов.

Вот пример, иллюстрирующий точку:

import functools

class Wrapper:
    def wrap(self):
        return self._wrapped   #functools.partial(self._wrapped, self)

    def _wrapped(self, *args, **kwargs):
        print('WRAPPED!', *args, **kwargs)
        print('..knows about self:', self)

wrapped = Wrapper().wrap()
wrapped('expect self here', 'but', 'get', 'all', 'output')

Вывод:

WRAPPED! expect self here but get all output
..knows about self: <__main__.Wrapper object at 0x2aaaab2d9f50>

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

wrapped = Wrapper._wrapped
wrapped('expect self here', 'but', 'get', 'all', 'output')

И теперь я получаю то, что сначала ожидал:

WRAPPED! but get all output
..knows about self: expect self here

В своем исходном коде я использовалfunctools.partial до карри - в self, но затем обнаружил, что это даже не требуется.

Мне нравится текущее поведение, но я пока не понимаю аргументацию относительно последовательности и "быть очевидным ".

Я работаю с Python 3.1.2 здесь.

этот вопрос с ответом на использование types.MethodType связано?Поиск здесь и в сети в основном дает основную информацию о каррировании / частичных вызовах функций и упаковке / распаковке списков аргументов.Может быть, я использовал неадекватные поисковые термины (например, «методы каррирования в python»).

Может кто-нибудь пролить свет на это поведение?

Это то же самое в Py2 и Py3?

Ответы [ 2 ]

1 голос
/ 01 декабря 2011

Когда вызывается метод экземпляра, этот вызов автоматически передается в экземпляре в качестве первого параметра. Вот что здесь происходит.

Когда вы делаете

return self._wrapped

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

«Хранение» этого - просто методы экземпляра знают, к какому экземпляру они принадлежат. Если вы не хотите, чтобы такое поведение возвращало метод несвязанного класса.

class Wrapper:
    def wrap(self):
        return Wrapper._wrapped

    def _wrapped(self, *args, **kwargs):
        print('WRAPPED!', *args, **kwargs)
        print('..knows about self:', self)
1 голос
/ 01 декабря 2011

Всякий раз, когда вы берете метод из экземпляра (как в return self._wrapped), тогда self запоминается.

Всякий раз, когда вы берете метод из класса (как в Wrapper._wrapped), тогда self не запоминается (не может быть запомнен).

Например, попробуйте следующее:

upper = 'hello'.upper
print(upper())

upper = str.upper
print(upper())

Вы увидите HELLO, затем TypeError: descriptor 'upper' of 'str' object needs an argument

...