网站开发容易学吗做企业网站多
web/
2025/9/30 21:47:28/
文章来源:
网站开发容易学吗,做企业网站多,没有公司做网站可以吗,苏州公司官网简介#xff1a; Python是一门强类型的动态类型语言#xff0c;开发者可以给对象动态指定类型#xff0c;但类型不匹配的操作是不被允许的。动态类型帮助开发者写代码轻松愉快#xff0c;然而#xff0c;俗话说#xff1a;动态一时爽#xff0c;重构火葬场。动态类型也带…简介 Python是一门强类型的动态类型语言开发者可以给对象动态指定类型但类型不匹配的操作是不被允许的。动态类型帮助开发者写代码轻松愉快然而俗话说动态一时爽重构火葬场。动态类型也带来了许多麻烦如果动态语言能加入静态类型标记的话会有什么好处呢本文将主要介绍Python对静态类型的支持、社区发展的现状、类型检查工具介绍与对比以及类型解析的实战。 作者 | 别象 来源 | 阿里技术公众号
一 背景
Python是一门强类型的动态类型语言开发者可以给对象动态指定类型动态但类型不匹配的操作是不被允许的强类型如str和int两个变量无法相加。
动态类型帮助开发者写代码轻松愉快然而俗话说动态一时爽重构火葬场。动态类型也带来了许多麻烦如果动态语言能加入静态类型标记的话主要有以下几点好处
编写更便捷。配合各种IDE工具可以实现定义跳转类型提示等。编码更可靠。既然有了类型定义的加持许多工具能够在静态编码阶段就能提前发现语义错误。重构更放心。明确了接口的出入参使代码重构更明确更稳定。
目前主流语言大多数是支持静态类型的如JavaGoRust。而动态语言PythonJS也在拥抱静态类型如TypeScript。
本文主要介绍一下Python对静态类型的支持、社区发展的现状、类型检查工具介绍与对比以及类型解析的实战。
二 Python的静态类型支持
早在06年的Python3.0就引入了类型annotation的语法并列出了许多改进项。
# 加类型前
def add(a, b):return a b# 加类型后
def add(a:int, b:int) - int:return a b
随着持续的演进到Python3.5能够做到Type Hints配合类型标注IDE可以做Type Checking。 进而到Python3.7静态类型支持基本完善。 下面我来具体介绍下类型检查工具和一些基础概念。
三 类型检查工具简介
Python作者和主流大厂都陆续推出了Python类型检查工具 这些类型解析工具的功能大同小异下面简单介绍下
1 mypy
最早的官方推出的mypy是由Python之父Guido van Rossum亲自开发被各种主流编辑器所集成如PyCharm, Emacs, Sublime Text, VS Code等用户基础和文档经验都很丰富。
2 pytype
谷歌的pytype可以做类型检查并且提供了一些实用小工具下文会简单介绍下其应用
annotate-ast过程中的AST树标记工具。merge-pyi把生成的pyi文件合并回原文件中甚至还能做到隐藏类型在类型检查时再加载。pytd-tool解析pyi文件的工具解析成pytype自定义的PYTD文件。pytype-single再给定所有依赖的pyi文件的前提下可以解析单个Python文件。pyxref交叉引用的生成器。
3 pyre
脸书的pyre-check有两个特别的功能
Watchman功能 可以监听代码文件追踪改动。Query功能可以对源码做局部区域性的检查例如查询某行中一个表达式的类型、查询一个类的全部方法并返回成列表等避免了全局检查。
4 pyright
微软的pyright是最晚开源推出的宣称有以下优点
速度快。相较于 mypy 及其它用 Python 写的检查工具它的速度是 5 倍甚至更多。不依赖 Python 环境。它用 TypeScript 写成运行于 node 上不依赖 Python 环境或第三方包。可配置性强。支持自由地配置支持指定不同的运行环境PYTHONPATH 设置、Python 版本、平台目标。检查项齐全。支持类型检查及其它语法项的检查如 PEP-484、PEP-526、PEP-544以及函数返回值、类变量、全局变量的检查甚至可以检查条件循环语句。命令行工具。它包含两个 VS Code 插件一个命令行工具和一个语言服务器协议Language Server Protocol。内置 Stubs 。使用的是 Typeshed 的副本注使用静态的 pyi 文件检查内置模块、标准库和三方件 。语言服务特性。悬停提示信息、符号定义的跳转、实时的编辑反馈。
四 Pytype使用介绍
接下来重点介绍一下pytype。为什么选取pytype呢首先mypy比较古老很多功能没有新出的工具新颖和实用。计划使用Python LSP来处理Python文件提供一些语法服务的功能pyre-check用的是Ocamel所以我们就拿Python语言的pytype来实现想要的功能而且pytype提供了一些实用工具比如解析一个pyi文件基于Python文件生成pyi文件等。
1 基本概念
pyi文件
pyi的“i”指的是interfiace将Python文件的类型定义用接口的形式存储到pyi文件里来辅助类型检查。
大家常用的Pycharm可以关注下项目空间的External Libraries Python 3.6 Typeshed Stubs里面就有许多内置的pyi文件来辅助编码过程的类型提示和定位。 Typeshed Stubs
上面提到了typeshed stubs这相当于是提前集成的pyi集合pycharm似乎自己维护了一份数据。许多比较大的开源项目也在陆续提供stubs比如pyTorch。Tensorflow也正在考虑。
很多Python大库去制作pyi工程量比较大而且还有很多C的API调用大家还需要耐心等待。
2 实战
我翻阅了pytype的源码把比较实用的代码和需求做了结合下面介绍几个示例
总体效果
import logging
import sys
import os
import importlab.environment
import importlab.fs
import importlab.graph
import importlab.output
from importlab import parsepyfrom sempy import util
from sempy import environment_utilfrom pytype.pyi import parser
示例Demo通过Importlab工具解析项目空间的依赖关系以及对应的pyi文件
def main():# 指定要解析的目录ROOT /path/to/demo_project# 指定TYPESHED目录可以从这里下载https://github.com/python/typeshedTYPESHED_HOME /path/to/typeshed_homeutil.setup_logging()# 载入typeshed如果TYPESHED_HOME配置的不对会返回Nonetypeshed environment_util.initialize_typeshed_or_return_none(TYPESHED_HOME)# 载入目标目录有效文件inputs util.load_all_py_files(ROOT)# 生成用于生成import_graph的环境env environment_util.create_importlab_environment(inputs, typeshed)# 基于pyi和工程文件生成import graphimport_graph importlab.graph.ImportGraph.create(env, inputs, trimTrue)# 打印整个依赖树logging.info(Source tree:\n%s, importlab.output.formatted_deps_list(import_graph))# import模块的别名 e.g. import numpy as np - {np: numpy}alias_map {}# 引入模块的名称和具体pyi文件的映射 e.g. import os - {os: /path/to/os/__init__.pyi}import_path_map {}# alias_map的value可以和import_path_map的key对应通过alias_map的key这个变量名去找真正的实现文件for file_name in inputs:# 如果有pyi文件匹配则会放入resolved# 如果依赖了Build_in依赖会被跳过不返回# 如果依赖了自定义依赖会放入unresolved需要自己进一步解析定位到项目工程文件(resolved, unresolved) import_graph.get_file_deps(file_name)for item in resolved:item_name item.replace(.pyi, ) \.replace(.py, ) \.replace(/__init__, ).split(/)[-1]import_path_map[item_name] itemfor item in unresolved:file_path os.path.join(ROOT, item.new_name .py)import_path_map[item.name] file_pathimport_stmts parsepy.get_imports(file_name, env.python_version)for import_stmt in import_stmts:alias_map[import_stmt.new_name] import_stmt.nameprint(以下为通过importlab解析方式获取的import关系\n\n)# 对于代码搜索场景只需要alias_map既可以通过正在使用的对象关联到引入的模块print(\n\n#################################\n\n)print(对于代码搜索场景只需要alias_map既可以通过正在使用的对象关联到引入的模块)print(alias_map: , alias_map)# 对于代码补全场景需要进一步解析当前文件以及引用的pyi文件如果当前文件是__init__文件则要进一步去该目录下的所有文件方法中全局搜索print(\n\n#################################\n\n)print(对于代码补全场景需要进一步解析当前文件以及引用的pyi文件如果当前文件是__init__文件则要进一步去该目录下的所有文件方法中全局搜索)print(import_path_map: , import_path_map)print(\n\n\n以下为通过pytype工具解析pyi文件AST来分析三方依赖返回类型从而解析出当前变量的类型\n\n)# 通过pytype的解析去解析依赖的pyi文件获得调用方法的返回值fname /path/to/parsed_filewith open(fname, r) as reader:lines reader.readlines()sourcecode \n.join(lines)ret parser.parse_string(sourcecode, filenamefname, python_version3)constant_map dict()function_map dict()for key in import_path_map.keys():v import_path_map[key]with open(v, r) as reader:lines reader.readlines()src \n.join(lines)try:res parser.parse_pyi(src, v, key, 3)except:continue# Alias# Classesfor constant in res.constants:constant_map[constant.name] constant.type.namefor function in res.functions:signatures function.signaturessig_list []for signature in signatures:sig_list.append((signature.params, signature.return_type))function_map[function.name] sig_listvar_type_from_pyi_list []for alias in ret.aliases:variable_name alias.nameif alias.type is not None:typename_in_source alias.type.nametypename typename_in_source# 引入别名的case把它转化回来if . not in typename:# 只是普通的别名不是函数调用的返回值忽略continueif typename.split(.)[0] in alias_map:real_module_name alias_map[typename.split(.)[0]]typename real_module_name typename[typename.index(.):]if typename in function_map:possible_return_types [item[1].name for item in function_map[typename]]var_type_from_pyi_list.append((variable_name, possible_return_types))if typename in constant_map:possible_return_type constant_map[typename]var_type_from_pyi_list.append((variable_name, possible_return_type))passprint(\n\n#################################\n\n)print(这些都是从PYI文件中分析出来的返回值类型)for item in var_type_from_pyi_list:print(变量名:, item[0], 返回类型:, item[1])if __name__ __main__:sys.exit(main())
被解析的示例代码
# demo.py
import os as abcdefg
import re
from demo import utils
from demo import refscwd abcdefg.getcwd()
support_version abcdefg.supports_bytes_environ
pattern re.compile(r.*)add_res utils.add(1, 3)
mul_res refs.multi(3, 5)c abs(1) 具体步骤
首先pytype利用了Google另一个开源项目ImportLab。
用于分析文件间的依赖关系此时可以把typeshed目录下的文件也放入环境中importlab能够生成依赖图。
env environment_util.create_importlab_environment(inputs, typeshed)
import_graph importlab.graph.ImportGraph.create(env, inputs, trimTrue)
# 如果有pyi文件匹配则会放入resolved
# 如果依赖了Build_in依赖会被跳过不返回
# 如果依赖了自定义依赖会放入unresolved需要自己进一步解析定位到项目工程文件
(resolved, unresolved) import_graph.get_file_deps(file_name)
通过import graph我们拿到了变量的来源包括引用别名方法调用返回值
{ast: ast, astpretty: astpretty, abcdefg: os, re: re, utils: demo.utils, refs: demo.refs, JsonRpcStreamReader: pyls_jsonrpc.streams.JsonRpcStreamReader}
通过依赖图还能直接引用的依赖在具体哪个位置
import_path_map: {ast: /Users/zhangxindong/Desktop/search/code/sempy/sempy/typeshed/stdlib/ast.pyi, astpretty: /Users/zhangxindong/Desktop/search/code/sempy/venv/lib/python3.9/site-packages/astpretty.py, os: /Users/zhangxindong/Desktop/search/code/sempy/sempy/typeshed/stdlib/os/__init__.pyi, re: /Users/zhangxindong/Desktop/search/code/sempy/sempy/typeshed/stdlib/re.pyi, utils: /Users/zhangxindong/Desktop/search/code/sempy/sempy/demo/utils.py, refs: /Users/zhangxindong/Desktop/search/code/sempy/sempy/demo/refs/__init__.py, streams: /Users/zhangxindong/Desktop/search/code/sempy/venv/lib/python3.9/site-packages/pyls_jsonrpc/streams.py}
接下来就是去具体解析对应的文件了。我的需求是获取一些方法的返回值类型对于pyi文件pytype能够帮助我们解析然后我们通过调用关系去匹配。
print(\n\n\n以下为通过pytype工具解析pyi文件AST来分析三方依赖返回类型从而解析出当前变量的类型\n\n)
# 通过pytype的解析去解析依赖的pyi文件获得调用方法的返回值
fname /path/to/parsed_file
with open(fname, r) as reader:lines reader.readlines()
sourcecode \n.join(lines)
ret parser.parse_string(sourcecode, filenamefname, python_version3)constant_map dict()
function_map dict()
for key in import_path_map.keys():v import_path_map[key]with open(v, r) as reader:lines reader.readlines()src \n.join(lines)try:res parser.parse_pyi(src, v, key, 3)except:continue# Alias# Classesfor constant in res.constants:constant_map[constant.name] constant.type.namefor function in res.functions:signatures function.signaturessig_list []for signature in signatures:sig_list.append((signature.params, signature.return_type))function_map[function.name] sig_listvar_type_from_pyi_list []
for alias in ret.aliases:variable_name alias.nameif alias.type is not None:typename_in_source alias.type.nametypename typename_in_source# 引入别名的case把它转化回来if . not in typename:# 只是普通的别名不是函数调用的返回值忽略continueif typename.split(.)[0] in alias_map:real_module_name alias_map[typename.split(.)[0]]typename real_module_name typename[typename.index(.):]if typename in function_map:possible_return_types [item[1].name for item in function_map[typename]]# print(The possible return type of, typename_in_source, is, possible_return_types)var_type_from_pyi_list.append((variable_name, possible_return_types))if typename in constant_map:possible_return_type constant_map[typename]var_type_from_pyi_list.append((variable_name, possible_return_type))pass
比如
pattern re.compile(r.*)
从/Users/zhangxindong/Desktop/search/code/sempy/sempy/typeshed/stdlib/re.pyi文件中我们载入了两个方法都是re.compile只是入参不同返回值都是Pattern类型。
于是我们就知道了pattern变量的类型是re.Pattern。
这些都是从pyi文件中分析出来的返回值类型。变量名 cwd 返回类型[str]变量名 support_version 返回类型bool变量名 pattern 返回类型[typing.Pattern, typing.Pattern]
五 应用
Python语法分析的功能有一部分已经应用在了阿里云Dev Studio的代码文档搜索推荐和代码智能补全中。
1 代码文档搜索推荐
当开发者不知道如何使用某个 API 时如调用方式或方法入参等可以将鼠标移动到指定 API 上即可展示智能编码插件提供的 API 概要信息。开发者点击“ API 文档详情”能在右侧栏看到 API 的官方文档、代码示例等详细信息也可以直接搜索所需的 API 代码文档。目前支持 JavaScript、Python 语言的代码文档搜索推荐。
文档采集过程中我们能够拿到API名称和API所对应的class在实际代码中我们通过语法分析就能基于调用的方法对应到调用的类信息从而用于文档搜索。 2 代码智能补全
开发者在编写代码时智能编码插件会自动感知代码上下文为开发者提供精准的代码补全候选项代码补全候选项中标记有✨符号的为代码智能补全结果。目前支持 Java、JavaScript、Python 语言的代码智能补全。
代码补全过程中通过语法分析能够更加精准地获悉用户使用变量的类信息帮助过滤掉深度学习模型推荐的不合理选项也能够基于类的内部方法集合召回一些合理的补全项。 六 总结
Python静态类型支持的理念和工具均以完善但由于历史包袱太重社区推动力不足实际能达到的效果比较有限。另外官方、各大厂以及本地IDE都有自己的实现和分析方式还没有达到统一的标准和格式。大家可以根据上述的优劣势以及配合的工具集与数据集选择适合自己的方式做解析。期待Python社区对静态类型的支持能越来越完善。
原文链接
本文为阿里云原创内容未经允许不得转载。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/84671.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!