Last Updated on
什么是自省(introspection)?
自省就是面向对象的语言缩写的程序在运行时,能够知道对象的类型。
例如,Python,Java,C++等都有自省的能力,像Python不单可以知道是什么类型,还能知道有什么属性和方法。
这个也是python彪悍的特性.
在Python中,与自省相关的方法有很多,常见的有help(),dir(),type(),hasattr(),getattr(),setattr(),isinstance(),issubclass(),id(),callable()等,大致作用如下:
help() | 打印查看函数或模块用途的详细说明。 |
type() | 返回对象的类型。 |
dir() | 返回对象所有属性。 |
hasattr() | 判断对象是否有特定属性或方法。 |
getattr() | 返回对象的特定属性。 |
setattr() | 设置对象的属性为指定值,若属性没有,则会新增属性并赋值。 |
isinstance() | 判断一个对象是否是一个已知的类型。 |
issubclass() | 判断一个类是不是另一个类的子类。 |
id() | 返回对象的内存地址。 |
callable() | 用于检查一个对象是否是可调用的。 |
下面将依次详细的说明一下这些方法的使用。
help()
打印查看函数或模块用途的详细说明,用途较少,常用于IDE中编写代码时,查看以下模块或方法的使用说明。
import requests
help(requests.get)
# 输出结果:
Help on function get in module requests.api:
get(url, params=None, **kwargs)
Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
如上,就可以查看到此方法的使用说明,此方法使用的较少,平时可能我们更多会直接点进去查看源码中的说明。
type()
返回对象的类型,此方法不会考虑继承关系。
看示例:
class A(object):
name = 'amos'
class B(A):
pass
b = B()
print(type(b))
print(type(B))
# 输出结果:
<class '__main__.B'>
<class 'type'>
如上所示,使用type()可以直接得到示例b的类型,为类B,而不是类A,而类的类型则是默认的元类type。而如果我们在类B中,使用自定义的元类,那么type返回的就会是我们自定义的元类,而不是默认的元类type。如下:
class A(type):
pass
class B(metaclass=A):
pass
b = B()
print(type(b))
print(type(B))
# 输出结果:
<class '__main__.B'>
<class '__main__.A'>
关于Python的元类,不了解的请查看我的另一篇博文:
dir()
返回对象所有属性。
dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
print(dir())
print(dir([1,2,3]))
# 输出结果:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
可以看到上面输出了list列表的各种方法如append,clear等,还有各种默认的魔法方法。
hasattr()
判断对象是否有特定属性或方法,有则返回True,没有则返回False。
只要是对象能够引用的属性和方法,只要有就会返回True,可以来自于继承或者动态绑定等。以下的getattr,setattr等都是涉及到对象的属性,关于Python的类属性和实例属性,不了解的可以查看我另一篇博文:
class A(object):
name = 'Amos'
age = 24
def work(self):
pass
@classmethod
def play(cls):
pass
class B(A):
def __init__(self):
self.sex = 'male'
b = B()
print(hasattr(b, 'sex'))
print(hasattr(b, 'name'))
print(hasattr(B, 'play'))
# 输出结果:
True
True
True
如上所示,不管是否是通过继承,只要对象有的属性和方法,就会返回True。这在程序中判断对象是否能具有某些属性和方法非常好用。
查看hasattr函数的源码解释,如下:
getattr()
返回对象的特定属性。
与hasatter类似,获取对象的属性或方法,同样是可以来自继承或动态定义。
先开看一下此方法的源码解释:
使用示例:
class B(object):
def __init__(self):
self.sex = 'male'
def work(self):
print("I'm working")
b = B()
print(getattr(b, 'sex'))
print(getattr(b, 'work'))
getattr(b, 'work')()
print(getattr(b, 'name', None))
print(getattr(b, 'name'))
# 输出结果:
male
<bound method B.work of <__main__.B object at 0x10b4bee80>>
I'm working
None
AttributeError: 'B' object has no attribute 'name'
如上所示,getattr方法可以直接返回对象的方法或属性,如源码中说明的一样,可以自定义默认返回参数,否则,若找不到属性则报错AttributeError。
setattr()
设置对象的属性为指定值,若指定的属性没有,则会新增属性并赋值。
还是先来看下源码中的说明:
看下面示例:
class B(object):
def __init__(self):
self.sex = 'male'
def work(self):
print("I'm working")
b = B()
setattr(b, 'name', 'Amos')
setattr(B, 'name', 'class_name')
setattr(b, 'sex', 'unkown')
print(b.name)
print(B.name)
print(b.sex)
# 数据结果:
Amos
class_name
unkown
如上所示,setattr设置对象的属性,如果对象是实例,则设置实例属性,对象是类,则设置类属性。
isinstance()
判断一个对象是否是一个已知的类型,是则返回True,否则返回False。
此方法类似于type(),但是此方法考虑继承关系,type()不考虑继承关系。
先来看下其源码中的说明:
看下面示例:
class A(type):
pass
class B(metaclass=A):
pass
class C(B):
pass
c = C()
print(isinstance(c, C))
print(isinstance(c, B))
print(isinstance(C, type))
# 输出结果:
True
True
True
如上所示,isinstance()方法会考虑继承关系,只要对象属于此类的实例或者此类子类的实例,都会返回True,即使是自定义的元类。由于自定义的元类继承自type元类,所以也是会返回True,所以所有类通过isinstance(classs_name, type)都会返回True。
当然,除此之外,isinstance方法还可以用于判断Python中所有的数据类型,即number,string,tuple,list,set,dict。
因为在Python中,这些所有的数据类型都是类,比如定义一个string字符串,就相当于用str类创建了一个实例。
print(isinstance(123, int))
print(isinstance(12.3, float))
print(isinstance(False, bool))
print(isinstance(1+2j, complex))
print(isinstance('da', str))
print(isinstance([], list))
print(isinstance((), tuple))
print(isinstance({}, dict))
print(isinstance(set(), set))
# 输出结果都为True:
True
True
True
True
True
True
True
True
True
issubclass()
判断一个类是不是另一个类的子类,是则返回True,否则返回False。
先来看以下源码中都说明:
看下面示例:
class A(object):
pass
class B(A):
pass
class C(B):
pass
class D(object):
pass
print(issubclass(B, A))
print(issubclass(C, A))
print(issubclass(C, (D, A)))
# 输出结果:
True
True
True
如上所示,此方法用法也是比较简单。跟isinstance()类似,第二个参数都可以跟元组。
id()
返回对象的内存地址
id()方法比较简单,就是返回对象的内存地址,通过内存地址可以判断变量是否相同。
看下面示例:
a = 1
b = 1
print(id(a))
print(id(b))
print(a is b)
print(a == b)
c = []
d = []
print(id(c))
print(id(d))
print(c is d)
print(c == d)
# 输出结果:
4437579712
4437579712
True
True
4441502088
4441501960
False
True
如上所示,你会发现有趣的一点,对于不可变对象,相同值的内存地址是一样的,通过is方法比较内存地址会发挥True。对于可变对象,即使值是相同的,但是实际上的内存地址是不同的,所以is方法判断返回False,但是通过==来判断值则是相同的,所以返回True。
callable()
用于检查一个对象是否是可调用的。如果返回 True,object 仍然可能调用失败;但如果返回 False,调用对象 object 绝对不会成功。
这个方法平时可能不是很常见,我们先来看下源码的说明:
看下面示例:
def a():
pass
class B(object):
pass
class C(object):
def __call__(self, *args, **kwargs):
pass
print(callable(a))
print(callable(B))
print(callable(B()))
print(callable(C()))
# 输出结果:
True
True
False
True
如上所示,类是可调用的,用于生成实例,而如果定义了实例的__call__方法,则实例也是可调用的,也会返回True。 而字符串,数字等对象不可调用,同样是因为str,int这些类中没有定义__call__方法。