关于python:Python-反射总结

5次阅读

共计 3702 个字符,预计需要花费 10 分钟才能阅读完成。

获取指定成员

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

正文完
 0