注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

python操作的数据从何而来  

2017-10-21 15:28:41|  分类: python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、python操作的输入数据从哪里来
作为一种实用的语言,它必定需要和很多周边系统进行交互,而交互的时候就需要语言从外部获得信息,这一点对于python同样也不例外。虽然严格来说,这些内容并不是python语言特性的一部分,但是毕竟python语言的特性还是决定了将问题模型化和解决的基本思路和特质,所以这里还是把它作为python的一个特性来看待。
考虑下大家最为熟悉的C语言,最常见的输入是命令行、套接字、文件等输入,但是由于C语言是伴随着Unix系统的流行而流行,而Unix通常会尝试把所有的内容都看作文件,所以可以简单的认为所有的输入都是基于文件来生成。
二、python中"鸭子类型"的由来
C/C++类型的语言,在编译时会根据语法动作将其直接转换为对应的机器指令;在python中,起始同样是根据语法动作将其转换为对应的虚拟机指令。例如下面的代码对应的虚拟机指令
tsecer@harry: cat duck.py 
x[1] = 10
z1, z2, z3 = y(1)
tsecer@harry: python -m dis duck.py 
  1           0 LOAD_CONST               0 (10)
              3 LOAD_NAME                0 (x)
              6 LOAD_CONST               1 (1)
              9 STORE_SUBSCR        

  2          10 LOAD_NAME                1 (y)
             13 LOAD_CONST               1 (1)
             16 CALL_FUNCTION            1
             19 UNPACK_SEQUENCE          3
             22 STORE_NAME               2 (z1)
             25 STORE_NAME               3 (z2)
             28 STORE_NAME               4 (z3)
             31 LOAD_CONST               2 (None)
             34 RETURN_VALUE        
tsecer@harry: 
从上面的代码中可以看到,在源代码中对于使用的所有变量都没有声明它们的类型而直接使用它们。在不知道变量类型的时候,语法中的操作动作决定了对应的虚拟机指令。或者说,在C语言中,是首先看语法动作(例如函数调用、数组元素赋值,列表赋值等),然后看操作类型是否支持该操作动作;而在python中,它只关注语法动作,无脑的根据语法动作直接生成对应的虚拟机指令。
也就是说,这里其实是语法动作决定了对应的虚拟机指令,它不对类型做假设,只是在运行的时候来完成真正的动作执行。这样看来,python生成的虚拟机指令是一种和原始脚本语言更加接近的指令类型,通过这些指令,它们可以在运行时获得不同类型的对应接口,而完成这种功能的基础是所有的类型都派生自相同的基类。事实上,即使表示python内部类型的元数据本身也派生自相同的PyObject基类。所有的类型都派生自相同的基类,对于虚拟机指令的生成有重要意义。例如在基于堆栈的虚拟机运行环境中,可以保证栈中所有元素有相同的接口,从而使虚拟机的运行环境涉及更加简洁。
三、python操作的输入从哪里来
既然python中的变量通常都是一个标签,并且不对变量类型进行假设,那么真正的数据容器或者说数据内容从哪里来呢?我的意思是,总不能大家都在玩金融,那么真正谁来做事业呢?python总是要有一些数据需要是外部输入或者自己创造出来的,而不能大家都是这种“标签式”的零和游戏。
1、以获得一个文件夹下所有文件名os.listdir为例说明
该命令的实现在Python-2.2.2\Modules\posixmodule.c中
static PyMethodDef posix_methods[] = {
……
{"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__},
{"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__},
{"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__},
……
{NULL, NULL}  /* Sentinel */
};
static PyObject *
posix_listdir(PyObject *self, PyObject *args)
{
……
if ((d = PyList_New(0)) == NULL)
return NULL;
……
return d;
}
从这个实现看,当调用os.listdir函数之后,起始是在解释器的对应指令返回一个内置的PyList结构,这个结构派生自PyObject结构,
2、派生自PyObject类的共同特性
从这个定义看,其中的ob_refcnt是用来进行垃圾回收的,所以可以暂时不用关注。比较重要的是它的_typeobject成员,这个执行了对象所属类型的大量原始信息。非正式的说,可以认为是一个C++对象的虚函数表,只不是这里指向的内容更加丰富。
这个_typeobject *ob_type结构的意义非常重大,它意味着每个PyObject对象都自带了自己可以运行时查找的操作集合,这也是python虚拟机运行环境的执行基础。

Python-2.2.2\Include\object.h
#define PyObject_HEAD \
int ob_refcnt; \
struct _typeobject *ob_type;
#define PyObject_HEAD_INIT(type) 1, type,
#endif /* !Py_TRACE_REFS */

#define PyObject_VAR_HEAD \
PyObject_HEAD \
int ob_size; /* Number of items in variable part */
 
typedef struct _object {
PyObject_HEAD
} PyObject;

3、再看下文件操作
3、1 open函数的实现
Python-2.2.2\Python\bltinmodule.c
PyObject *
_PyBuiltin_Init(void)
{
……
/* Note that open() is just an alias of file(). */
SETBUILTIN("open", &PyFile_Type);
SETBUILTIN("file", &PyFile_Type);
……
}
这里的注释说明,起始open和file对应的内容相同,都是一个PyFile_Type类型。
3、2 PyFile_Type的创建
当调用一个类型的构造函数时,如果该类型有tp_init接口则调用该接口
static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
……
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) &&
    type->tp_init != NULL &&
    type->tp_init(obj, args, kwds) < 0) {
Py_DECREF(obj);
obj = NULL;
}
……
}
而PyFile_Type类型的tp_init指向file_init函数,其中进行文件的打开。可以看到,其中操作的PyObject是一个PyFileObject类型。
static int
file_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyFileObject *foself = (PyFileObject *)self;
……
if (fill_file_fields(foself, NULL, name, mode, fclose) == NULL)
goto Error;
if (open_the_file(foself, name, mode) == NULL)
goto Error;
……
}
3、3 文件的读取
也就是说,调用read接口的时候,文件内容在下面的file_read接口中实现,并且返回的是一个string类型的PyObject对象实例。
static PyObject *
file_read(PyFileObject *f, PyObject *args)
{
……
v = PyString_FromStringAndSize((char *)NULL, buffersize);
if (v == NULL)
return NULL;
bytesread = 0;
for (;;) {
Py_BEGIN_ALLOW_THREADS
errno = 0;
chunksize = fread(BUF(v) + bytesread, 1,
  buffersize - bytesread, f->f_fp);
……
}
return v;
}
四、简单总结
也就是说,其实我们通过python操作的所有内容都是基于派生自PyObject的对象来完成的,它们自带操作表,而业务逻辑的大部分操作都是基于特定类型的特定接口来实现。

  评论这张
 
阅读(38)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018