获取指定成员

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