首先需要知道的是,dir() 是 Python 提供的一个 API 函数,dir() 函数会自动寻找一个对象的所有属性,包括搜索 __dict__ 中列出的属性。

  不是所有的对象都有 __dict__ 属性。例如,如果你在一个类中添加了 __slots__ 属性,那么这个类的实例将不会拥有 __dict__ 属性,但是 dir() 仍然可以找到并列出它的实例所有有效属性。

>>> class Foo(object):
... __slots__ = ('bar', )
... bar = 'spam'
... >>> Foo.__dict__
dict_proxy({'__module__': '__main__', 'bar': 'spam', '__slots__': ('bar',), '__doc__': None}) >>> Foo().__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__dict__' >>> dir(Foo)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar'] >>> dir(Foo())
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar']

  同理许多内建类型都没有 __dict__ 属性,例如 list 没有 __dict__ 属性,但你仍然可以通过 dir() 列出 list 所有的属性。

>>> [].__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__dict__'
>>> dir([])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

dir() 函数和类实例

  Python 的实例拥有它们自己的 __dict__,而它们对应的类也有自己的 __dict__:

>>> class Foo(object):
... bar = 'spam'
...
>>> Foo().__dict__
{}
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__module__': '__main__', 'bar': 'spam', '__doc__': None})

  dir() 函数会遍历 Foo,Foo() 和 object 的 __dict__ 属性,从而为 Foo 类,它的实例以及它所有的被继承类创建一个完整有效的属性列表。

  当你对一个类设置属性时,它的实例也会受到影响:

>>> f = Foo()
>>> f.ham
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'ham'
>>> Foo.ham = 'eggs'
>>> f.ham
'eggs'

  这是因为 'ham' 属性被添加到了 Foo 类的 __dict__ 属性中:

>>> Foo.__dict__
dict_proxy({'__module__': '__main__', 'bar': 'spam', 'ham': 'eggs', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None})
>>> f.__dict__
{}

  尽管 Foo 的实例 f 自己的 __dict__ 为空,但它还是能拥有作为类属性的 'ham'。Python 中,一个对象的属性查找顺序遵循首先查找实例对象自己,然后是类,接着是类的父类。

  所以当你直接对一个实例设置属性时,会看到实例的 __dict__ 中添加了该属性,但它所属的类的 __dict__ 却不受影响。

>>> f.stack = 'overflow'
>>> f.__dict__
{'stack': 'overflow'}
>>> Foo.__dict__
dict_proxy({'__module__': '__main__', 'bar': 'spam', 'ham': 'eggs', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None})

  dir() 所做的不是查找一个对象的 __dict__ 属性(这个属性有时甚至都不存在),它使用的是对象的继承关系来反馈一个对象的完整的有效属性。

  一个实例的 __dict__ 属性仅仅是那个实例的局部属性集合,不包含该实例所有有效属性。所以如果你想获取一个对象所有有效属性,你应该使用 dir() 来替代 __dict__ 或者 __slots__。

关于__slots__

  合理使用 __slots__ 属性可以节省一个对象所消耗的空间。一个普通对象使用一个 dict 来保存它自己的属性,你可以动态地向其中添加或删除属性,但是如果使用 __slots__ 属性,那么该对象用来保存其自身属性的结构一旦创建则不能再进行任何修改。因此使用 slot 结构的对象节省了一部分开销。虽然有时这是一个很有用的优化方案,但是它也可能没那么有用,因为如果 Python 解释器足够动态,那么它完全可以在向对象添加属性时只请求该对象所使用的 dict。

  如何让 CPython 变得足够强大可以自动节省空间而不使用 __slots__ 属性是一项重大工程,这也许就是为什么它依然存在于 P3k 中的原因吧。

Python 官方关于 __slots__ 的介绍

  在默认情况下,Python 的新类和旧类的实例都有一个字典来存储属性值。这对于那些没什么实例属性的对象来说太浪费空间了,当需要创建大量实例的时候,这个问题变得尤为突出。

  因此这种默认做法可以通过在新式类中定义一个 __slots__ 属性从而得到解决。__slots__ 声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量,因为没有为每个实例都创建一个字典,从而节省空间。

__slots__

这个类变量可以是 string,iterable 或者是被实例使用的一连串 string。如果在新式类中定义了 __slots__,__slots__ 会为声明了的变量预留空间,同时阻止该类为它的每个实例自动创建 __dict__ 和 __weakref__。

使用 __slots__ 须知

  • 当继承一个没有 __slots__ 属性的类时,子类会自动拥有 __dict__ 属性,因此在子类中定义 __slots__ 是毫无意义的,你可以自由访问子类的 __dict__ 属性,所有未在 __slots__ 中声明的属性会保存在 __dict__ 中。
  • 在缺少 __dict__ 变量的情况下,实例不能接受新的不在 __slots__ 声明内的变量作为属性,如果试图给这样的类赋予一个未在 __slots__ 声明的变量作为属性会抛出 AttributeError。但是如果你确实需要能够动态添加属性,那么将字符串 '__dict__' 纳入 __slots__ 的声明中。如果同时在类中定义 __dict__ 和 __slots__ 是不行的,因为 __slots__ 会阻止 __dict__ 属性的创建。(本条 Python 2.3 及其以后有效)
  • 在缺少 __weakref__ 变量的情况下,定义了 __slots__ 的类不支持对其实例的弱引用。如果需要,请将字符串 '__weakref__' 纳入 __slots__ 声明中。(本条Python 2.3及其以后有效)
  • __slots__ 的实现原理是在 class 级别为其所声明的每个变量创建 descriptor,由此带来的结果就是,类属性不能用于为 __slots__ 声明中的实例变量设置默认值,否则类属性会覆写描述符的赋值功能。
  • __slots__ 声明只对它所处的类有效,因此,含有 __slots__ 的类的子类会自动创建一个 __dict__,除非在子类中也声明一个 __slots__ (在这个 __slots__ 声明应该只包含父类未声明的变量)。
  • 如果一个类中定义了一个在基类中相同的变量,那么子类实例将不能访问基类中定义的实例变量,除非直接从基类中读取描述符。在将来,可能会添加一个检查来阻止这种情况。
  • 非空的 __slots__ 对某些类无效,某些类是指源自含有长度属性的内建类型,例如 long, str 和 tuple。
  • 任何非字符串的可迭代的对象都可以赋值给 __slots__ 。具有映射特性的对象也可以赋值为 __slots__。但是,在将来,每个键的值可能会赋予特别的含义。
  • __class__ 赋值只有当两个类都具有相同的 __slots__ 时才有效。(本条 Python 2.6 及其以后有效)

[Python] dir() 与 __dict__,__slots__ 的区别的更多相关文章

  1. python的dir()和__dict__属性的区别

    只要是有属性的数据对象(不一定是面向对象的对象实例,而是指具有数据类型的数据对象),都可以通过__dict__和dir()来显示数据对象的相关属性. __dict__可以看作是数据对象的名称空间,所以 ...

  2. python基础教程:dir()和__dict__属性的区别

    只要是有属性的数据对象(不一定是面向对象的对象实例,而是指具有数据类型的数据对象),都可以通过- ---- __dict__和dir()来显示数据对象的相关属性. __ dict__可以看作是数据对象 ...

  3. python 中dir()和__dict__的区别

    Python __dict__与dir() 出处(http://blog.csdn.net/lis_12/article/details/53521554). Python下一切皆对象,每个对象都有多 ...

  4. python中的__dict__和dir()的区别

    Python下一切皆对象,每个对象都有多个属性(attribute),Python对属性有一套统一的管理方案. __dict__与dir()的区别: dir()是一个函数,返回的是list: __di ...

  5. python魔法函数__dict__和__getattr__的妙用

    python魔法函数__dict__和__getattr__的妙用 __dict__ __dict__是用来存储对象属性的一个字典,其键为属性名,值为属性的值. 既然__dict__是个字典那么我们就 ...

  6. 利用Python特殊变量__dict__快速实现__repr__的一种方法

    在<第8.15节 Python重写自定义类的__repr__方法>.<Python中repr(变量)和str(变量)的返回值有什么区别和联系>.<第8.13节 Pytho ...

  7. Python之str()与repr()的区别

    Python之str()与repr()的区别 str()一般是将数值转成字符串,主要面向用户.  repr()是将一个对象转成字符串显示,注意只是显示用,有些对象转成字符串没有直接的意思.如list, ...

  8. Python中type与Object的区别

    Python中type与Object的区别 在查看了Python的API后,总算明白了.现在总结如下: 先来看object的说明: Python中关于object的说明很少,甚至只有一句话: clas ...

  9. Python 头部 #!/usr/bin/python 和 #!/usr/bin/env 的区别

    这个网址 https://www.cnblogs.com/scofi/p/4867851.html 讲述了Python 头部 #!/usr/bin/python 和 #!/usr/bin/env 的区 ...

随机推荐

  1. 基础知识系列☞各版本下IIS请求处理过程区别

    转载地址→http://www.cnblogs.com/fsjohnhuang/articles/2332074.html ASP.NET是一个非常强大的构建Web应用的平台, 它提供了极大的灵活性和 ...

  2. IOS开发之----NSDictionary,JSON和XML互相转换

    本文永久地址为 http://www.cnblogs.com/ChenYilong/p/4044521.html,转载请注明出处.     -(void)test {     //XML文本范例   ...

  3. 【深入JVM内核—原理、诊断与优化】第2期开课了

    [深入JVM内核—原理.诊断与优化]的讲师“葛一鸣”,人称“一哥”,毕业于浙江工业大学,计算机软件与理论专业硕士,是国家认证系统分析师,OCP.2012年出版过<Java程序性能优化>,荣 ...

  4. Mahout 的安装

    Mahout 的安装 Mahout是Hadoop的一种高级应用.运行Mahout需要提前安装好Hadoop,Mahout只在Hadoop集群的NameNode节点上安装一个即可,其他数据节点上不需要安 ...

  5. Java File 常用操作回顾

    最近项目中要用到File这个类,温故而知新,回过头来回顾下这个File类,File类主要是对磁盘目录,文件进行操作的Api,具体其实查JDK api 的File全能获取到. 下面写一些文件目录的基本操 ...

  6. iOS 图片 的 聊天气泡显示 Objective-C

    - (void)viewDidLoad { [super viewDidLoad]; UIImageView *ImageView01 = [[UIImageView alloc] init]; [I ...

  7. git commit --amend

    任何时候,你都有可能需要撤消刚才所做的某些操作.接下来,我们会介绍一些基本的撤消操作相关的命令.请注意,有些撤销操作是不可逆的,所以请务必谨慎小心,一旦失误,就有可能丢失部分工作成果. 有时候我们提交 ...

  8. 160809209_李梦鑫_C语言程序设计实验2 选择结构程序设计

    实验2-1 输入3个数,并按由大到小的顺序输出. 实验要求: 编写一个C程序,输入3个数,并按由大到小的顺序输出. 源码:#include <stdio.h> int main() { i ...

  9. Spatial pyramid pooling (SPP)-net (空间金字塔池化)笔记(转)

    在学习r-cnn系列时,一直看到SPP-net的身影,许多有疑问的地方在这篇论文里找到了答案. 论文:Spatial Pyramid Pooling in Deep Convolutional Net ...

  10. SNMP简单网络管理协议(转载)

    SNMP SNMP 网络管理的历史 美国国防部设计了世界上头几个包交换网之一的ARPANET,在70年代,TCP/IP协议族正式被定为军方通信标准,随着此协议的广泛使用,网络管理成了一件大事.在80年 ...