python语法中的左值、右值和字符

您所在的位置:网站首页 python右移位 python语法中的左值、右值和字符

python语法中的左值、右值和字符

#python语法中的左值、右值和字符| 来源: 网络整理| 查看: 265

位置决定语义

在下面的python代码中,忽略掉语法错误,源码中同样一个单词tsecer在不同的位置有不同的意义

import之后

在import之后的tsecer是作为一个简单的字面字符串来处理:这里的意思是这个tsecer不会有任何变量(及相关展开)的意义,它更类似于C语言中的字符串,也就是字面量,在源代码中看到是什么显示就是什么。

import tsecer tsecer = tsecer + 1 等号右侧

这个让人不禁想起C语言中左值/右值的概念:在等号的右侧,同样的tsecer表示的并不是tsecer字符串本身,编译器需要将这个变量展开为对其具体内容的引用。由于这里只是引用,所以需要保证这个变量之前一定已经定义过。

等号左边

这个和右边取内容(dereference)的操作不同,这里更类似于是一个取地址的操作,也就是需要更新tsecer变量地址中的地址。或者对于赋值操作 lhs = rhs来说,它的语义是把rhs的内容存储/更新到lhs代表的地址中。

syntax tree visit

和通常的语言处理逻辑相同:python的语法树处理同样经过了符号表和语法分析,其中的符号表处理在symtable.c文件,而语法分析则在compile.c文件。这两个文件可以说是整个python语法分析最为关键的两个部分,这两个文件中的主体函数都是各种的visit函数,例如在符号表中有symtable_visit_expr函数,在语法分析中的compiler_visit_expr函数,两个函数的第二个参数都是相同的。从C++的视角来看,如果把第一个参数看做是类的话,那么它们操作的都是相同的数据类型。

symtable_visit_expr(struct symtable *st, expr_ty e) compiler_visit_expr(struct compiler *c, expr_ty e) import代码生成

对于import的生成,它生成的字节码只是从consts中读取内容,也就是把字后的NAME作为一个const(类似于C语言的字面字符串)处理,不会进行任何的变量展开。

static int compiler_import(struct compiler *c, stmt_ty s) { /* The Import node stores a module name like a.b.c as a single string. This is convenient for all cases except import a.b.c as d where we need to parse that string to extract the individual module names. XXX Perhaps change the representation to make this case simpler? */ Py_ssize_t i, n = asdl_seq_LEN(s->v.Import.names); for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.Import.names, i); int r; PyObject *level; level = PyLong_FromLong(0); if (level == NULL) return 0; ADDOP_O(c, LOAD_CONST, level, consts); Py_DECREF(level); ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP_NAME(c, IMPORT_NAME, alias->name, names); if (alias->asname) { r = compiler_import_as(c, alias->name, alias->asname); if (!r) return r; } else { identifier tmp = alias->name; Py_ssize_t dot = PyUnicode_FindChar( alias->name, '.', 0, PyUnicode_GET_LENGTH(alias->name), 1); if (dot != -1) { tmp = PyUnicode_Substring(alias->name, 0, dot); if (tmp == NULL) return 0; } r = compiler_nameop(c, tmp, Store); if (dot != -1) { Py_DECREF(tmp); } if (!r) return r; } } return 1; } 一个全局变量的load/store为例

下面是python字节码中对于全局变量存储和读取的操作,典型的场景下这个对应的赋值操作左侧和右侧变量访问时对应的机器码。这个代码其实也很直观,如果把字典看做一个map的话,load的时候是map.find(key),而store则是map.insert(key, value);

/// @file: Python-3.6.0\Python\ceval.c PyObject * _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) { ///.... TARGET(STORE_GLOBAL) { PyObject *name = GETITEM(names, oparg); PyObject *v = POP(); int err; err = PyDict_SetItem(f->f_globals, name, v); Py_DECREF(v); if (err != 0) goto error; DISPATCH(); } ///.... TARGET(LOAD_GLOBAL) { PyObject *name = GETITEM(names, oparg); PyObject *v; if (PyDict_CheckExact(f->f_globals) && PyDict_CheckExact(f->f_builtins)) { v = _PyDict_LoadGlobal((PyDictObject *)f->f_globals, (PyDictObject *)f->f_builtins, name); if (v == NULL) { if (!_PyErr_OCCURRED()) { /* _PyDict_LoadGlobal() returns NULL without raising * an exception if the key doesn't exist */ format_exc_check_arg(PyExc_NameError, NAME_ERROR_MSG, name); } goto error; } Py_INCREF(v); } else { /* Slow-path if globals or builtins is not a dict */ /* namespace 1: globals */ v = PyObject_GetItem(f->f_globals, name); if (v == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; PyErr_Clear(); /* namespace 2: builtins */ v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) format_exc_check_arg( PyExc_NameError, NAME_ERROR_MSG, name); goto error; } } } PUSH(v); DISPATCH(); } ///... } 为什么 tsecer + 1 = 1会有语法错误

从虚拟机实现来看,tsecer+1这个表达式在内部也是有唯一确定的内存地址的,所以这个赋值在操作上并不是实现不了,只是编译器在语法分析的时候拒绝了这种情况。

根本的原因在于虽然tsecer+1有一个唯一的内部变量地址,但是如果不把它放到一个可以再次访问的位置(例如变量中),下次如何再访问这个修改的值呢?也就是tsecer+1这个操作不是可重入的,下次执行它返回变量的地址可能就已经变化,所以前一次赋值并没有意义。

我们通常说,python这种动态语言的变量不需要声明就可以直接使用,其实从某种角度看,它还是需要声明(至少是一个形式上)的:声明的方式就是变量必须先在赋值之类的store操作中作为左值出现。

回到正题

由于import后面的内容只是字面量(而不会展开),所以如果想通过import来动态加载一个模块无法实现,也就是下面的语法是无法达到目的的。

tsecer@harry: cat -n import_var_mod.py 1 import sys 2 3 print(sys.argv) 4 5 modname = sys.argv[1] 6 import modname tsecer@harry: python import_var_mod.py tsecer ['import_var_mod.py', 'tsecer'] Traceback (most recent call last): File "import_var_mod.py", line 6, in import modname ImportError: No module named modname tsecer@harry:

这里给出了动态加载的操作方法

import importlib function_string = 'mypackage.mymodule.myfunc' mod_name, func_name = function_string.rsplit('.',1) mod = importlib.import_module(mod_name) func = getattr(mod, func_name) result = func() python语法中的左值、右值和字符的更多相关文章 i++和++i以及左值,右值

左值(LValue)和右值(RValue)的一个快捷记法是赋值运算,左值是赋值运算左边的值,右值就是右边(=,=废话).例如: int a = 5; a就是左值,5就是右值. 当然,如果真是这么个含义 ...

C++ 左值 右值

最近在研究C++ 左值 右值,搬运.收集了一些别人的资料,供自己记录和学习,若以后看到了更好的解释,会继续补充.(打“?”是我自己不明白的地方 )   参考: 完美转发

左值与右值 什么是左值?什么是右值? 在C++里没有明确定义.看了几个版本,有名字的是左值,没名字的是右值.能被&取地址的是左值,不能被&取地址的是右值.而且左值与右值可以发生转换. ...

左值&右值

一.引子 我们所谓的左值.右值,正确的说法应该是左值表达式.右值表达式. 因为C++的表达式不是左值就是右值. 在C中,左值指的是既能够出现在等号左边也能出现在等号右边的表达式,右值指的则是只能出现在 ...

C++11之右值引用(一):从左值右值到右值引用

C++98中规定了左值和右值的概念,但是一般程序员不需要理解的过于深入,因为对于C++98,左值和右值的划分一般用处不大,但是到了C++11,它的重要性开始显现出来. C++98标准明确规定: 左值是 ...

C语言几个术语: 数据对象,左值,右值

1. 数据对象 赋值表达式语句的目的是把值存储到内存位置上. 用于存储值的数据存储区域统称为数据对象. 2. 左值 左值是C语言的术语, 用于标识特定数据对象的名称或表达式. 对象指的是实际的数据存储 ...

c++ 左值右值 函数模板

1.先看一段代码,这就是一种函数模板的用法,但是红色的部分如果把a写成a++或者写成一个常量比如1,都是编译不过的,因为如果是a++的话,实际上首先是取得a的 值0,而0作为一个常量没有地址.写成1也 ...

C和C指针小记(八)-操作符、左值右值

1.移位操作符 移位操作符分为左移操作符() 对于无符号数:左右位移操作都是逻辑位移 对于有符号数:到底是采用逻辑位移还是算术位移取决于编译器.如果一个 ...

理解 ES6 语法中 yield 关键字的返回值

在 ES6 中新增了生成器函数的语法,本文解释了生成器函数内 yield 关键字的返回值. 描述 根据语法规范,yield 关键字用来暂停和继续执行一个生成器函数.当外部调用生成器的 next() 方 ...

Python 字典中一键对应多个值

#字典的一键多值 print'方案一 list作为dict的值 值允许重复' d1={} key=1 value=2 d1.setdefault(key,[]).append(value) value ...

随机推荐 MySQL 8.0 新特性-原子DDL

背景 MySQL 8.0 原子DDL 是一个复杂的过程,涉及比较多的模块,例如:MDL 锁,表定义缓存,行格式,Row Log,DDL Log,online 属性,表空间物理文件操作等.本文主要通过与 ...

SQLServer 查询所有外键关联表信息

有时候需要清除一些数据,但是总会有一些外键关联多加阻拦,下面是一些外键关联查询,方便更快捷有效的查询到需要处理的外键信息. 一.外键信息 查询列从左到右分别是: 外键约束名,子表名,外键列名,父表名 ...

vue实现图片上传且实时预览



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3