簡體   English   中英

為什么當你設置一個不存在的類的屬性時,python 不會拋出異常

[英]Why doesn't python throw an exception when you set an attribute of a class that doesn't exist

我一直試圖調試我的代碼,結果發現這是我錯誤的原因,很難找到。 一個簡單的例子來演示我在說什么:

class Test():
  def __init__(self):
    self.a = 0

x = Test()
x.b = 2
print(x.a)
print(x.b)

此代碼不會拋出任何錯誤。 事實上,它會成功打印出 0 和 2。所以即使 Test 不包含實例變量b ,它仍然會在我分配它時創建它。 如果我初始化第二個測試變量

y = Test()
print(y.b)

它會按預期拋出錯誤。

那么,為什么首先存在此功能,以便能夠在類的實例上創建新屬性? 幕后發生了什么來實現這種行為? 有什么方法可以禁用這種行為,或者至少在編程時以某種方式捕獲它?

標准 Python 類將實例屬性存儲在引擎蓋下的dict (名為__dict__ )。 沒有特殊的規則讓它在__init__內的賦值和其他任何地方的賦值之間做出有意義的區分; __init__之前它是一個空命名空間, __init__可以添加到該命名空間,但其他任何人也可以(現代 CPython 有一些優化來減少內存使用,如果你只在__init__創建相同的屬性集並且不再創建更多,但它會回退如果您違反該規則以保留現有行為,則轉移到舊的、內存密集型的存儲)。

這有時很方便,例如,當類上的另一個方法僅在調用該方法並且需要計算時才想要懶惰地計算屬性。 它只是讓屬性未定義,在需要它的地方,它捕獲AttributeError並計算(並緩存)當時的值。

這是高級腳本類語言中非常常見的設計(除了 Python,其他默認允許這樣做的語言包括 Perl、Ruby 和 JavaScript,僅舉幾例),因為它們對類實例的基本定義只是“一個字符串鍵控dict ,上面有一些魔法”。 雖然他們可以制定規則使事情變得更加嚴格,但這樣做並沒有什么好處,所以他們只是讓事情盡可能靈活。

正如您所注意到的,像這樣的屬性自動激活可能會變得混亂,而且有時是不可取的。 如果這是一個問題,並且您想預先定義一組可以定義的受限屬性,只需使用有效屬性的字符串名稱在類本身上定義__slots__ 這將用底層屬性數組中連續分配的插槽替換屬性的底層dict (插槽名稱成為知道如何唯一訪問每個插槽的描述符)。 它通過避免每個實例相對浪費的dict節省內存,並且它會阻止創建新屬性。 對於您的情況,您只需執行以下操作:

class Test():
  __slots__ = 'a',
  def __init__(self):
      self.a = 0

並嘗試分配給b屬性(在類內部或外部)將因:

AttributeError: 'Test' object has no attribute 'b'

請注意,這也禁止對類的實例進行弱引用; 如果你想允許,你必須明確地將'__weakref__'列為類中的一個插槽。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM