解释 __dict__ 属性
- 2025-03-10 08:49:00
- admin 原创
- 43
问题描述:
我对这个__dict__
属性真的很困惑。我搜索了很多,但仍然不确定输出结果。
有人可以从零开始解释一下这个属性的用法吗,当它在对象、类或函数中使用时?
解决方案 1:
基本上,它包含描述相关对象的所有属性。它可用于更改或读取属性。引用自文档__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}
解决方案 2:
Python文档定义__dict__
为:
用于存储对象(可写)属性的字典或其他映射对象。
但这个定义有点模糊,导致了很多错误的使用__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
给定上述类A
并了解__dict__
的定义,你能猜出的值是多少吗a.__dict__
?
是否
foo
具有可写属性a
?是否
bar
具有可写属性a
?是否
baz
具有可写属性a
?是否
_baz
具有可写属性a
?
答案如下:
>>> a.__dict__
{'bar': 2}
令人惊讶的是,foo
没有显示。 事实上,尽管可以通过 访问a.foo
,但它是类的属性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
类的属性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__
包含针对该特定对象存储在程序内存中的数据。
就是这样,__dict__
公开了我们的对象地址中实际存储在内存中的内容。
Python数据模型文档也将其定义为对象的命名空间:
类实例具有以字典形式实现的命名空间,这是搜索属性引用的第一个位置。如果未在此处找到属性,而实例的类具有同名属性,则继续搜索类属性。
然而,我相信将其视为__dict__
对象的内存表可以更好地直观地显示该命名空间中包含的内容和不包含的内容。
但是!有一个问题...
你以为我们已经完成了吗__dict__
?
__dict__
并不是处理对象内存占用的方法,而是一种办法。
确实还有另一种方法:__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__
必须从计算机的角度来看待,而不是从开发人员的角度来看待。通常,我们认为的对象的“属性”与实际存储在内存中的内容没有直接联系。特别是使用属性或__getattr__
例如,这会增加一个抽象级别以方便我们理解。
尽管在大多数情况下使用__dict__
来检查对象的属性都是可行的,但我们不能 100% 地依赖它。这对于用于编写通用逻辑的东西来说是一种遗憾。
的使用案例__dict__
可能应该仅限于检查对象的内存内容。这并不常见。请记住,__dict__
在定义插槽时,可能根本没有定义(或缺少实际存储在内存中的某些属性)。
它在 Python 的控制台中也非常有用,可以快速检查类的属性和方法。或者对象的属性(我知道我刚才说过我们不能依赖它,但在控制台中,谁会在乎它有时会失败或者不准确)。
谢谢,但是...那么我该如何浏览我的对象的属性呢?
我们看到它__dict__
经常被误用,我们不能真正依赖它来检查对象的属性。但是,正确的做法是什么呢?有没有办法从开发人员的抽象角度浏览对象的属性?
是的。有几种方法可以进行自省,正确的方法取决于您实际想要获得什么。实例属性、类属性、属性、私有属性,甚至方法……从技术上讲,所有这些都是属性,根据您的情况,您可能希望包含一些属性,但排除其他属性。上下文也很重要。也许您正在使用一个已经通过其 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
>>> d = D()
>>> 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>>
}
解决方案 3:
dict可以以字典的形式获取对象中的实例变量(数据属性)。
因此,如果有Person
以下类:
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
我们可以将对象中的所有内容以列表形式获取,如下所示:
['__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']
而且,据我研究和提问, Python 中没有函数可以获取对象中的静态或特殊变量或普通、类、静态或特殊方法。