Introspecting instances of built-in types

http://blog.donews.com/limodou/archive/2005/01/02/223720.aspx

For instances of built-in types (and for new-style classes in general), x.__class__ is now the same as type(x):

>>> type([])
<type ‘list’>
>>> [].__class__
<type ‘list’>
>>> list
<type ‘list’>
>>> isinstance([], list)
1
>>> isinstance([], dict)
0
>>> isinstance([], object)
1
>>>

对于内置类型的实例(通常是new-style class的实例),x.__class__现在与type(x)是一样的。(因此在一些文章中你会看到type(x)就表示x的父类或基类。因为object是所有类的基类,因此isinstance([], object)返回为True。这里用得是1,但现在已经改为True和False了。)

In classic Python, the method names of lists were available as the __methods__ attribute of list objects, with the same effect as using the built-in dir() function:

Python 2.1 (#30, Apr 18 2001, 00:47:18) 
[GCC egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)] on linux2
Type “copyright”, “credits” or “license” for more information.
>>> [].__methods__
['append', 'count', 'extend', 'index', 'insert', 'pop',
'remove', 'reverse', 'sort']
>>> 
>>> dir([])
['append', 'count', 'extend', 'index', 'insert', 'pop',
'remove', 'reverse', 'sort']

在classic Python中(python 2.2版之前),列表的方法名可以使用列表对象的__methods__来得到,这同使用内置的dir()函数的效果一样。

Under the new proposal, the __methods__ attribute no longer exists:

Python 2.2c1 (#803, Dec 13 2001, 23:06:05) 
[GCC egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)] on linux2
Type “copyright”, “credits” or “license” for more information.
>>> [].__methods__
Traceback (most recent call last):
  File “<stdin>”, line 1, in ?
AttributeError: ‘list’ object has no attribute ‘__methods__’
>>>

但在新的提案中,__methods__属性不再存在了。

Instead, you can get the same information from the dir() function, which gives more information:

>>> dir([])
['__add__', '__class__', '__contains__', '__delattr__',
'__delitem__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__',
'__imul__', '__init__', '__le__', '__len__', '__lt__', '__mul__',
'__ne__', '__new__', '__reduce__', '__repr__', '__rmul__',
'__setattr__', '__setitem__', '__setslice__', '__str__', 'append',
'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse',
'sort']
>>>

然而你可以使用dir()函数得到同样的信息,它还会给你更多的信息。

The new dir() gives more information than the old one: in addition to the names of instance variables and regular methods, it also shows the methods that are normally invoked through special notations, like __iadd__ (+=), __len__ (len), __ne__ (!=).

新的dir()给出的信息比新的更多:除了给出实例变量的名称和正常的方法以外,它还给出通常是通过特别标记来调用的方法,如:__iadd__(+=), __len__(len), __ne__(!=)。

More about the new dir() function:

  • dir() on an instance (classic or new-style) shows the instance variables as well as the methods and class attributes defined by the instance’s class and all its base classes. 

    对一个实例(classic或new-style)使用dir()会显示实例变量的方法也包括定义在实例变量所对应的类和基类的类属性。(类变量和类方法都是类属性。)

  • dir() on a class (classic or new-style) shows the contents of the __dict__ of the class and all its base classes. It does not show class attributes that are defined by a metaclass. 

    对一个类(classic或new-style)使用dir()会显示类的__dict__中的内容,以及所有基类中__dict__中的内容(也就是类属性)。但并不显示通过metaclass来定义的属性。

  • dir() on a module shows the contents of the module’s __dict__. (This is unchanged.)

  • dir() without arguments shows the caller’s local variables. (Again, unchanged.)

  • There’s a new C API that implements the dir() function: PyObject_Dir().

  • There are more details; in particular, for objects that override __dict__ or __class__, these are honored, and for backwards compatibility, __members__ and __methods__ are honored if they are defined.

You can use a method of a built-in type as an “unbound method”:

>>> a = ['tic', 'tac']
>>> list.__len__(a)          # same as len(a)
2
>>> list.append(a, ‘toe’)    # same as a.append(‘toe’)
>>> a
['tic', 'tac', 'toe']
>>>

This is just like using an unbound method of a user-defined class – and similarly, it’s mostly useful from inside a subclass method, to call the corresponding base class method.

使用未绑定(unbound)的方法在一个子类中调用基类的相应方法时用得最多。unbound与bound的区别我的理解就是:是否与一个实例绑定。一般在一个类中定义的方法,如果是通过类名进行调用,那么这种调用方式就是未绑定的。如果是通过实例来调用,则是已绑定。类函数有一个属性是im_self,如果为空则未被绑定。如果为实例对象,则为已绑定。因此,直接通过类来调用类方法时,需要传入一个实例对象。这就是我们常用的调用基类的方式。

Unlike user-defined classes, you cannot change built-in types: attempts to assign an attribute of a built-in type raises a TypeError, and their __dict__ is a read-only proxy object. The restriction on attribute assignment is lifted for new-style user-defined classes, including subclasses of built-in types; however even those have a read-only __dict__ proxy, and you must use attribute assignment to replace or add a method of a new-style class. Example session:

不象用户自定义的类,你不能改变built-in type:试图向一个build-in type的属性(已存在的)赋值会引发一个TypeError异常,而且它们的__dict__是一个只读的proxy(代理)对象。对属性赋值的限制也对new-style的用户自定义的类起作用,包括从built-in type派生的子类。然而尽管有着只读的__dict__代理,你也必需使用属性赋值来向new-style class替换或添加方法。(好奇怪,看下面的例子)

>>> list.append            #list有一个append方法
<method ‘append’ of ‘list’ objects>
>>> list.append = list.append    #不可以对built-in list的属性作出修改
Traceback (most recent call last):
  File “<stdin>”, line 1, in ?
TypeError: can’t set attributes of built-in/extension type ‘list’
>>> list.answer = 42  #也不可以向list增加新的属性
Traceback (most recent call last):
  File “<stdin>”, line 1, in ?
TypeError: can’t set attributes of built-in/extension type ‘list’
>>> list.__dict__['append'] #在list中的__dict__中可以找到append方法
<method ‘append’ of ‘list’ objects>
>>> list.__dict__['answer'] = 42  #list的__dict__是只读的
Traceback (most recent call last):
  File “<stdin>”, line 1, in ?
TypeError: object does not support item assignment
>>> class L(list):  #从list派生了一个子类
…     pass
… 
>>> L.append = list.append  #可以对子类使用属性赋值
>>> L.answer = 42
>>> L.__dict__['answer']   #L的__dict__中有新加的属性
42
>>> L.__dict__['answer'] = 42  #但直接使用L的__dict__是不允许的
Traceback (most recent call last):
  File “<stdin>”, line 1, in ?
TypeError: object does not support item assignment
>>>

For the curious: there are two reasons why changing built-in classes is disallowed. First, it would be too easy to break an invariant of a built-in type that is relied upon elsewhere, either by the standard library, or by the run-time code. Second, when Python is embedded in another application that creates multiple Python interpreters, the built-in class objects (being statically allocated data structures) are shared between all interpreters; thus, code running in one interpreter might wreak havoc on another interpreter, which is a no-no.

为什么不允许修改built-in class有两个原因。第一,会很容易破坏那些依赖这些built-in type的不变 性的标准库或运行代码。第二,当Python嵌入到另一种创建多个Python解释器的应用程序中时,built-in class对象(静态分配数据结构)是在所有解释器中 共享的,因此运行在一个解释器中的代码可能会别的解释器造成破坏,这是要禁止的。

经过我的测试,的确象上样说的一样(注意都是对于type或类来说的,不是指实例):

  • 对于built-in type,不可以增加新的属性,不可以修改存在的属性。通过__dict__可以读出,不能修改,因为它是dictproxy类型,是只读的。
  • 对于new-style class或从built-in派生来的子类,可以通过属性赋值来增加属性和替换存在的属性。可以通过__dict__来读出属性,不能直接通过__dict__来增加和修改属性,因为它是只读的。
  • 实例的__dict__不是只读的,因此可以使用。

想一想的确是有道理。类是实例的模板,它如果可以容易改变的话很可能会造成一些混乱,特别是如果可以对内置类型修改的话。最好的办法是派生不同的类。这样首先要对类进行规划,然后允分地利用实例来解决问题。 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值