我有以下代碼:
class Test。
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'enter {self.name}')
def __exit__(self, exctype, excinst, exctb) -> bool:
print(f'exiting {self.name}'/span>)
return True
with Test('first) as test:
print(f'in {test.name}')
test = Test('second')
with test:
print(f'in {test.name}')
運行它產生的輸出如下:
首先進入
退出第一個
進入第二個
進入第二個
退出第二個
但我期望它產生:
進入第一個
在第一個
退出第一個
進入第二個
在第二個
退出第二個
為什么我的第一個例子內的代碼沒有被呼叫?
uj5u.com熱心網友回復:
__enter__方法應該回傳背景關系物件。with ... as ...使用__enter__的回傳值來決定給你什么物件。由于你的__enter__沒有回傳任何東西,它隱含地回傳None,所以test是None。
with Test('first') as test:
print(f'in {test.name}')
test = Test('second')
with test:
print(f'in {test.name}')
所以test是沒有的。那么test.name就是一個錯誤。這個錯誤被提出,所以Test('first').__exit__被呼叫。__exit__ 回傳 True,這表明錯誤已經被處理了 (基本上,你的 __exit__ 就像一個 except 塊,所以代碼在第一個 with 塊之后繼續,因為你告訴 Python 一切順利。
考慮一下
def __enter__(self) 。
print(f'enter {self.name}')
return self
你也可以考慮不從__exit__回傳True,除非你真的打算無條件地抑制所有塊中的錯誤(并充分理解抑制其他程式員的錯誤,以及KeyboardInterrupt,StopIteration和各種系統信號的后果)
uj5u.com熱心網友回復:
問題是,你的__enter__方法回傳None。因此,test被分配為None。
然后你試圖訪問(None).name,這引發了一個錯誤。由于你的__exit__方法總是回傳True,它將抑制任何錯誤。根據檔案:
從這個方法回傳一個真值將導致with陳述句 抑制例外,并繼續執行緊隨其后的陳述句 緊隨其后的陳述句繼續執行。
uj5u.com熱心網友回復:
我相信這種行為是因為__enter__必須回傳一些將被操作的東西,在這種情況下,將以test的名字被訪問。通過將__enter__改為以下內容
def __enter__(self)。
print(f "enter {self.name}")
return self
我們得到了預期的行為。
我們得到了預期的行為。
uj5u.com熱心網友回復:
原因是第一種情況和第二種情況的做法不一樣。
在第一種:
- 物件被創建,它呼叫
__init__; - 然后
with呼叫__enter__; - 然后
as將__enter__的結果存入test。
- 由于
__enter__沒有一個回傳值,test是None。
在second:
- 物件被創建,它呼叫
__init__; - 然后分配給
test; - 然后
with呼叫__enter__; - 但是沒有對
__enter__的結果進行任何處理; - 因此,
test繼續參考最初創建的物件。
在這兩種情況下,__exit__都是為with正在處理的物件而呼叫的,所以你看到正確的標簽被列印出來;只是在first中,test的識別符號沒有系結到同一個物件上。
NB __enter__不一定要回傳self。它可能會回傳其他完全不同的東西,例如,你可能會打開一個檔案并使__enter__回傳流,而__exit__可能會關閉它。如果給定__enter__應該回傳self,那將是多余的,可以直接隱含。
uj5u.com熱心網友回復:
解釋:
__enter__給出None作為輸出,因為沒有return,因此它將直接觸發__exit__,因為None沒有屬性name,例如:
>>> None.name
回溯(最近一次呼叫)。
檔案"<pyshell#0>",行1,in <module>
None.__name__
AttributeError: 'NoneType' object沒有屬性'name'。
>>>
如果你把它設定為呼叫__class__.__name__(None物件有該屬性,從而得到NoneType),你可以很容易地找到問題所在:
class Test。
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'enter {self.name}')
def __exit__(self, exctype, excinst, exctb) -> bool:
print(f'exiting {self.name}'/span>)
return True
with Test('first) as test:
print(f'in {test.__class__.__name__}')
test = Test('second')
with test。
print(f'in {test.__class__.__name__}')
輸出:
先輸入
in NoneType
退出第一個
進入第二個
in Test
退出第二個
正如你所看到的,它說in NoneType,不回傳任何值就是這個原因。在很多情況下,__enter__不需要回傳,但是在這種情況下,Test類需要它回傳。
解決方案:
解決方案是保留Test實體,這樣它就可以在背景關系管理器__enter__結果之后呼叫回傳的self的名稱。到目前為止,__enter__的結果是None,因此None.name屬性并不存在。所以如果你回傳self,test.name屬性就會存在。
解決方案是在__enter__魔法方法實作中回傳self:
...
def __enter__(self):
print(f'enter {self.name}')
return self
...
完整的代碼:
class Test。
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'enter {self.name}')
return self
def __exit__(self, exctype, excinst, exctb) -> bool。
print(f'exiting {self.name}'/span>)
return True
with Test('first) as test:
print(f'in {test.name}')
test = Test('second')
with test:
print(f'in {test.name}')
輸出:
先輸入
in first
退出第一項
進入第二個
in second
退出第二個
我給出的其他答案沒有給出的額外資訊是對__enter__方法實作給出None的更具體證明。我還展示了一個例子。
uj5u.com熱心網友回復:
一個with陳述句不會創建一個作用域(就像if、for和while也不會創建一個作用域)。
因此,Python 將分析代碼并看到你在 with 陳述句中做了一個賦值,因此這將使變數成為區域的 (在真正的范圍內)。
在Python中,變數不需要在所有的代碼路徑中進行初始化:作為一個程式員,你有責任在使用一個變數之前確保它被賦值。這可以使代碼更短:例如,你肯定知道一個串列至少包含一個元素,那么你可以在for loop中賦值。在Java中,在for loop中的賦值被認為是不安全的(因為有可能回圈的主體從未被執行)。
在 with 陳述句只用于背景關系管理的目的。它強制 (通過語法) 你在
標籤: 上一篇:React訪問檔案資料
with范圍之前初始化是比較安全的,因為在with陳述句之后,我們可以安全地假定該變數存在。另一方面,如果變數應該在 with 陳述句中被賦值,在 with 陳述句之前不初始化它實際上會導致額外的檢查。如果在 with 陳述句中跳過了賦值,Python 會出錯。
with 中打開的背景關系在縮進結束時被關閉。
