Python:解释__dict__属性

Zak*_*kos 45 python

好吧,我对__dict__属性感到很困惑.我搜索了很多,但我仍然不确定输出.在对象或类或函数中使用的情况下,是否可以解释从零开始使用此属性?谢谢你的每一件衣服

the*_*eye 51

基本上它包含描述所讨论对象的所有属性.它可用于更改或读取属性.引用这个

用于存储对象(可写)属性的字典或其他映射对象.

记住,一切都是python中的一个对象.当我说出所有内容时,我的意思是函数,类,对象等所有内容(Ya,你读得正确,类.类也是对象).例如,

def func():
    pass
func.temp = 1

print func.__dict__

class TempClass(object):
    a = 1
    def tempFunction(self):
        pass

print TempClass.__dict__
Run Code Online (Sandbox Code Playgroud)

产量

{'temp': 1}
{'a': 1, '__module__': '__main__', 'tempFunction': <function tempFunction at 0x7f77951a95f0>, '__dict__': <attribute '__dict__' of 'TempClass' objects>, '__weakref__': <attribute '__weakref__' of 'TempClass' objects>, '__doc__': None}
Run Code Online (Sandbox Code Playgroud)

  • __dict__ 不会打印 'a' : 1 因为它没有在 __init__ 方法中设置 (2认同)
  • @rotemx在这里我们不是在谈论成员变量。如果看到`print`语句,则说明我们正在打印类本身的'__dict__',而不是它的实例。 (2认同)
  • 我在 Python 中的默认数据类型上尝试了此操作,但出现了“AttribueError”错误,这是任何原因。 (2认同)

Ant*_*ard 15

Python 文档定义__dict__为:

\n
\n

用于存储对象\xe2\x80\x99s(可写)属性的字典或其他映射对象。

\n
\n

然而这个定义有点模糊,这导致了很多错误的使用__dict__

\n

事实上,当您阅读这个定义时,您能区分什么是“可写”属性以及什么不是吗?

\n
\n

例子

\n

让我们举几个例子来说明它是多么令人困惑和不准确。

\n
class A:\n\n    foo = 1\n\n    def __init__(self):\n        self.bar = 2\n\n    @property\n    def baz(self):\n        return self._baz\n\n    @bar.setter\n    def baz(self, value):\n        self._baz = value\n\n>>> a = A()\n>>> a.foo\n1\n>>> a.bar\n2\n
Run Code Online (Sandbox Code Playgroud)\n

给定上面的类A并知道__dict__\ 的定义,你能猜出 的值是多少吗a.__dict__

\n
    \n
  • 是 的foo可写属性吗a
  • \n
  • 是 的bar可写属性吗a
  • \n
  • 是 的baz可写属性吗a
  • \n
  • 是 的_baz可写属性吗a
  • \n
\n

答案如下:

\n
>>> a.__dict__\n{\'bar\': 2}\n
Run Code Online (Sandbox Code Playgroud)\n

令人惊讶的是,foo没有出现。事实上,虽然可以使用 访问a.foo,但它是类的属性A,而不是实例的属性a

\n

现在,如果我们将其明确定义为 的属性,会发生什么a

\n
>>> a.foo = 1\n>>> a.__dict__\n{\'bar\': 2, \'foo\': 1}\n
Run Code Online (Sandbox Code Playgroud)\n

从我们的角度来看,没有什么真正改变,a.foo仍然等于1,但现在它出现在 中__dict__。请注意,我们可以通过删除来继续使用它a.foo

\n
>>> del a.foo\n>>> a.__dict__\n{\'bar\': 2}\n>>> a.foo\n1\n
Run Code Online (Sandbox Code Playgroud)\n

这里发生的事情是我们删除了实例属性,并且调用a.foo再次回退到A.foo.

\n

现在我们就来看看吧baz。我们可以假设我们找不到它,a.__dict__因为我们还没有给它赋值。

\n
>>> a.baz = 3\n
Run Code Online (Sandbox Code Playgroud)\n

好吧,现在我们定义了a.baz. 那么,是baz可写属性吗?关于什么_baz

\n
>>> a.__dict__\n{\'bar\': 2, \'_baz\': 3}\n
Run Code Online (Sandbox Code Playgroud)\n

__dict__\ 的角度来看,_baz是一个可写属性,但baz实际上不是。再次解释,这是baz类的属性A,而不是实例的属性a

\n
>>> A.__dict__[\'baz\']\n<property at 0x7fb1e30c9590>\n
Run Code Online (Sandbox Code Playgroud)\n

a.baz只是一个A.baz.fget(a)在幕后调用的抽象层。

\n

让我们和亲爱的朋友一起更加偷偷摸摸地挑战它对“可写”的定义。

\n
class B:\n\n    def __init__(self):\n        self.foobar = \'baz\'\n\n    def __setattr__(self, name, value):\n        if name == \'foobar\' and \'foobar\' in self.__dict__:\n            # Allow the first definition of foobar\n            # but prevent any subsequent redefinition\n            raise TypeError("\'foobar\' is a read-only attribute")\n        super().__setattr__(name, value)\n\n>>> b = B()\n>>> b.foobar\n\'baz\'\n>>> b.foobar = \'bazbar\'\nTypeError: \'foobar\' is a read-only attribute\n>>> # From our developer\'s perspective, \'foobar\' is not a writable attribute\n>>> # But __dict__ doesn\'t share this point of view\n>>> b.__dict__\n{\'foobar\': \'baz\'}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

那么__dict__究竟是什么?

\n

由于上述示例中注意到的行为,我们现在可以更好地了解__dict__实际情况。但我们需要从开发者的角度切换到计算机的角度:

\n

__dict__包含存储在程序内存中的该特定对象的数据。

\n

就是这样,__dict__暴露了我们对象地址中实际存储在内存中的内容。

\n

关于数据模型的 Python 文档还将其定义为对象的命名空间

\n
\n

类实例有一个作为字典实现的命名空间,它是搜索属性引用的第一个地方。当在那里找不到属性,并且 instance\xe2\x80\x99s 类具有该名称的属性时,将继续搜索类属性。

\n
\n

然而,我相信将__dict__对象的内存表视为该名称空间中包含的内容和不包含的内容可以更好地可视化。

\n

但!有一个问题...

\n
\n

你以为我们就这样完了吗__dict__

\n

__dict__不是处理对象内存占用的方法,而是一种方法

\n

确实还有另一种方法:__slots__。我不会在这里详细说明它是如何工作的,如果您想了解更多信息,已经有一个非常完整的答案。__slots__但对我们来说重要的是,如果定义了槽:

\n
class C:\n    __slots__ = (\'foo\', \'bar\')\n\n    def __init__(self):\n        self.foo = 1\n        self.bar = 2\n\n>>> c = C()\n>>> c.foo\n1\n>>> c.bar\n2\n>>> c.__dict__\nAttributeError: \'C\' object has no attribute \'__dict__\'\n
Run Code Online (Sandbox Code Playgroud)\n

我们可以说“再见”了__dict__

\n
\n

那么我应该什么时候使用__dict__呢?

\n

正如我们所看到的,__dict__必须是从计算机的角度来看,而不是从开发者的角度来看。通常,我们所认为的对象的“属性”与内存中实际存储的内容并不直接相关。特别是使用属性,或者__getattr__例如,为我们的舒适度添加抽象级别。

\n

尽管使用 来__dict__检查对象的属性在大多数简单情况下都可以工作,但我们不能 100% 依赖它。对于用来编写通用逻辑的东西来说这是一种耻辱。

\n

的用例__dict__可能应该仅限于检查对象的内存内容。这并不常见。请记住,__dict__当定义槽时,可能根本没有定义(或缺少实际存储在内存中的某些属性)。

\n

它在 Python 控制台中也非常有用,可以快速检查类的属性和方法。或者一个对象的属性(我知道我只是说我们不能依赖它,但是在控制台中谁关心它是否有时会失败或者它是否不准确)。

\n
\n

谢谢但是...那么我该如何浏览我的对象的属性呢?

\n

我们看到它__dict__经常被滥用,并且我们不能真正依赖它来检查对象的属性。但是,正确的做法是什么呢?有没有办法从开发人员的抽象角度浏览对象属性?

\n

是的。进行内省的方法有很多种,正确的方法取决于你真正想要得到什么。实例属性、类属性、属性、私有属性、甚至方法……从技术上讲,所有这些都是属性,根据您的情况,您可能希望包含一些属性,但排除其他属性。背景也很重要。也许您正在使用的库已经通过其 API 公开了您想要的属性。

\n

但一般来说,你可以依赖该inspect模块

\n
class D:\n\n    foo = 1\n    __slots = (\'bar\', \'_baz\')\n\n    @property\n    def baz(self):\n        return self._baz\n\n    @baz.setter\n    def baz(self, value):\n        self._baz = value\n\n    def __init__(self):\n        self.bar = 2\n        self.baz = 3\n\n    def pointless_method(self):\n        pass\n\n\n>>> d = D()\n>>> import inspect\n>>> dict((k, v) for k, v in inspect.getmembers(d) if k[0] != \'_\')\n{\n \'bar\': 2,\n \'baz\': 3,\n \'foo\': 1\n}\n>>> dict((k, getattr(d, k)) for k, v in inspect.getmembers(D) if inspect.isdatadescriptor(v) or inspect.isfunction(v))\n{\n \'__init__\': <bound method D.__init__ of <__main__.D object at 0x7fb1e26a5b40>>,\n \'_baz\': 3,\n \'bar\': 2,\n \'baz\': 3,\n \'pointless_method\': <bound method D.pointless_method of <__main__.D object at 0x7fb1e26a5b40>>\n}\n
Run Code Online (Sandbox Code Playgroud)\n


小智 5

__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
Run Code Online (Sandbox Code Playgroud)

__dict__获取nameage以及它们在字典中的值,如下所示:

{'name': 'John', 'age': 27}
Run Code Online (Sandbox Code Playgroud)

并且,如果在实例化后添加新的实例变量gender,如下所示:

# ...

obj = Person("John", 27)
obj.gender = "Male" # Here
print(obj.__dict__)
Run Code Online (Sandbox Code Playgroud)

__dict__ getsnameage并将gender它们的值存储在字典中,如下所示:

{'name': 'John', 'age': 27, 'gender': 'Male'}
Run Code Online (Sandbox Code Playgroud)

另外,如果使用dir()如下所示:

# ...

obj = Person("John", 27)
obj.gender = "Male" 
print(dir(obj)) # Here
Run Code Online (Sandbox Code Playgroud)

我们可以将对象中的所有内容作为列表获取,如下所示:

['__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']
Run Code Online (Sandbox Code Playgroud)

而且,据我研究并提出这个问题,没有函数可以仅获取 Python 中对象中的静态或特殊变量或普通、类、静态或特殊方法。