2017-01-15 2 views
0

Экспериментируя с заплатами менеджер контекста во время выполнения, я заметил, что следующий код не ведет себя, как я ожидал:Почему `with` использует` __enter__` класса, а не объект?

class B: 
    def __enter__(self): 
     print('normal') 
    def __exit__(self, *stuff): 
     pass 

    def modify(self, x): 
     def other(self): 
      print('not normal: ', x) 
     self.__enter__ = other.__get__(self, type(self)) 

def main(): 
    b = B() 
    b.__enter__() 
    b.modify('hi') 
    b.__enter__() 
    with b: 
     print('in with') 
    b.__enter__() 

if __name__ == '__main__': 
    main() 

Выполненного, это печатает:

normal 
not normal: hi 
normal 
in with 
not normal: hi 

В то время как первая часть main , с явными обращениями к __enter__, ведет себя как и ожидалось (метод - правильно изменен), это означает, что это проигнорирует with.

После некоторого поиска я обнаружил, что согласно PEP 343 показан пример перевода, который объясняет поведение; а именно перевод with mgr: ... внутренне использует что-то вроде

type(mgr).__enter__(mgr) 

вместо прямого вызова метода, как я делал выше.

И я задавался вопросом, почему это сделано. Это просто для того, чтобы люди, подобные мне, не возились, или есть более глубокая причина?

ответ

0

Существует то, что называется special method lookup, определенное в описании языка. В принципе, этот вид поиска (с использованием type(obj).__method__(obj)) происходит для всех «магических методов»; две причины:

  • Оптимизация производительности возможны
  • Есть случаи с работы только правильно с этим вариантом, а именно, когда специальный поиск сдвинуто типа (например, int.__hash__ быть не то же самое, как type(int).__hash__, который мы обычно очень хотим)
+0

Возможно, я не полностью понял второй аргумент. Так что улучши его, если хочешь. – phg

Смежные вопросы