[英]Explain __dict__ attribute
我真的很困惑__dict__
屬性。 我搜索了很多,但仍然不確定 output。
如果在 object、class 或 function 中使用,有人可以從零開始解釋該屬性的使用嗎?
基本上它包含描述相關對象的所有屬性。 它可用於更改或讀取屬性。 引用__dict__
的文檔
用於存儲對象(可寫)屬性的字典或其他映射對象。
請記住,在 Python 中一切都是對象。 當我說一切時,我的意思是所有的東西,比如函數、類、對象等(是的,你沒看錯,類。類也是對象)。 例如:
def func():
pass
func.temp = 1
print(func.__dict__)
class TempClass:
a = 1
def temp_function(self):
pass
print(TempClass.__dict__)
會輸出
{'temp': 1}
{'__module__': '__main__',
'a': 1,
'temp_function': <function TempClass.temp_function at 0x10a3a2950>,
'__dict__': <attribute '__dict__' of 'TempClass' objects>,
'__weakref__': <attribute '__weakref__' of 'TempClass' objects>,
'__doc__': None}
__dict__可以得到一個object中的實例變量(數據屬性)作為一個字典。
所以,如果下面有Person
class:
class Person:
x1 = "Hello"
x2 = "World"
def __init__(self, name, age):
self.name = name
self.age = age
def test1(self):
pass
@classmethod
def test2(cls):
pass
@staticmethod
def test3():
pass
obj = Person("John", 27)
print(obj.__dict__) # Here
__dict__獲取name
和age
及其在字典中的值,如下所示:
{'name': 'John', 'age': 27}
並且,如果在實例化后添加新的實例變量gender
如下所示:
# ...
obj = Person("John", 27)
obj.gender = "Male" # Here
print(obj.__dict__)
__dict__獲取name
、 age
和gender
及其在字典中的值,如下所示:
{'name': 'John', 'age': 27, 'gender': 'Male'}
此外,如果使用dir()如下所示:
# ...
obj = Person("John", 27)
obj.gender = "Male"
print(dir(obj)) # Here
我們可以得到一個 object 中的所有列表,如下所示:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'age', 'gender', 'name', 'test1', 'test2', 'test3', 'x1', 'x2']
而且,據我研究和問這個問題,沒有函數可以只獲取 static 或特殊變量或正常,class、static 或 Python 中的 object 中的特殊方法。
Python 文檔將__dict__
定義為:
字典或其他映射 object 用於存儲對象的(可寫)屬性。
然而,這個定義有點模糊,導致__dict__
的很多錯誤用法。
事實上,當您閱讀這個定義時,您能分辨出什么是“可寫”屬性,什么不是嗎?
例子
讓我們舉幾個例子來說明它是多么的混亂和不准確。
class A:
foo = 1
def __init__(self):
self.bar = 2
@property
def baz(self):
return self._baz
@bar.setter
def baz(self, value):
self._baz = value
>>> a = A()
>>> a.foo
1
>>> a.bar
2
鑒於上面的 class A
和已知的__dict__
的定義,你能猜出a.__dict__
的值是多少嗎?
foo
是a
的可寫屬性嗎?bar
是a
的可寫屬性嗎?baz
是a
的可寫屬性嗎?_baz
是a
的可寫屬性嗎?這是答案:
>>> a.__dict__
{'bar': 2}
令人驚訝的是, foo
沒有出現。 實際上,雖然可以通過a.foo
訪問,但它是 class A
的屬性,而不是實例a
的屬性。
現在如果我們將它明確定義為 a 的屬性會a
什么?
>>> a.foo = 1
>>> a.__dict__
{'bar': 2, 'foo': 1}
從我們的角度來看,沒有什么真正改變, a.foo
仍然等於1
,但現在它出現在__dict__
中。 請注意,我們可以通過刪除a.foo
來繼續使用它,例如:
>>> del a.foo
>>> a.__dict__
{'bar': 2}
>>> a.foo
1
這里發生的事情是我們刪除了實例屬性,並且調用a.foo
再次回落到A.foo
。
現在讓我們看看baz
。 我們可以假設我們無法在a.__dict__
中找到它,因為我們還沒有給它賦值。
>>> a.baz = 3
好了,現在我們定義a.baz
。 那么, baz
是一個可寫屬性嗎? _baz
呢?
>>> a.__dict__
{'bar': 2, '_baz': 3}
從__dict__
的角度來看, _baz
是一個可寫屬性,但baz
不是。 同樣,解釋是baz
是 class A
的屬性,而不是實例a
。
>>> A.__dict__['baz']
<property at 0x7fb1e30c9590>
a.baz
只是一個在幕后調用A.baz.fget(a)
的抽象層。
讓我們和我們親愛的朋友更加鬼鬼祟祟,挑戰它對“可寫”的定義。
class B:
def __init__(self):
self.foobar = 'baz'
def __setattr__(self, name, value):
if name == 'foobar' and 'foobar' in self.__dict__:
# Allow the first definition of foobar
# but prevent any subsequent redefinition
raise TypeError("'foobar' is a read-only attribute")
super().__setattr__(name, value)
>>> b = B()
>>> b.foobar
'baz'
>>> b.foobar = 'bazbar'
TypeError: 'foobar' is a read-only attribute
>>> # From our developer's perspective, 'foobar' is not a writable attribute
>>> # But __dict__ doesn't share this point of view
>>> b.__dict__
{'foobar': 'baz'}
那么__dict__
到底是什么?
由於在上述示例中注意到的行為,我們現在可以更好地理解__dict__
的實際作用。 但是我們需要從開發者的角度切換到計算機的角度:
__dict__
包含存儲在程序的 memory 中的數據,用於這個特定的 object。
就是這樣, __dict__
在我們的對象地址公開了實際存儲在 memory 中的內容。
Python 關於數據 model的文檔也將其定義為對象的命名空間:
一個 class 實例有一個作為字典實現的命名空間,這是搜索屬性引用的第一個地方。 如果在那里找不到屬性,並且實例的 class 具有該名稱的屬性,則繼續搜索 class 屬性。
但是,我相信將__dict__
視為對象的 memory 表可以更好地可視化此命名空間中包含的內容,以及不包含的內容。
但! 有一個陷阱...
你以為我們已經完成了__dict__
?
__dict__
不是處理對象的 memory 足跡的方式,而是一種方式。
確實有另一種方式: __slots__
。 我不會在這里詳細說明它是如何工作的,如果你想了解更多關於__slots__
已經有一個非常完整的答案。 但對我們來說重要的是,如果定義了插槽:
class C:
__slots__ = ('foo', 'bar')
def __init__(self):
self.foo = 1
self.bar = 2
>>> c = C()
>>> c.foo
1
>>> c.bar
2
>>> c.__dict__
AttributeError: 'C' object has no attribute '__dict__'
我們可以對__dict__
說“再見”。
那么什么時候應該使用__dict__
呢?
正如我們所見, __dict__
必須從計算機的角度來看,而不是從開發人員的角度來看。 通常情況下,我們認為 object 的“屬性”並不直接與 memory 中實際存儲的內容相關聯。尤其是使用屬性或__getattr__
等實例時,它們增加了一個抽象級別,讓我們感到舒適。
盡管使用__dict__
來檢查 object 的屬性在大多數情況下都可行,但我們不能 100% 依賴它。 對於用於編寫通用邏輯的東西來說,這是一種恥辱。
__dict__
的用例應該僅限於檢查對象的 memory 內容。 這並不常見。 請記住,在定義插槽時, __dict__
可能根本沒有定義(或者缺少一些實際存儲在內存中的屬性)。
它在 Python 的控制台中也非常有用,可以快速檢查類的屬性和方法。 或者一個對象的屬性(我知道我只是說我們不能依賴它,但在控制台中誰會關心它是否有時會失敗或者它是否不准確)。
謝謝,但是...那我該如何瀏覽對象的屬性呢?
我們看到__dict__
經常被誤用,我們不能真正依賴它來檢查對象的屬性。 但是,正確的做法是什么? 有什么方法可以從開發人員的抽象角度瀏覽對象屬性嗎?
是的。 有幾種方法可以進行內省,正確的方法將取決於您實際想要得到什么。 實例屬性,class 屬性,屬性,私有屬性,甚至方法,...從技術上講,所有這些都是屬性,根據您的情況,您可能希望包括一些但排除其他的。 上下文也很重要。 也許您使用的庫已經通過其 API 公開了您想要的屬性。
但一般來說,你可以依賴inspect
模塊。
class D:
foo = 1
__slots = ('bar', '_baz')
@property
def baz(self):
return self._baz
@baz.setter
def baz(self, value):
self._baz = value
def __init__(self):
self.bar = 2
self.baz = 3
def pointless_method(self):
pass
>>> import inspect
>>> dict((k, v) for k, v in inspect.getmembers(d) if k[0] != '_')
{'bar': 2, 'baz': 3, 'foo': 1}
>>> dict((k, getattr(d, k)) for k, v in inspect.getmembers(D) if inspect.isdatadescriptor(v) or inspect.isfunction(v))
{
'__init__': <bound method D.__init__ of <__main__.D object at 0x7fb1e26a5b40>>,
'_baz': 3,
'bar': 2,
'baz': 3,
'pointless_method': <bound method D.pointless_method of <__main__.D object at 0x7fb1e26a5b40>>
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.