python inspect解析

inspect用法,用于判断python的对象的类型.

ismodule

{}
def ismodule(object):
    """Return true if the object is a module.

    Module objects provide these attributes:
        __doc__         documentation string
        __file__        filename (missing for built-in modules)"""
    return isinstance(object, types.ModuleType)

isclass

def isclass(object):
    """Return true if the object is a class.

    Class objects provide these attributes:
        __doc__         documentation string
        __module__      name of module in which this class was defined"""
    return isinstance(object, (type, types.ClassType))

ismethod

用来判断一个方法是否是一个类的成员方法

def ismethod(object):
    """Return true if the object is an instance method.

    Instance method objects provide these attributes:
        __doc__         documentation string
        __name__        name with which this method was defined
        im_class        class object in which this method belongs
        im_func         function object containing implementation of method
        im_self         instance to which this method is bound, or None"""
    return isinstance(object, types.MethodType)

ismethoddescriptor

def ismethoddescriptor(object):
    """Return true if the object is a method descriptor.

    But not if ismethod() or isclass() or isfunction() are true.

    This is new in Python 2.2, and, for example, is true of int.__add__.
    An object passing this test has a __get__ attribute but not a __set__
    attribute, but beyond that the set of attributes varies.  __name__ is
    usually sensible, and __doc__ often is.

    Methods implemented via descriptors that also pass one of the other
    tests return false from the ismethoddescriptor() test, simply because
    the other tests promise more -- you can, e.g., count on having the
    im_func attribute (etc) when an object passes ismethod()."""
    return (hasattr(object, "__get__")
            and not hasattr(object, "__set__") # else it's a data descriptor
            and not ismethod(object)           # mutual exclusion
            and not isfunction(object)
            and not isclass(object))

isdatadescriptor

def isdatadescriptor(object):
    """Return true if the object is a data descriptor.

    Data descriptors have both a __get__ and a __set__ attribute.  Examples are
    properties (defined in Python) and getsets and members (defined in C).
    Typically, data descriptors will also have __name__ and __doc__ attributes
    (properties, getsets, and members have both of these attributes), but this
    is not guaranteed."""
    return (hasattr(object, "__set__") and hasattr(object, "__get__"))

ismemberdescriptor(object)

if hasattr(types, 'MemberDescriptorType'):
    # CPython and equivalent
    def ismemberdescriptor(object):
        """Return true if the object is a member descriptor.

        Member descriptors are specialized descriptors defined in extension
        modules."""
        return isinstance(object, types.MemberDescriptorType)
else:
    # Other implementations
    def ismemberdescriptor(object):
        """Return true if the object is a member descriptor.

        Member descriptors are specialized descriptors defined in extension
        modules."""
        return False

isgetsetdescriptor

if hasattr(types, 'GetSetDescriptorType'):
    # CPython and equivalent
    def isgetsetdescriptor(object):
        """Return true if the object is a getset descriptor.

        getset descriptors are specialized descriptors defined in extension
        modules."""
        return isinstance(object, types.GetSetDescriptorType)
else:
    # Other implementations
    def isgetsetdescriptor(object):
        """Return true if the object is a getset descriptor.

        getset descriptors are specialized descriptors defined in extension
        modules."""
        return False

isfunction

def isfunction(object):
    """Return true if the object is a user-defined function.

    Function objects provide these attributes:
        __doc__         documentation string
        __name__        name with which this function was defined
        func_code       code object containing compiled function bytecode
        func_defaults   tuple of any default values for arguments
        func_doc        (same as __doc__)
        func_globals    global namespace in which this function was defined
        func_name       (same as __name__)"""
    if _signature_is_functionlike(object):
        return True
    return isinstance(object, types.FunctionType)

def _signature_is_functionlike(obj):
    if not callable(obj) or isclass(obj):
        return False
    return type(obj).__name__ == 'cython_function_or_method'

isgeneratorfunction

def isgeneratorfunction(object):
    """Return true if the object is a user-defined generator function.

    Generator function objects provides same attributes as functions.

    See help(isfunction) for attributes listing."""
    return bool((isfunction(object) or ismethod(object)) and
                object.func_code.co_flags & CO_GENERATOR)

istraceback

def istraceback(object):
    """Return true if the object is a traceback.

    Traceback objects provide these attributes:
        tb_frame        frame object at this level
        tb_lasti        index of last attempted instruction in bytecode
        tb_lineno       current line number in Python source code
        tb_next         next inner traceback object (called by this level)"""
    return isinstance(object, types.TracebackType)

isframe

栈帧表示程序运行时函数调用栈中的某一帧。函数没有属性可以获取它,因为它在函数调用时才会产生,而生成器则是由函数调用返回的,所以有属性指向栈帧。获取某个函数的相关的栈帧,则必须在调用这个函数且这个函数尚未放回时获取。通过sys.getframe()函数或者inspect模块的currentframe()函数获取当前栈帧。属性为只读

def isframe(object):
    """Return true if the object is a frame object.

    Frame objects provide these attributes:
        f_back          next outer frame object (this frame's caller) 调用栈的前一帧
        f_builtins      built-in namespace seen by this frame
        f_code          code object being executed in this frame 栈帧对应的code对象
        f_exc_traceback traceback if raised in this frame, or None
        f_exc_type      exception type if raised in this frame, or None
        f_exc_value     exception value if raised in this frame, or None
        f_globals       global namespace seen by this frame
        f_lasti         index of last attempted instruction in bytecode
        f_lineno        current line number in Python source code
        f_locals        local namespace seen by this frame
        f_restricted    0 or 1 if frame is in restricted execution mode
        f_trace         tracing function for this frame, or None"""
    return isinstance(object, types.FrameType)
// 每个栈用PyFrameObject结构表示
typedef struct _frame {
    PyObject_VAR_HEAD
    struct _frame *f_back;     // 前一个运行栈,调用方
    PyCodeObject *f_code;      // 执行的PyCodeObject对象
    PyObject *f_builtins;      // builtins环境变量集合
    PyObject *f_globals;       // globals全局变量集合
    PyObject *f_locals;        // locals本地变量集合
    PyObject **f_valuestack;   // 栈起始地址,最后一个本地变量之后
    PyObject **f_stacktop;     // 栈针位置,指向栈中下一个空闲位置
    PyObject *f_trace;         // trace函数
    PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;  // 记录异常处理
    PyThreadState *f_tstate;   // 当前的线程
    int f_lasti;		       // 当前执行的字节码的地址
    int f_lineno;		       // 当前的行号
    int f_iblock;		       // 一些局部block块
    PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
    PyObject *f_localsplus[1];	// 栈地址,大小为 本地变量+co_stacksize
} PyFrameObject;

image 当执行函数调用时会进入新的栈帧,那么当前栈帧就作为下一个栈帧的f_back字段。
image 多个栈帧链属于一个线程,而同时可能存在多个线程,每个线程拥有一个栈帧链。这样形成了Python的虚拟机运行环境。
image

参考:https://fanchao01.github.io/blog/2016/11/13/python-pycode_and_frame/

iscode

代码块由类源代码、函数源代码或者简单的代码编译。code属性全部是只读的。

  • co_argcount:普通参数的总数,不包括* 参数和 ** 参数
  • co_names:所有的参数名(包括* 参数和 ** 参数)和局部变量名的元组,
  • co_varnames:所有局部变量的元组。
  • co_filename: 源代码所在的文件名。
  • co_flags:一个数值,每一个二进制度包含了特定的信息。 0b100(0x4)和0b1000(0x8),如果co_flags & 0b100 != 0,说明使用了* args参数;如果co_flags & 0b1000 != 0,说明使用了**kwargs参数。如果co_flags & 0b100000(0x20) != 0,则说明这是一个生成器函数(generator function)
def iscode(object):
    """Return true if the object is a code object.

    Code objects provide these attributes:
        co_argcount     number of arguments (not including * or ** args)
        co_code         string of raw compiled bytecode
        co_consts       tuple of constants used in the bytecode
        co_filename     name of file in which this code object was created
        co_firstlineno  number of first line in Python source code
        co_flags        bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
        co_lnotab       encoded mapping of line numbers to bytecode indices
        co_name         name with which this code object was defined
        co_names        tuple of names of local variables
        co_nlocals      number of local variables
        co_stacksize    virtual machine stack space required
        co_varnames     tuple of names of arguments and local variables"""
    return isinstance(object, types.CodeType)
typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置参数个数 */
    int co_nlocals;         /* 局部变量个数 */
    int co_stacksize;       /* 栈大小 */
    int co_flags;
    PyObject *co_code;      /* 字节码指令序列 */
    PyObject *co_consts;    /* 所有常量集合 */
    PyObject *co_names;     /* 所有符号名称集合 */
    PyObject *co_varnames;  /* 局部变量名称集合 */
    PyObject *co_freevars;  /* 闭包用的的变量名集合 */
    PyObject *co_cellvars;  /* 内部嵌套函数引用的变量名集合 */

    /* The rest doesn’t count for hash/cmp */
    PyObject *co_filename;  /* 代码所在文件名 */
    PyObject *co_name;      /* 模块名|函数名|类名 */
    int co_firstlineno;     /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;    /* 字节码指令和行号的对应关系 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

加载模块时,模块对应的PyCodeObject对象被写入.pyc文件,格式:

block type description
MAGIC long 魔数,区分不同版本的python
MTIME long 修改时间
TYPE_CODE byte PyCodeObject对象
co_argcount long 对应PyCodeObject结构体各个域
co_nlocals long  
co_stacksize long  
co_flags long  
TYPE_STRING byte “字符串表示方法,对应PyCodeObject中的co_code”
co_code size long  
co_code value bytes  
TYPE_LIST byte 列表
co_const size long 列表co_consts的元素个数
TYPE_INT byte co_const[0]是一个整型
co_const[0] long  
TYPE_STRING byte co_consts[1]是一个字符串
co_consts[1] size long  
co_consts[1] value bytes  
TYPE_CODE byte co_consts[2]是一个PyCodeObject对象,对应的代码可能是一个函数或者类
co_consts[2]    

co_flags

#define CO_OPTIMIZED	0x0001
#define CO_NEWLOCALS	0x0002
#define CO_VARARGS	0x0004
#define CO_VARKEYWORDS	0x0008
#define CO_NESTED       0x0010
#define CO_GENERATOR    0x0020
/* The CO_NOFREE flag is set if there are no free or cell variables.
   This information is redundant, but it allows a single flag test
   to determine whether there is any extra work to be done when the
   call frame it setup.
*/
#define CO_NOFREE       0x0040

#if 0
/* This is no longer used.  Stopped defining in 2.5, do not re-use. */
#define CO_GENERATOR_ALLOWED    0x1000
#endif
#define CO_FUTURE_DIVISION    	0x2000
#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */
#define CO_FUTURE_WITH_STATEMENT  0x8000
#define CO_FUTURE_PRINT_FUNCTION  0x10000
#define CO_FUTURE_UNICODE_LITERALS 0x20000

/* This should be defined if a future statement modifies the syntax.
   For example, when a keyword is added.
*/
#if 1
#define PY_PARSER_REQUIRES_FUTURE_KEYWORD
#endif
def f(x, y = 10, z = 16):
    print z

dir(f) —> [‘func_closure’, ‘func_code’, ‘func_defaults’, ‘func_dict’, ‘func_doc’, ‘func_globals’, ‘func_name’] 在funcobject.c中对PyFunction_Type的定义:

static PyMemberDef func_memberlist[] = {
    {"func_closure", T_OBJECT, OFF(func_closure), RESTRICTED|READONLY},
    {"func_doc",     T_OBJECT, OFF(func_doc), WRITE_RESTRICTED},
    {"__doc__",      T_OBJECT, OFF(func_doc), WRITE_RESTRICTED},
    {"func_globals", T_OBJECT, OFF(func_globals), RESTRICTED|READONLY},
    {"__module__",   T_OBJECT, OFF(func_module), WRITE_RESTRICTED},
    {NULL}  /* Sentinel */
};

static PyGetSetDef func_getsetlist[] = {
    {"func_code", (getter)func_get_code, (setter)func_set_code},
    {"func_defaults", (getter)func_get_defaults, (setter)func_set_defaults},
    {"func_dict", (getter)func_get_dict, (setter)func_set_dict},
    {"__dict__", (getter)func_get_dict, (setter)func_set_dict},
    {"func_name", (getter)func_get_name, (setter)func_set_name},
    {"__name__", (getter)func_get_name, (setter)func_set_name},
    {NULL} /* Sentinel */
};

f.func_defaults记录的是函数的默认值,tuple(10, 16)

f.func_code是一个code对象,对应PyCodeObject,有三个co_varnames, co_freevars, co_cellvars,分别对应了 local variables, free variables, cell variables.free variables指enclosing scope中的变量,cell variables则是指会被多个scope访问的变量。

def foo():
    a = 5
    def bar():
        return a
    print "cellvars:", bar.func_code.co_cellvars
    print "freevars:", bar.func_code.co_freevars
    return bar

g = foo()
print foo.func_code.co_freevars
print foo.func_code.co_cellvars

LOAD_GLOBAL在代码对象的co_names属性中寻找变量名。
LOAD_FAST和STORE_FAST在代码对象的co_varnames属性中寻找变量名。
注意这些命令的参数都为整数,变量名是通过将参数作为下标从对应的变量名元组中获得的。所以:“LOAD_GLOBAL 0”相当于载入f1.func_globals[f1.func_code.co_names[0]]对象。对于局域变量,Python做了尽可能的优化处理,因此它使用LOAD_FAST和STORE_FAST对局域变量进行存取,它们可以通过其参数直接存取一个用来保存局域变量的C语言数组。

isbuiltin

def isbuiltin(object):
    """Return true if the object is a built-in function or method.

    Built-in functions and methods provide these attributes:
        __doc__         documentation string
        __name__        original name of this function or method
        __self__        instance to which a method is bound, or None"""
    return isinstance(object, types.BuiltinFunctionType)

isroutine

def isroutine(object):
    """Return true if the object is any kind of function or method."""
    return (isbuiltin(object)
            or isfunction(object)
            or ismethod(object)
            or ismethoddescriptor(object))

isabstract

def isabstract(object):
    """Return true if the object is an abstract base class (ABC)."""
    return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT)

getmembers

def getmembers(object, predicate=None):
    """Return all members of an object as (name, value) pairs sorted by name.
    Optionally, only return members that satisfy a given predicate."""
    results = []
    for key in dir(object):
        try:
            value = getattr(object, key)
        except AttributeError:
            continue
        if not predicate or predicate(value):
            results.append((key, value))
    results.sort()
    return results

getmro

def getmro(cls):
    "Return tuple of base classes (including cls) in method resolution order."
    if hasattr(cls, "__mro__"):
        return cls.__mro__
    else:
        result = []
        _searchbases(cls, result)
        return tuple(result)