获取指定成员
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: 3
getattr class_mem_x: 8
a.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 sys
from types import MethodType
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))
def test(self):
pass
a = 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 sys
from types import MethodType
from typing import get_type_hints
class 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):
pass
a = 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