获取指定成员
Python本身有getattr和setattr办法,作为根本的反射能力,能够用名字拜访类成员和实例成员。例如:
class A: class_mem_x: int = 8 def __init__(self, p): self.instance_mem_p = p # print('self.y in __init__: {}'.format(self.instance_mem_p))a = A(3)print('getattr instance_mem_p: {}'.format(getattr(a, 'instance_mem_p')))print('getattr class_mem_x: {}'.format(getattr(a, 'class_mem_x')))setattr(a, 'instance_mem_p', 5)print('a.instance_mem_p after setattr: {}'.format(a.instance_mem_p))
输入:
getattr instance_mem_p: 3getattr class_mem_x: 8a.instance_mem_p after setattr: 5
它们既能够拜访类成员,也能够拜访实例成员,就像间接用'.'拜访的成果一样。但这两个办法都必须指定成员名称,如果要写的代码不晓得传入的对象有哪些成员,就无奈获取了。
遍历实例成员
所以python还为每一个对象提供了__dict__外部变量(类型就是dict),用于获取所有实例成员:
print('a.__dict__:{}'.format(a.__dict__))
输入
a.__dict__:{'instance_mem_p': 5}
能够看进去,其实实例成员就是用一个dict存储的,这一点跟js很像。不过这个办法可取不到类成员,class A的类成员class_mem_x,就没在外面。
遍历类成员
python提供了一个叫dir的办法,能够获取对象的所有属性,返回后果是属性名称的数组:
print('dir(a):{}'.format(dir(a)))
输入
dir(a):['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'class_mem_x', 'instance_mem_p']
能够看出类属性和实例属性都在外面了,可厌恶的是, 所有内置属性也都列出来了。咱们要想剔除它们,就得用双划线这种字符串前后缀来辨别。即便剔除了内置属性,也没法区分类成员和实例成员,当然,联合下面__dict__内置属性的内容,再剔除实例成员,也能够失去类成员。
上面是一个小工具办法:
def get_class_attr_names(obj): return [attr_name for attr_name in dir(obj) if (not attr_name.startswith('__') and attr_name not in obj.__dict__)]print('get_class_attr_names: {}'.format(get_class_attr_names(a)))
输入
get_class_attr_names: ['class_mem_x']
下面的办法有个缺点:无奈排除实例办法,能够下一节获取类型再解决。
获取成员变量类型
貌似没有间接获取类型定义的办法,毕竟python不是强类型,而是所谓duck-typing语言,只能先获取成员属性的值,再用type函数判断类型了。type函数的根本用法如下:
type(a.class_mem_x)
输入
<class 'int'>
返回值是一个Types类型
那么能够用上面的小函数,来获取每个属性的类型:
def get_obj_attr_types(obj): return [(attr_name, type(getattr(obj, attr_name))) for attr_name in dir(obj) if not attr_name.startswith('__')]
输入
get_obj_attr_types: [('class_mem_x', <class 'int'>), ('instance_mem_p', <class 'int'>), ('test', <class 'method'>)]
返回值是一个tuple元素组成的数据,tuple由名称,类型对组成。
获取类成员改良
有了type函数判断类型,还能够对之前的get_class_attr_names做一点小改良——因为dir函数其实还会返回类办法,而__dict__内置属性里可没有类办法,要想把办法也剔除掉,就得借助类型判断,于是准确的类成员如下:
import sysfrom types import MethodTypeclass A: class_mem_x: int = 8 def __init__(self, p): self.instance_mem_p = p # print('self.y in __init__: {}'.format(self.instance_mem_p)) def test(self): passa = A(3)def get_class_attr_names(obj): return [ attr_name for attr_name in dir(obj) if ( not attr_name.startswith('__') and attr_name not in obj.__dict__ and type(getattr(obj, attr_name)) != MethodType ) ]print('get_class_attr_names: {}'.format(get_class_attr_names(a)))
输入
get_class_attr_names: ['class_mem_x']
留神: MethodType不是字符串,而是一个class,须要引入types模块。
get_type_hints获取类信息
从python 3.5当前,typing模块提供了更好的办法get_type_hints,来获取类信息。它的益处在于,能够间接获取类成员定义的类型——如果给定义为str,而赋值为None,也能正确获取类型。上面看一下和自定义实现的比照后果
import sysfrom types import MethodTypefrom typing import get_type_hintsclass A: class_mem_x: int = 8 class_mem_y: str = None def __init__(self, p): self.instance_mem_p = p # print('self.y in __init__: {}'.format(self.instance_mem_p)) def test(self): passa = A(3)def get_class_attr_names(obj): return [ attr_name for attr_name in dir(obj) if ( not attr_name.startswith('__') and attr_name not in obj.__dict__ and type(getattr(obj, attr_name)) != MethodType ) ]class_attr_names = get_class_attr_names(a)def get_all_types(obj, names): return [(name, type(getattr(obj, name))) for name in names]print('get_all_types: {}'.format(get_all_types(a, class_attr_names)))print('get_type_hints: {}'.format(get_type_hints(a)))sys.exit()
输入
get_all_types: [('class_mem_x', <class 'int'>), ('class_mem_y', <class 'NoneType'>)]get_type_hints: {'class_mem_x': <class 'int'>, 'class_mem_y': <class 'str'>}
从后果能够清晰看出差别:get_all_types 返回的y属性类型是NoneType,而get_type_hints返回的却是str