Python 作用域,python定义域
本文主要为大家介绍Python作用域和命名空间的源代码学习笔记。有需要的朋友可以参考一下,希望能有所帮助。祝大家进步很大,早日升职加薪。溴
00-1010范围和名称空间1。名称绑定1.1赋值1.2模块导入1.3函数和类定义1.4作为关键字2。作用域2.1静态作用域2.2划分作用域2.3关闭作用域2.4类作用域2.5复杂嵌套2.5.1函数嵌套类2.5.2类嵌套类3 .命名空间3.1全局3.2局部3.3包含
目录
问题:
圆周率=3.14
定义圆_面积(r):
返回PI * r ** 2
类人(对象):
def __init__(self,name):
self.name=name
def say(self):
打印(“我是”,self.name)
以这个节目为例。代码中出现的每个变量的范围是什么?程序中涉及到多少个名称空间?Python是按什么顺序找变量的?
作用域与名字空间
1. 名字绑定
在Python中,变量只是一个绑定在实际对象上的名字,变量的定义本质上是建立名字和对象之间的约束关系。所以赋值语句本质上就是建立这样一种约束关系,把右边的对象和左边的名字绑定起来:
a=1
赋值语句是将名称绑定到对象的最基本方法,还有许多其他方法可以做到这一点。
1.1 赋值
当我们导入一个模块时,我们也在当前上下文中创建一个名称,并将其绑定到导入的对象。
#在当前上下文中创建一个名称测试,并将其绑定到导入的模块对象。
导入测试
1.2 模块导入
#函数名circle_area绑定到函数对象。
定义圆_面积(r):
返回PI * r ** 2
#类名Person被绑定到类型object
类人(对象):
def __init__(self):
及格
1.3 函数、类定义
#将名称T绑定到模块对象
将测试作为测试导入
1.4 as关键字
问题:当我们介绍一个名字时,它的可视范围有多大?
a=1
定义函数1():
打印(一)# 1
定义函数2():
a=2
打印(一)# 2
打印(一)# 1
不同代码区域引入的名称有不同的影响。第1行中定义的A可以影响func1,但func2中定义的A不能。此外,一个名称可以在多个代码区域中定义,但最终只能在一个代码区域中使用。
一个。
2.1 静态作用域
一个名字能够施加影响的程序正文区域,便是该名字的作用域。在Python中,一个名字在程序中某个区域能否起作用,是由名字引入的位置决定的,而不是运行时动态决定的。因此,Python具有静态作用域,也称为词法作用域。那么,作用域具体是如何划分的呢?
2.2 划分作用域
- Python在编译时,根据语法规则将代码划分为不同的代码块,每个代码块形成一个作用域。首先,整个.py文件构成最顶层的作用域,这就是全局作用域,也成为模块作用域;其次,当代码遇到函数定义,函数体成为当前作用域的子作用域;再者,当代码遇到类定义,类定义体成为当前作用域的子作用域。
- 一个名字在某个作用域引入后,它的影响范围就被限制在该作用域内。其中,全局作用域对所有直接或间接内嵌于其中的子作用域可见;函数作用域对其直接子作用域可见,并且可以传递。
- 例子中的作用域的嵌套关系如下:
访问关系如下:
2.3 闭包作用域
闭包的概念:在计算机科学中,闭包,又称词法闭包或函数闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例
代码示例:
>>> pi = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
print(name, pi * r * r)
return circle_area
>>> circle_area1 = closure_print("circle1: ")
>>> circle_area2 = closure_print("circle2: ")
>>> circle_area1(1)
circle1: 3.14
>>> circle_area2(2)
circle2: 12.56
划分作用域:
思考:circle_area1和circle_area2函数对象是怎么拿到name的?
2.4 类作用域
代码示例:
>>> language = chinese>>> class Male:
gender: str = male
def __init__(self, name: str):
self.name = name
def Speak(self):
print(i speak, language)
def Gender(self):
print(i am, gender)
>>> male = Male(zhangsan)
>>> male.Gender()
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
male.Gender()
File "<pyshell#9>", line 8, in Gender
print(i am, gender)
NameError: name gender is not defined
>>> male.Speak()
i speak chinese
作用域分析:
全局作用域对其他所有内嵌其中的作用域均可见,所以在函数Speak()中可以访问到language
类作用域和函数作用域不一样,它对其子作用域是不可见的,所以在函数Gende()中gender是不可见的
思考:
>>> male.gendermale
>>> Male.gender
male
>>> male.gender = male2
>>> male.gender
male2
>>> Male.gender
male
2.5 复杂嵌套
2.5.1 函数嵌套类
在Python中,类可以动态创建,甚至在函数中返回。通过在函数中创建并返回类,可以按函数参数对类进行动态定制
代码示例:
>>> language = chinese>>> def MakeMale(sSortName: str):
class Male:
sortName = sSortName
def __init__(self, name: str):
self.name = name
def Speak(self):
print(i speak, language)
def Sort(self):
print(sSortName)
return Male
>>> ChineseMale: type = MakeMale(Chinese Men)
>>> chineseMale = ChineseMale(zhangsan)
>>> chineseMale.Speak()
i speak chinese
>>> chineseMale.sortName
Chinese Men
>>> chineseMale.Sort()
Chinese Men
2.5.2 类嵌套类
代码示例:
>>> class OutClass:inName = in
class InClass:
name = inName
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
class OutClass:
File "<pyshell#26>", line 3, in OutClass
class InClass:
File "<pyshell#26>", line 4, in InClass
name = inName
NameError: name inName is not defined
3. 名字空间
作用域是语法层面的概念,是静态的。当程序开始执行后,作用域中的名字绑定关系需要存储起来,存储的地方就是名字空间。由于名字绑定关系是由名字和对象组成的键值对,因此用dict是理想的存储容器(之前在介绍dict的相关内容时也有提到)
以计算圆面积的例子来认识作用域背后的运行时实体——名字空间。代码示例如下:
>>> PI = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
print(name, PI * r * r)
return circle_area
3.1 Globals
在Python中,每个模块都有一个dict对象,用于存储全局作用域中的名字,这就是全局名字空间Globals。在上述的例子中,根据我们之前对作用域的划分,可以肯定全局名字空间中一定包含两个名字:PI和closure_print。
如果其他模块也需要使用PI或closure_print函数,就需要通过import语句将模块导入,导入后我们就可以获得一个模块对象:
# 假设我们在test.py中导入上述模块testglobal.py>>> import testglobal
>>> testglobal
<module testglobal from D:\\myspace\\code\\pythonCode\\mix\\namespace\\testglobal.py>
>>> type(testglobal)
<class module>
通过内置函数dir()我们可以知道模块对象下有哪些属性可以访问:
>>> dir(testglobal)[PI, __builtins__, __cached__, __doc__, __file__, __loader__, __name__, __package__, __spec__, closure_print]
>>> testglobal.closure_print
<function closure_print at 0x000002F33B14A050>
在Python中,一个对象可以访问哪些属性,成为对象的属性空间。因此,模块的属性空间和全局名字空间本质上就是同一个东西,都通过一个dict对象进行存储。那么如何找到这个dict对象呢——通过__dict__属性:
>>> testglobal.__dict__
此外,我们也可以通过内置函数globals()来获取当前模块的全局名字空间:
>>> globals()
我们分别打印它们的id,本质上就是同一个对象:
>>> id(testglobal.__dict__)2219833831040
>>> id(globals())
2219833831040
3.2 Locals
Python执行一个作用域内的代码时,需要一个容器来访问当前作用域的名字,这就是局部名字空间Locals
当Python执行closure_print()函数时,将分配一个栈帧对象PyFrameObject来保存上下文信息以及执行状态。作为代码执行时必不可少的上下文信息,全局名字空间和局部名字空间也会在PyFrameObject上记录:
struct _frame {PyObject_VAR_HEAD
struct _frame *f_back; /* previous frame, or NULL */
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */
PyObject **f_valuestack; /* points after the last local */
PyObject *f_trace; /* Trace function */
int f_stackdepth; /* Depth of value stack */
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
/* Borrowed reference to a generator, or NULL */
PyObject *f_gen;
int f_lasti; /* Last instruction if called */
int f_lineno; /* Current line number. Only valid if non-zero */
int f_iblock; /* index in f_blockstack */
PyFrameState f_state; /* What state the frame is in */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
};
3.3 Enclosings
在作用域存在嵌套的情况下,Python将内层代码块依赖的所有外层名字存储在一个容器内,这就是闭包名字空间Enclosings
对于示例:
>>> pi = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
name = 1
print(name, pi * r * r)
return circle_area
当Python执行到print(name, pi * r * r)语句时,按照Locals、Enclosings、Globals这样的顺序查找语句中涉及的名字:名字name在Enclosings中找到,名字pi在Globals中找到,名字r在Locals中找到。那么还有一个名字print是如何找到的呢?
3.4 Builtin
Python在builtin模块中提供了很多内建函数和类型,构成运行时的另一个名字空间:内建名字空间Builtin
全局名字空间中有一个名字指向内建名字空间:
>>> import builtins>>> id(testglobal.__builtins__)
3065787874688
>>> id(builtins.__dict__)
3065787874688
4. 问题与总结
函数作用域对内部所有的作用域均可见,包括内部嵌套的类作用域和函数作用域(例如闭包);类作用域对内部所有的作用域均不可见,包括内部嵌套的类作用域和函数作用域。
只要在当前Locals命名空间中无同名变量且没有global,nonlocal等关键字的声明的话,就一定创建一个该名字的新局部变量,以nonlocal的使用为例:
示例1:
>>> def closure_print(name: str):def circle_area(r: int):
print(locals())
print(name, PI * r * r)
return circle_area
>>> c = closure_print(circle1)
>>> c(1)
{r: 1, name: circle1}
circle1 3.14
示例2:
>>> PI = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
print(locals())
name += 1
print(name, PI * r * r)
return circle_area
>>> c = closure_print(circle1)
>>> c(1)
{r: 1}
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
c(1)
File "<pyshell#2>", line 4, in circle_area
name += 1
UnboundLocalError: local variable name referenced before assignment
示例3:
>>> PI = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
print(locals())
name = circle2
print(locals())
print(name, PI * r * r)
return circle_area
>>> c = closure_print(circle1)
>>> c(1)
{r: 1}
{r: 1, name: circle2}
circle2 3.14
示例4:
>>> PI = 3.14>>> def closure_print(name: str):
def circle_area(r: int):
print(locals())
nonlocal name
name += 1
print(locals())
print(name, PI * r * r)
return circle_area
>>> c = closure_print(circle1)
>>> c(1)
{r: 1, name: circle1}
{r: 1, name: circle11}
circle11 3.14
locals()输出的到底是什么?C源码如下:
intPyFrame_FastToLocalsWithError(PyFrameObject *f)
{
/* Merge fast locals into f->f_locals */
PyObject *locals, *map;
PyObject **fast;
PyCodeObject *co;
Py_ssize_t j;
Py_ssize_t ncells, nfreevars;
if (f == NULL) {
PyErr_BadInternalCall();
return -1;
}
// 初始赋值locals为f->f_locals
locals = f->f_locals;
if (locals == NULL) {
locals = f->f_locals = PyDict_New();
if (locals == NULL)
return -1;
}
// 获取对应的PyCodeObject
co = f->f_code;
// 获取co_varnames字段
map = co->co_varnames;
if (!PyTuple_Check(map)) {
PyErr_Format(PyExc_SystemError,
"co_varnames must be a tuple, not %s",
Py_TYPE(map)->tp_name);
return -1;
}
fast = f->f_localsplus;
j = PyTuple_GET_SIZE(map);
if (j > co->co_nlocals)
j = co->co_nlocals;
if (co->co_nlocals) {
// 将co_varnames加入到locals中
if (map_to_dict(map, j, locals, fast, 0) < 0)
return -1;
}
// 闭包相关
ncells = PyTuple_GET_SIZE(co->co_cellvars);
nfreevars = PyTuple_GET_SIZE(co->co_freevars);
if (ncells nfreevars) {
// 将co_cellvars加入到locals
if (map_to_dict(co->co_cellvars, ncells,
locals, fast + co->co_nlocals, 1))
return -1;
/* If the namespace is unoptimized, then one of the
following cases applies:
1. It does not contain free variables, because it
uses import * or is a top-level namespace.
2. It is a class namespace.
We dont want to accidentally copy free variables
into the locals dict used by the class.
*/
if (co->co_flags & CO_OPTIMIZED) {
// 将co_freevars加入到locals
if (map_to_dict(co->co_freevars, nfreevars,
locals, fast + co->co_nlocals + ncells, 1) < 0)
return -1;
}
}
return 0;
}
以上就是Python作用域与名字空间源码学习笔记的详细内容,更多关于Python作用域名字空间的资料请关注盛行IT软件开发工作室其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。