获取对象信息
拿到一个变量,除了用 isinstance() 判断它是否是某种类型的实例外,还有没有别的方法获取到更多的信息呢?
例如,已有定义:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name
首先可以用 type() 函数获取变量的类型,它返回一个 Type 对象:
>>> type(123)
<type 'int'>
>>> s = Student('Bob', 'Male', 88)
>>> type(s)
<class '__main__.Student'>
其次,可以用 dir() 函数获取变量的所有属性:
>>> dir(123) # 整数也有很多属性...
['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]
>>> dir(s)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score', 'whoAmI']
对于实例变量,dir()返回所有实例属性,包括__class__
这类有特殊意义的属性。注意到方法whoAmI
也是 s 的一个属性。
如何去掉__xxx__
这类的特殊属性,只保留我们自己定义的属性?回顾一下filter()函数的用法。
dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了:
>>> getattr(s, 'name') # 获取name属性
'Bob'
>>> setattr(s, 'name', 'Adam') # 设置新的name属性
>>> s.name
'Adam'
>>> getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'
>>> getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值20:
20
新式类和旧式类
python的新式类是2.2版本引进来的,我们可以将之前的类叫做经典类或者旧类。
为什么要在2.2中引进
new style class
呢?官方给的解释是:为了统一类(class)
和类型(type)
。
在2.2之前,比如2.1版本中,类和类型是不同的,
如
a
是ClassA
的一个实例,那么
a.__class__
返回class __main__.ClassA
,type(a)返回总是<type ‘instance‘>
。而引入新类后,比如
ClassB
是个新类,b
是ClassB
的实例,b.__class__
和type(b)
都是返回‘class ‘__main__.ClassB‘
,这样就统一了。
引入新类后,还有其他的好处,比如更多的内置属性将会引入,描述符的引入,属性可以来计算等等。为了向前兼容,默认情况下用户定义的类为经典类,新类需要继承自所有类的基类 object 或者继承自object的新类。
? 值得注意的地方是,虽然使用的是最新的python(2.7),但是一些特性不会在旧式类起作用。
? 所以,为了确保自己使用的是新式类,有以下方法:
? 把这个赋值语句放在类模块代码的最前面
__metaclass__ = type
(前面有提过)。? 自己的类都从内建类
object
直接或者间接地继承。? 如果不需要兼容旧式类,旧版本的类,那么就保持都是新式类。
? 当然,在
Python3
里面,不存在这些问题了,因为所有的类都是object
类的子类(隐式)。
# encoding:utf-8
# 新式类和旧式类的对比
class OldStyle:
def __init__(self, name, description):
self.name = name
self.description = description
#新类,可以在这里加 __metaclass__ = type
class NewStyle(object): #新类,也可以直接继承至object类
def __init__(self, name, description):
self.name = name
self.description = description
if __name__ == '__main__':
old = OldStyle('old', 'Old style class')
print(old.__class__)
print((type(old)))
print((dir(old)))
print('-------------------------------------------------------')
new = NewStyle('new', 'New style class')
print(new.__class__)
print((type(new)))
print((dir(new)))
运行结果:
__main__.OldStyle
<type 'instance'>
['__doc__', '__init__', '__module__', 'description', 'name']
-------------------------------------------------------
<class '__main__.NewStyle'>
<class '__main__.NewStyle'>
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'description', 'name']
[Finished in 0.1s]
多继承属性查找机制
如果将每个类看成一个点,众多的类就构成一张图,那么Python中继承的机制是就是一种搜索
那么地,这种搜索是深度优先搜索,还是广度优先搜索呢?
上述的问题也叫做mro
即method resolution order
,主要用于在多继承时判断调的属性的路径(来自于哪个类)。
在旧式类(在Python2.3之前)之中类的多继承机制是基于深度优先搜索,但在新式类(从Python2.3起)之中类的多继承机制是基于类似于广度优先搜索的C3算法。
C3算法
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
- 本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
- 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
在新式类中,查找一个要调用的函数或者属性的时候,是广度优先搜索的。
在旧式类当中,是深度优先搜索的。如下图所示:
Python中几种提供继承顺序的方法:
mro
方法__mro__
属性inspect
模块的getmro
方法
import inspect
class A:
pass
class B(A):
pass
print(B.mro())
print(B.__mro__)
print(inspect.getmro(B))
运行结果:
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
从上结果来看,以元组为结果的__mro__
和inspect.getmro
看来更让人满意
深度优先
和广度优先
- 旧式类的深度优先搜索的继承机制
# -*- coding:utf-8 -*-
from inspect import getmro
class D:
pass
class B(D):
pass
class C(D):
pass
class A(B, C):
pass
if __name__ == '__main__':
print(getmro(A))
运行结果:
(<class __main__.A at 0x0000000002CEC888>, <class __main__.B at 0x0000000002CECE88>,
<class __main__.D at 0x0000000002CEC8E8>, <class __main__.C at 0x0000000002CECD68>)
[Finished in 0.1s]
- 新式类的广度优先搜索的继承机制
# -*- coding:utf-8 -*-
from inspect import getmro
class D(object):
pass
class B(D):
pass
class C(D):
pass
class A(B, C):
pass
if __name__ == '__main__':
print(getmro(A))
运行结果:
(<class '__main__.A'>, <class '__main__.B'>,
<class '__main__.C'>, <class '__main__.D'>,
<type 'object'>)
[Finished in 0.1s]