`
jianpx
  • 浏览: 170998 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

python attribute lookup

阅读更多

    分享下自己最近对python的attribute lookup的学习, 也顺便做下记录。如果有说的不对的请拍砖!

 

 

1. python会为所有(更准确的说是所有非built-in对象)对象包括class和instance创建一个__dict__属性,

这个属性是一个dict类型, 存储的是这个对象所有的user-provided的属性名字和属性对象的对应关系, 

包括一些属性值和函数等等。这个__dict__的作用除了可以让你了解到里面存储的是神马东西以外,

你也可以在程序里面遍历、设置__dict__里面的值。其实这个__dict__就像metaclass里面继承type的时候

要实现的__init__或者__new__方法里面的最后一个参数,只不过metaclass可以允许你在类创建之前就能

访问并且这是这个值了。不过也有例外的情况,

 

* 特殊的__dict__属性的情况:

1) 如果class里面定义__slots__属性的话,它的instance将不会拥有__dict__属性,

这个比较特殊。后面再讲__slots__.

2) 如果是python built-in的对象,像dict、list、int等的__dict__属性都是一个dictproxy对象,一个类似

dict的对象, 你可以通过这样来看看里面有神马东西:

for k, v in dict.__dict__.items():

   print k

   print v

   print '*' * 20

 

* 从对象的继承链来看搜索属性的顺序:

1) 看obj.__dict__有没有  --->(没有)   2) 看obj.__class__.__dict__有没有 --->(没有) 3) 看obj.__class__.__base__有没有

--->(没有) 4) raise AttributeError

 

* 从python的一些内部函数来看属性的获取顺序:

1) object.__getattribute__(self, name) --->(没有name)   2)[optional, if offered] object.__getattr__(self, name)

               |

               |

              (有name)

   3) 看下改属性是否是descriptor, 如果是,那么调用descriptor的__get__方法返回,

   如果不是,则直接将这个属性值返回。

 

当调用a.x这样的语法的时候,会首先调用a.__getattribute__('x')这样的方法,找到就检查该属性是否

是descriptor,是的话就调用它的__get__方法返回了, 不是descriptor的话就直接返回该属性。

如果也找不到x属性,那么就看下是否定义了__getattr__方法,有的话就调用__getattr__方法.

如果最后找不到的话就会抛AttributeError的错误!

可以用这个例子看看:(暂时忽略descriptor对属性查找的影响)

[http://mail.python.org/pipermail/python-dev/2010-March/098562.html]

 

(关于__getattribute__ & __getattr__的说明可以查看这里:

http://docs.python.org/reference/datamodel.html#object.__getattr__)

 

class A(object):
   def __getattribute__(self, name):
       print "A getattribute", name
       #raise AttributeError('A error') #test1
       return object.__getattribute__(self, name) #test2

   def __getattr__(self, name):
       print "A getattr", name
       return "hello A"

class B(A):
   def __getattribute__(self, name):
       print "B getattribute", name
       return super(B, self).__getattribute__(name)

   def __getattr__(self, name):
       print "B getattr", name
       return "hello B"


print A().obj
print "-" * 20
print B().obj
print "-" * 20
print getattr(B(), "obj")
print "-" * 20
print object.__getattribute__(B(), "obj") # DOES NOT CALL __getattr__() !!!

 =======

output

======

A getattribute obj

A getattr obj

hello A

--------------------

B getattribute obj

A getattribute obj

B getattr obj

hello B

--------------------

B getattribute obj

A getattribute obj

B getattr obj

hello B

--------------------

Traceback (most recent call last):

 File "./attrs_test.py", line 35, in <module>

   print object.__getattribute__(B(), "obj") # DOES NOT CBLL __getattr__() !!!

AttributeError: 'B' object has no attribute 'obj'

 

** 分析:

上面的例子证明了之前说的__getattribute__和__getattr__的调用顺序,而且假设你把#test1

的那段raise注释打开, 把#test2的代码注释掉的话,你会发现结果是一样的,这就说明python

内部应该还有一个类似总控的地方捕捉到__getattribute__抛出来的AttributeError异常(其他异常

是不行的)然后调用__getattr__函数了。 而最后一步: 

print object.__getattribute__(B(), "obj") 

则是因为它并不是经过实例B()的点属性语法的,所以不是走的上面的流程,所以直接报错了

 

** __getattribute__ & __getattr__的作用:

说了关于属性的查找过程,对我们的作用就是当我们需要重写获取属性的途径、方法的时候,

我们可以重写__getattribute__方法来实现,要注意的是在__getattribute__函数里面要为了

避免递归调用实例的点属性,建议调用父类的或者最上层的object的__getattribute__方法.

 

 

2 关于__slots__的[http://docs.python.org/reference/datamodel.html#slots]

1) 如果需要创建大量的实例(来自同一个类)的话,可以避免在每个类里面都保存__dict__来存储实例属性,可省空间.

2) 如果你要禁止在实例被创建之后会被动态添加属性的话,也就是你的属性只是可读的.可以用__slots__来预先定义.

但是继承自有定义__slots__的子类是不受父类的__slots__影响的, 所以如果要继续有效,必须所有的子类都定义跟

父类一样的__slots__

3) __slots__相当于为每个它定义的属性值写好了get和set的descriptor属性方法。所以A.a = 1 和print A.a才会生效。

4) 凡是定义了类的静态属性__slots__, 比如:

class A(object):

   __slots__ = ['x', 'y']

那么A的实例就不能动态定义除了x和y之外的其他属性了,会报错。 而且实例里不会有__dict__属性。

 

3 获取属性或者设置属性的时候最好是用setattr和getattr函数,因为他们内部帮你处理好了很多事情,比如getattr能

自动识别descriptor等. 判断是否有该属性的话可以用hasattr

 

4 用__getattribute__实现了一个常量类,用于定义常量值和它对应的描述的,值可以通过点属性方式获取,

或者有些web界面有需要渲染对应值和它的中文描述的对应关系的,例如web界面上的select框就很常见。

这个常量类会提供这样一些有用的方法 更好的针对web应用. 

比如用户输入了一个值,我们需要判断它是否在你定义的常量集合的合法值里面。

具体代码在附件的const.py里面

1
3
分享到:
评论

相关推荐

    Python multiprocess pool模块报错pickling error问题解决方法分析

    本文实例讲述了Python ...PicklingError: Can’t pickle &lt;type&gt;: attribute lookup __builtin__.function failed 查了下官方文档发现python默认只能pickle以下的类型: None, True, and False integers, floa

    python对象模型

    属性查找(Attribute Lookup):属性查找是Python处理实例和类属性访问的过程。当查找实例x的属性y时,解释器会按照一定的顺序来查找,首先检查实例对象x的__dict__,然后是类的__dict__,然后是基类的__dict__...

    python3.6.5参考手册 chm

    PEP 520: Preserving Class Attribute Definition Order PEP 468: Preserving Keyword Argument Order New dict implementation PEP 523: Adding a frame evaluation API to CPython PYTHONMALLOC environment ...

    Python异常和错误实践手册 新手必备

    5. AttributeError:对象没有某个属性时引发的异常 6. BufferError:缓存 buffer 操作错误引发的异常 7. EOFError:文件结尾时引发的异常 8. ImportError:导入模块错误引发的异常 9. LookupError:查找错误引发的...

    python2.6库函数参考手册

    - **AttributeError**: 属性引用或赋值失败时抛出。 - **BufferError**: 缓冲区操作失败时抛出。 - **EOFError**: 输入结束后仍试图执行读取操作时抛出。 - **ImportError**: 导入模块失败时抛出。 - **LookupError*...

    Python:通用异常类型表

    14. **LookupError**:无效数据查询的基类,包括以下子类: - **IndexError**:序列中没有此索引,当尝试访问序列中不存在的索引时抛出。 - **KeyError**:映射中没有这个键,发生在字典或其他映射数据类型中尝试...

    Python 中的内置异常

    Python中的内置异常是编程过程中遇到错误时自动抛出的预定义异常类型,它们都是从`BaseException`类派生而来的。`BaseException`作为所有异常的顶级基类,但通常用户定义的异常不会直接继承它,而是继承自`Exception...

    Python 异常处理、异常名称及实例.docx

    此外,还有`AssertionError`、`AttributeError`、`EOFError`、`ImportError`、`LookupError`(包括`IndexError`和`KeyError`)、`MemoryError`、`NameError`、`UnboundLocalError`、`ReferenceError`、`RuntimeError...

    Python异常处理函数和模块PPT学习教案.pptx

    `AssertionError`用于断言失败,`AttributeError`表示尝试访问或修改对象不存在的属性,`EOFError`发生在读取文件或其他输入流到达末尾时,`ImportError`是在导入模块时出现问题,`KeyboardInterrupt`表示用户中断了...

    -pickle.PicklingError: Can&#039;t pickle &lt;function... pycharm2023运行报错

    在Python编程中,`pickle`模块是一个非常重要的序列化库,它允许我们将Python对象转换为字节流(序列化),以便存储或通过网络传输。同时,`pickle`还可以将这些字节流恢复为原来的Python对象(反序列化)。然而,在...

    Python的异常名称友情推荐!

    - **`AttributeError`** - **描述**:对象没有这个属性。 - **应用场景**:尝试访问不存在的属性时触发。 - **`EOFError`** - **描述**:没有内建输入,到达EOF标记。 - **应用场景**:当输入流结束时触发。 -...

    深入理解Python异常处理的哲学

    - 其他还有`EOFError`、`ImportError`、`LookupError`(包括`IndexError`和`KeyError`)、`MemoryError`、`NameError`(包括`UnboundLocalError`)、`ReferenceError`、`RuntimeError`等。 了解这些内置异常类型有...

    Python异常处理知识点总结

    - `LookupError`:无效数据查询的基类,包括`IndexError`(序列索引越界)和`KeyError`(字典键不存在)。 - `MemoryError`:内存溢出错误。 - `NameError`:未声明或初始化的对象引用。 - `UnboundLocalError`:...

    聊聊Python中的pypy

    | attribute_lookup.py | 258.544s | 200.387s | 2.667s | | attrs.py | 0.622s | 1.658s | 0.086s | | closures.py | 0.485s | 6.658s | 0.058s | | empty_loop.py | 3.532s | 19.248s | 0.248s | | fib2.py | 3....

    Python异常继承关系和自定义异常实现代码实例

    - **`AttributeError`**:属性引用或赋值失败。 - **`EnvironmentError`**:操作系统环境相关的错误。 - **`IOError`**:I/O 操作失败(例如文件读写)。 - **`OSError`**:操作系统功能相关的错误。 - **`...

    对Python中内置异常层次结构详解

    - `AttributeError`: 访问或修改对象不存在的属性时抛出。 - `EnvironmentError`: 环境相关的错误,包括: - `IOError`: 输入/输出操作失败。 - `OSError`: 操作系统错误,例如文件不存在、权限不足等。在...

    Python中的异常处理try/except/finally/raise用法分析

    | AttributeError | 对象没有这个属性 | | EOFError | 没有内建输入,到达EOF标记 | | EnvironmentError | 操作系统错误的基类 | | IOError | 输入/输出操作失败 | | OSError | 操作系统错误 | | WindowsError | 系统...

    python try 异常处理(史上最全)

    - **AttributeError**: 对象没有这个属性。 - **EOFError**: 没有内建输入,到达EOF标记。 - **EnvironmentError**: 操作系统错误的基类。 - **IOError**: 输入/输出操作失败。 - **OSError**: 操作系统错误。 - **...

Global site tag (gtag.js) - Google Analytics