4

假設我們有以下mod.py爲什麼模塊不能成爲上下文管理器(對'with'語句)?

def __enter__(): 
    print("__enter__<") 

def __exit__(*exc): 
    print("__exit__< {0}".format(exc)) 

class cls: 
    def __enter__(self): 
     print("cls.__enter__<") 

    def __exit__(self, *exc): 
     print("cls.__exit__< {0}".format(exc)) 

及以下使用它:

import mod 

with mod: 
    pass 

我得到一個錯誤:

Traceback (most recent call last): 
    File "./test.py", line 3, in <module> 
    with mod: 
AttributeError: __exit__ 

根據文檔的文檔with語句應執行如下(我相信它在第2步失敗,因此截斷列表):

  1. The context expression (the expression given in the with_item) is evaluated to obtain a context manager.
  2. The context manager’s __exit__() is loaded for later use.
  3. The context manager’s __enter__() method is invoked.
  4. etc...

正如我的理解是沒有理由__exit__找不到。有什麼我錯過了,使模塊無法作爲上下文管理器工作?

回答

7

__exit__特殊方法,所以Python查找它在的類型。 module類型沒有這種方法,這就是失敗的原因。

見的Python Special method lookup section數據模型文檔:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

請注意,這適用於所有的專用方法。例如,如果您向模塊添加了__str____repr__函數,則在打印模塊時也不會調用該函數。

Python做到這一點,以確保類型對象可哈希和可表示;如果Python 沒有這樣做,那麼當爲該類定義__hash__方法(因爲該方法預期實例被傳入self)時嘗試將類對象放入字典中將失敗。

2

由於@Martijn Pieters answer中陳述的原因,您不能輕易做到這一點。但有一點額外的工作,它可能是,因爲sys.modules中的值不一定是內置模塊類的實例,它們可以是自定義類的實例,具有上下文管理器所需的特殊方法。

這是應用到你想要做的。鑑於以下mod.py

import sys 

class MyModule(object): 
    def __enter__(self): 
     print("__enter__<") 

    def __exit__(self, *exc): 
     print("__exit__> {0}".format(exc)) 

# replace entry in sys.modules for this module with an instance of MyModule 
_ref = sys.modules[__name__] 
sys.modules[__name__] = MyModule() 

而下面的使用它:

import mod 

with mod: 
    print('running within context') 

威爾產生這樣的輸出:

__enter__< 
running within context 
__exit__> (None, None, None) 

請參閱有關爲什麼需要_ref信息this問題。

+1

在這一點上,你必須問自己*爲什麼你需要甚至這樣做*? –

+0

@Martijn:用一個自定義類的實例替換一個模塊有時是有用的,因爲它允許做一些常規模塊對象(例如控制屬性訪問)無法做到的事情 - 但是我傾向於同意這樣做,因此模塊可以被用作上下文管理器可能有點兒難。 – martineau

+0

是的,我知道有用例,我不確定這是否是其中之一。 –

相關問題