函数执行时的名字空间 在Python虚拟机函数机制之无参调用(一)这一章中,我们对Python中的函数调用机制有个大概的了解,在此基础上,我们再来看一些细节上的问题.在执行MAKE_FUNCTION指令时,调用了PyFunction_New方法,这个方法有一个参数是globals,这个globals最终将称为与函数f对应的PyFrameObject中的global名字空间——f_globals ceval.c case MAKE_FUNCTION: v = POP(); /* code obje…
位置参数的默认值 在Python中,允许函数的参数有默认值.假如函数f的参数value的默认值是1,在我们调用函数时,如果传递了value参数,那么f调用时value的值即为我们传递的值,如果调用时没有传递value的值,那么f将使用value的默认值,即为1.那么,带有默认值的位置参数,其实现机制与一般的位置参数有何不同呢? 我们先来看一下demo3.py # cat demo3.py def f(a=1, b=2): print(a + b) f() f(b=5) 然后我们用dis模块编译下…
参数类别 我们在Python虚拟机函数机制之无参调用(一)和Python虚拟机函数机制之名字空间(二)这两个章节中,分别PyFunctionObject对象和函数执行时的名字空间.本章,我们来剖析一下函数参数的实现. 在Python中,函数的参数根据形势的不同可以分为四种类别: 位置参数:如f(a, b),a和b称为位置参数 键参数:f(a, b, name="Python"),其中的name="Python"被称为键参数 扩展位置参数:f(a, b, *args)…
扩展位置参数和扩展键参数 在Python虚拟机函数机制之参数类别(三)的例3和例4中,我们看到了使用扩展位置参数和扩展键参数时指示参数个数的变量的值.在那里,我们发现在函数内部没有使用局部变量时,co_nlocals和co_argcount的值已经不再相同了.从它们的差异我们猜测,当使用扩展位置参数*args或者扩展键参数**kwargs时,实际是作为一个局部变量来实现的.同时,我们还猜测,在Python内部,*args是由PyTuppleObject实现的,而**kwargs是由PyDictO…
函数中局部变量的访问 在完成了对函数参数的剖析后,我们再来看看,在Python中,函数的局部变量时如何实现的.前面提到过,函数参数也是一种局部变量.所以,其实局部变量的实现机制与函数参数的实现机制是完全一样的.这个“一样”是什么意思呢? 之前我们剖析过Python虚拟机的一些指令,如果要访问一个变量,应该使用LOAD_NAME指令,应该依照local.global.builtin这三个名字空间里去检索变量名所对应的变量值.然后在调用函数时,Python虚拟机通过PyFrame_New创建新的Py…
PyFunctionObject对象 在Python中,任何一个东西都是对象,函数也不例外.函数这种抽象机制,是通过一个Python对象——PyFunctionObject来实现的 typedef struct { PyObject_HEAD PyObject *func_code; //编译后的PyCodeObject对象 PyObject *func_globals; //函数运行时的global名字空间 PyObject *func_defaults; //默认参数(tupple或NULL…
位置参数的传递 前面我们已经分析了无参函数的调用过程,我们来看看Python是如何来实现带参函数的调用的.其实,基本的调用流程与无参函数一样,而不同的是,在调用带参函数时,Python虚拟机必须传递参数.我们先来看一段代码: # cat demo2.py def f(name, age): age += 5 print("[", name, age, "]") age = 5 f("Robert", age) 我们用dis模块来编译一下对应的字节…
填充tp_dict 在Python虚拟机类机制之对象模型(一)这一章中,我们介绍了Python的内置类型type如果要完成到class对象的转变,有一个重要的步骤就是填充tp_dict对象,这是一个极其繁杂的过程 typeobject.c int PyType_Ready(PyTypeObject *type) { PyObject *dict, *bases; PyTypeObject *base; Py_ssize_t i, n; …… //设定tp_dict dict = type->tp…
instance对象中的__dict__ 在Python虚拟机类机制之从class对象到instance对象(五)这一章中最后的属性访问算法中,我们看到“a.__dict__”这样的形式. # 首先寻找'f'对应的descriptor(descriptor在之后会细致剖析) # 注意:hasattr会在<class A>的mro列表中寻找符号'f' if hasattr(A, 'f'): descriptor = A.f type = descriptor.__class__ if hasat…
Python虚拟机中的for循环控制流 在Python虚拟机之if控制流(一)这一章中,我们了解if控制流的字节码实现,在if控制结构中,虽然Python虚拟机会在不同的分支摇摆,但大体还是向前执行,但是在for循环控制结构中,我们将会看到一种新的指令跳跃方式,即指令回退.在if控制流章节中,我们看到了指令跳跃时,通常跳跃的距离都是当前指令与目标指令之间的距离.如果按照这种逻辑,进行回退时,这个跳跃是否是负数呢?别急,我们下面一点一点来剖析for循环控制流的实现 # cat demo3.py l…
Bound Method和Unbound Method 在Python中,当对作为属性的函数进行引用时,会有两种形式,一种称为Bound Method,这种形式是通过类的实例对象进行属性引用,而另一种则是通过类进行属性引用,称为Unbound Method.当然,对Bound Method和Unbound Method的调用形式是不同的,其原因可以追溯到LOAD_ATTR中 demo2.py class A(object): def g(self, value): self.value = va…
从class对象到instance对象 现在,我们来看看如何通过class对象,创建instance对象 demo1.py class A(object): name = "Python" def __init__(self): print("A::__init__") def f(self): print("A::f") def g(self, aValue): self.value = aValue print(self.value) a =…
从slot到descriptor 在Python虚拟机类机制之填充tp_dict(二)这一章的末尾,我们介绍了slot,slot包含了很多关于一个操作的信息,但是很可惜,在tp_dict中,与__getitem__关联在一起的,一定不会是一个slot,原因很简单,slot不是一个PyObject,它不能存放在dict对象中.当然,我们再深入思考一下,会发现slot也不会被“调用”.既然slot不是一个PyObject,那么它就没有type,也就无从谈起什么tp_call了,所以slot是无论如何…
用户自定义class 在本章中,我们将研究对用户自定义class的剖析,在demo1.py中,我们将研究单个class的实现,所以在这里并没有关于继承及多态的讨论.然而在demo1.py中,我们看到了许多类的内容,其中包括类的定义.类的构造函数.对象的实例化.类成员函数的调用等 demo1.py class A(object): name = "Python" def __init__(self): print("A::__init__") def f(self):…
Python对象模型 在Python2.2之前,Python中存在着一个巨大的裂缝,就是Python的内置类type,比如:int和dict,这些内置类与程序员在Python中自定义的类并不是同一级别的类.比如说,程序员定义了class A,又定义了class B,B可以继承于A,这是理所当然的.但是,Python的内置类却不能被继承.程序员不能创建一个类继承于int或者dict.于是,Python的开发者在Python2.2中花费了巨大的精力填补了内置类和用户自定义class之间的鸿沟,使得两…
复杂内建对象的创建 在上一章Python虚拟机中的一般表达式(一)中,我们看到了Python是如何创建一个空的字典对象和列表对象,那么如果创建一个非空的字典对象和列表对象,Python的行为又是如何呢?demo2.py里面包含一个字典对象和列表对象,这两个对象都是在初始化时就包含元素,首先,我们看一下对应PyCodeObject中的符号表和常量表 # cat demo2.py i = 1 s = "Python" d = {"1": 1, "2"…
函数进阶: 一.动态参数:*args  **kwargs *args是元祖形式,接收除去键值对以外的所有参数 # args可以换成任意变量名,约定俗成用args **kwargs接收的只是键值对的参数,并保存在字典中. # kwargs可以换成任意变量名,约定俗成用kwargs *的魔性用法: l1 = [1,2,3] l2 = ['alex','taibai'] def func1(*args): print(args) # 结果:('1','2','3','alex','taibai') f…
函数对象 # 函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象​# 函数对应的原因# 1 可以直接被引用# 2 可以当作函数参数传递# 3 可以作为函数的返回值# 4 可以作为容器类型的元素 名称空间 # 名称空间:存放名字与内存空间地址对应关系的容器# 作用:解决由于名字有限,导致名字重复发送冲突的问题​# 三种名称空间# Built-in:内置名称空间:系统级,一个:随解释器执行而产生,解释器停止而销毁# Global:全局名称空间:文件级,多个:随所…
名称空间:存储名字的空间,分为三种,内置空间,全局空间,局部空间 名称可以是:变量名,函数名,类名等 当遇到一个名字时,首先在自己空间找,再到自己外的空间找 比如 test.py print f # 首先在全局空间test里面找,没有发现,就到内置空间找没有发现,报错 def pop(): print c # 首先在pop这个局部空间找c,没有发现,到全局空间test找,也没有发现,就到内置空间找. 一旦找到就不会再找了,所以要求不要定义一些和内置空间相同的变量(函数等)…
Vsftp:/root/perl/7# cat scan1.pm package scan1; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(fun1 frobnicate); ###导出fun1函数 sub fun1() { my $a=shift; my $b=shift; return 67 + $a + $b; }; Vsftp:/root/perl/7# cat a7.pl unshift(@INC,"/root/perl…
一 .函数对象 一 .函数是第一类对象,即函数可以当作数据传递 可以被引用 可以当作参数传递 返回值可以是函数 可以当作容器类型的元素 二. 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, #foo是内存地址 'bar':bar, #bar是内存地址 } while True: choice=input('>>: ').strip() if choice in dic: dic…
本篇目录: 一.函数嵌套 二.函数名称空间与作用域 三.函数对象 四.闭包函数 ============================================================================== 一.函数嵌套 1. 函数的嵌套调用 函数内又调用了其他函数(函数平级) def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=m…
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()…
阅读目录 一 函数对象 二 函数嵌套 三 名称空间与作用域 四 闭包函数 五 装饰器 六 练习题 一 函数对象 1 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 2 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input(…
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()…
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 1 可以被引用 2 可以当作参数传递 3 返回值可以是函数 3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]() 二 函数…
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素二 利用该特性,优雅的取代多分支的if def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]() 二…
作用域与名字空间 Python有一个核心概念是名字空间(namespace),namespace是一个name到object 的映射关系,Python有很多namespace,因此,在代码中如果碰到一个标志符(name),需要有一个规则来决定去哪个namespace查找——这就是LEGB. LEGB决定了name的查找顺序:locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参:…
Python具有静态作用域,变量的作用域由它定义的位置决定,而与调用的位置无关. a = 2 def f(): a = 2 第一行的a的作用域是全局作用域,作用于定义位置后面的所有位置. 第四行的a的作用域是局部作用域,作用于f函数里. Python能够形成局部作用域的只有函数与类,其他语句不形成局部作用域. 函数与类的局部作用域 def f(): a = 1 class A: b = 2 if 1 == 1: c = 3 for _ in range(1): d = 4 while True:…
在C++中,static有一个感觉被较少提及的用法:修饰非成员函数,这个用法实际是从C语言继承来的.其作用是表明这个函数只在当前编译单元中有效.这就使这个函数的所有引用在编译时就可以全部确定,无需进入链接阶段,链接器没有机会看到这个函数相关的一切符号,无论导入还是导出(理论上,实际编译器如何处理这个事情可能不尽相同,未作深入研究).即使多个编译单元都包含相同的signature相同名字的函数,链接器也不会报错或合并符号. 比如以下代码: // main.cpp void foo(); void…