[{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/categories/","section":"","summary":"","title":"","type":"categories"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/posts/","section":"","summary":"","title":"","type":"posts"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/tags/","section":"","summary":"","title":"","type":"tags"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/tags/elixir/","section":"","summary":"","title":"Elixir","type":"tags"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/categories/elixir/","section":"","summary":"","title":"Elixir","type":"categories"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/tags/erlang/","section":"","summary":"","title":"Erlang","type":"tags"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/tags/linux/","section":"","summary":"","title":"Linux","type":"tags"},{"content":"","date":"July 17, 2024","externalUrl":null,"permalink":"/","section":"希言の博客","summary":"","title":"希言の博客","type":"page"},{"content":" Erlang是一种开源编程语言，用于构建对高可用性有要求的大规模可扩展的软实时系统。它通常用于电信，银行，电子商务，计算机电话和即时消息中。Erlang的runtime系统具有对并发，分发和容错的内置支持。它是在爱立信计算机科学实验室设计的。\n检查旧版本 # $ uname -a Linux ws-ersyia-0 5.4.241-1-tlinux4-0017.10 #1 SMP Wed May 8 17:01:03 CST 2024 x86_64 x86_64 x86_64 GNU/Linux $ elixir --version Erlang/OTP 24 [erts-12.3.2.17] [source] [64-bit] [smp:8:2] [ds:8:2:10] [async-threads:1] [jit] Elixir 1.13.3 (compiled with Erlang/OTP 22) 卸载旧版本 # $ where elixir /opt/elixir $ where erl /opt/erlang $ rm -rf /opt/elixir /opt/erlang 下载安装包 # $ wget https://github.com/erlang/otp/releases/download/OTP-27.0.1/otp_src_27.0.1.tar.gz $ wget https://github.com/elixir-lang/elixir/archive/refs/tags/v1.17.2.zip 安装 erlang # $ tar zxvf otp_src_27.0.1.tar.gz $ cd otp_src_27.0.1 $ ./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; make install $ erl -s erlang halt Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:8:2] [ds:8:2:10] [async-threads:1] [jit:ns] 安装 elixir # $ unzip v1.17.2.zip $ cd elixir-1.17.2 $ make clean compile $ ./bin/elixir --version Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:8:2] [ds:8:2:10] [async-threads:1] [jit:ns] Elixir 1.17.2 (compiled with Erlang/OTP 27) 配置 PATH # $ vim ~/.zshrc 添加路径到PATH\nexport PATH=$PATH:/workspace/elixir-1.17.2/bin 如果要配置文件立即生效，还需要source一下配置文件\n$ source ~/.zshrc 查看版本：\n$ elixir --version Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:8:2] [ds:8:2:10] [async-threads:1] [jit:ns] Elixir 1.17.2 (compiled with Erlang/OTP 27) mix ok \u0026amp; install rebar3 # 配置mix hex和rebar3\n$ mix local.hex -y $ wget https://s3.amazonaws.com/rebar3/rebar3 $ chmod +x ./rebar3 $ ./rebar3 local install $ vim ~/.zshrc export PATH=/root/.cache/rebar3/bin:$PATH $ rebar3 -v rebar 3.23.0 on Erlang/OTP 27 Erts 15.0.1 如果要配置文件立即生效，还需要source一下配置文件\n$ source ~/.zshrc ","date":"July 17, 2024","externalUrl":null,"permalink":"/posts/install-elixir-erlang/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eErlang是一种开源编程语言，用于构建对高可用性有要求的大规模可扩展的软实时系统。它通常用于电信，银行，电子商务，计算机电话和即时消息中。Erlang的runtime系统具有对并发，分发和容错的内置支持。它是在爱立信计算机科学实验室设计的。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"在Linux(CentOS、Ubuntu等等)中安装Erlang和Elixir","type":"posts"},{"content":"","date":"April 20, 2024","externalUrl":null,"permalink":"/tags/rust/","section":"","summary":"","title":"Rust","type":"tags"},{"content":"","date":"April 20, 2024","externalUrl":null,"permalink":"/categories/rust/","section":"","summary":"","title":"Rust","type":"categories"},{"content":" Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全，让您在编译期就能够消除各种各样的错误。\n本文是最简最快最小化安装\n重点提示：\n如果不想安装VS消耗时间和6-8G的空间，可以按本文安装。 如果系统中已经安装了VS，那么直接运行rustup-init安装Rust，并一路回车即可。 前置条件：安装C++环境 # rust底层是依赖C环境的连接器，所以需要先安装C/C++编译环境,\n点击下载64位mingw-builds-binaries\n下载后解压到任意盘根目录，然后把文件夹的bin目录路径配置到系统环境变量path。\n比如：C:\\mingw64\\bin\n验证配置是否成功\ngcc -v 安装Rust # 如果不想默认安装到C盘，安装之前要先设置一些系统环境变量，改变默认安装的位置。 比如全部安装到D盘的rust文件夹下。\nRUSTUP_HOME D:\\rust\\.rustup // 默认路径 ~/.rustup 或 %USERPROFILE%/.rustup CARGO_HOME D:\\rust\\.cargo // 默认路径 ~/.cargo 或 %USERPROFILE%/.cargo 如果默认从官网安装，下载很慢且容易失败，推荐使用镜像加速安装，先设置以下环境变量：\nRUSTUP_DIST_SERVER https://rsproxy.cn RUSTUP_UPDATE_ROOT https://rsproxy.cn/rustup 点击下载rustup-init.exe\n做好以上准备工作之后，就可以用rustup-init.exe安装了\n总体安装步骤中有三次需要输入数字选项，总结下来就是：3，2，1\n运行rustup-init.exe后出现如下提示：\nRust Visual C++ prerequisites Rust requires a linker and Windows API libraries but they don\u0026#39;t seem to be available. These components can be acquired through a Visual Studio installer. 1) Quick install via the Visual Studio Community installer (free for individuals, academic uses, and open source). 2) Manually install the prerequisites (for enterprise and advanced users). 3) Don\u0026#39;t install the prerequisites (if you\u0026#39;re targeting the GNU ABI). \u0026gt;3 输入3回车：\n1) Proceed with standard installation (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt;2 输入2回车：\n\u0026gt;2 I\u0026#39;m going to ask you the value of each of these installation options. You may simply press the Enter key to leave unchanged. Default host triple? [x86_64-pc-windows-msvc] x86_64-pc-windows-gnu 这里默认是x86_64-pc-windows-msvc，也就是需要安装VS的msvc那一堆。这里我们使用自己单独下载配置好的编译环境。\n输入x86_64-pc-windows-gnu，（小提示，先复制x86_64-pc-windows-gnu，然后命令行上鼠标右键就自动粘贴上去了，可以不用手写输入。\n出现提示：\nDefault toolchain? (stable/beta/nightly/none) [stable] 直接回车，默认选择是稳定版。\n出现提示：\nProfile (which tools and data to install)? (minimal/default/complete) [default] 直接回车，默认选择默认。\n出现提示：\nModify PATH variable? (Y/n) y 输入Y回车（不分大小写），表示修改环境变量。\n出现提示：\nCurrent installation options: default host triple: x86_64-pc-windows-gnu default toolchain: stable profile: default modify PATH variable: yes 1) Proceed with selected options (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt;1 输入1回车，然后开始安装并打印相关信息：\ninfo: profile set to \u0026#39;default\u0026#39; info: setting default host triple to x86_64-pc-windows-gnu info: syncing channel updates for \u0026#39;stable-x86_64-pc-windows-gnu\u0026#39; info: latest update on 2022-12-15, rust version 1.66.0 (69f9c33d7 2022-12-12) info: downloading component \u0026#39;cargo\u0026#39; 7.3 MiB / 7.3 MiB (100 %) 1.9 MiB/s in 5s ETA: 0s info: downloading component \u0026#39;clippy\u0026#39; 3.6 MiB / 3.6 MiB (100 %) 1.7 MiB/s in 2s ETA: 0s info: downloading component \u0026#39;rust-docs\u0026#39; 19.0 MiB / 19.0 MiB (100 %) 2.1 MiB/s in 10s ETA: 0s info: downloading component \u0026#39;rust-mingw\u0026#39; 4.2 MiB / 4.2 MiB (100 %) 2.6 MiB/s in 1s ETA: 0s info: downloading component \u0026#39;rust-std\u0026#39; 27.8 MiB / 27.8 MiB (100 %) 1.8 MiB/s in 14s ETA: 0s info: downloading component \u0026#39;rustc\u0026#39; 74.3 MiB / 74.3 MiB (100 %) 1.5 MiB/s in 49s ETA: 0s info: downloading component \u0026#39;rustfmt\u0026#39; 6.6 MiB / 6.6 MiB (100 %) 2.2 MiB/s in 3s ETA: 0s info: installing component \u0026#39;cargo\u0026#39; info: installing component \u0026#39;clippy\u0026#39; info: installing component \u0026#39;rust-docs\u0026#39; 19.0 MiB / 19.0 MiB (100 %) 3.0 MiB/s in 5s ETA: 0s info: installing component \u0026#39;rust-mingw\u0026#39; info: installing component \u0026#39;rust-std\u0026#39; 27.8 MiB / 27.8 MiB (100 %) 9.0 MiB/s in 3s ETA: 0s info: installing component \u0026#39;rustc\u0026#39; 74.3 MiB / 74.3 MiB (100 %) 10.3 MiB/s in 7s ETA: 0s info: installing component \u0026#39;rustfmt\u0026#39; info: default toolchain set to \u0026#39;stable-x86_64-pc-windows-gnu\u0026#39; stable-x86_64-pc-windows-gnu installed - rustc 1.66.0 (69f9c33d7 2022-12-12) Rust is installed now. Great! To get started you may need to restart your current shell. This would reload its PATH environment variable to include Cargo\u0026#39;s bin directory (D:\\Programs\\rust\\cargo_home\\bin). Press the Enter key to continue. 等待它下载文件完毕，这里需要等待的时间有点长，但耐心就好，假如下载出现错误，就重设上面的设定，再来一次流程。\n当出现最后一行，敲回车，然后窗口自动关闭。\n验证安装是否成功\n打开命令提示符，也就是cmd。\n分别输入每一行命令后回车，出现相关信息后，说明rust安装成功了。\nrustc -V rustup -V cargo -V rustup show ","date":"April 20, 2024","externalUrl":null,"permalink":"/posts/fast-install-rust/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eRust 丰富的类型系统和所有权模型保证了内存安全和线程安全，让您在编译期就能够消除各种各样的错误。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Windows快速安装Rust","type":"posts"},{"content":" Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收，它能够胜任对性能要求特别高的服务，可以在嵌入式设备上运行，还能轻松和其他语言集成。\n前置条件：安装C++环境 rust底层是依赖C环境的连接器，所以需要先安装C/C++编译环境, 有两种选择:安装微软的msvc或者安装mingw/cygwin。 如果使用msvc的Visual Studio，只需要安装好C/C++编译环境,然后一路默认就行了，缺点是体积比较大，下载安装都要好几个G，\nMinGW-w64\nMingW-W64-builds\n64位下载地址 mingw-builds-binaries\n如果你是64位的系统，那就选择x86_64开头的，反之32位的选择i686开头的。\n线程模型有posix和win32，如果你想在vscode里面搞断点调试，我推荐用posix。\n然后是异常处理机制，我目前用的seh\n● seh 零开销exception，64位系统可用 ● sjlj 不是零成本，有较小的性能损失 ● dwarf 只支持32位\nMinGW-w64、UCRT 和 MSVCRT 是 Windows 平台上常用的 C/C++ 运行库，它们有以下不同点：\nMinGW-w64：是一个基于 GCC 的编译器套件，可以用于编译 Windows 平台上的 C/C++ 程序。MinGW-w64 使用的是 POSIX 标准的运行库，因此可以很好地兼容 Unix/Linux 程序。MinGW-w64 的默认运行库是 MSVCRT（Microsoft Visual C++ Runtime），但也可以使用其他运行库。 UCRT（Universal CRT）：是微软提供的一个通用 C 运行库，可以用于编译 Windows 平台上的 C/C++ 程序。UCRT 提供了许多与标准 C 库兼容的函数，并支持新的 C11 和 C++11 标准。UCRT 适用于 Windows 10 或更高版本的操作系统。 MSVCRT（Microsoft Visual C++ Runtime）：是微软提供的一个 C++ 运行库，可以用于编译 Windows 平台上的 C/C++ 程序。MSVCRT 提供了许多与标准 C 库兼容的函数，但不支持新的 C11 和 C++11 标准。MSVCRT 在 Windows 操作系统中已经内置，并且随着 Visual Studio 版本的升级而不断更新。 我选择的是：x86_64-13.2.0-release-posix-seh-ucrt-rt_v11-rev0.7z，下载到本地解压后，把bin文件夹路径配置到Path系统环境变量即可。\n验证版本\ngcc 和 g++ 都是 GNU 工具链中的编译器，它们的主要区别在于它们所编译的代码的语言不同。\ngcc 是 GNU C 编译器，它主要用于编译 C 语言程序，可以将 C 语言源代码编译成可执行文件或动态链接库。\ng++ 则是 GNU C++ 编译器，它主要用于编译 C++ 语言程序，可以将 C++ 语言源代码编译成可执行文件或动态链接库。\n虽然 gcc 和 g++ 是两个不同的编译器，但它们都基于同一套 GNU 工具链，并共享许多相同的功能和选项。因此，除了语言不同外，它们的使用方式和命令选项也非常相似。\n需要注意的是，C++ 语言相对于 C 语言来说增加了一些特性，例如类、继承、多态等。如果您想编译 C++ 代码，建议使用 g++ 编译器，因为它可以更好地支持这些特性。而如果您只是编译 C 代码，gcc 编译器就足够了。\ngcc -v gdb -v mingw32-make -v 安装Rust 安装之前，先想想要安装到什么地方，我不喜欢安装到C盘，可以设置一些系统环境变量，改变默认安装的位置\nRUSTUP_HOME D:\\rust\\.rustup // 默认路径 ~/.rustup 或 %USERPROFILE%/.rustup CARGO_HOME D:\\rust\\.cargo // 默认路径 ~/.cargo 或 %USERPROFILE%/.cargo 如果直接从官方网站下载，国内的环境复杂，下载会很慢，而且容易失败。推荐使用镜像加速安装，设置以下环境变量：\nRUSTUP_DIST_SERVER https://rsproxy.cn RUSTUP_UPDATE_ROOT https://rsproxy.cn/rustup 这一切准备工作做好之后，就可以用rustup-init来安装了 如果使用msvc环境的话，一切默认就行了 而mingw就需要手动选择gnu toolchain\nmsvc的toolchain： stable-x86_64-pc-windows-msvc mingw/cygwin: stable-x86_64-pc-windows-gnu 运行rustup-init后出现如下提示：\nRust Visual C++ prerequisites Rust requires a linker and Windows API libraries but they don\u0026#39;t seem to be available. These components can be acquired through a Visual Studio installer. 1) Quick install via the Visual Studio Community installer (free for individuals, academic uses, and open source). 2) Manually install the prerequisites (for enterprise and advanced users). 3) Don\u0026#39;t install the prerequisites (if you\u0026#39;re targeting the GNU ABI). \u0026gt; 它说rust需要windows API库和链接器，你可以选择下面3项的一项。\n1、安装Visual Studio。 2、手动安装的预设条件。 3、无需预设条件。\n我们这里选择3，mingw-w64也是C/C++编译器，就是 GCC 的 Windows 版本 。回车：\n\u0026gt;3 Welcome to Rust! This will download and install the official compiler for the Rust programming language, and its package manager, Cargo. Rustup metadata and toolchains will be installed into the Rustup home directory, located at: C:\\Users\\markh\\.rustup This can be modified with the RUSTUP_HOME environment variable. The Cargo home directory is located at: C:\\Users\\markh\\.cargo This can be modified with the CARGO_HOME environment variable. The cargo, rustc, rustup and other commands will be added to Cargo\u0026#39;s bin directory, located at: C:\\Users\\markh\\.cargo\\bin This path will then be added to your PATH environment variable by modifying the HKEY_CURRENT_USER/Environment/PATH registry key. You can uninstall at any time with rustup self uninstall and these changes will be reverted. Current installation options: default host triple: x86_64-pc-windows-msvc default toolchain: stable (default) profile: default modify PATH variable: yes 1) Proceed with standard installation (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt; 然后输入2，就是自定义安装，出现提示：\n\u0026gt;2 I\u0026#39;m going to ask you the value of each of these installation options. You may simply press the Enter key to leave unchanged. Default host triple? [x86_64-pc-windows-msvc] 输入x86_64-pc-windows-gnu，（小提示，你先复制x86_64-pc-windows-gnu，在rustup右键可以直接粘贴文字，可以不用手写输入。出现提示：\nDefault toolchain? (stable/beta/nightly/none) [stable] 直接回车，默认选择是稳定版。出现提示：\nProfile (which tools and data to install)? (minimal/default/complete) [default] 直接回车，默认选择默认。出现提示：\nModify PATH variable? (Y/n) 输入Y回车（不分大小写），表示修改环境变量，然后回到了第一步，提示：\nCurrent installation options: default host triple: x86_64-pc-windows-gnu default toolchain: stable profile: default modify PATH variable: yes 1) Proceed with selected options (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt; 输入1，然后出现：\ninfo: profile set to \u0026#39;default\u0026#39; info: setting default host triple to x86_64-pc-windows-gnu info: syncing channel updates for \u0026#39;stable-x86_64-pc-windows-gnu\u0026#39; info: latest update on 2022-12-15, rust version 1.66.0 (69f9c33d7 2022-12-12) info: downloading component \u0026#39;cargo\u0026#39; 7.3 MiB / 7.3 MiB (100 %) 1.9 MiB/s in 5s ETA: 0s info: downloading component \u0026#39;clippy\u0026#39; 3.6 MiB / 3.6 MiB (100 %) 1.7 MiB/s in 2s ETA: 0s info: downloading component \u0026#39;rust-docs\u0026#39; 19.0 MiB / 19.0 MiB (100 %) 2.1 MiB/s in 10s ETA: 0s info: downloading component \u0026#39;rust-mingw\u0026#39; 4.2 MiB / 4.2 MiB (100 %) 2.6 MiB/s in 1s ETA: 0s info: downloading component \u0026#39;rust-std\u0026#39; 27.8 MiB / 27.8 MiB (100 %) 1.8 MiB/s in 14s ETA: 0s info: downloading component \u0026#39;rustc\u0026#39; 74.3 MiB / 74.3 MiB (100 %) 1.5 MiB/s in 49s ETA: 0s info: downloading component \u0026#39;rustfmt\u0026#39; 6.6 MiB / 6.6 MiB (100 %) 2.2 MiB/s in 3s ETA: 0s info: installing component \u0026#39;cargo\u0026#39; info: installing component \u0026#39;clippy\u0026#39; info: installing component \u0026#39;rust-docs\u0026#39; 19.0 MiB / 19.0 MiB (100 %) 3.0 MiB/s in 5s ETA: 0s info: installing component \u0026#39;rust-mingw\u0026#39; info: installing component \u0026#39;rust-std\u0026#39; 27.8 MiB / 27.8 MiB (100 %) 9.0 MiB/s in 3s ETA: 0s info: installing component \u0026#39;rustc\u0026#39; 74.3 MiB / 74.3 MiB (100 %) 10.3 MiB/s in 7s ETA: 0s info: installing component \u0026#39;rustfmt\u0026#39; info: default toolchain set to \u0026#39;stable-x86_64-pc-windows-gnu\u0026#39; stable-x86_64-pc-windows-gnu installed - rustc 1.66.0 (69f9c33d7 2022-12-12) Rust is installed now. Great! To get started you may need to restart your current shell. This would reload its PATH environment variable to include Cargo\u0026#39;s bin directory (D:\\Programs\\rust\\cargo_home\\bin). Press the Enter key to continue. 等待它下载文件完毕，这里需要等待的时间有点长，但耐心就好，假如下载出现错误，就重设上面的设定，再来一次流程。\n当出现最后一行，敲回车，然后窗口关闭。\n打开命令提示符，也就是cmd。\n分别输入每一行命令后回车，出现相关信息后，标明安装rust成功了。\nrustc -V rustup -V cargo -V rustup show 其他相关命令\n#安装其他类型的toolchain开发环境 rustup toolchain install \u0026lt;toolchain\u0026gt; #例如 rustup toolchain install stable-x86_64-pc-windows-gnu #切换默认的toolchain rustup default [toolchain] #例如 rustup default stable-x86_64-pc-windows-gnu #更新： rustup update stable rustup default stable Rust Visual C++ prerequisites Rust requires a linker and Windows API libraries but they don\u0026#39;t seem to be available. These components can be acquired through a Visual Studio installer. 1) Quick install via the Visual Studio Community installer (free for individuals, academic uses, and open source). 2) Manually install the prerequisites (for enterprise and advanced users). 3) Don\u0026#39;t install the prerequisites (if you\u0026#39;re targeting the GNU ABI). \u0026gt;3 Welcome to Rust! This will download and install the official compiler for the Rust programming language, and its package manager, Cargo. Rustup metadata and toolchains will be installed into the Rustup home directory, located at: C:\\Users\\markh\\.rustup This can be modified with the RUSTUP_HOME environment variable. The Cargo home directory is located at: C:\\Users\\markh\\.cargo This can be modified with the CARGO_HOME environment variable. The cargo, rustc, rustup and other commands will be added to Cargo\u0026#39;s bin directory, located at: C:\\Users\\markh\\.cargo\\bin This path will then be added to your PATH environment variable by modifying the HKEY_CURRENT_USER/Environment/PATH registry key. You can uninstall at any time with rustup self uninstall and these changes will be reverted. Current installation options: default host triple: x86_64-pc-windows-msvc default toolchain: stable (default) profile: default modify PATH variable: yes 1) Proceed with standard installation (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt;2 I\u0026#39;m going to ask you the value of each of these installation options. You may simply press the Enter key to leave unchanged. Default host triple? [x86_64-pc-windows-msvc] x86_64-pc-windows-gnu Default toolchain? (stable/beta/nightly/none) [stable] Profile (which tools and data to install)? (minimal/default/complete) [default] Modify PATH variable? (Y/n) y Current installation options: default host triple: x86_64-pc-windows-gnu default toolchain: stable profile: default modify PATH variable: yes 1) Proceed with selected options (default - just press enter) 2) Customize installation 3) Cancel installation \u0026gt;1 参考：\nhttps://blog.csdn.net/Mr_Roki/article/details/136213972 报错信息：\nerror: linker `link.exe` not found | = note: program not found note: the msvc targets depend on the msvc linker but `link.exe` was not found note: please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. note: VS Code is a different product, and is not sufficient. error: aborting due to 1 previous error 解决方案：\n如果cmd运行 rustc -version 显示rust版本，则运行\nrustup default stable-x86_64-pc-windows-gnu 如果没有则运行：\nrustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 下载安装依赖 visualcppbuildtools_full.exe\nMicrosoft C++ 生成工具\n","date":"April 20, 2024","externalUrl":null,"permalink":"/posts/install-rust/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eRust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收，它能够胜任对性能要求特别高的服务，可以在嵌入式设备上运行，还能轻松和其他语言集成。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Windows安装Rust（详细版）","type":"posts"},{"content":"","date":"November 28, 2022","externalUrl":null,"permalink":"/tags/hexo/","section":"","summary":"","title":"Hexo","type":"tags"},{"content":" 迁移博客，开源节流\n博客之前一直部署在自己购买的服务器上，由于2023年2月服务器到期，也就刚好两个月时间，想想还是提前迁移了。\n当前是静态博客，直接push到 GitHub，使用 Netlify进行持续部署且一键HTTPS。\n图床使用 sm.ms，之前是使用付费七牛云和付费又拍云。\n评论系统使用 Valine，数据托管在 LeanCloud\n","date":"November 28, 2022","externalUrl":null,"permalink":"/posts/to-netlify/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e迁移博客，开源节流\u003c/p\u003e\n\u003c/blockquote\u003e","title":"博客迁移","type":"posts"},{"content":"","date":"November 28, 2022","externalUrl":null,"permalink":"/categories/%E9%BB%98%E8%AE%A4%E5%88%86%E7%B1%BB/","section":"","summary":"","title":"默认分类","type":"categories"},{"content":"","date":"November 14, 2022","externalUrl":null,"permalink":"/tags/pip/","section":"","summary":"","title":"Pip","type":"tags"},{"content":"","date":"November 14, 2022","externalUrl":null,"permalink":"/tags/pypi/","section":"","summary":"","title":"Pypi","type":"tags"},{"content":"","date":"November 14, 2022","externalUrl":null,"permalink":"/tags/python/","section":"","summary":"","title":"Python","type":"tags"},{"content":"","date":"November 14, 2022","externalUrl":null,"permalink":"/categories/python/","section":"","summary":"","title":"Python","type":"categories"},{"content":" 很多人只用过别人写的包，那么有没有想过如何将自己的包发布出去让别人用\n首先到pypi.org注册账户，然后生成一个Token，注意生成Token的页面不要关闭不要刷新，因为只显示一次，在Token下面会有一个说明，就是在你的用户目录下创建一个文件，然后把那些内容复制粘贴进去保存就行了。这样做是为了在上传的时候不用输入用户名和密码，防止403报错等等。 提示：Windows的用户目录就是C:\\Users\\zhangsan 这个路径，打开步骤就是C盘-\u0026gt;用户-\u0026gt;用户名\n项目目录结构：\nporject_example/ ├── LICENSE ├── pyproject.toml ├── README.md ├── src/ │ └── pkg_name/ │ ├── __init__.py │ └── example.py └── tests/ 构建工具 # 这里列出4个常见的构建工具，示例里面使用安装时常看到的setuptools，当然也根据个人喜好进行选择。 选择一个构建工具添加到pyproject.toml\nsetuptools\n[build-system] requires = [\u0026#34;setuptools\u0026gt;=61.0\u0026#34;] build-backend = \u0026#34;setuptools.build_meta\u0026#34; Hatchling\n[build-system] requires = [\u0026#34;hatchling\u0026#34;] build-backend = \u0026#34;hatchling.build\u0026#34; Flit\n[build-system] requires = [\u0026#34;flit_core\u0026gt;=3.2\u0026#34;] build-backend = \u0026#34;flit_core.buildapi\u0026#34; PDM\n[build-system] requires = [\u0026#34;pdm-pep517\u0026#34;] build-backend = \u0026#34;pdm.pep517.api\u0026#34; requires是构建包所需的包列表。您不需要安装它们；像pip这样的构建前端会自动将它们安装在一个临时的、隔离的虚拟环境中，以便在构建过程中使用。 build-backend是前端将用于执行构建的 Python 对象的名称。\n配置元数据 # 打开pyproject.toml并输入以下内容。更改name 以包含您的用户名；这确保您有一个唯一的包名称，不会与其他人按照本教程上传的包发生冲突。\n[project] name = \u0026#34;pkg_name\u0026#34; version = \u0026#34;0.0.1\u0026#34; authors = [ { name=\u0026#34;Zhang San\u0026#34;, email=\u0026#34;admin@gmail.com\u0026#34; }, ] description = \u0026#34;A small example package\u0026#34; readme = \u0026#34;README.md\u0026#34; requires-python = \u0026#34;\u0026gt;=3.7\u0026#34; classifiers = [ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, ] [project.urls] \u0026#34;Homepage\u0026#34; = \u0026#34;https://github.com/zhangsan/porject_example\u0026#34; \u0026#34;Bug Tracker\u0026#34; = \u0026#34;https://github.com/zhangsan/porject_example/issues\u0026#34; name是您的包的分发名称。这可以是任何名称，只要它只包含字母、数字、.、_和-。它也不能已经在 PyPI 上使用。请务必使用您在本教程中的用户名更新此名称，因为这可确保您不会尝试上传与已存在名称相同的包。\nversion是包版本。有关版本的更多详细信息，请参阅版本说明符规范 。一些构建后端允许以其他方式指定它，例如从文件或 git 标签。\nauthors用于识别包的作者；您为每位作者指定姓名和电子邮件。您也可以maintainers以相同的格式列出。\ndescription是包的简短单句摘要。\nreadme是包含包详细描述的文件的路径。这显示在 PyPI 的包详细信息页面上。README.md在这种情况下，描述是从（这是一种常见模式）加载的。项目元数据规范中还描述了一种更高级的表格形式 。\nrequires-python给出项目支持的 Python 版本。像pip这样的安装程序会回顾旧版本的包，直到找到一个具有匹配 Python 版本的包。\nclassifiers为 index 和pip提供一些关于您的包的额外元数据。在这种情况下，该包仅与 Python 3 兼容，根据 MIT 许可获得许可，并且独立于操作系统。您应该始终至少包括您的包适用于哪些 Python 版本、您的包在哪个许可下可用以及您的包将在哪些操作系统上运行。有关分类器的完整列表，请参阅 https://pypi.org/classifiers/。\nurls允许您列出任意数量的额外链接以显示在 PyPI 上。通常这可能是源代码、文档、问题跟踪器等。\n有关可以在 表中定义的这些字段和其他字段的详细信息，请参阅项目元数据规范。[project]其他常见字段keywords用于提高可发现性以及 dependencies安装包所需的字段。 https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#declaring-project-metadata\n重点：已经发布过得版本号不能再次使用！否则会400报错。创建并上传成功的包名，即使你在pypi管理页面删除了，也不能再次使用！所以不要随意删除你已占用到的名称的项目，不然就只能更换包名了！\npyproject.toml 文件最终配置完成的示例：\n[build-system] requires = [\u0026#34;setuptools\u0026gt;=61.0\u0026#34;] build-backend = \u0026#34;setuptools.build_meta\u0026#34; [project] name = \u0026#34;pkg_name\u0026#34; version = \u0026#34;0.0.1\u0026#34; authors = [ { name=\u0026#34;Zhang San\u0026#34;, email=\u0026#34;admin@gmail.com\u0026#34; }, ] description = \u0026#34;A small example package\u0026#34; readme = \u0026#34;README.md\u0026#34; requires-python = \u0026#34;\u0026gt;=3.7\u0026#34; classifiers = [ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, ] [project.urls] \u0026#34;Homepage\u0026#34; = \u0026#34;https://github.com/zhangsan/porject_example\u0026#34; \u0026#34;Bug Tracker\u0026#34; = \u0026#34;https://github.com/zhangsan/porject_example/issues\u0026#34; 创建 README.md # 这个文件是包的说明文档，使用Markdown语法(不会的话花几分钟看看)，内容包括项目介绍，安装，使用范例，开发人员，下载量，开源协议等等，不知道怎么写可以看看GitHub上其他项目是怎么写的，参考着写就行了。\n创建许可证 # 上传到pypi的每个包都包含一个许可证，这是告诉使用这个软件包的用户需要遵守那些条款。 但是既然发布一个包总想着长期维护吧，所以在GitHub上创建仓库的时候会有一个选择许可证的下拉框，一般常用的是MIT和Apache2.0，区别是使用的时候一个是不署名，一个要署名。\n生成分发档案 # 先升级PyPA到最新版本\npy -m pip install --upgrade build 在项目根目录运行以下步骤的命令，也就是能看到pyproject.toml文件的位置\n将项目进行打包\n$ py -m build 上传分发档案 # 将自己的项目上传到pypi\n$ py -m twine upload dist/* 安装你新上传的包 # 这个步骤和你安装其他包是一样的，打开pypi项目主页也会显示命令，复制粘贴安装就行了。 怎么导入包，使用包，这些都是基础知识，不多赘述了。\n重要提示 # 以上是首次发布的步骤，但是你要更新版本号的话需要注意：\n要在pyproject.toml里面修改版本号，必须是该项目在pypi上没有用过的版本号 重新构建前必须删除之前构建生成的所有文件，比如：dist文件夹，src文件夹下的*.egg-info文件夹 参考链接：\nhttps://packaging.python.org/en/latest/tutorials/packaging-projects/ https://python-poetry.org/ ","date":"November 14, 2022","externalUrl":null,"permalink":"/posts/pypi-pkg/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e很多人只用过别人写的包，那么有没有想过如何将自己的包发布出去让别人用\u003c/p\u003e\n\u003c/blockquote\u003e","title":"发布包到pypi","type":"posts"},{"content":"","date":"November 6, 2022","externalUrl":null,"permalink":"/tags/lua/","section":"","summary":"","title":"Lua","type":"tags"},{"content":"","date":"November 6, 2022","externalUrl":null,"permalink":"/tags/neovim/","section":"","summary":"","title":"NeoVim","type":"tags"},{"content":" 使用PackerSync安装失败的解决方法\n问题 # 在NeoVim配置好插件后需要运行PackerSync进行配置的更新，但是由于国内访问GitHub不稳定，所以非常容易导致下载失败。下面推荐几个办法。\n方法 # 使用代理（梯子） # 最简单粗暴的办法就是使用代理来提高对GitHub的访问稳定性以及速度，简单快速实用，但是梯子不是每个人都有。 对于梯子不便透露，自己找。\n使用镜像 # 虽然有人做过插件的一些镜像，但是都已经失效，当然也可以自己来制作镜像，但是意义不大，谁没事整天配置nvim玩，再说了这是用来干活的，还有镜像也就用的时候起点作用，要是给别人用，那服务器也是得花钱，也没人愿意用爱发电，当然也是那些镜像失效的原因。\n使用镜像操作简单，修改配置 packe 里面的 xxx 为 https://xxxx.com: 就可以了。 xxx.com 就是镜像地址。\n使用ssh方式 # 首先要更改配置里面的访问方式，pack.nvim 里面的 修改为 git@github.com: [xxx/xxx] 修改为 [git@github.com:xxx/xxx.git] 或者修改为 git@gitee.com: ，毕竟是在国内，肯定快，当然还有把 [xxx/xxx] 修改为 [git@gitee.com:xxx/xxx.git]\n完成上面的配置后在命令行运行 nvim +PackerSync 或者进入nvim后运行 :PackerSync ，就可以看到nvim能进行正常安装了。\n注意：如果使用Gitee的话，需要先把用到的插件导入到Gitee，而且是公开的。 配置ssh到GitHub或者Gitee的步骤：生成key，在GitHub或者Gitee的配置里面添加key。\ngit config --global user.name \u0026#34;name\u0026#34; git config --global user.email \u0026#34;email\u0026#34; git config --global --list ssh-keygen -t rsa -C \u0026#34;email\u0026#34; 以上就是如何生成key的步骤，把真实的 name和email替换进去就行了，最后生成的时候有一些选项，比如设置密码什么的，这不是必需的，一路回车就行，然后就创建好了。\n总结 # 根据自己的具体情况来做选择，有梯子就直接上梯子，没梯子想折腾镜像就折腾镜像，以上都没有或者懒得搞，那就ssh，这个除了改的时候麻烦点，其他都好，把改好的配置文件最好在GitHub或者Gitee上保留一份，用的时候clone下来就行了。\n","date":"November 6, 2022","externalUrl":null,"permalink":"/posts/nvim/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e使用PackerSync安装失败的解决方法\u003c/p\u003e\n\u003c/blockquote\u003e","title":"NeoVim使用PackerSync失败问题解决（初稿待完善）","type":"posts"},{"content":"","date":"November 6, 2022","externalUrl":null,"permalink":"/tags/vim/","section":"","summary":"","title":"Vim","type":"tags"},{"content":"","date":"August 10, 2022","externalUrl":null,"permalink":"/categories/lua/","section":"","summary":"","title":"Lua","type":"categories"},{"content":" Lua是一门快速小巧的脚本语言，这篇文章让你快速入门Lua，只需一个蹲坑的时间\n打印/输出 # print(\u0026#34;Hello World\u0026#34;) 输出 # --this is a comment print(\u0026#34;hello\u0026#34;) --this is another comment -- the next line will not do anything because it is commented out --print(\u0026#34;world\u0026#34;) 变量 # -- Different types local x = 10 --number local name = \u0026#34;john doe\u0026#34; --string local isAlive = false -- boolean local a = nil --no value or invalid value Numbers\noperators\n+ addition - minus * multiply / divide ^ power % modulus -- examples local a = 1 local b = 2 local c = a + b print(c) -- 3 local d = b - a print(d) -- 1 local x = 1 * 3 * 4 -- 12 print(x) local y = (1+3) * 2 -- 8 print(y) print(10/2) -- 5 print (2^2) -- 4 print(5%2) -- 1 print(-b) -- -2 -- increment local level = 1 level = level + 1 print(level) -- 2 字符串\n-- concatenate strings local phrase = \u0026#34;My name is \u0026#34; local name = \u0026#34;John Doe\u0026#34; print(phase .. name) --My name is John Doe -- strings and numbers local age = 12 local name = \u0026#34;Billy\u0026#34; print(name .. \u0026#34; is \u0026#34; .. age .. \u0026#34; years old\u0026#34;) 布尔\nlocal isAlive = true print(isAlive) --true isAlive = false print(isAlive) --false 条件语句 # --number comparisions local age = 10 if age \u0026lt; 18 then print(\u0026#34;over 18\u0026#34;) --this will not be executed end --elseif and else age = 20 if age \u0026gt; 18 then print(\u0026#34;dog\u0026#34;) elseif age == 18 then print(\u0026#34;cat\u0026#34;) else print(\u0026#34;mouse\u0026#34;) end 比较运算符\n== 相等 \u0026lt; 小于 \u0026gt; 大于 \u0026lt;= 小于等于 \u0026gt;= 大于等于 ~= 不等于 --boolean comparision local isAlive = true if isAlive then print(\u0026#34;dog\u0026#34;) end --string comparisions local name = \u0026#34;billy\u0026#34; if name == \u0026#34;Billy\u0026#34; then --false print(\u0026#34;Billy\u0026#34;) elseif name == \u0026#34;billy\u0026#34; then --true print(\u0026#34;billy\u0026#34;) end 组合语句\nlocal x = 10 if x == 10 and x \u0026lt; 0 then --both are true print(\u0026#34;dog\u0026#34;) elseif x == 100 or x \u0026lt; 0 then --1 or more are true print(\u0026#34;cat\u0026#34;) end --result: cat 嵌套语句\nlocal x = 10 local isAlive = true if x==10 then if isAlive == true then print(\u0026#34;dog\u0026#34;) else print(\u0026#34;cat\u0026#34;) end end 反转值\n您还可以使用 not 关键字取反\nlocal x = 10 if not x == 10 then print(\u0026#34;here\u0026#34;) end 函数 # function printTax(price) local tax = price * 0.21 print(\u0026#34;tax:\u0026#34; .. tax) end printTax(200) --function that returns a value function calculateTax(price) return price * 0.21 end local result = calculateTax(100) print(result) --reusing the function but this time using variables local bread = 130 local milk = 110 local breadTax = calculateTax(bread) --27.3 local milkTax = calculateTax(milk) --23.1 print(\u0026#34;Bread Tax = \u0026#34; .. breadTax) print(\u0026#34;Milk Tax = \u0026#34; .. milkTax) --multiple parameters function displayInfo(name, age, country) print(name .. \u0026#34; is \u0026#34; .. age .. \u0026#34; years old and is from \u0026#34; .. country) end displayInfo(\u0026#34;Billy\u0026#34;, 12, \u0026#34;Jupiter\u0026#34;) 作用域 # 变量有不同的作用域。一旦到达范围的末尾，该范围中的值将不再可访问\nfunction foo() local a = 10 end print(a) --nil local isAlive = true if isAlive then local a = 10 end print(a) --nil 全局变量\nlocal _G.myValue = 69 --doing this can sometimes be bad practice 循环 # 有几种不同的方法可以在 lua 中执行循环\n--while loop local i = 0 local count = 0 while i \u0026lt;= 10 do count = count + 1 end print(\u0026#34;count is \u0026#34; .. count) --count is 7 --for loop count = 0 for i=1, 5 do count = count + 1 end print(\u0026#34;count is \u0026#34; .. count) 无限循环（死循环）\n--infinite loop will never end local i = 0 while i \u0026gt;= 0 do i = i + 1 print(i) end 嵌套循环\nlocal count = 0 for a=1, 10 do for b=1, 10 do count = count + 1 end end print(count) -- 100 表 # --basic table local colors = { \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34; } print(colors[1]) --red print(colors[2]) --green print(colors[3]) --blue --using a loop to iterate though your table for i=1, #colors do print(colors[i]) end 表操作\n--insert local colors = { \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34; } table.insert(colors, \u0026#34;orange\u0026#34;) local index = #colors --4 (this is the last index in the table) print(colors[index]) --orange --insert at index local colors = { \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34; } table.insert(colors, 2, \u0026#34;pink\u0026#34;) for i=1, #colors do print(colors[i]) end --red, pink, green, blue --remove local colors = { \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34; } table.remove(colors, 1) for i=1, #colors do print(colors[i]) end -- \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34; 2 尺寸表\n--tables within tables local data = { { \u0026#34;billy\u0026#34;, 12 }, { \u0026#34;john\u0026#34;, 20 }, { \u0026#34;andy\u0026#34;, 65 } } for a=1, #data do print(data[a][1] .. \u0026#34; is \u0026#34; .. data[a][2] .. \u0026#34; years old\u0026#34;) end 键表\n二维表不适合不同类型的数据，而是使用表的键\nlocal teams = { [\u0026#34;teamA\u0026#34;] = 12, [\u0026#34;teamB\u0026#34;] = 15 } print(teams[\u0026#34;teamA\u0026#34;]) -- 12 for key,value in pairs(teams) do print(key .. \u0026#34;:\u0026#34; .. value) end --insert into key table teams[\u0026#34;teamC\u0026#34;] = 1 --remove key from table teams[\u0026#34;teamA\u0026#34;] = nil 从函数返回表\n这可用于从函数返回多个值\nfunction getTeamScores() local scores = { [\u0026#34;teamA\u0026#34;] = 12, [\u0026#34;teamB\u0026#34;] = 15 } return scores end local scores = getTeamScores() local total = 0 for key, val in pairs(scores) do total += val end print(\u0026#34;Total score of all teams:\u0026#34; .. total) Math # 数学类有许多处理数字的函数。您可能不需要它们，但这里有一些更有用的功能:\n更多: Wiki\nabs (绝对值) local x = -10 print(math.abs(x)) --result: 10 local a = 10 print(math.abs(a)) --result: 10 ceil (四舍五入的十进制值) local x = 1.2 print(math.ceil(x)) --result: 2 deg (将值从弧度转换为度) print(math.deg(math.pi)) -- result: 180 floor (向下取整十进制值) local x = 1.2 print(math.floor(x)) --result: 1 pi (pi 的常数值) print(math.pi) --3.1415926535898 3.1415926535898 rad (将值从度数转换为弧度) print(math.rad(180)) --result: 3.1415926535898 random (随机数生成) --random value between 0 tand 1 print(math.random()) --result: 0.0012512588885159 --random integer value from 1 to 100 (both inclusive) print(math.random(100)) --result: 20 --random integer value from 20 to 100 (both inclusive) print(math.random(20, 100)) --result: 54 sqrt (数字的平方根) print(math.sqrt(100)) --result: 10 模块 # Include code other files\nrequire(\u0026#34;otherfile\u0026#34;) ","date":"August 10, 2022","externalUrl":null,"permalink":"/posts/learnlua/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eLua是一门快速小巧的脚本语言，这篇文章让你快速入门Lua，只需一个蹲坑的时间\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Lua快速入门","type":"posts"},{"content":"","date":"August 5, 2022","externalUrl":null,"permalink":"/tags/archlinux/","section":"","summary":"","title":"ArchLinux","type":"tags"},{"content":" ArchLinux是桌面版开发里最好的发行版之一，基于ArchLinux的EndeavourOS你值得拥有，至少值得一试\nEndeavourOS # https://endeavouros.com 为什么选择EndeavourOS # EndeavourOS 相比另一个衍生版Manjaro来说更干净，没有自带太多乱七八糟的东西，而且对我的电脑更友好。我的笔记本是ThinkPad的T系列，安装Manjaro后觉得不是很流畅，反应有些慢，自带很多软件我也不喜欢。\n最早使用Linux的是Redhat系，但除了服务器上用的多，桌面版用的比较少；后来用的最多的就是Debian系的Debian，使用xfce桌面，但是软件源老旧，应该是追求稳定，而且有一个比较新的软件源直接没有，使用起来麻烦；Arch系最早滚来滚去嫌麻烦，只是体验了一下，现在正式转到Arch，嗯~ 真香！\nEndeavourOS安装 # 有友好的向导，安装简单。可以在一开始就更换到国内源，然后在线安装，强烈推荐使用Plasma桌面。\n后记 # 在安装Sogou输入法的时候，卸载了 ibus ，用的 pacman -Rc 命令，而且平常会细心查看命令行提示的我，这次看都没看直接在出现 （yes/no?） 的时候快速输入 y 并回车，导致卸载了 plasma 以及其所依赖的 libibus 。 plasma 依赖输入法所依赖的 libibus 真的是非常奇葩！\n重启系统后,输入密码登录到桌面的后，直接是黑屏，但是显示鼠标，可以用 Alt + F2 搜索快捷键，而且用这个打开其他任何软件都可以正常使用。最后通过 Ctrl + Alt + F2 切换到无界面模式，执行命令 pacman -S plasma-meta 重新安装 plasma 后恢复正常。\n不管是安装和卸载，最好都仔细确认一下给出的提示。卸载应该使用 pacman -Rs ，而不是 pacman -Rc 或者 pacman -Rcn ，这样会卸载其依赖的所有软件包。\n就在写文章的时候发现另一个衍生版，看着也不错： 官网： https://archcraft.io ","date":"August 5, 2022","externalUrl":null,"permalink":"/posts/archlinux/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eArchLinux是桌面版开发里最好的发行版之一，基于ArchLinux的EndeavourOS你值得拥有，至少值得一试\u003c/p\u003e\n\u003c/blockquote\u003e","title":"ArchLinux之EndeavourOS","type":"posts"},{"content":"","date":"August 5, 2022","externalUrl":null,"permalink":"/tags/endeavouros/","section":"","summary":"","title":"EndeavourOS","type":"tags"},{"content":"","date":"August 5, 2022","externalUrl":null,"permalink":"/categories/linux/","section":"","summary":"","title":"Linux","type":"categories"},{"content":"","date":"July 28, 2022","externalUrl":null,"permalink":"/categories/ide/","section":"","summary":"","title":"IDE","type":"categories"},{"content":" Vim或者EMACS都是非常值得学习的编辑器，由于其经久不衰，长期来看其实是降低了使用IDE的成本，不管是学习还是金钱。任何新语言都会优先推出这两个编辑器的插件。\n常用的IDE # 之前一直使用JetBrains，如：PyCharm，GoLand，或者Sublime，VSCode。 使用VSCode写前端挺舒服，写Python和Go也还可以。JB出品，必属精品，但是收费，而且启动速度慢，还很吃资源，毕竟是Java开发的。\n换到Vim # 为啥会选择Vim而不是EMACS，主要是因为服务器上默认都自带Vi，这样用起来就方便了，还要vscode等其他编辑器也有Vim的插件可以使用，浏览器也有Vim插件。 对于快捷键和功能方面，我觉得EMACS比较友好一些，但我是手残，经常按错，而且服务器不自带，其强大之处不用多说，伪装成编辑器的系统，能打游戏能听歌能浏览网页能收发邮件等等。 总之，Vim和EMACS都很强大，主要是看哪个能满足自己的需求，同时用的趁手，争论哪个更好毫无意义，工具的意义是为了提升效率，而不是为了装杯。\nVim当前版本已到9.0，更换到Vim方便对服务器上的文件进行编辑。 换到NeoVim # NeoVim是Vim的衍生版其中的一个，而且是当前最火的一个，拥有非常多的插件，以及支持Lua配置，不过Vim现在也开始支持。\nNeoVim相关配置资源 # SpaceVim： https://github.com/SpaceVim/SpaceVim LunarVim： https://github.com/LunarVim/LunarVim NvChad： https://github.com/NvChad/NvChad 我目前使用的是 NvChad 配置，正在学习并尝试完全转到Vim\n","date":"July 28, 2022","externalUrl":null,"permalink":"/posts/neovim/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eVim或者EMACS都是非常值得学习的编辑器，由于其经久不衰，长期来看其实是降低了使用IDE的成本，不管是学习还是金钱。任何新语言都会优先推出这两个编辑器的插件。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"从IDE/vscode到Vim/NeoVim","type":"posts"},{"content":" 十一快乐，我又回来了\n整站以后的版式估计不会再有大的改动，将以内容更新为主 2022.7.16 然而又鸽了\u0026hellip; ","date":"October 8, 2021","externalUrl":null,"permalink":"/posts/iamback/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e十一快乐，我又回来了\u003c/p\u003e\n\u003c/blockquote\u003e","title":"我回来了！","type":"posts"},{"content":"","date":"March 30, 2020","externalUrl":null,"permalink":"/tags/hugo/","section":"","summary":"","title":"Hugo","type":"tags"},{"content":" 安装Hugo # 首先安装Git # 在底部附近找到Windows文件（它们是按字母顺序排列，因此Windows是最后一个）–根据您使用的是32位还是64位Windows，下载32位或64位文件。（如果您不知道，请参见此处。） 将ZIP文件移动到您的C:\\Hugo\\bin文件夹中。 双击ZIP文件并提取其内容。确保将内容提取到同一C:\\Hugo\\bin文件夹中-Windows会默认执行此操作，除非您告诉它要提取其他位置。 现在，您应该有了三个新文件：hugo可执行文件（hugo.exe）LICENSE，和README.md。 现在，您需要将Hugo添加到Windows PATH设置中：\n安装 # $ brew install hugo 验证安装 # $ hugo version $ hugo help 新建网站： # $ hugo new site quickstart 添加内容 # $ content/\u0026lt;CATEGORY\u0026gt;/\u0026lt;FILE\u0026gt;.\u0026lt;FORMAT\u0026gt; $ hugo new posts/my-first-post.md Hugo命令 # 命令hugo将您的站点渲染到public/dir中，并准备将其部署到Web服务器：\n启动Hugo服务器 # $ hugo server -D http://localhost:1313\n安装主题 # config.toml\nbaseURL = \u0026#34;https://example.org/\u0026#34; languageCode = \u0026#34;en-us\u0026#34; title = \u0026#34;My New Hugo Site\u0026#34; theme = \u0026#34;ananke\u0026#34; 建立静态页面 # $ hugo -D ./public/默认情况下，输出将位于目录中（-d/ \u0026ndash;destination标志进行更改，或publishdir在配置文件中设置）。\n运行调试 # 配置文件 # 上线到GitHub, Gitee, Coding, Gitlab # ","date":"March 30, 2020","externalUrl":null,"permalink":"/posts/hugo/","section":"","summary":"安装Hugo # 首先安装Git # 在底部附近找到Windows文件（它们是按字母顺序排列，因此Windows是最后一个）–根据您使用的是32位还是64位Windows，下载32位或64位文件。（如果您不知道，请参见此处。） 将ZIP文件移动到您的C:\\Hugo\\bin文件夹中。 双击ZIP文件并提取其内容。确保将内容提取到同一C:\\Hugo\\bin文件夹中-Windows会默认执行此操作，除非您告诉它要提取其他位置。 现在，您应该有了三个新文件：hugo可执行文件（hugo.","title":"Hugo搭建博客教程","type":"posts"},{"content":" Erlang是一种开源编程语言，用于构建对高可用性有要求的大规模可扩展的软实时系统。它通常用于电信，银行，电子商务，计算机电话和即时消息中。Erlang的runtime系统具有对并发，分发和容错的内置支持。它是在爱立信计算机科学实验室设计的。\n安装之前，先看一下它们的简要说明\nErlang # Erlang是一种开源编程语言，用于构建对高可用性有要求的大规模可扩展的软实时系统。它通常用于电信，银行，电子商务，计算机电话和即时消息中。Erlang的runtime系统具有对并发，分发和容错的内置支持。它是在爱立信计算机科学实验室设计的。\nElixir # Elixir是一种动态的功能语言，旨在用于构建可伸缩和可维护的应用程序。Elixir利用了以运行低延迟，分布式和容错系统而著称的Erlang VM，同时也成功地用于Web开发和嵌入式软件领域。\n现在开始在CentOS 7.7 64位服务器中安装Erlang和Elixir。\n安装前的准备 # 安装Erlang和Elixir之前，需要安装以下依赖。\n$ yum update $ yum install epel-release $ yum install gcc gcc-c++ glibc-devel make ncurses-devel openssl-devel autoconf java-1.8.0-openjdk-devel git wget wxBase.x86_64 安装Erlang # 由于官方存储库中的Erlang版本可能比较旧，这里我将下载并安装最新的Erlang版本。\n添加Erlang官方存储库以安装最新的Erlang。\n首先到 Erlang存储库页面，根据你使用的发行版下载存储库：\n由于现在在CentOS 7.7中安装Erlang，因此我将添加以下存储库。\n$ wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm $ rpm -Uvh erlang-solutions-1.0-1.noarch.rpm 使用以下命令更新存储库列表：\n$ yum update 使用以下命令安装erlang\n$ yum install erlang 现在最新版本的Erlang已安装完成。\n验证Erlang # 运行以下命令以验证是否已安装Erlang。\n$ erl 输出示例：\nErlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe] Eshell V10.6 (abort with ^G) 1\u0026gt; 如果看到如上的Erlang shell，表示安装成功！要关闭shell，只需按两次 Ctrl-C。\n在Erlang中测试示例“ hello_world”程序 # 创建一个名为“ hello.erl”的新文件。\n$ vi hello.erl 添加以下代码：\n-module(hello). -export([hello_world/0]). hello_world() -\u0026gt; io:fwrite(\u0026#34;hello, world\\n\u0026#34;). :wq 保存并关闭文件。\n使用以下命令打开Erlang shell：\n$ erl 运行以下命令，切记在每个命令的末尾添加英文点：\n$ c(hello). $ hello:hello_world(). 输出示例：\n[root@localhost ~]# erl Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe] Eshell V10.6 (abort with ^G) 1\u0026gt; c(hello). {ok,hello} 2\u0026gt; hello:hello_world(). hello, world ok 3\u0026gt; 安装Elixir # 安装Elixir之前必须要先安装Erlang！\nElixir在EPEL存储库中可用，但已经过时了。为了安装最新版本，这里使用源码编译安装。\n现在将Elixir从GitHub拉取到本地：\n$ git clone -b v1.9.4 https://github.com/elixir-lang/elixir.git 上面的命令会将最新版本拉取到当前工作目录中名为 elixir的文件夹中。 注意：最好指定版本，不然的话你安装完查看版本，可能就是如下所示一样，源码和版本号不匹配\nErlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe] Elixir 1.10.0-dev (ee758f9) (compiled with Erlang/OTP 22) 接下来进到 elixir 目录：\n$ cd elixir/ 运行以下命令开始编译Elixir：\n$ make clean test 输出示例：\n[root@localhost elixir]# make clean test rm -rf ebin rm -rf lib/*/ebin rm -rf lib/elixir/src/elixir_parser.erl make[1]: 进入目录“/root/elixir” rm -rf lib/*/_build/ rm -rf lib/*/tmp/ rm -rf lib/elixir/test/ebin/ rm -rf lib/mix/test/fixtures/deps_on_git_repo/ rm -rf lib/mix/test/fixtures/git_rebar/ rm -rf lib/mix/test/fixtures/git_repo/ rm -rf lib/mix/test/fixtures/git_sparse_repo/ rm -f erl_crash.dump make[2]: 进入目录“/root/elixir” rm -f man/elixir.1 rm -f man/elixir.1.bak rm -f man/iex.1 rm -f man/iex.1.bak make[2]: 离开目录“/root/elixir” make[1]: 离开目录“/root/elixir” Recompile: src/elixir_utils Recompile: src/elixir_tokenizer Recompile: src/elixir_sup Recompile: src/elixir_rewrite Recompile: src/elixir_quote Recompile: src/elixir_parser Recompile: src/elixir_overridable Recompile: src/elixir_module Recompile: src/elixir_map Recompile: src/elixir_locals Recompile: src/elixir_lexical Recompile: src/elixir_interpolation Recompile: src/elixir_import Recompile: src/elixir_fn Recompile: src/elixir_expand Recompile: src/elixir_errors Recompile: src/elixir_erl_var Recompile: src/elixir_erl_try Recompile: src/elixir_erl_pass Recompile: src/elixir_erl_for Recompile: src/elixir_erl_compiler Recompile: src/elixir_erl_clauses Recompile: src/elixir_erl Recompile: src/elixir_env Recompile: src/elixir_dispatch Recompile: src/elixir_def Recompile: src/elixir_config Recompile: src/elixir_compiler Recompile: src/elixir_code_server Recompile: src/elixir_clauses Recompile: src/elixir_bootstrap Recompile: src/elixir_bitstring Recompile: src/elixir_aliases Recompile: src/elixir Generated elixir app ==\u0026gt; bootstrap (compile) Compiled lib/elixir/lib/kernel.ex Compiled lib/elixir/lib/macro/env.ex Compiled lib/elixir/lib/keyword.ex Compiled lib/elixir/lib/module.ex Compiled lib/elixir/lib/list.ex Compiled lib/elixir/lib/macro.ex Compiled lib/elixir/lib/kernel/typespec.ex Compiled lib/elixir/lib/code.ex Compiled lib/elixir/lib/code/identifier.ex Compiled lib/elixir/lib/module/checker.ex Compiled lib/elixir/lib/module/locals_tracker.ex Compiled lib/elixir/lib/module/parallel_checker.ex Compiled lib/elixir/lib/module/types/helpers.ex Compiled lib/elixir/lib/module/types/infer.ex Compiled lib/elixir/lib/module/types/expr.ex Compiled lib/elixir/lib/module/types/pattern.ex Compiled lib/elixir/lib/module/types.ex Compiled lib/elixir/lib/kernel/utils.ex Compiled lib/elixir/lib/exception.ex Compiled lib/elixir/lib/protocol.ex Compiled lib/elixir/lib/stream/reducers.ex Compiled lib/elixir/lib/enum.ex Compiled lib/elixir/lib/map.ex Compiled lib/elixir/lib/inspect/algebra.ex Compiled lib/elixir/lib/inspect.ex Compiled lib/elixir/lib/access.ex Compiled lib/elixir/lib/range.ex Compiled lib/elixir/lib/regex.ex Compiled lib/elixir/lib/string.ex Compiled lib/elixir/lib/string/chars.ex Compiled lib/elixir/lib/io.ex Compiled lib/elixir/lib/path.ex Compiled lib/elixir/lib/file.ex Compiled lib/elixir/lib/system.ex Compiled lib/elixir/lib/kernel/cli.ex Compiled lib/elixir/lib/kernel/error_handler.ex Compiled lib/elixir/lib/kernel/parallel_compiler.ex Compiled lib/elixir/lib/kernel/lexical_tracker.ex make[1]: 进入目录“/root/elixir” ==\u0026gt; unicode (compile) Compiling /root/elixir/lib/elixir/unicode/unicode.ex (it\u0026#39;s taking more than 15s) make[1]: 离开目录“/root/elixir” ==\u0026gt; elixir (compile) Compiling /root/elixir/lib/elixir/lib/base.ex (it\u0026#39;s taking more than 15s) make[1]: 进入目录“/root/elixir” Generated elixir app make[1]: 离开目录“/root/elixir” ==\u0026gt; eex (compile) ==\u0026gt; mix (compile) Generated mix app ==\u0026gt; ex_unit (compile) Generated ex_unit app ==\u0026gt; logger (compile) Generated logger app Generated eex app ==\u0026gt; iex (compile) Generated iex app ==\u0026gt; elixir (eunit) All 190 tests passed. ==\u0026gt; elixir (ex_unit) ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ Finished in 81.6 seconds (47.2s on load, 34.4s on tests) 1569 doctests, 3374 tests, 0 failures, 7 excluded Randomized with seed 847643 ==\u0026gt; ex_unit (ex_unit) ............................................................................................................................................................................................................................................................................................................................................................... Finished in 6.2 seconds (3.9s on load, 2.3s on tests) 42 doctests, 309 tests, 0 failures Randomized with seed 541463 ==\u0026gt; logger (ex_unit) ............................................................................................................................ Finished in 2.4 seconds (1.9s on load, 0.4s on tests) 3 doctests, 121 tests, 0 failures Randomized with seed 211604 ==\u0026gt; mix (ex_unit) Excluding tags: [windows: true] ............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ Finished in 158.5 seconds (12.1s on load, 146.4s on tests) 9 doctests, 611 tests, 0 failures Randomized with seed 334499 ==\u0026gt; eex (ex_unit) ........................................................................................... Finished in 0.5 seconds (0.4s on load, 0.06s on tests) 5 doctests, 86 tests, 0 failures Randomized with seed 599118 ==\u0026gt; iex (ex_unit) ....................................................................................................................................................................................................................................... Finished in 8.5 seconds (2.1s on load, 6.3s on tests) 231 tests, 0 failures Randomized with seed 207545 [root@localhost elixir]# 如果测试通过，则表示编译安装完成。\n接下来将Elixir的bin路径添加到PATH环境变量中，否则Elixir将无法正常工作。\n方法一（暂时生效） 现在运行以下命令：\n$ export PATH=\u0026#34;\\$PATH:/root/elixir/bin\u0026#34; 在这里，我把Elixir安装在了 /root/elixir/ 。你必须将此路径替换为你实际的Elixir安装路径。\n方法二（只对当前登陆用户生效，永久生效）\n$ vi \\~/.bash_profile 默认如下：\nPATH=$PATH:$HOME/bin 添加后：\nPATH=$PATH:$HOME/bin:$PATH:/root/elixir/bin 接下来执行 source ~/.bash_profile 使其立即生效或者 reboot 重启生效\n方法三（对所有系统用户生效，永久生效）\n$ vi /etc/profile 在文件最后添加以下两行：\nPATH=\u0026#34;$PATH:/root/elixir/bin\u0026#34; export PATH 接下来执行 source /etc/profile 使其立即生效或者 reboot 重启生效\n验证Elixir # 要验证是否已安装Elixir，请运行：\n$ iex 输出示例：\nErlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe] Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help) iex(1)\u0026gt; 如果你看到Elixir shell，则表示安装成功！\n同样，要关闭Elixir shell，只需按两次Ctrl-C。\n查看Elixir版本：\n$ elixir --version 输出示例：\nErlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe] Elixir 1.10.0-dev (ee758f9) (compiled with Erlang/OTP 22) 现在CentOS 7.7服务器中已成功安装并设置可工作的Erlang和Elixir开发环境，所有安装已完成！\n简便方法 # 直接使用yum或者apt来安装，详情查看 Erlang Solutions 软件仓库镜像使用帮助\n参考链接：\nUnixmen Erlang网站 Erlang文档 Elixir网站 Elixir文档 安装Erlang ","date":"December 11, 2019","externalUrl":null,"permalink":"/posts/install-elixir/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eErlang是一种开源编程语言，用于构建对高可用性有要求的大规模可扩展的软实时系统。它通常用于电信，银行，电子商务，计算机电话和即时消息中。Erlang的runtime系统具有对并发，分发和容错的内置支持。它是在爱立信计算机科学实验室设计的。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"CentOS 7.7安装Erlang和Elixir","type":"posts"},{"content":"","date":"December 11, 2019","externalUrl":null,"permalink":"/tags/centos7/","section":"","summary":"","title":"CentOS7","type":"tags"},{"content":"","date":"December 10, 2019","externalUrl":null,"permalink":"/tags/gitlab/","section":"","summary":"","title":"Gitlab","type":"tags"},{"content":" 1.准备环境 # 在正式开始安装之前，先更新软件包并打开相关服务的权限。\n更新软件包:\n$ yum update -y 安装 sshd # 安装 sshd：\n$ yum install -y curl policycoreutils-python openssh-server 启用并启动 sshd：\n$ systemctl enable sshd $ systemctl start sshd 配置防火墙 # 打开vim /etc/sysctl.conf 文件，在文件最后添加新的一行并保存：\nnet.ipv4.ip_forward = 1 启用并启动防火墙：\n$ systemctl enable firewalld $ systemctl start firewalld 放通 HTTP：\n$ firewall-cmd --permanent --add-service=http 重启防火墙：\n$ systemctl reload firewalld 在实际使用中，可以使用 systemctl status firewalld 命令查看防火墙的状态。\n安装 postfix # GitLab 需要使用 postfix 来发送邮件。当然，也可以使用 SMTP 服务器，具体步骤请参考 官方教程。 安装：\n$ yum install -y postfix 打开 vim /etc/postfix/main.cf 文件，在第 119 行找到 inet_protocols = all，将 all 改为 ipv4 并保存：\ninet_protocols = ipv4 启用并启动 postfix：\n$ systemctl enable postfix $ systemctl start postfix 配置 swap 交换分区 # 由于 GitLab 较为消耗资源，我们需要先创建交换分区，以降低物理内存的压力。 在实际生产环境中，如果服务器配置够高，则不必配置交换分区。 新建 2 GB 大小的交换分区：\n$ dd if=/dev/zero of=/root/swapfile bs=1M count=4096 格式化为交换分区文件并启用：\n$ mkswap /root/swapfile $ swapon /root/swapfile 添加自启用。打开 vim /etc/fstab 文件，在文件最后添加新的一行并保存：\n/root/swapfile swap swap defaults 0 0 2.安装 GitLab # 将软件源修改为国内源 # 由于网络环境的原因，将 repo 源修改为清华大学 。 在 /etc/yum.repos.d 目录下新建 gitlab-ce.repo 文件并保存。内容如下：\n$ vim /etc/yum.repos.d/gitlab-ce.repo [gitlab-ce] name=Gitlab CE Repository baseurl=https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el$releasever/ gpgcheck=0 enabled=1 安装 GitLab # 刚才修改过了 yum 源，因此先重新生成缓存： （此步骤执行时间较长，一般需要 3~5 分钟左右，请耐心等待）\n\u0026gt; yum makecache 安装 GitLab： （此步骤执行时间较长，一般需要 3~5 分钟左右，请耐心等待）\n$ yum install -y gitlab-ce 3初始化 GitLab # 配置 GitLab 的域名（非必需） # 打开 vim /etc/gitlab/gitlab.rb 文件，在第 23 行附近找到 external_url 'http://gitlab.example.com'，将单引号中的内容改为自己的域名（带上协议头，末尾无斜杠），并保存。 例如：\nexternal_url \u0026#39;http://gitlab.markhoo.com\u0026#39; 记得将域名通过 A 记录解析到你的服务器IP。\n初始化 GitLab # 特别重要！ 使用如下命令初始化 GitLab： （此步骤执行时间较长，一般需要 5~10 分钟左右，请耐心等待）\n$ sudo gitlab-ctl reconfigure 开始使用吧！ # 至此，我们已经成功地在 CentOS 7 上搭建了 GitLab。 现在可以访问 GitLab 了。\n在实际生产中，建议您使用 2 核 4 GB 或更高配置的 CVM。点击这里 可以查看 GitLab 官方推荐的配置和可承载人数对应表。\n再次提醒您，定期执行 yum update -y 以保持各软件包的最新状态。 谢谢！\nsudo docker run --detach \\ --hostname localhost \\ --publish 443:443 --publish 80:80 --publish 22:22 \\ --name gitlab \\ --restart always \\ --volume /srv/gitlab/config:/etc/gitlab \\ --volume /srv/gitlab/logs:/var/log/gitlab \\ --volume /srv/gitlab/data:/var/opt/gitlab \\ gitlab/gitlab-ce:latest sudo docker run \u0026ndash;detach \u0026ndash;hostname localhost \u0026ndash;publish 1443:443 \u0026ndash;publish 18080:8080 \u0026ndash;publish 122:22 \u0026ndash;name gitlab \u0026ndash;restart always \u0026ndash;volume /home/gitlab/config:/etc/gitlab \u0026ndash;volume /home/gitlab/logs:/var/log/gitlab \u0026ndash;volume /home/gitlab/data:/var/opt/gitlab gitlab/gitlab-ce:latest\ndocker run \u0026ndash;detach \u0026ndash;hostname 192.168.1.133 \u0026ndash;publish 7001:443 \u0026ndash;publish 7002:80 \u0026ndash;publish 7003:22 \u0026ndash;name gitlab \u0026ndash;restart always \u0026ndash;volume /home/gitlab/config:/etc/gitlab \u0026ndash;volume /home/gitlab/logs:/var/log/gitlab \u0026ndash;volume /home/gitlab/data:/var/opt/gitlab 8e28c88b6a21\n","date":"December 10, 2019","externalUrl":null,"permalink":"/posts/gitlab/","section":"","summary":"1.","title":"Gitlab安装","type":"posts"},{"content":" 简介 # Phoenix是一个用Elixir编写的Web开发框架，它实现了服务器端模型视图控制器（MVC）模式。我们的其他人似乎对在其他Web框架（例如Ruby on Rails或Python的Django）中拥有丰富的经验感到熟悉。\nPhoenix提供了两全其美的优势-高开发人员生产力和高应用程序性能。它还具有一些有趣的新特性，例如用于实现实时功能的通道和用于快速提高速度的预编译模板。\n安装 # 如果我们是第一次安装Elixir，则还需要安装Hex软件包管理器。要使Phoenix应用程序运行（通过安装依赖项）并安装我们在此过程中可能需要的任何其他依赖项，必须使用Hex。\n这是安装Hex的命令（如果已经安装了Hex，它将升级Hex到最新版本）：\n$ mix local.hex 安装Phoenix # $ mix archive.install hex phx_new 1.4.10 PostgreSQL # PostgreSQL是一个关系数据库服务器。Phoenix将应用程序配置为默认使用它，但是我们可以\u0026ndash;database mysql在创建新应用程序时通过传递标志来切换到MySQL 。\n我们的第一个Phoenix应用程序 # 现在我们已经安装了所有东西，让我们创建第一个Phoenix应用程序并启动并运行。\n我们可以mix phx.new从任何目录运行，以引导我们的Phoenix应用程序。Phoenix将接受新项目目录的绝对路径或相对路径。假设我们的应用程序名称为hello，我们运行以下命令：\n$ mix phx.new hello 在开始之前，有关Webpack的说明：Phoenix默认情况下将使用webpack进行资产管理。Webpack的依赖项是通过节点包管理器安装的，而不是混合安装的。Phoenix 将在mix phx.new任务结束时提示我们安装它们。如果那时我们说“ no”，并且以后不使用来安装那些依赖项npm install，则当我们尝试启动它时，我们的 应用程 \u0026gt; 序将引发错误，并且资产可能无法正确加载。如果我们根本不想使用webpack，可以将传递\u0026ndash;no-webpack给mix phx.new。 关于Ecto的说明：Ecto允许我们的Phoenix应用程序与数据存储进行通信，例如PostgreSQL，MySQL等。如果我们的应用程序不需要此组件，则可以通过将\u0026ndash;no-ecto标志传递给来跳过此依赖关系mix phx.new。该标志也可以与之组合\u0026ndash;no-webpack以创建框架应用程序。\n$ mix phx.new hello * creating hello/config/config.exs * creating hello/config/dev.exs * creating hello/config/prod.exs ... * creating hello/assets/static/images/phoenix.png * creating hello/assets/static/favicon.ico Fetch and install dependencies? [Yn] Phoenix生成目录结构以及应用程序所需的所有文件。完成后，它将询问我们是否要它为我们安装依赖项。让我们对此表示肯定。\nFetch and install dependencies? [Yn] Y * running mix deps.get * running mix deps.compile * running cd assets \u0026amp;\u0026amp; npm install \u0026amp;\u0026amp; node node_modules/webpack/bin/webpack.js --mode development We are almost there! The following steps are missing: $ cd hello Then configure your database in config/dev.exs and run: $ mix ecto.create Start your Phoenix app with: $ mix phx.server You can also run your app inside IEx (Interactive Elixir) as: $ iex -S mix phx.server 安装依赖项后，该任务将提示我们更改为项目目录并启动我们的应用程序。\nPhoenix假设我们的PostgreSQL数据库将具有一个postgres具有正确权限的用户帐户，密码为“ postgres”。如果不是这种情况,请修改config目录下dev.ex文件中的数据库配置信息。\n好的，让我们尝试一下。首先，我们cd进入hello/刚刚创建的目录：\n$ cd hello 现在，我们将创建数据库：\n$ mix ecto.create Compiling 13 files (.ex) Generated hello app The database for Hello.Repo has been created 注意：如果这是您第一次运行此命令，Phoenix可能还会要求安装Rebar。由于Rebar用于构建Erlang软件包，因此请继续进行安装。\n最后，我们将启动Phoenix服务器：\n$ mix phx.server [info] Running HelloWeb.Endpoint with cowboy 2.5.0 at http://localhost:4000 Webpack is watching the files… ... 如果在生成新应用程序时选择不让Phoenix安装依赖项，则该mix phx.new任务将提示我们在确实要安装依赖项时采取必要的步骤。\nFetch and install dependencies? [Yn] n We are almost there! The following steps are missing: $ cd hello $ mix deps.get $ cd assets \u0026amp;\u0026amp; npm install \u0026amp;\u0026amp; node node_modules/webpack/bin/webpack.js --mode development Then configure your database in config/dev.exs and run: $ mix ecto.create Start your Phoenix app with: $ mix phx.server You can also run your app inside IEx (Interactive Elixir) as: $ iex -S mix phx.server 默认情况下，Phoenix接受端口4000上的请求。如果我们将喜欢的Web浏览器指向http：// localhost：4000，则应该看到Phoenix框架欢迎页面。\n如果您的屏幕看起来像上面的图片，那么恭喜！您现在有一个正在运行的Phoenix应用程序。如果看不到上面的页面，请尝试通过http://127.0.0.1:4000访问该页面，然后确保您的操作系统已将“ localhost”定义为“ 127.0.0.1”。 在本地，我们的应用程序在iex会话中运行。要停止它，我们打了ctrl-c两次，就像我们iex正常停止一样。\n","date":"October 23, 2019","externalUrl":null,"permalink":"/posts/phoenix/","section":"","summary":"简介 # Phoenix是一个用Elixir编写的Web开发框架，它实现了服务器端模型视图控制器（MVC）模式。我们的其他人似乎对在其他Web框架（例如Ruby on Rails或Python的Django）中拥有丰富的经验感到熟悉。","title":"Elixir语言之Phoenix框架安装","type":"posts"},{"content":"","date":"October 23, 2019","externalUrl":null,"permalink":"/tags/phoenix/","section":"","summary":"","title":"Phoenix","type":"tags"},{"content":"","date":"September 18, 2019","externalUrl":null,"permalink":"/tags/git/","section":"","summary":"","title":"Git","type":"tags"},{"content":" 待完善 # 如何编写更好的 Git 提交消息——分步指南\n编写更好的提交消息的 5 个步骤 # 让我们总结一下建议的准则：\n大写和标点符号：第一个单词大写，不要以标点符号结尾。如果使用常规提交，请记住全部使用小写。 语气：在主题行中使用祈使语气。示例 – Add fix for dark mode toggle state。命令式语气给出了您发出命令或请求的语气。 提交类型：指定提交类型。建议使用一组一致的词来描述您的更改，这可能会更有益。示例：修正、更新、重构、凹凸等。有关其他信息，请参阅下面的常规提交部分。 长度：理想情况下，第一行不应超过 50 个字符，正文应限制为 72 个字符。 内容：直截了当，尽量消除这些句子中的填充词和短语（例如：虽然，也许，我认为，有点）。像记者一样思考。 如何找到你内心的记者 # 我从没想过我的新闻学辅修课程会有益于我未来的软件工程师职业生涯，但我们来了！\n记者和作家会问自己问题，以确保他们的文章详细、直截了当，并回答了读者的所有问题。\n在写一篇文章时，他们希望回答_谁_、什么、在哪里、何时、为什么_和_如何。 出于提交目的，最重要的是回答提交消息的内容和原因。\n要提出深思熟虑的提交，请考虑以下事项：\n为什么我做了这些改变？ 我的更改有什么影响？ 为什么需要改变？ 参考有哪些变化？ 假设读者不理解提交所针对的内容。他们可能无法访问解决更改详细背景的故事。\n不要期望代码是不言自明的。这与上面的观点类似。\n如果您要更新 CSS 样式之类的东西，因为它是可视的，这对程序员来说似乎很明显。您可能对当时为什么需要这些更改非常了解，但您不太可能回想起后来为什么要进行数百个拉取请求。\n明确_说明_进行更改的_原因_，并注意它是否对功能至关重要。\n请参阅以下差异：\ngit commit -m 'Add margin' git commit -m 'Add margin to nav items to prevent them from overlapping the logo' 很明显，哪些对未来的读者更有用。\n假设您正在撰写具有新闻价值的重要文章。给出标题，总结发生的事情和重要的事情。然后，以有组织的方式提供正文中的更多细节。\n在电影制作中，与口头解释正在发生的事情相比，使用视觉作为交流媒介经常引用“展示，不要告诉”。\n在我们的例子中，“告诉，不要 [只是] 显示”——尽管我们有一些视觉效果可供我们使用，例如浏览器，但大部分细节来自阅读物理代码。\n如果您是 VSCode 用户，请下载 Git Blame扩展。这是有用的提交消息何时对未来的开发人员有帮助的一个主要例子。\n此插件将列出进行更改的人员、更改日期以及内联注释的提交消息。\n想象一下，这在排除错误或回溯所做的更改时会有多大用处。查看 Git 历史信息的其他荣誉提及是 Git History和 GitLens。\n常规提交 # 既然我们已经介绍了良好提交消息的基本提交结构，我想介绍常规提交以帮助提供有关创建可靠提交消息的一些细节。\n在 D2iQ，我们使用常规提交，这是工程团队的一个很好的实践。常规提交是一种格式约定，它提供了一组规则来制定一致的提交消息结构，如下所示：\n\u0026lt;type\u0026gt;[optional scope]: \u0026lt;description\u0026gt; [optional body] [optional footer(s)] 提交类型可以包括以下内容：\nfeat - 引入了一个新功能的变化 fix – 发生了错误修复 chore – 与修复或功能无关且不修改 src 或测试文件的更改（例如更新依赖项） refactor – 重构的代码既不修复错误也不添加功能 docs – 更新文档，例如自述文件或其他降价文件 style – 不影响代码含义的更改，可能与代码格式有关，例如空格、缺少分号等。 test – 包括新的或纠正以前的测试 perf – 性能改进 ci – 持续集成相关 build – 影响构建系统或外部依赖项的更改 revert – 恢复之前的提交 提交类型主题行应全部小写，并有字符限制以鼓励简洁的描述。\n应使用可选的提交正文来提供无法符合主题行描述的字符限制的更多详细信息。\n这也是一个很好的位置，可以BREAKING CHANGE: \u0026lt;description\u0026gt;用来记录提交中发生重大更改的原因。\n页脚也是可选的。我们使用页脚链接JIRA故事，将这些变化，例如关闭：Closes D2IQ-\u0026lt;JIRA #\u0026gt;。\n完整的常规提交示例 # fix: fix foo to enable bar This fixes the broken behavior of the component by doing xyz. BREAKING CHANGE Before this fix foo wasn't enabled at all, behavior changes from \u0026lt;old\u0026gt; to \u0026lt;new\u0026gt; Closes D2IQ-12345 为了确保这些提交约定在开发人员之间保持一致，可以在推送更改之前配置提交消息 linting。 Commitizen是执行标准、同步语义版本控制以及其他有用功能的绝佳工具。\n为了帮助采用这些约定，在您的项目中的贡献或 README 降价文件中包含提交指南是有帮助的。\n常规提交特别适用于语义版本控制（在 SemVer.org 上了解更多 信息），其中提交类型可以更新适当的版本以进行发布。您还 可以在此处阅读有关常规提交的更多信息。\n提交消息比较 # 查看以下消息并查看他们在每个类别中核对了多少建议的指南。\n好的 # feat: improve performance with lazy load implementation for images chore: update npm dependency to latest version Fix bug preventing users from submitting the subscribe form Update incorrect client phone number within footer body per client request 坏的 # fixed bug on landing page Changed style oops I think I fixed it this time? 空提交消息 Git 手册\n","date":"September 18, 2019","externalUrl":null,"permalink":"/posts/git-commit/","section":"","summary":"待完善 # 如何编写更好的 Git 提交消息——分步指南","title":"如何编写更好的 Git 提交消息——分步指南","type":"posts"},{"content":"","date":"August 27, 2019","externalUrl":null,"permalink":"/tags/nginx/","section":"","summary":"","title":"Nginx","type":"tags"},{"content":"","date":"August 27, 2019","externalUrl":null,"permalink":"/categories/nginx/","section":"","summary":"","title":"Nginx","type":"categories"},{"content":" Nginx配置详解\n#运行用户 #user nobody; #启动进程，通常设置成和cpu的数量相等或者2倍于cpu的个数(具体结合cpu和内存)。默认为1 worker_processes 1; #全局的错误日志和日志级别[ debug | info | notice | warn | error | crit ] #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid进程文件 #pid logs/nginx.pid; #一个nginx进程打开的最多文件描述符数目，理论值应该是最多打开文件数（系统的值ulimit -n） #与nginx进程数相除，但是nginx分配请求并不均匀，所以建议与ulimit -n的值保持一致。 #默认不需设置 #worker_rlimit_nofile 65535; #工作模式以及连接数上限 events { #epoll是多路复用IO(I/O Multiplexing)中的一种方式， #仅用于linux2.6以上内核，可以大大的提高nginx的性能 #参考事件模型，use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; #在FreeBSD上面，就用kqueue模型 #window下没有epoll #use epoll; #单个后台worker process进程的最大并发连接数。默认为1024 #单个进程最大连接数（最大连接数=连接数*进程数） worker_connections 1024; #并发总数是worker_processes和worker_connections的乘积 #即 max_clients=worker_processes * worker_connections #在设置了反向代理的情况下，max_clients=worker_processes * worker_connections / 4 #worker_connections 值的设置跟物理内存大小有关 #因为并发受IO约束，max_clients的值须小于系统可以打开的最大文件数 #而系统可以打开的最大文件数和内存大小成正比，一般1GB内存的机器上可以打开的文件数大约是10万左右 #我们来看看360M内存的VPS可以打开的文件句柄数是多少： #$ cat /proc/sys/fs/file-max #输出 34336 #32000 \u0026lt; 34336，即并发连接总数小于系统可以打开的文件句柄总数，这样就在操作系统可以承受的范围之内 #所以，worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置 #使得并发总数小于操作系统可以打开的最大文件数目 #其实质也就是根据主机的物理CPU和内存进行配置 #当然，理论上的并发总数可能会和实际有所偏差，因为主机还有其他的工作进程需要消耗系统资源。 #ulimit -SHn 65535 } #设定http服务器 http { #设定mime类型,类型由mime.type文件定义。文件扩展名与文件类型映射表 include mime.types; ##默认文件类型 default_type application/octet-stream; #默认编码 #charset utf-8; #服务器名字的hash表大小 #server_names_hash_bucket_size 128; #上传文件大小限制 #client_header_buffer_size 32k; #设定请求缓 #large_client_header_buffers 4 64k; #设定请求缓 #client_max_body_size 8m; #开启高效文件传输模式，sendfile指令指定nginx是否调用sendfile函数来输出文件， #对于普通应用设为 on，如果用来进行下载等应用磁盘IO重负载应用，可设置为off， #以平衡磁盘与网络I/O处理速度，降低系统的负载。注意：如果图片显示不正常把这个改成off。默认开启状态 sendfile on; #开启目录列表访问，合适下载服务器，默认关闭。 #autoindex on; #防止网络阻塞 #tcp_nopush on; #防止网络阻塞 #tcp_nodelay on; #长连接超时时间，单位是秒 #keepalive_timeout 120; keepalive_timeout 65; #FastCGI相关参数是为了改善网站的性能：减少资源占用，提高访问速度。下面参数看字面意思都能理解。 #fastcgi_connect_timeout 300; #fastcgi_send_timeout 300; #fastcgi_read_timeout 300; #fastcgi_buffer_size 64k; #fastcgi_buffers 4 64k; #fastcgi_busy_buffers_size 128k; #fastcgi_temp_file_write_size 128k; #gzip模块设置 #开启gzip压缩输出 #gzip on; #gzip_min_length 1k; #最小压缩文件大小 #gzip_buffers 4 16k; #压缩缓冲区 #gzip_http_version 1.0; #压缩版本（默认1.1，前端如果是squid2.5请使用1.0） #gzip_comp_level 2; #压缩等级 #gzip_types text/plain application/x-javascript text/css application/xml; #压缩类型，默认就已经包含text/html，所以下面就不用再写了，写上去也不会有问题，但是会有一个warn。 #gzip_vary on; #开启限制IP连接数的时候需要使用 #limit_zone crawler $binary_remote_addr 10m; upstream www.xttblog.com { #upstream的负载均衡，weight是权重，可以根据机器配置定义权重。weigth参数表示权值，权值越高被分配到的几率越大。 #server 192.168.80.121:80 weight=3; #每个请求按访问ip的hash结果分配，这样每个访客固定访问一个后端服务器，可以解决session的问题。 ip_hash; server 10.10.2.71:8080; #server 192.168.80.123:80 weight=3; } #设定日志格式 #log_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; # \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; # \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34;\u0026#39;; #access日志文件的路径,采用上面定义的main 格式记录 #access_log logs/access.log main; #虚拟主机的配置 server { #侦听80端口 listen 80; #定义使用 localhost，可以自定义域名访问 #域名可以有多个，用空格隔开 server_name localhost;\t#编码 #charset koi8-r; #access_log logs/host.access.log main; #默认请求 location / { proxy_pass http://www.xttblog.com; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #以下是一些反向代理的配置，可选。 proxy_set_header Host $host; #定义服务器的默认网站根目录位置 root D:/apache-tomcat-6.0.41/webapps/test; #定义首页索引文件的名称。定义多个用空格分隔 index index.jsp; } #定义404错误提示页面 #error_page 404 /404.html; #定义50x错误提示页面 error_page 500 502 503 504 /50x.html; location = /50x.html { #定义服务器的默认网站错误页面位置 root html; } #静态文件，nginx自己处理。正则表达式 #location ~ ^/(images|javascript|js|css|flash|media|static|htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)/ { #过期30天，静态文件不怎么更新，过期可以设大一点， #如果频繁更新，则可以设置得小一点。 #expires 30d; #} #禁止访问 .htxxx 文件 #location ~ /\\.ht { # deny all; #} #对 \u0026#34;/\u0026#34; 启用反向代理 #location / { #proxy_pass http://127.0.0.1:88; #proxy_redirect off; #proxy_set_header X-Real-IP $remote_addr; #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #以下是一些反向代理的配置，可选。 #proxy_set_header Host $host; #允许客户端请求的最大单文件字节数 #client_max_body_size 10m; #缓冲区代理缓冲用户端请求的最大字节数 #client_body_buffer_size 128k; #nginx跟后端服务器连接超时时间(代理连接超时) #proxy_connect_timeout 90; #后端服务器数据回传时间(代理发送超时) #proxy_send_timeout 90; #连接成功后，后端服务器响应时间(代理接收超时) #proxy_read_timeout 90; #设置代理服务器（nginx）保存用户头信息的缓冲区大小 #proxy_buffer_size 4k; #proxy_buffers缓冲区，网页平均在32k以下的设置 #proxy_buffers 4 32k; #高负荷下缓冲大小（proxy_buffers*2） #proxy_busy_buffers_size 64k; #设定缓存文件夹大小，大于这个值，将从upstream服务器传 #proxy_temp_file_write_size 64k; #} #设定查看Nginx状态的地址 #location /NginxStatus { #stub_status on; #access_log on; #auth_basic \u0026#34;NginxStatus\u0026#34;; #auth_basic_user_file conf/htpasswd; #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。 #} #本地动静分离反向代理配置 #所有jsp的页面均交由tomcat或resin处理 #location ~ .(jsp|jspx|do)?$ { #proxy_set_header Host $host; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #proxy_pass http://127.0.0.1:8080; #} } #另一个虚拟主机使用混合的IP的配置 #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} #HTTPS server的配置，默认不开启 #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} } ","date":"August 27, 2019","externalUrl":null,"permalink":"/posts/nginx-config/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eNginx配置详解\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Nginx配置详解","type":"posts"},{"content":"","date":"June 8, 2019","externalUrl":null,"permalink":"/tags/go/","section":"","summary":"","title":"Go","type":"tags"},{"content":"","date":"June 8, 2019","externalUrl":null,"permalink":"/categories/go/","section":"","summary":"","title":"Go","type":"categories"},{"content":" Go by Example\nHello World # 我们第一个程序就是打印经典的“hello world”，下面是完整的代码\npackage main import \u0026#34;fmt\u0026#34; func main(){ fmt.Println(\u0026#34;hello world\u0026#34;) } 要运行这个程序，将代码保存为 hello-world.go，然后使用go run\n有时候我们想让程序编译成二进制文件，可以使用go build，然后就可以直接运行了。\nValues # Go 有多种值的类型，包括 string，integer，float，boolean 等。如下是几个基本例子。\npackage main import \u0026#34;fmt\u0026#34; func main(){ //string 可以使用 + 连接在一起 fmt.Println(\u0026#34;go\u0026#34;+\u0026#34;lang\u0026#34;) fmt.Println(\u0026#34;1+1=\u0026#34;,1+1) fmt.Pritnln(\u0026#34;7.0/3.0=\u0026#34;,7.0/3.0) fmt.Println(true\u0026amp;\u0026amp;false) fmt.Println(true||false) fmt.Pritnln(!true) } Variables # 在 Go 中，变量被编译器显式的声明和使用，例如检查函数调用类型的正确性。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 声明一个或多个变量 var a string=\u0026#34;initial\u0026#34; fmt.Println(a) var b,c int = 1,2 fmt.Println(b,c) //Go 将推断变量的初始化类型 var d = true fmt.Println(d) // 声明没有初始值的变量将被初始化为零值，如 int 的零值是 0 var e int fmt.Println(e) //var f string = \u0026#34;short\u0026#34; 的简化写法 f:=\u0026#34;short\u0026#34; fmt.Println(f) } Constants # Go 支持的常量有字符、字符串、布尔值以及数值\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;math\u0026#34; const s string = \u0026#34;constant\u0026#34; func main(){ fmt.Println(s) //const 声明可以出现在任何 var 声明出现的地方 const n = 50000000 // 常量表达式可以任意精度进行运算 const d = 3e20 / n fmt.Println(d) // 数值常量没有类型，除非进行了显式转换等类型赋予 fmt.Println(int64(d)) // 数值在使用环境上下文中会得到类型，如变量赋值或者方法调用，如 math.Sin 需要的是一个 float64 fmt.Println(math.Sin(n)) } For # for 是 Go 中唯一的循环结构，下面是三种基本的 for 循环类型。\npackage main import \u0026#34;fmt\u0026#34; func main(){ i := 1 // 最基本的类型，只有一个条件 for i\u0026lt;=3{ fmt.Println(i) i = i+1 } // 经典的 初始化 / 条件 / 循环后 for 循环 for j:=7;j\u0026lt;=9;j++{ fmt.Println(j) } // 没有条件的 for 语句将无限循环，除非在内部的方法中使用了 break 或者 return for{ fmt.Println(\u0026#34;loop\u0026#34;) break } // 也可以使用 continue 来直接到下一个循环 for n:=0;n\u0026lt;=5;n++ { if n%2 == 0{ continue } fmt.Println(n) } } If/Else # package main import \u0026#34;fmt\u0026#34; func main(){ if 7%2==0{ fmt.Println(\u0026#34;7 is even\u0026#34;) }else{ fmt.Println(\u0026#34;7 is odd\u0026#34;) } // 可以单独使用 if if 8%4==0{ fmt.Println(\u0026#34;8 is divisible by 4\u0026#34;) } // 条件之前可以有语句，声明在该语句中的任何变量可以用于所有分支 if num:=9;num\u0026lt;0{ fmt.Println(num,\u0026#34;is negative\u0026#34;) }else if num\u0026lt;10{ fmt.Println(num,\u0026#34;has 1 digit\u0026#34;) }else{ fmt.Println(num,\u0026#34;has multiple digits\u0026#34;) } } 注意，Go 中条件周围不需要圆括号，但是花括号是必要的。\nGo 中没有三目 if 语句，所以对于最基本的条件也需要写完整的 if 语句。\nSwitch # package main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; func main(){ i:=2 fmt.Print(\u0026#34;write\u0026#34;,i,\u0026#34;as\u0026#34;) switch i{ case 1: fmt.Println(\u0026#34;one\u0026#34;) case 2: fmt.Println(\u0026#34;two\u0026#34;) case 3: fmt.Println(\u0026#34;three\u0026#34;) } // 可以在同一个 case 语句中使用逗号来分隔多个表达式 switch time.Now().weekday(){ case time.Saturday,time.Sunday: fmt.Println(\u0026#34;it\u0026#39;s the weekend\u0026#34;) // 这里也使用了 default case default: fmt.Println(\u0026#34;it\u0026#39;s a weekday\u0026#34;) } t:=time.Now() // 没有表达式的 switch 是另一种实现 if/else 逻辑的路子，同时这也展示了 case 表达式可以是非常量值。 switch{ case t.Hour()\u0026lt;12: fmt.Println(\u0026#34;it\u0026#39;s before noon\u0026#34;) default: fmt.Println(\u0026#34;it\u0026#39;s after noon\u0026#34;) } // 类型 switch 比较了类型而非值 whatAmI := func(i interface{}){ switch t:=i.(type){ case bool: fmt.Println(\u0026#34;I\u0026#39;m a bool\u0026#34;) case int: fmt.Println(\u0026#34;I\u0026#39;m an int\u0026#34;) default: fmt.Println(\u0026#34;Don\u0026#39;t know type %T\\n\u0026#34;,t) } } whatAmI(true) whatAmI(1) whatAmI(\u0026#34;hey\u0026#34;) } Arrays # 在 Go 中，数组是特定长度元素的编号序列。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 这里创建了一个 5 个 int 元素的数组。默认是零值，对于 int 而言就是 0 var a [5]int fmt.Println(\u0026#34;emp:\u0026#34;,a) // 可以通过 array[index]=value 语法设值，或者通过 array[index] 取值 a[4]=100 fmt.Println(\u0026#34;set:\u0026#34;,a) fmt.Println(\u0026#34;get:\u0026#34;,a[4]) // 内置的 len 函数返回数组的长度 fmt.Println(\u0026#34;len:\u0026#34;,len(a)) // 声明并初始化数组 b:=[5]int{1,2,3,4,5} fmt.Prinln(\u0026#34;dc1:\u0026#34;,b) // 数组是一维的，但你可以组合类型来构建多维数组结构 var twoD [2][3]int for i:=0;i\u0026lt;2;i++{ for j:=0;j\u0026lt;3;j++{ twoD[i][j]=i+j } } fmt.Println(\u0026#34;2d:\u0026#34;,twoD) } 当使用 fmt.Println 方法打印时，数组将以 [v1 v2 v3 …] 的形式展现\nSlices # slice 是一个重要的数据类型，对于序列提供了比数组更强大的接口\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 与数组不同，切片仅由其包含的元素（不是元素的数量）键入。 // 要创建一个非零长度的空切片，请使用内置的 make。 这里我们制作长度为 3 的字符串（最初为零值）。 s:=make([]string,3) fmt.Println(\u0026#34;emp:\u0026#34;,s) // 可以像数组一样 set 和 get s[0]=\u0026#34;a\u0026#34; s[1]=\u0026#34;b\u0026#34; s[2]=\u0026#34;c\u0026#34; fmt.Println(\u0026#34;set:\u0026#34;,s) fmt.Println(\u0026#34;get:\u0026#34;,s[2]) //len 能够返回 slice 的长度 fmt.Println(\u0026#34;len:\u0026#34;,len(s)) // 作为这些基本操作的补充，slice 支持一些令其比数组更丰富的东西。 // 其中一个是 append 函数，其返回一个包含一个或多个新值的 slice // 注意需要接受 append 的返回值来获取新的 slice 值 s=append(s,\u0026#34;d\u0026#34;) s=append(s,\u0026#34;e\u0026#34;,\u0026#34;f\u0026#34;) fmt.Println(\u0026#34;apd:\u0026#34;,s) //slice 可以复制 c:=make([]string,len(s)) copy(c,s) fmt.Println(\u0026#34;cpy:\u0026#34;,c) //slice 支持切片操作，语法为 slice[low:high]。如下将得到元素 s[2],s[3] 和 s[4] l:=s[2:5] fmt.Println(\u0026#34;sl1:\u0026#34;,l) // 如下切片截止到（不包含）s[5] l=s[:5] fmt.Pritnln(\u0026#34;sl2:\u0026#34;,l) // 如下切片从 s[2] 开始（包含） l=s[2:] fmt.Println(\u0026#34;sl3:\u0026#34;,l) // 声明并初始化 slice t:=[]string{\u0026#34;g\u0026#34;,\u0026#34;h\u0026#34;,\u0026#34;i\u0026#34;} fmt.Println(\u0026#34;dcl:\u0026#34;,t) //slice 也可以组织成一个多为数据结构，内部的 slice 长度可以变化，这与多维数组不同 twoD:=make([][]int,3) for i:=0;i\u0026lt;3;i++{ innerLen:=i+1 twoD[i]=make([]int,innerLen) for j:=0;j\u0026lt;innerLen;j++{ twoD[i][j]=i+j } } fmt.Println(\u0026#34;2d:\u0026#34;,twoD) } 虽然 slice 与 array 是不同的类型，但是使用 fmt.Println 的展示结果很相似\nMaps # Map 是 Go 内置的关联数据类型（其他语言可能成为哈希或者字典）\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 要创建一个空的 map，使用内置 make 函数：make(map[key-type]val-type) m:=make(map[string]int) // 通过经典的 name[key]=val 语法来设置 key/value 对 m[\u0026#34;k1\u0026#34;]=7 m[\u0026#34;k2\u0026#34;]=13 fmt.Println(\u0026#34;map:\u0026#34;,m) // 通过 name[key] 来获取一个 value v1:=m[\u0026#34;k1\u0026#34;] fmt.Println(\u0026#34;v1:\u0026#34;,v1) //len 函数返回 map 中键值对的个数 fmt.Println(\u0026#34;len:\u0026#34;,len(m)) // 内置的 delete 函数将移除 map 中的键值对 delete(m,\u0026#34;k2\u0026#34;) fmt.Println(\u0026#34;map:\u0026#34;,m) // 第一个值是该 key 的 value，但此处不需要，故使用空白占位符“_”忽略 // 第二个可选的返回值表明该键是否在 map 中，这样可以消除不存在的键，和键值为 0 或者 \u0026#34;\u0026#34; 的歧义 _,prs:=m[\u0026#34;k2\u0026#34;] fmt.Println(\u0026#34;prs:\u0026#34;,prs) // 声明并初始化 map n:=map[string]int{\u0026#34;foo\u0026#34;:1,\u0026#34;bar\u0026#34;:2} fmt.Println(\u0026#34;map:\u0026#34;,n) } map 将以 [k:v k:v] 的形式打印\nRange # range 可以遍历各种数据结构中的元素。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 这里使用 range 来计算元素的和，数组也是类似的用法 nums:=[]int{2,3,4} sum:=0 for _,num:=range nums{ sum+=num } fmt.Println(\u0026#34;sum:\u0026#34;,sum) // 在 slice 和 array 上的 range 均为每个条目提供了索引和值 for i,num:=range nums{ if num==3{ fmt.Pritnln(\u0026#34;index\u0026#34;,i) } } //map 上的 range 通过 key/value 对进行遍历 kvs:=map[string]string{\u0026#34;a\u0026#34;:\u0026#34;apple\u0026#34;,\u0026#34;b\u0026#34;:\u0026#34;banana\u0026#34;} for k,v:=range kvs{ fmt.Printf(\u0026#34;%s -\u0026gt; %s\\n\u0026#34;,k,v) } //range 可以仅通过 key 进行遍历 for k:= range kvs{ fmt.Println(\u0026#34;key:\u0026#34;,k) } //range 作用在 string 上将得到 unicode code points，第一个值是字符的起始字节索引，第二个值是字符本身 for i,c:range \u0026#34;go\u0026#34; { cmt.Println(i,c) } } Functions # package main import \u0026#34;fmt\u0026#34; // 这个函数接收两个 int 并以 int 类型返回他们的和 func plus(a int,b int) int{ //Go 需要显式的 return 语句，它不会自动返回最后一个表达式的值 return a+b } // 如果有多个连续的相同类型的参数，可以忽略前面的类型声明 func plusPlus(a,b,c int) int{ return a+b+c } func main(){ // 如你所想的那样调用函数 res := plus(1,2) fmt.Println(\u0026#34;1+2=\u0026#34;,res) res = plusPlus(1,2,3) fmt.Println(\u0026#34;1+2+3=\u0026#34;,res) } Multiple Return Values # Go 内置支持了返回多个值。这一特点经常用于 Go 的习惯用法，例如同时返回结果和错误值\npackage main import \u0026#34;fmt\u0026#34; // 方法签名中的 (int,int) 表明它将返回两个 int 值 func vals()(int,int){ return 3,7 } func main(){ // 这里我们通过多重赋值来使用两个不同的返回值 a,b := vals() fmt.Println(a) fmt.Println(b) // 如果只需要返回结果的子集，使用空白占位符 _ _,c := vals() fmt.Println(c) } Variadic Functions # 变参函数，可以使用任意数量的参数来进行调用。例如 fmt.Println 是一个常见的变参函数。\npackage main import \u0026#34;fmt\u0026#34; // 这是一个接收任意数量的 int 值的函数 func sum(nums ...int){ fmt.Print(nums,\u0026#34; \u0026#34;) total := 0 for _, num := range nums{ total += num } fmt.Println(total) } func main(){ sum(1,2) sum(1,2,3) // 如果你已经在一个 slice 中定义了多个参数，可以使用 func(slice...) 来直接应用到变参函数中 nums :=[]int{1,2,3,4} sum(nums...) } Closures # Go 支持匿名函数，它可以形成闭包。当你想要定义一个不记名的内部函数时，匿名函数就很有用了。\npackage main import \u0026#34;fmt\u0026#34; //intSeq 函数返回另一个函数，它定义在了 intSeq 函数内部，并且是匿名的。 // 返回的函数关闭变量 i 以形成闭包 func intSeq() func int{ i := 0 return func() int{ i+=1 return i } } func main(){ // 调用 intSeq，并将结果 (一个函数) 赋予 nextInt // 这个函数持有一个自己的 i 值，每次调用 nextInt 时都会更新 nextInt:=intSeq() // 多次调用 nextInt 函数可以看到闭包的效果 //zephyr: 如非闭包写法，每次函数都会进行初始化变量 i，反复调用 intSeq 不会有这个效果 fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Pritnln(nextInt()) // 要确认该状态对该特定函数是唯一的，请创建并测试一个新函数。 newInts:=intSeq() fmt.Println(newInts()) } Recursion # Go 支持递归函数。下面是一个经典的斐波那契数列示例\npackage main import \u0026#34;fmt\u0026#34; func fact(n int) int { if n==0 { return 1 } //fact 函数调用自身，直到 n 为 0 return n * fact(n-1) } func main(){ fmt.Println(fact(7)) } Pointers # Go 可以使用指针，让你在程序中传递值或记录的引用。\n下面通过两种方式的对比来展示指针的使用：zeroval和zeroptr\npackage main import \u0026#34;fmt\u0026#34; //zeroval 将取得 ival 的值的拷贝，与调用方不同 func zeroval(ival int){ ival = 0 } //zeroptr 有一个 *int 类型的参数，代表它接收的是一个指针 func zeroptr(iptr *int){ //*iptr 解引用，从内存指定地址中获取存放的值 // 对解引用指针的赋值将改变指定地址上的值 *iptr = 0 } func main(){ i:=1 fmt.Println(\u0026#34;initial:\u0026#34;,i) zeroval(i) fmt.Println(\u0026#34;zeroval:\u0026#34;,i) //\u0026amp;i 语法将获得变量 i 的内存地址，也就是指向变量 i 的指针 zeroptr(\u0026amp;i) fmt.Println(\u0026#34;zeroptr:\u0026#34;,i) // 指针也可以被打印 fmt.Println(\u0026#34;pointer:\u0026#34;,\u0026amp;i) } zeroval 没有改变 main 函数中 i 的值，而 zeroptr 会，因为它拥有指向变量 i 的内存地址。\nStructs # Go 的 Struct 结构是字段类型的集合。对于从记录中将数据组织到一起很有帮助。\npackage main import \u0026#34;fmt\u0026#34; type person struct{ name string age int } func main(){ // 这个语法创建了一个新的 struct fmt.Println(person{\u0026#34;Bob\u0026#34;,20}) // 在初始化 struct 时，可以指定字段名 fmt.Println(person{name:\u0026#34;Alice\u0026#34;,age:30}) // 被忽略的字段将会被初始化为零 fmt.Println(person{name:\u0026#34;Fred\u0026#34;}) // 一个 \u0026amp; 将产生 struct 的指针 fmt.Println(\u0026amp;person{name:\u0026#34;Ann\u0026#34;,age:40}) s:=person{name:\u0026#34;Sean\u0026#34;,age:50} // 通过点号访问结构体中的字段 fmt.Println(s.name) sp:=\u0026amp;s fmt.Println(sp.age) // 对于结构体的指针也可以使用点号操作符，指针将会自动解引用 sp.age=51 // 结构体是可变的 fmt.Println(sp.age) } Methods # Go 支持在结构体上定义方法。\npackage main import \u0026#34;fmt\u0026#34; type rect struct{ width,height int } //area 方法有一个 rect 指针类型的接收器 func (r *rect) area() int{ return r.width * r.height } // 即可以定义指针类型的接收器，也可以定义值类型的接收器 func (r rect) perim() int{ return 2*r.width+2*r.height } func main(){ r:=rect{width:10,height:5} // 调用定义的方法 fmt.Println(\u0026#34;area:\u0026#34;,r.area()) fmt.Println(\u0026#34;perim:\u0026#34;,r.perim()) //Go 为方法调用自动处理了值和引用的转换。使用指针接收器可以避免获得方法调用的拷贝 (?) 或允许方法修改接收到的 struct 值 rp:=\u0026amp;r fmt.Println(\u0026#34;area:\u0026#34;,rp.area()) fmt.Println(\u0026#34;perim:\u0026#34;,rp.perim()) } Interfaces # 接口是方法签名的命名集合。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;math\u0026#34; // 这是一个 geometry 的基本接口，本例中将在 rect 类型和 circle 类型中实现这个接口 type geometry interface{ area() float64 perim() float64 } type rect struct{ width,height float64 } type circle struct{ radius float64 } // 在 Go 中，实现一个接口只需要实现其中定义的所有方法即可 func (r rect) area() float64{ return r.width * r.height } func (r rect) perim() float64{ return 2*r.width+2*r.height } func (c circle) area() float64{ return math.Pi * c.radius * c.radius } func (c circle) perim() float64{ return 2 * math.Pi * c.radius } // 如果变量是接口类型，那么它可以调用接口内定义的方法 // 这是一个通用的 measure 方法，利用它能够工作在任何 geometry 上 func measure(g geometry){ fmt.Println(g) fmt.Println(g.area()) fmt.Println(g.perim()) } func main(){ r:=rect{width:3,height:4} c:=circle{radius:5} //rect 和 circle 均实现了 geometry 接口，所以可以作为 measure 的参数 measure(r) measure(c) } Errors # 在 Go 中，传递错误的惯用法是通过明确的，分离的返回值。这和 Java 活 Ruby 中的 exception 以及 C 中重载使用单个的结果 / 错误值不同。Go 的方法使得很容易看出哪些函数返回错误，并使用与任何其他非错误任务相同的语言结构来处理它们。\npackage main import \u0026#34;errors\u0026#34; import \u0026#34;fmt\u0026#34; // 按照惯例，错误是最后一个返回值，类型为 error，一个内置的接口。 func f1(arg int)(int,error){ if arg==42{ //errors.New 使用给定的错误信息构建了一个基本的 error 值 return -1,errors.New(\u0026#34;can\u0026#39;t work with 42\u0026#34;) } // 在 error 位置上放 nil 值代表没有错误 return arg+3,nil } // 通过实现 Error() 方法，可以自定义错误类型。这里有一个上例的变种 // 使用一个自定义类型来显式地展示一个参数错误 type argError struct{ arg int prob string } type argError struct{ arg int prob string } func (e *argError) Error() string{ return fmt.Sprintf(\u0026#34;%d - %s\u0026#34;,e.arg,e.prob) } func f2(arg int)(int,error){ if arg==42{ // 这里我们使用 \u0026amp;argError 语法来创建一个新的结构体，提供了 arg 和 prob 两个域的值 return -1,\u0026amp;argError{arg,\u0026#34;can\u0026#39;t work with it\u0026#34;} } return arg+3,nil } func main(){ // 下面的两个循环测试了每个返回 error 的函数 // 注意在 if 中的错误检查是 Go 代码中的习惯用法 for _,i:=range []int{7,42}{ if r,e:=f1(i);e!=nil{ fmt.Println(\u0026#34;f1 failed:\u0026#34;,e) }else{ fmt.Println(\u0026#34;f1 worked:\u0026#34;,r) } } for _,i := range []int{7,42}{ if r,e :=f2(i);e!=nil{ fmt.Println(\u0026#34;f1 failed:\u0026#34;,e) }else{ fmt.Println(\u0026#34;f1 worked:\u0026#34;,r) } } // 如果要以编程方式使用自定义错误中的数据， // 则需要通过类型断言将错误作为自定义错误类型的实例获取。 _,e:=f2(42) if ae,ok := e.(*argError);ok{ fmt.Println(ae.arg) fmt.Println(ae.prob) } } Goroutines # goroutine 是一个轻量级的执行线程。\npackage main import \u0026#34;fmt\u0026#34; func f(from string){ for i:=0;i\u0026lt;3;i++{ fmt.Println(from,\u0026#34;;\u0026#34;,i) } } func main(){ // 假设我们有个 f(s) 的函数调用。这里我们通过一般方法调用，令其同步执行 f(\u0026#34;direct\u0026#34;) // 要想让这个函数在 goroutine 中触发，使用 go f(s)。这个新的 goroutine 将会与调用它的并行执行 go f(\u0026#34;goroutine\u0026#34;) // 我们也可以启动一个调用匿名函数的 goroutine go func(msg string){ fmt.Println(msg) }(\u0026#34;going\u0026#34;) // 现在，这两个方法调用在独立的 goroutine 中异步执行了，故方法执行直接落到了这里 //Scanln 代码需要在程序退出前按下一个键 var input string fmt.Scanln(\u0026amp;input) fmt.Println(\u0026#34;done\u0026#34;) } 当我们运行这个程序的时候，我们将首先看到阻塞调用，然后是两个 goroutine 的交错输出。这个交错反应了 goroutine 在 Go 运行时是并发执行的。\nChannels # Channel 是连接并发执行的 goroutine 的管道。你可以从一个 goroutine 传递值到 channel 中，再在另一个 goroutine 接收它。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 通过 make(chan val-type) 创建新的 channel //channel 的类型依赖于它们要传递的值 messages:=make(chan string) // 向 channel 传递值使用 channel \u0026lt;- 语法。在这里我们从一个新的 goroutine 中发送了一个 \u0026#34;ping\u0026#34; 到 message 通道中 go func(){messages\u0026lt;-\u0026#34;ping\u0026#34;}() //\u0026lt;-channel 语法从 channel 中获取值。这里我们接收了上面发送的 \u0026#34;ping\u0026#34; 信息并打印 msg:=\u0026lt;-messages fmt.Println(msg) } 当我们运行这个程序时，\u0026ldquo;ping\u0026rdquo; 信息成功的通过我们的 channel 从一个 goroutine 传递到了另一个。\n默认情况下，发送和接收在发送者和接受者都准备好之前阻塞。这个特性允许我们在程序结尾等待 \u0026ldquo;ping\u0026rdquo; 信息而无需使用其他的同步手段\nChannel Buffering # 默认下 channel 没有缓冲区，这意味着他们将只有在响应的接收者 (\u0026lt;-chan) 准备好时，才能允许发送 (chan\u0026lt;-)。具有缓冲区的 channel，接受有限个数的值，而无需相应的接收者。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 这里我们创建了一个能够缓冲 2 个字符串值的 channel messages:=make(chan string,2) // 由于 channel 带有缓冲区，我们可以发送值，无需响应的并发接收 messages\u0026lt;-\u0026#34;buffered\u0026#34; messages\u0026lt;-\u0026#34;channel\u0026#34; // 稍后，我们像往常一样，接收了这两个值 fmt.Println(\u0026lt;-messages) fmt.Println(\u0026lt;-messages) } Channel Synchronization # 我们可以使用 channel 来跨 goroutine 同步执行。这里是一个使用阻塞接收来等待 goroutine 结束的例子。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; // 这个方法将在一个 goroutine 中运行。 //done channel 用来通知其他的 goroutine 这个方法执行完毕 func worker(done chan bool){ fmt.Print(\u0026#34;working...\u0026#34;) time.Sleep(time.Second) fmt.Println(\u0026#34;done\u0026#34;) // 发送一个值来通知这里已经做完 done\u0026lt;-true } func main(){ 启动一个worker goroutine，赋予它用以通知的channel done:=make(chan bool,1) go worker(done) // 在 channel 接收到来自 worker 的通知前，保持阻塞 \u0026lt;-done } 如果你移除了 \u0026lt;-done 行，这个程序可能会在 worker 开始前就结束。\nChannel Directions # 当把 channel 用作函数的参数时，你可以指定一个 channel 是否只发送或者只接收数据。这种特异性增加了程序的类型安全性。\npackage main import \u0026#34;fmt\u0026#34; //ping 函数只接受一个发送数据的 channel，如果试图在其上获取数据，将会引发编译时异常 func ping(pings chan\u0026lt;- string,msg string){ pings\u0026lt;-msg } //pong 函数接受一个通道用于接收 (pings)，另一个用于发送 (pongs) func pong(pings \u0026lt;-chan string,pongs chan\u0026lt;- string){ msg:=\u0026lt;-pings pongs\u0026lt;-msg } func main(){ pings:=make(chan string,1) pongs:=make(chan string,1) ping(pings,\u0026#34;passed message\u0026#34;) pong(pings,pongs) fmt.Println(\u0026lt;-pongs) } Select # Go 的 select 让你能够等待多个 channel 操作。通过 select 结合 goroutine 和 channel 是 Go 的重要特色。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ // 本例中我们将在两个通道中进行选择 c1:=make(chan string) c2:=make(chan string) // 每个通道都会在一定时间后接收到一个值，在并发的 goroutine 中模拟阻塞 RPC 操作执行 go func(){ time.Sleep(time.Second*1) c1\u0026lt;-\u0026#34;one\u0026#34; }() go func(){ time.Sleep(time.Second*2) c2\u0026lt;-\u0026#34;two\u0026#34; }() // 我们将使用 select 来同时等待这两个值，当它们到达时打印。 for i:=0;i\u0026lt;2;i++{ select{ case msg1:=\u0026lt;-c1: fmt.Println(\u0026#34;received\u0026#34;,msg1) case msg2:=\u0026lt;-c2: fmt.Println(\u0026#34;received\u0026#34;,msg2) } } } 按照预期，我们将接收到 \u0026ldquo;one\u0026rdquo;，然后 \u0026ldquo;two\u0026rdquo;。\n注意，整个执行只需要大约 2 秒的时间，因为 1 秒和 2 秒的沉睡是并发执行的。\nTimeouts # 超时对于连接到外部资源的程序或其他需要绑定执行时间的程序很重要。在 Go 中，可以通过 channel 和 select 轻松而优雅的实现超时。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ // 在本例中，假设我们执行了一个外部调用，它在两秒后将结果返回到通道 c1 上 c1:=make(chan string,1) go func(){ time.Sleep(time.Second*2) c1\u0026lt;-\u0026#34;result 1\u0026#34; }() // 这里是用 select 实现超时。res:=\u0026lt;-c1 等待一个结果，而 \u0026lt;-Time.After 等待超时 1 秒后发送一个值。 // 由于 select 将在有第一个准备就绪的接收时继续，我们会在操作超过允许的 1 秒时进入超时事件。 select{ case res:=\u0026lt;-c1: fmt.Println(res) case \u0026lt;-time.After(time.Second*1): fmt.Println(\u0026#34;timeout 1\u0026#34;) } // 如果我们允许一个更长的超时时间 3 秒，则将能成功得到 c2 的值，并打印 c2:=make(chan string,1) go func(){ time.Sleep(time.Second*2) c2\u0026lt;-\u0026#34;result 2\u0026#34; }() select{ case res:=\u0026lt;-c2: fmt.Println(res) case \u0026lt;-time.After(time.Second*3): fmt.Printnln(\u0026#34;timeout 2\u0026#34;) } } 运行这个程序，将显示第一个操作超时了，第二个则成功。\n使用 select 超时模式需要在通道上进行结果通讯。一般情况下这是很好的主意，因为其他的重要 Go 特性基于通道和选择。我们将在之后看到有关的两个例子：timer 和 ticker\nNon-Blocking Channel Operations # channel 上简单的发送和接收是阻塞的。然而，我们可以使用 select 和 default 子句来实现非阻塞发送、接收甚至非阻塞的多路选择。\npackage main import \u0026#34;fmt\u0026#34; func main(){ messages:=make(chan string) signals:=make(chan bool) // 这是一个非阻塞的接收。如果 message 的值可以获取，select 将随值进入 \u0026lt;-message 子句 // 否则将立刻进入 default 事件 select{ case msg:=\u0026lt;-messages: fmt.Println(\u0026#34;received message\u0026#34;,msg) default: fmt.Pritln(\u0026#34;no message received\u0026#34;) } msg:=\u0026#34;hi\u0026#34; // 类似的有非阻塞发送 select{ case messages\u0026lt;-msg: cmt.Println(\u0026#34;sent message\u0026#34;,msg) default: fmt.Println(\u0026#34;no message sent\u0026#34;) } // 我们可以在 default 上使用多个事件来实现多路非阻塞 select。 // 这里我们试图在 message 和 signal 上均进行非阻塞接收 select{ case msg:=\u0026lt;-messages: fmt.Println(\u0026#34;received message\u0026#34;,msg) case sig:=\u0026lt;-signals: fmt.Println(\u0026#34;received signal\u0026#34;,sig) default: fmt.Println(\u0026#34;no activity\u0026#34;) } } Closing Channels # 关闭通道意味着不会再有值在其上发送。这对于通道的接收方通讯完成很有帮助\npackage main import \u0026#34;fmt\u0026#34; // 在这个例子中，我们将使用一个作业通道将工作从主函数 goroutine 传递给工人 goroutine。当没有更多作业给工人时，我们将关闭工作渠道。 func main(){ jobs:=make(chan int,5) done:=make(chan bool) // 下面是工人 goroutine。它通过 j,more:=\u0026lt;-jobs 反复获取作业 // 在这个 2 返回值的接收中，如果作业关闭，所有值都已接收，more 会变为 false // 我们用其在完成所有作业时进行已完成通知 go func(){ for{ j,more:=\u0026lt;-jobs if more{ fmt.Println(\u0026#34;received job\u0026#34;,j) }else{ fmt.Println(\u0026#34;received all jobs\u0026#34;) done\u0026lt;-true return } } }() // 此处向工人发送了 3 个作业，然后关闭它 for j:=1;j\u0026lt;=3;j++{ jobs\u0026lt;-j fmt.Println(\u0026#34;sent job\u0026#34;,j) } close(jobs) fmt.Println(\u0026#34;sent all jobs\u0026#34;) // 使用前面见过的同步机制来等待工人、 \u0026lt;-done } Range over Channels # 在前面的例子中，我们看到如何使用 for 和 range 来遍历基本的数据结构。我们同样可以使用这个语法来遍历通道上接收的值。\npackage main import \u0026#34;fmt\u0026#34; func main(){ // 遍历 queue 通道上的 2 个值 queue:=make(chan string,2) queue\u0026lt;-\u0026#34;one\u0026#34; queue\u0026lt;-\u0026#34;two\u0026#34; close(queue) //range 遍历通道上接收的每个值。由于在上面关闭了通道，这个遍历将在接收 2 个值后结束。 for elem:=range queue{ fmt.Println(elem) } } Timers # 我们常想在未来的某一刻执行 Go 代码，或者在某一时间内重复执行。Go 内置的 timer 和 ticker 使这些任务十分简易。首先我们看看 timer，然后再看 ticker。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ //timer 代表未来的一个单独事件。你要告诉它要等待多久，它提供一个通道，在指定时间发出通知。下面这个 timer 将等待 2 秒钟 timer1:=time.NewTimer(time.Second*2) // 定时器通道由于操作 \u0026lt;-timer1.c 发生阻塞，直到它发送了一个值来表明定时器到时 \u0026lt;-timer1.C fmt.Println(\u0026#34;Timer 1 expired\u0026#34;) // 如果你仅仅想等待一段时间，可以用 time.Sleep，使用 timer 的一个原因是，你可以在计时结束前取消，如下例： timer2:=time.NewTimer(time.Second) go func(){ \u0026lt;-timer2.C fmt.Println(\u0026#34;Timer 2 expired\u0026#34;) }() stop2:=timer2.Stop() if stop2{ fmt.Println(\u0026#34;Timer 2 stopped\u0026#34;) } } 第一个 timer 将在启动程序后大约 2 秒到时，但第二个应会在其有机会到时前先行停止。\nTickers # timer 用来在将来的某一时间做某事一次。而 ticker 会在一个指定时间间隔重复做某事。这里是一个 ticker 的例子，它会在我们停止之前定期触发。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ //ticker 与 timer 的机制相似，都是一个发送值的通道 // 这里我们使用 channel 内置的 range 来遍历每 500ms 到达的值 ticker:=time.NewTicker(time.Millisecond*500) go func(){ for t:=range ticker.C{ fmt.Prinltn(\u0026#34;Tick at\u0026#34;,t) } }() //ticker 可以像 timer 一样停止。一旦 ticker 停止，将不会在其通道上接收到任何信息。我们将在 1600ms 后结束。 time.Sleep(time.Millisecond*1600) ticker.Stop() fmt.Println(\u0026#34;Ticker stopped\u0026#34;) } 运行这个程序，在结束之前，应该会 tick 三次。\nWorker Pools # 本例中我们将看到如何使用 goroutine 和 channel 来实现一个工人池\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; // 这里是我们将要运行多个并发实例的工人。他们将从 jobs 通道获取任务，并将相应的结果返回到 results 上。我们将在每个作业沉睡 1 秒，来模拟一个复杂的任务。 func worker(id int,jobs \u0026lt;-chan int,results chan\u0026lt;- int){ for j:=range jobs{ fmt.Println(\u0026#34;worker\u0026#34;,id,\u0026#34;started job\u0026#34;,j) time.Sleep(time.Second) fmt.Println(\u0026#34;worker\u0026#34;,id,\u0026#34;finished job\u0026#34;,j) results \u0026lt;- j*2 } } func main(){ // 为了使用工人池，我们需要派发任务并且回收结果。我们使用两个通道来做这件事 jobs:=make(chan int,100) results:=make(chan int,100) // 这里启动了 3 个工人，初始时阻塞，因为当前没有作业 for w:=1;w\u0026lt;=3;w++{ go worker(w,jobs,results) } // 添加 5 个作业，然后关闭通道来表明这就是所有的工作 for j:=1;j\u0026lt;=5;j++{ jobs\u0026lt;-j } close(jobs) for a:=1;a\u0026lt;=5;a++{ \u0026lt;-results } } 运行的项目展示了有 5 个作业得以被不同的工人执行。尽管总共有 5 秒钟的时间，这个程序只需要 2 秒钟，因为有 3 名工作人员同时进行操作。\n同时启动了 3 个 worker，来监听通道是否有作业发出，无作业时 worker 不会进入循环体，为空操作。从而形成工人池\nRate Limiting # 速率限制是控制资源利用和维护服务质量的重要机制。Go 通过 goroutine，channel 和 ticker 可以优雅的支持速率控制。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ // 首先，我们看下基本的速率控制。 // 假设我们想要控制处理的输入请求，我们通过同一个通道来为这些请求提供服务 requests:=make(chan int,5) for i:=1;i\u0026lt;=5;i++{ request\u0026lt;-i } close(requests) //limiter 通道将每过 200 毫秒接收一次数据。这是速率控制策略中的调节器 limiter:=time.Tick(time.Millisecond*200) // 在服务每个请求之前，通过 limiter 通道阻塞接收，我们将自己限制在每 200ms 处理 1 个请求上。 for req:=range request{ \u0026lt;-limiter fmt.Println(\u0026#34;request\u0026#34;,req,time.now()) } // 我们可能希望在我们的速率限制方案中允许短时间的请求，同时保留整体速率限制。 // 我们可以通过缓冲限制器通道来实现这一点。 // 这个 burstyLimiter 通道将允许多达 3 个事件的突发。 burstyLimiter:=make(chan time.Time,3) // 填充通道，来展示可允许的突发 for i:=0;i\u0026lt;3;i++{ burstyLimiter\u0026lt;-time.Now() } // 每过 200 毫秒将试图添加一个新值到 burstyLimiter，最多 3 个 go func(){ for t:=range time.Tick(time.Millisecond*200){ burstyLimiter\u0026lt;-t } }() // 模拟 5 个输入请求。前 3 个将受益于 burstyLimiter 的突发能力 burstyRequests:=make(chan int,5) for i:=1;i\u0026lt;=5;i++{ burstyRequest\u0026lt;-i } close(burstyRequests) for req:=range burstyRequests{ \u0026lt;-burstyLimiter fmt.Println(\u0026#34;request\u0026#34;,req,time.Now()) } } 运行我们的程序，我们看到第一批请求根据需要每 200 毫秒处理一次。\n对于第二批请求，由于可突发速率限制，我们立即为前 3 个服务，然后以约 200ms 的延迟提供剩余的 2 个。\nAtomic Counters # Go 中管理状态的主要机制是通过渠道进行沟通。 我们以工人池为例。 还有一些管理状态的其他选项。 这里我们来看一下使用 sync / atomic 包来进行多个 goroutines 访问的原子计数器。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; import \u0026#34;sync/atomic\u0026#34; func main(){ // 我们使用无符号整数来代表我们的计数器 var ops unit64=0 // 为了模拟并发更新操作，我们将启动 50 个 goroutine，每个都会在每过 1 毫秒时增长一次计数器 for i:=0;i\u0026lt;50;i++{ go func(){ for{ // 为了原子地增加计数器，我们使用 AddUint64，使用＆语法给它我们的 ops 计数器的内存地址。 atomic.AddUnit64(\u0026amp;ops,1) // 在两个增长之间稍作等待 time.Sleep(time.Millisecond) } }() } // 等一下让一些操作积累起来。 time.Sleep(time.Second) // 为了安全地使用计数器，当它仍被其他 goroutine 更新时，我们通过 LoadUint64 将当前值的副本提取到 opsFinal 中。 // 如上所述，我们需要给出这个函数来获取值的内存地址和操作。 opsFinal:=atomic.LoadUnit64(\u0026amp;ops) fmt.Println(\u0026#34;ops:\u0026#34;,opsFinal) } 运行程序可以看到我们执行了大约 40,000 次操作。\nMutexes # 在前面的例子中我们看到如何使用原子操作管理简单的计数器状态。为了处理更复杂的状态，我们可以使用一个mutext来安全的访问不同 gorotine 之间的数据。\npackage main import( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;sync\u0026#34; \u0026#34;sync/atomic\u0026#34; \u0026#34;time\u0026#34; ) func main(){ // 这个例子中，状态是一个 map var state=make(map[int]int) // 这个 mutext 将同步访问状态值 var mutex=\u0026amp;sync.Mutex{} // 我们将记录做了多少读写操作 var readOps unit64=0 var writeOps unit64=0 // 这里我们启动了 100 个 goroutine 来重复读取状态值 // 每毫秒在每个 goroutine 执行一次 for r:=0;r\u0026lt;100;r++{ go func(){ total:=0 for{ // 每次读取时我们将获得一个进入的钥匙 //Lock() 住 mutex 来保证独家访问状态值 // 在选中的钥匙上读取值，Unlock() 掉 mutext // 对 readOps 计数加 1 key:=rand.Intn(5) mutex.Lock() total+=state[key] mutex.Unlock() atomic.AddUnit64(\u0026amp;readOps,1) // 每次操作等待一下 time.Sleep(time.Millisecond) } }() } // 再启动 10 个模拟写的 goroutine，与读取类似的模式 for w:=0;w\u0026lt;10;w++{ go func(){ for{ key:=rand.Intn(5) val:=rand.Intn(100) mutex.Lock() state[key]=val mutex.Unlock() atomic.AddUnit64(\u0026amp;writeOps,1) time.Sleep(time.Millisecond) } }() } // 让这 10 个 go 协程在 state 和 mutext 上工作一会儿 time.Sleep(time.Second) // 获取并且报告最终操作的个数。 readOpsFinal:=atomic.LoadUnit64(\u0026amp;readOps) fmt.Println(\u0026#34;readOps:\u0026#34;,readOpsFinal) writeOpsFinal:=atomic.LoadUnit64(\u0026amp;writeOps) fmt.Println(\u0026#34;writeOps:\u0026#34;,writeOpsFinal) // 随着最后一次锁住状态，展示它是如何结束的 mutex.Lock() fmt.Println(\u0026#34;state:\u0026#34;,state) mutex.Unlock() } 运行程序可以看到我们在 mutex 同步状态上执行了将近 90,000 操作。\nStateful Goroutines # 在上个例子我们使用 mutex 显式锁定多个 goroutine 要同步访问的共享状态。另一个选择是使用 goroutine 和 channel 内置的同步功能来达到相同的结果。这种基于渠道的方法与 Go 通过通信和拥有完全一个 goroutine 的每个数据来共享内存的想法相一致。\npackage main import( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;sync/atomic\u0026#34; \u0026#34;time\u0026#34; ) // 在这个例子中，状态值将被一个单独的 goroutine 拥有 // 这保证了数据不会受到并发访问的影响 // 为了读或写状态值，其它 goroutine 将向拥有它的 goroutine 发送一个消息，然后接收其回复。 //readOp 和 writeOp 结构封装了这些请求和响应 type readOp struct{ key int resp chan int } type writeOp struct{ key int val int resp chan bool } func main() { // 像之前一样我们记录执行了多少次操作 var readOp unit64=0 var writeOps unit64=0 //reads 和 writes 通道将被用于其它 goroutine 分别发送读写请求 reads:=make(chan *readOp) writes:=make(chan *writeOp) // 这里便是拥有状态值的 goroutine，与之前一样是个 map，但被私有化 // 这个 goroutine 反复选择 reads 和 writes 通道，响应到达的请求。 // 首先执行所请求的操作然后在响应通道上发送值来表示成功执行响应 //(或者 reads 期望的数据) go func(){ var state=make(map[int]int) for{ select{ case read:=\u0026lt;-reads: read.resp\u0026lt;-state[read.key] case write:=\u0026lt;-writes: state[write.key]=write.val write.resp\u0026lt;-true } } }() // 启动 100 个 goroutine，通过读取通道来读取有状态的 goroutine // 每次读取需要构建一个 readOp，通过 reads 发送给它 // 再通过所提供的 resp 通道获取结果 for r:=0;r\u0026lt;100;r++{ go func(){ for { read:=\u0026amp;readOp{ key:rand.Intn(5), resp:make(chan int) } reads\u0026lt;-read \u0026lt;-read.resp atomic.AddUnit64(\u0026amp;readOps,1) time.Sleep(time.Millisecond) } }() } // 启动 10 个写操作 for w:=0;w\u0026lt;10;w++{ go func(){ for { write:=\u0026amp;writeOp{ key:rand.Intn(5), val:rand.Intn(100), resp:make(chan bool) } } }() } time.Sleep(time.Second) readOpsFinal:=atomic.LoadUnit64(\u0026amp;readOps) fmt.Println(\u0026#34;readOps:\u0026#34;,readOpsFinal) writeOpsFinal:=atomic.LoadUnit64(\u0026amp;writeOps) fmt.Println(\u0026#34;writeOps:\u0026#34;,writeOpsFinal) } 运行项目显示基于 gouroutine 的状态管理完成了大约 80,000 操作。\nSorting # sort 包实现了内置和自定义类型的排序。首先看看内置类型的排序。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;sort\u0026#34; func main(){ //sort 改变了给定的 slice，而不是返回一个新的 strs:=[]string{\u0026#34;c\u0026#34;,\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;} sort.Strings(strs) fmt.Println(\u0026#34;Strings:\u0026#34;,strs) ints:=[]int{7,2,4} sort.Ints(ints) fmt.Println(\u0026#34;Ints:\u0026#34;,ints) // 可以使用 sort 检查一个 slice 是不是已经排好序了 s:=sort.IntsAreSorted(ints) fmt.Println(\u0026#34;Sorted:\u0026#34;,s) } Sorting by Functions # 有时候我们想要对一个集合进行非自然顺序的排序。例如，我们想要把字符串根据长度而非字典顺序排序，下面是一个定制排序的例子。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;sort\u0026#34; // 为了根据自定义函数排序，我们需要相应的类型 // 这里我们创建了一个 ByLength 类型 // 这就是一个 []string 类型的别名 type ByLength []string // 我们在 ByLength 上实现了 sort 接口的 Len、Less 和 Swap 方法 // 这里我们想要按照字符串长度的增序排列 func (s ByLength) Len() int{ return len(s) } func (s ByLength) Swap(i,j int){ s[i],s[j] = s[j],s[i] } func (s ByLength) Less(i,j int) bool { return len(s[i])\u0026lt;len(s[j]) } // 通过将原有的 fruits 片段转换为 ByLength // 就可以使用 sort.Sort 来进行自定义排序了。 func main(){ fruits:=[]string{\u0026#34;peach\u0026#34;,\u0026#34;banana\u0026#34;,\u0026#34;kiwi\u0026#34;} sort.Sort(ByLength(fruits)) fmt.Println(fruits) } 通过类似的模式创建自定义类型，实现三个接口方法，然后调用 sort，我们可以对集合进行任意的排序。\nPanic # panic通常指发生了未曾预料的错误。大多数情况下，我们使用它来将不应当在正常操作中发生的东西快速失败，或者不准备妥善处理。\npackage main import \u0026#34;os\u0026#34; func main(){ // 我们将在整个网站使用 panic 来检查意外的错误。 // 这是该网站上唯一旨在 panic 的程序。 panic(\u0026#34;a problem\u0026#34;) //panic 的一个常见作用是终止一个函数返回了一个不知道如何处理或者不想处理的错误。 // 这里是一个 panic 的例子，在创建一个新文件时发生意外错误 _,err:=os.Create(\u0026#34;/tmp/file\u0026#34;) if err!=nil{ panic(err) } } 运行这个程序将引起一个 panic，打印错误信息和 goroutine 踪迹，并以非 0 状态退出。\n注意，不像一些用异常来处理大多错误的语言，在 Go 的习惯用法中，尽可能使用错误指示的返回值。\nDefer # defer 用于确保一个函数调用在程序执行中延迟作用，经常用于清理目的。defer常用语其他语言的ensure和finnaly用的地方。\npackgae main import \u0026#34;fmt\u0026#34; import \u0026#34;os\u0026#34; // 假设我们想要创建并写一个文件，在完成后关闭它。 func main(){ // 在获取一个文件对象后立即使用 defer 并关闭这个文件 // 这个将在 main 函数末尾关闭的时候执行，在 writeFile 完成后 f:=createFile(\u0026#34;/tmp/defer.txt\u0026#34;) defer closeFile(f) writeFile(f) } func createFile(p string) *os.File{ fmt.Println(\u0026#34;creating\u0026#34;) f,err:=os.Create(p) if err!=nil{ panic(err) } return f } func writeFile(f *os.File){ fmt.Println(\u0026#34;writing\u0026#34;) fmt.Println(f,\u0026#34;data\u0026#34;) } func closeFile(f *os.File){ fmt.Println(\u0026#34;closing\u0026#34;) f.Close() } 运行程序，确认这个文件的确在写之后再关闭的。\nCollection Functions # 我们经常需要我们的程序对数据集合执行操作，例如选择满足给定谓词的所有项目或将所有项目映射到具有自定义函数的新集合。\n一些语言通常的惯用法是使用泛型数据结构和算法。 Go 不支持泛型; 在 Go 中，通常在程序和数据类型特别需要时提供集合功能。\n以下是一些用于字符串切片的示例集合函数。 您可以使用这些示例来构建自己的函数。 请注意，在某些情况下，直接内联集合操作代码可能是最为清晰的，而不是创建和调用帮助函数。\npackage main import \u0026#34;strings\u0026#34; import \u0026#34;fmt\u0026#34; // 返回目标字符串 t 的第一个索引，如果没有找到则返回 -1 func Index(vs []string,t string) int{ for i,v:=range vs{ if v==t{ return i } } return -1 } // 如果字符串在切片中，则返回 true func Include(vs []string,t string) bool{ return Index(vs,t)\u0026gt;=0 } // 如果有一个字符串满足期望的 f 则返回 true func Any(vs []string,f func(string) bool) bool{ for _,v:=range vs{ if f(v){ return true } } return false } // 所有的字符串满足期望的 f 则返回 true func All(vs []string,f func(string) bool) bool{ for _,v:=range vs{ if !f(v){ return false } } return true } // 返回一个满足给定方法的所有字符串 func Filter(vs []string,f func(string) bool)[]string{ vsf :=make([]string,0) for _,v:=range vs{ if f(v){ vsf=append(vsf,v) } } return vsf } // 返回一个包含将 f 函数应用于原始切片中每个字符串的结果的新切片。 func Map(vs []string,f func(string) string)[] string{ vsm := make([]string,len(vs)) for i,v:=range vs{ vsm[i]=f(v) } return vsm } func main(){ var strs=[]string{\u0026#34;peach\u0026#34;,\u0026#34;apple\u0026#34;,\u0026#34;pear\u0026#34;,\u0026#34;plum\u0026#34;} fmt.Println(Index(strs,\u0026#34;pear\u0026#34;)) fmt.Println(Include(strs,\u0026#34;grape\u0026#34;)) fmt.Println(Any(strs,func(v string) bool{ return strings.HasPrefix(v,\u0026#34;p\u0026#34;) })) fmt.Println(All(strs,func(v string) bool{ return strings.HasPrefix(v,\u0026#34;p\u0026#34;) })) fmt.Println(Filter(strs,func(v string) bool{ return strings.Contains(v,\u0026#34;e\u0026#34;) })) // 以上示例全部使用匿名函数，但也可以使用正确类型的命名函数。 fmt.Println(Map(strs,strings.ToUpper)) } String Functions # 标准库的 String 包提供了许多有用的字符串相关的函数。这里有些示例让你对这个包有个初步的认识。\npackage main import s \u0026#34;strings\u0026#34; import \u0026#34;fmt\u0026#34; // 鉴于后文大量用到，我们给字符串输出函数起一个别名 var p=fmt.Println func main(){ // 这里是字符串可以用的函数示例 // 由于这些函数是包中的而不是字符串对象本身的方法，我们需要给他们传递一个字符串参数 // 你可以在 strings 包下的 doc 找到更多的函数 p(\u0026#34;Contains:\u0026#34;,s.Contains(\u0026#34;test\u0026#34;,\u0026#34;es\u0026#34;)) p(\u0026#34;Count:\u0026#34;,s.Count(\u0026#34;test\u0026#34;,\u0026#34;t\u0026#34;)) p(\u0026#34;HasPrefix:\u0026#34;,s.HasPrefix(\u0026#34;test\u0026#34;,\u0026#34;te\u0026#34;)) p(\u0026#34;HasSuffix:\u0026#34;,s.HasSuffix(\u0026#34;test\u0026#34;,\u0026#34;st\u0026#34;)) p(\u0026#34;Index:\u0026#34;,s.Index(\u0026#34;test\u0026#34;,\u0026#34;e\u0026#34;)) p(\u0026#34;Join:\u0026#34;,s.Join([]string{\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;},\u0026#34;-\u0026#34;)) p(\u0026#34;Repeat:\u0026#34;,s.Repeat(\u0026#34;a\u0026#34;,5)) p(\u0026#34;Replace:\u0026#34;,s.Replace(\u0026#34;foo\u0026#34;,\u0026#34;o\u0026#34;,\u0026#34;0\u0026#34;,-1)) p(\u0026#34;Replace\u0026#34;,s.Replace(\u0026#34;foo\u0026#34;,\u0026#34;o\u0026#34;,\u0026#34;0\u0026#34;,1)) p(\u0026#34;Split:\u0026#34;,s.Split(\u0026#34;a-b-c-d-e\u0026#34;,\u0026#34;-\u0026#34;)) p(\u0026#34;ToLower:\u0026#34;,s.ToLower(\u0026#34;TEST\u0026#34;)) p(\u0026#34;ToUpper:\u0026#34;,s.ToUpper(\u0026#34;test\u0026#34;)) p() // 并非 strings 包的一部分，但是这里值得一提 // 即获取字符串长度以及字符串中的某个字符 p(\u0026#34;Len:\u0026#34;,len(\u0026#34;hello\u0026#34;)) p(\u0026#34;Char:\u0026#34;,\u0026#34;hello\u0026#34;[1]) } 注意，获取长度和索引字符是工作在字节级别上的。Go 使用 UTF-8 编码字符串。\nString Formatting # Go 在经典的printf上提供了优秀的字符串格式化支持。这里有一些常见格式化的例子。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;os\u0026#34; type point struct{ x,y int } func main(){ //Go 提供了一些修饰符来格式化一般的 Go 数值 // 例如，这里输出了一个 point 结构体的实例 p:=point{1,2} fmt.Printf(\u0026#34;%v\\n\u0026#34;,p) // 如果值是结构体，%+v 变量将输出结构体中的域名称 fmt.Printf(\u0026#34;%+v\\n\u0026#34;,p) //%#v 将打印 Go 语法形式 fmt.Printf(\u0026#34;%#v\\n\u0026#34;,p) // 要想打印值的类型，使用 %T fmt.Printf(\u0026#34;%T\\n\u0026#34;,p) // 格式化输出布尔值 fmt.Printf(\u0026#34;%t\\n\u0026#34;,true) // 整数有许多格式化选项，使用 %d 来进行标准十进制格式化 fmt.Printf(\u0026#34;%d\\n\u0026#34;,123) // 这样打印了二进制形式 fmt.Printf(\u0026#34;%b\\n\u0026#34;,14) // 这里打印了十进制数字对应的字符 fmt.Printf(\u0026#34;%c\\n\u0026#34;,33) //%x 提供了十六进制编码 fmt.Printf(\u0026#34;%x\\n\u0026#34;,456) // 浮点数也有许多格式化选项，标准十进制使用 %f fmt.Printf(\u0026#34;%f\\n\u0026#34;,78.9) //%e 和 %E 格式化浮点数为科学计数法 fmt.Printf(\u0026#34;%e\\n\u0026#34;,123400000.0) fmt.Printf(\u0026#34;%E\\n\u0026#34;,123400000.0) // 基本的字符串输出，使用 %s fmt.Pirntf(\u0026#34;%s\\n\u0026#34;,\u0026#34;\\\u0026#34;string\\\u0026#34;\u0026#34;) // 将 Go 代码中的字符串加引号输出 (等同参数形式) fmt.Printf(\u0026#34;%q\\n\u0026#34;,\u0026#34;\\\u0026#34;string\\\u0026#34;\u0026#34;) // 将字符串十六进制编码化 fmt.Printf(\u0026#34;%x\\n\u0026#34;,\u0026#34;hex this\u0026#34;) // 打印一个指针 fmt.Printf(\u0026#34;%p\\n\u0026#34;,\u0026amp;p) // 控制数字输出的宽度和精度，在 % 后面跟上数字可以控制宽度,. 后面跟上数字控制精度 // 默认靠右显示，用空格填充 fmt.Printf(\u0026#34;|%6d|%6d|\\n\u0026#34;,12,345) fmt.Printf(\u0026#34;|%6.2f|%6.2f|\\n\u0026#34;,1.2,3.45) // 靠左对齐使用 - fmt.Printf(\u0026#34;|%-6.2f|%-6.2f|\\n\u0026#34;,1.2,3.45) // 你可能也需要控制字符串输出的格式，特别是要以表格形式输出的时候 fmt.Printf(\u0026#34;|%6s|%6s|\\n\u0026#34;,\u0026#34;foo\u0026#34;,\u0026#34;b\u0026#34;) fmt.Printf(\u0026#34;|%-6s|%-6s|\\n\u0026#34;,\u0026#34;foo\u0026#34;,\u0026#34;b\u0026#34;) //Printf 将格式化的字符串打印到标准输出上 //Sprintf 格式化并返回这个字符串而不打印 s:=fmt.Sprintf(\u0026#34;a %s\u0026#34;,\u0026#34;string\u0026#34;) fmt.Println(s) // 你也可以格式化并打印到 io.Writers 而非 os.StdOut fmt.Fprintf(os.Stderr,\u0026#34;an %s\\n\u0026#34;,\u0026#34;error\u0026#34;) } Regular Expressions # Go 为正则表达式提供了内置的支持。这里是一些常用的正则相关的任务。\npackage main import \u0026#34;bytes\u0026#34; import \u0026#34;fmt\u0026#34; import \u0026#34;regexp\u0026#34; func main(){ // 测试模式是否符合字符串 math,_:=regexp.MatchString(\u0026#34;p([a-z]+)ch\u0026#34;,\u0026#34;peach\u0026#34;) fmt.Printf(match) // 上面我们直接使用了字符串模式。但是对于其他正则任务，你需要先编译一个正则表达式结构体 r,_:=regexp.Compile(\u0026#34;p([a-z]+)ch\u0026#34;) // 这种结构体上有许多方法。这里是一个如同上面的匹配测试 fmt.Println(r.MatchString(\u0026#34;peach\u0026#34;)) // 这里找到一个匹配 fmt.Println(r.FindString(\u0026#34;peach punch\u0026#34;)) // 这里也是寻找第一个匹配，但返回起止的索引而非字符串 fmt.Println(r.FindStringIndex(\u0026#34;peach punch\u0026#34;)) //submatch 包含整串匹配，也包含内部的匹配。 fmt.Println(r.FindStringSubmatch(\u0026#34;peach punch\u0026#34;)) // 与上面类似，返回整串匹配和内部匹配的索引信息 fmt.Println(r.FindStringSubmatchIndex(\u0026#34;peach punch\u0026#34;)) // 这些函数的 All 修饰将返回输入中所有匹配的，不仅仅是第一个。例如找到所有匹配正则表达式的 fmt.Println(r.FindAllString(\u0026#34;peach punch pinch\u0026#34;,-1)) fmt.Println(r.FindAllStringSubmatchIndex(\u0026#34;peach punch pinch\u0026#34;,-1)) // 第二个参数如果是非负数，则将限制最多匹配的个数 fmt.Println(r.FindAllString(\u0026#34;peach punch pinch\u0026#34;,2)) // 例子中都是字符串参数，并且用了类似于 MatchString 这样的名字 // 我们可以提供 []byte 参数，并将函数名中的 String 去掉 fmt.Println(r.Match([]byte(\u0026#34;peach\u0026#34;))) // 当用正则表达式创建一个常量的时候你可以使用 MustCompile。 // 一个纯 Compile 不能用于常量，因为它有 2 个返回值。 r=regexp.MustComplie(\u0026#34;p([a-z]+)ch\u0026#34;) fmt.Println(r) //regexp 包也能用于使用其他值替换字符串的子集 fmt.Println(r.ReplaceAllString(\u0026#34;a peach\u0026#34;,\u0026#34;\u0026lt;fruit\u0026gt;\u0026#34;)) //Func 修饰允许你使用一个给定的函数修改匹配的字符串 in:=[]byte(\u0026#34;a peach\u0026#34;) out:=r.ReplaceAllFunc(in,bytes.ToUpper) fmt.Println(string(out)) } JSON # Go 内置提供了 JSON 的编码和解码是，包括内置和自定义的数据类型。\npackage main import \u0026#34;encoding/json\u0026#34; import \u0026#34;fmt\u0026#34; import \u0026#34;os\u0026#34; // 我们使用这两个结构体来展示自定义类型的编码和解码 type Response1 struct{ Page int Fruits []string } type Response2 struct{ Page int `json:\u0026#34;page\u0026#34;` Fruits []string `json:\u0026#34;fruits\u0026#34;` } func main(){ // 首先我们来看编码基本数据类型到 JSON 字符串。 // 这里有一些原子类型的示例 bolB,_:=json.Marshal(true) fmt.Println(string(bolB)) intB,_:=json.Marshal(1) fmt.Println(string(intB)) fltB,_:=json.Marshal(2.34) fmt.Println(string(fltB)) strB,_:=json.Marshal(\u0026#34;gopher\u0026#34;) fmt.Println(string(strB)) // 这里有些 slice 和 map 的例子，他们将按需编码成 JSON 数组和对象。 slcD:=[]string{\u0026#34;apple\u0026#34;,\u0026#34;peach\u0026#34;,\u0026#34;pear\u0026#34;} slcB,_:=json.Marshal(slcD) fmt.Println(string(slcB)) mapD:=map[string]int{\u0026#34;apple\u0026#34;:5,\u0026#34;lettuce\u0026#34;:7} mapB,_:=json.Marshal(mapD) fmt.Println(string(mapB)) //JSON 包将自动编码你的自定义数据类型 // 他将在编码后的输出中只包含对外的域并且用名字作为 JSON 键 res1D:=\u0026amp;Response1{ Page:1, Fruits:[]string{\u0026#34;apple\u0026#34;,\u0026#34;peach\u0026#34;,\u0026#34;pear\u0026#34;} } res1B,_:=json.Marshal(res1D) fmt.Println(string(res1B)) // 你可以在结构体的域声明上定制编码后的 JSON 键名。 // 可以看上面 Reponse2 的定义 res2D:=\u0026amp;Reponse2{ Page:1, Fruits:[]string{\u0026#34;apple\u0026#34;,\u0026#34;peach\u0026#34;,\u0026#34;pear\u0026#34;} } res2B,_:=json.Marshal(res2D) fmt.Println(string(res2B)) // 我们需要提供一个变量来放 JSON 包编码的值。 //map[string]interface{} 将承载一个字符串数据类型的 map byt:=[]byte(`{\u0026#34;num\u0026#34;:6.13,\u0026#34;strs\u0026#34;:[\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;]}`) var dat map[string]interface{} // 这里是相应的解码，以及相关错误的检查 if err:=json.Unmarshal(byt,\u0026amp;dat);err!=nil{ panic(err) } fmt.Println(dat) // 为了使用解码的 map 的值，我们需要将它们转换为合适的类型 // 例如这里我们将 num 中饿值转变为 float64 类型 num:dat[\u0026#34;num\u0026#34;].(float64) fmt.Println(num) // 访问内部数据需要一系列的转换 strs:=dat[\u0026#34;strs\u0026#34;].([]interface{}) str1:=strs[0].(string) fmt.Println(str1) // 我们也可以将 JSON 解码到定制的数据类型 // 这样为程序添加了额外的类型安全，并不再需要在访问数据的时候进行类型断言 str:=`{\u0026#34;page\u0026#34;:1,\u0026#34;fruits\u0026#34;:[\u0026#34;apple\u0026#34;,\u0026#34;peach\u0026#34;]}` res:=Response2{} json.Unmarshal([]byte(str),\u0026amp;res) fmt.Println(res) fmt.Println(res.Fruits[0]) // 在上面的例子中我们经常使用 bytes 和字符串在标准输出上来进行数据和 JSON 形式的交互 // 我们也可以将 JSON 编码流入到 os.Writers 甚至 HTTP 响应体 enc:json.NewEncoder(os.Stdout) d:=map[string]int{\u0026#34;apple\u0026#34;:5,\u0026#34;lettuce\u0026#34;:7} enc.Encode(d) } Time # Go 为时间和持续时间提供了额外支持。这里是一些例子。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; func main(){ p:=fmt.Pritnln // 首先获取当前时间 now:=time.Now() p(now) // 你可以创建一个时间结构体，提供年月日等。 // 时间经常与地区，例如时区关联 then:=time.Date(2009,11,17,20,34,58,651387237,time.UTC) p(then) // 可以按照需要抽取时间变量中的不同部分 p(then.Year()) p(then.Month()) p(then.Day()) p(then.Hour()) p(then.Minute()) p(then.Second()) p(then.Nanosecond()) p(then.Location()) p(then.Weekday()) p(then.Before(now)) p(then.After(now)) p(then.Equal(now)) //sub 方法返回两个时间中的时间长度 diff:=now.Sub(then) p(diff) // 我们可以计算不同单位的持续时间长度 p(diff.Hours()) p(diff.Minutes()) p(diff.Seconds()) p(diff.Nanoseconds()) // 可以使用 Add 来向前推进一个给定的时间 // 或者使用减号来倒退 p(then.Add(diff)) p(then.Add(-diff)) } Epoch # 程序中的一个常见需求是获取秒、毫秒或微秒，自 Unix 时代，这里是 Go 的做法。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; func main(){ now:=time.Now() secs:=now.Unix() nanos:=now.UnixNano() fmt.Println(now) millis:=nanos/1000000 fmt.Println(secs) fmt.Println(millis) fmt.Println(nanos) fmt.Println(time.Unix(secs,0)) fmt.Println(time.Unix(0,nanos)) } Time Formatting / Parsing # Go 支持时间格式化和解析，根据基于模式的布局。\npackage main import \u0026#34;fmt\u0026#34; import \u0026#34;time\u0026#34; func main(){ p:=fmt.Println // 这里是一个根据 RFC3339 基本的格式化时间的例子，使用响应的布局常量 t:=time.Now() p(t.Format(time.RFC3339)) // 时间解析使用格式化相同的布局值 t1,e:=time.Parse( time.RFC3339, \u0026#34;2012-11-01T22:08:41+00:00\u0026#34; ) p(t1) // 格式化和解析使用基于示例的布局 // 通常你使用常量在进行布局。但你也可以提供自定义的格式。 // 但你必须使用 Mon Jan 2 15:04:05 MST 2006 来作为示例 p(t.Format(\u0026#34;3:04PM\u0026#34;)) p(t.Format(\u0026#34;Mon Jan _2 15:04:05 2006\u0026#34;)) p(t.Format(\u0026#34;2006-01-02T15:04:05.999999-07:00\u0026#34;)) form:=\u0026#34;3 04 PM\u0026#34; t2,e:=time.Parse(form,\u0026#34;8 41 PM\u0026#34;) p(t2) // 纯数字展示你可以使用标准的字符串格式化及响应部分的时间值 fmt.Printf(\u0026#34;%d-%02d-%02dT%02d:%02d:%02d-00:00\\n\u0026#34;, t.Year(),t.Month(),t.Day(), t.Hour(),t.Minute(),t.Second() ) // 解析返回一个错误来说明是什么问题 ansic:=\u0026#34;Mon Jan _2 15:04:05 2006\u0026#34; _,e=time.Parse(ansic,\u0026#34;8:41PM\u0026#34;) p(e) } Random Numbers # Go 的 math/rand 包提供产生伪随机数。\npackage main import \u0026#34;time\u0026#34; import \u0026#34;fmt\u0026#34; import \u0026#34;math/rand\u0026#34; func main(){ // 例如，rand.Intn 产生一个随机的整数 n，0\u0026lt;=n\u0026lt;100 fmt.Print(rand.Intn(100),\u0026#34;,\u0026#34;) fmt.Print(rand.Intn(100)) fmt.Println() //rand.Float64 返回一个随机的浮点数，0.0\u0026lt;=f\u0026lt;1.0 fmt.Println(rand.Float64()) // 这个可以用于产生其他区间的浮点数，如 5.0\u0026lt;=f\u0026lt;10.0 fmt.Print((rand.Float64()*5)+5,\u0026#34;,\u0026#34;) fmt.Print((rand.Float64()*5)+5) fmt.Println() // 默认的数字产生器是 deterministic，故它每次善生的数字序列是固定的 // 为了产生不同的序列，赋予它一个变化的种子 // 注意，随即数字用来加密并不安全，用 crypto/rand 来做 s1:=rand.NewSource(time.Now().UnixNano()) r1:=rand.New(s1) // 调用产生的 rand.Rand fmt.Print(r1.Intn(100),\u0026#34;,\u0026#34;) fmt.Print(r1.Intn(100)) fmt.Println() // 如果你给 Source 设置了相同的数字种子，他将产生相同的随即数字序列。 s2:=rand.NewSrouce(42) r2:=rand.New(s2) fmt.Print(r2.Intn(100),\u0026#34;,\u0026#34;) fmt.Print(r2.Intn(100)) fmt.Println() s3:=rand.NewSource(42) r3:=rand.New(s3) fmt.Print(r3.Intn(100),\u0026#34;,\u0026#34;) fmt.Print(r3.Intn(100)) } Number Parsing # 从字符串中解析数字是一个常见的任务，这里是 Go 的做法。\npackage main // 内置的 strconv 包提供了数字解析 import \u0026#34;strconv\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ //64 代表要解析的浮点数精度 f,_:=strconv.ParseFloat(\u0026#34;1.234\u0026#34;,64) fmt.Println(f) //0 代表根据字符串推断基数，64 要求结果要适应 64 位 i,_:=strconv.ParseInt(\u0026#34;123\u0026#34;,0,64) fmt.Println(i) //ParseInt 可以识别十六进制 d,_:=strconv,Parseint(\u0026#34;0x1c8\u0026#34;,0,64) fmt.Println(d) u,_:=strconv.ParseUint(\u0026#34;789\u0026#34;,0,64) fmt.Println(u) //atoi 是十进制整数解析的简便函数 k,_:=strconv.Atoi(\u0026#34;135\u0026#34;) fmt.Println(k) // 不合法的输入将导致解析函数返回一个错误 _,e:=strconv.Atoi(\u0026#34;wat\u0026#34;) fmt.Println(e) } URL Parsing # package main import \u0026#34;fmt\u0026#34; import \u0026#34;net\u0026#34; import \u0026#34;net/url\u0026#34; func main(){ // 我们解析这个示例 URL，包含一个协议，授权信息，地址，端口，路径，查询参数以及查询拆分 s:=\u0026#34;postgres://user:pass@host.com:5432/path?k=v#f\u0026#34; // 解析这个 URL 并且保证没有错误 u,err:=url.Parse(s) if err!=nil{ panic(err) } // 可以直接访问协议 fmt.Println(u.Scheme) //User 包含所有授权信息，调用 Username 和 Password 可以得到单独的值 fmt.Println(u.User) fmt.Println(u.User.Username()) p,_:=u.User.Password() fmt.Println(p) //Host 包含地址和端口。使用 SplitHostPort 来抽取他们 fmt.Println(u.Host) host,port,_:=net.SplitHostPort(u.Host) fmt.Println(host) fmt.Println(port) fmt.Println(u.Path) fmt.Println(u.Fragment) // 为了以 k=v 的格式得到查询参数，使用 RawQuery // 你也可以将查询参数解析到一个 map 中 // 解析的查询参数是从字符串到字符串的片段，故索引 0 可以只得到第一个值 fmt.Println(u.RawQuery) m,_:=url.ParseQuery(u.RawQuery) fmt.Println(m) fmt.Println(m[\u0026#34;k\u0026#34;][0]) } SHA1 Hashes # SHA1 哈希经常用于计算二进制或者文本块的短标识。例如，git 版本控制系统使用 SHA1 来标示文本和目录。这里是 Go 如何计算 SHA1 哈希值。\npackage main //Go 实现了多种哈希函数，在 crypto 包下 import \u0026#34;crypto/sha1\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ s:=\u0026#34;sha1 this string\u0026#34; // 产生一个哈希的模式是 sha1.New()，sha1.Write(bytes) 然后 sha1.Sum([]bytes{}) h:=sha1.New() h.Write([]byte(s)) // 这里得到最终的哈希结果字节片段值。参数用于向已存在的字节片段追加，通常不需要 bs:=h.Sum(nil) //SHA1 值经常用于打印成十六进制，如 git 提交时。使用 %x 格式参数来转换为十六进制 fmt.Println(s) fmt.Printf(\u0026#34;%x\\n\u0026#34;,bs) } Base64 Encoding # package main // 这里为引入的包起了名称，来节省下面代码的空间 import b64 \u0026#34;encoding/base64\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ data:=\u0026#34;abc123!?$*\u0026amp;()\u0026#39;-=@~\u0026#34; sEnc:=b64.StdEncoding.EncodeToString([]byte(data)) fmt.Println(sEnc) sDec,_:=b64.StdEncoding.DecodeString(sEnc) fmt.Println(string(sDec)) fmt.Println() uEnc:=b64.URLEncoding.EncodeToString([]byte(data)) fmt.Println(uEnc) uDec,_:=b64.URLEncoding.DecodeString(uEnc) fmt.Println(string(uDec)) } Reading Files # 读取和写入文件是许多 Go 程序的基本任务需求。首先我们来看读取文件。\npackage main import( \u0026#34;bufio\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;io/ioutil\u0026#34; \u0026#34;os\u0026#34; ) // 读取文件需要检查大多数调用是否错误，这个 helper 可以流水化错误检查 func check(e error){ if e!=nil{ panic(e) } } func main(){ // 最基本的一个文件读取任务是将所有内容放入内存中 dat,err:=ioutil.ReadFile(\u0026#34;/tmp/dat\u0026#34;) check(err) fmt.Print(string(dat)) // 如果你想对文件的那部分如何进行读取有更多的控制 // 首先你需要打开它 f,err:=os.Open(\u0026#34;/tmp/dat\u0026#34;) check(err) // 从文件的开头读取一些字节。允许到 5，同时也是实际读取了的字节。 b1:=make([]byte,5) n1,err:=f.Read(b1) check(err) fmt.Printf(\u0026#34;%d bytes: %s\\n\u0026#34;,n1,string(b1)) // 你也可以找到一个已知的位置并从那里开始读取 o2,err:=f.Seek(6,0) check(err) b2:=make([]byte,2) n2,err:=f.Read(b2) check(err) fmt.Printf(\u0026#34;%d bytes @[](https://hacpai.com/member/) %d: %s\\n\u0026#34;,n2,o2,string(b2)) //io 包提供了一些函数，对于文件读取可能很有帮助 // 例如上面的继续读取的例子，可以使用 ReadAtLeast 更稳定的实现 o3,err:=f.Seek(6,0) check(err) b3:=make([]byte,2) n3,err:=io.ReadAtLeast(f,b3,2) check(err) fmt.Printf(\u0026#34;%d bytes @[](https://hacpai.com/member/) %d: %s\\n\u0026#34;,n3,o3,string(b3)) // 没有内置的退回，但是 Seek(0,0) 完成了这个事情 _,err=f.Seek(0,0) check(err) //bufio 包实现了一个带缓冲区的读取，它对于一些小的读取以及由于它所提供的额外方法会很有帮助 r4:=bufio.NewReader(f) b4,err:=r4.Peek(5) check(err) fmt.Printf(\u0026#34;5 bytes: %s\\n\u0026#34;,string(b4)) // 在完成时关闭文件 (通常会在打开时通过 defer 计划执行) f.Close() } Writing Files # 写文件与读取的模式类似。\npackage main import( \u0026#34;bufio\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;io/ioutil\u0026#34; \u0026#34;os\u0026#34; ) func check(e error){ if e!=nil{ panic(e) } } func main(){ // 这里将一个字符串 (或者仅仅是字节) 写入到一个文件。 d1:=[]byte(\u0026#34;hello\\ngo\\n\u0026#34;) err:=ioutil.WriteFile(\u0026#34;/tmp/dat1\u0026#34;,d1,0644) check(err) // 打开一个文件以供写入 f,err:=os.Create(\u0026#34;/tmp/dat2\u0026#34;) check(err) // 这是一个惯用法，在打开时立刻通过 defer 关闭 defer f.Close() d2:=[]byte{115,111,109,101,10} n2,err:=f.Write(d2) check(err) fmt.Printf(\u0026#34;wrote %d bytes\\n\u0026#34;,n2) n3,err:=f.WriteString(\u0026#34;writes\\n\u0026#34;) fmt.Printf(\u0026#34;wrote %d bytes\\n\u0026#34;,n3) f.Sync() w:=bufio.NewWriter(f) n4,err:=w.WriteString(\u0026#34;buffered\\n\u0026#34;) fmt.Printf(\u0026#34;wrote %d bytes\\n\u0026#34;,n4) w.Flush() } Line Filters # 一个行过滤器经常见于读取标准输入流的输入，处理然后输出到标准输出的程序中。grep 和 sed 是常见的行过滤器。\n// 下面这个行过滤器示例将所有输入的文字转换为大写的版本 package main import ( \u0026#34;bufio\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strings\u0026#34; ) func main(){ // 使用一个带缓冲的 scanner 可以方便的使用 Scan 方法来直接读取一行 // 每次调用该方法可以让 scanner 读取下一行 scanner:=bufio.NewScanner(os.Stdin) //Text 方法返回当前的 token，现在是输入的下一行 for scanner.Scan(){ ucl:=strings.ToUpper(scanner.Text()) // 输出大写的行 fmt.Println(ucl) } // 检查 scanner 的错误，文件结束符不会被当作是一个错误 if err:=scanner.Err();err!=nil{ fmt.Fprintln(os.Stderr,\u0026#34;error:\u0026#34;,err) os.Exit(1) } } 可以使用如下命令来试验这个行过滤器：\n$ echo \u0026lsquo;hello\u0026rsquo; \u0026gt; /tmp/lines $ echo \u0026lsquo;filter\u0026rsquo; \u0026raquo; /tmp/lines $ cat /tmp/lines | go run line-filters.go\nCommand-Line Arguments # package main import \u0026#34;os\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ //os.Args 提供原始命令行参数访问功能 // 切片的第一个值是程序的路径 argsWithProg:=os.Args //os.Args[1:] 保存程序的所有参数 argsWithoutProg:=os.Args[1:] // 你可以通过自然索引获取到每个单独的参数 arg:=os.Args[3] fmt.Println(argsWithProg) fmt.Println(argsWithoutProg) fmt.Println(arg) } 本例应当先 go build，然后再运行并指定参数\nCommand-Line Flags # 命令行标志是一个指定特殊选项的常用方法。例如，在 wc -l 的 -l 就是一个命令行标志。\npackage main //flag 包支持基本的命令行标志解析 import \u0026#34;flag\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ // 基本的标志声明仅支持字符串、整数和布尔值选项 // 这里声明了一个默认值为 foo 的字符串标志 word，并带有一个简短的描述。flag.String 返回一个字符串指针。 wordPtr:=flag.String(\u0026#34;word\u0026#34;,\u0026#34;foo\u0026#34;,\u0026#34;a string\u0026#34;) // 类似声明一个整数和布尔值标志。 numbPtr:=flag.Int(\u0026#34;numb\u0026#34;,42,\u0026#34;an int\u0026#34;) boolPtr:=flag.Bool(\u0026#34;fork\u0026#34;,false,\u0026#34;a bool\u0026#34;) // 可以使用程序中已有的参数声明一个标志，声明时需要指定该参数的指针 var svar string flag.StringVar(\u0026amp;svar,\u0026#34;svar\u0026#34;,\u0026#34;bar\u0026#34;,\u0026#34;a string var\u0026#34;) // 所有标志声明完成后，调用 flag.Parse() 来执行命令行解析 flag.Parse() // 通过对指针解引用来获取选项的实际值 fmt.Println(\u0026#34;word:\u0026#34;,*wordPtr) fmt.Println(\u0026#34;numb:\u0026#34;,*numbPtr) fmt.Println(\u0026#34;fork:\u0026#34;,*boolPtr) fmt.Println(\u0026#34;svar:\u0026#34;,svar) fmt.Println(\u0026#34;tail:\u0026#34;,flag.Args()) } 测试用例：\n$ go build command-line-flags.go # 省略的标志将自动设定为默认值 $ ./command-line-flags -word=opt # 位置参数可以出现在任何标志后面 $ ./command-line-flags -word=opt a1 a2 a3 # flag 包需要的所有标志出现在位置参数之前，否则标志将会被解析为位置参数 $ ./command-line-flags -word=opt a1 a2 a3 -numb=7 # 使用 -h 或者 \u0026ndash;help标志来得到自动生成的命令行帮助文本 $ ./command-line-flags -h # 如果提供了一个没有用 flag 包指定的标志，将会得到错误信息和帮助文档 $ ./command-line-flags -wat\nEnvironment Variables # package main import \u0026#34;os\u0026#34; import \u0026#34;strings\u0026#34; import \u0026#34;fmt\u0026#34; func main(){ // 使用 os.Setenv 来设置一个键值对 // 使用 os.Getenv 来获取一个环境变量，如果不存在，返回空字符串 os.Setenv(\u0026#34;FOO\u0026#34;,\u0026#34;1\u0026#34;) fmt.Println(\u0026#34;FOO:\u0026#34;,os.Getenv(\u0026#34;FOO\u0026#34;)) fmt.Println(\u0026#34;BAR:\u0026#34;,os.Getenv(\u0026#34;BAR\u0026#34;)) fmt.Println() // 使用 os.Environ 来列出所有环境变量键值对 for _,e:=range os.Environ(){ pair:=strings.Split(e,\u0026#34;=\u0026#34;) fmt.Prinln(pair[0]) } } Spawning Processes # package main import \u0026#34;fmt\u0026#34; import \u0026#34;io/iouitl\u0026#34; import \u0026#34;os/exec\u0026#34; func main(){ //exec.Command 函数帮助我们创建一个表示这个外部进程的对象 dateCmd:=exec.Command(\u0026#34;date\u0026#34;) //Output 等待命令运行完成，并收集命令的输出 dateOut,err:=dateCmd.Output() if err!=nil{ panic(err) } fmt.Println(\u0026#34;\u0026gt; date\u0026#34;) fmt.Println(string(dateOut)) grepCmd:=exec.Command(\u0026#34;grep\u0026#34;,\u0026#34;hello\u0026#34;) // 获取输入输出管道 grepIn,_:=grepCmd.StdinPipe() grepOut,_:=grepCmd.StdoutPipe() // 运行进程，写入输入信息，读取输出结果，等待程序运行结束 grepCmd.Start() grepIn.Write([]byte(\u0026#34;hello grep\\ngoodbye grep\u0026#34;)) grepIn.Close() grepByte,_:=ioutil.ReadAll(grepOut) grepCmd.Wait() fmt.Println(\u0026#34;\u0026gt; grep hello\u0026#34;) fmt.Println(string(grepBytes)) // 通过 bash 命令的 -c 选项来执行一个字符串包含的完整命令 lsCmd:=exec.Command(\u0026#34;bash\u0026#34;,\u0026#34;-c\u0026#34;,\u0026#34;ls -a -l -h\u0026#34;) lsOut,err:=lsCmd.Output() if err!=nil{ panic(err) } fmt.Println(\u0026#34;\u0026gt; ls -a -l -h\u0026#34;) fmt.Pritnln(string(lsOut)) } Exec’ing Processes # package main import \u0026#34;syscall\u0026#34; import \u0026#34;os\u0026#34; import \u0026#34;os/exec\u0026#34; func main(){ // 通过 LookPath 得到需要执行的可执行文件的绝对路径 binary,lookErr:=exec.LookPath(\u0026#34;ls\u0026#34;) if lookErr!=nil{ panic(lookErr) } //Exec 需要的参数是切片形式的，第一个参数为执行程序名 args:=[]string{\u0026#34;ls\u0026#34;,\u0026#34;-a\u0026#34;,\u0026#34;-l\u0026#34;,\u0026#34;-h\u0026#34;} env:=os.Environ() execErr:=syscall.Exec(binary,args,env) if execErr!=nil{ panic(execErr) } } Signals # package main import \u0026#34;fmt\u0026#34; import \u0026#34;os\u0026#34; import \u0026#34;os/signal\u0026#34; import \u0026#34;syscall\u0026#34; func main(){ //Go 通过向一个通道发送 os.Signal 值来进行信号通知 sigs:=make(chan os.Signal,1) // 同时创建一个用于在程序可以结束时进行通知的通道 done:=make(chan bool,1) // 注册给定通道用于接收特定信号 signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM) //Go 协程执行一个阻塞的信号接收操作，当它得到一个值时，打印并通知程序可以退出 go func(){ sig:=\u0026lt;-sigs fmt.Println() fmt.Println(sig) done\u0026lt;-true }() fmt.Println(\u0026#34;awaiting signal\u0026#34;) \u0026lt;-done fmt.Println(\u0026#34;exiting\u0026#34;) } 运行，使用 ctrl-c 发送信号\nExit # package main import \u0026#34;fmt\u0026#34; import \u0026#34;os\u0026#34; func main(){ // 当使用 os.Exit 时，defer 将不会执行 defer fmt.Println(\u0026#34;!\u0026#34;) // 退出，并且状态为 3 os.Exit(3) } 官网：https://gobyexample.com\n","date":"June 8, 2019","externalUrl":null,"permalink":"/posts/gobyexample/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eGo by Example\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Go by Example","type":"posts"},{"content":"","date":"May 6, 2019","externalUrl":null,"permalink":"/tags/jwt/","section":"","summary":"","title":"JWT","type":"tags"},{"content":" JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。\n本文翻译自JWT官方网站对JWT是什么以及能做什么的简介。\nJWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准（RFC 7519），定义了一种简洁的，自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在，这些信息是可信的，JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。\n简洁(Compact): 可以通过URL，POST参数或者在HTTP header发送，因为数据量小，传输速度也很快\n自包含(Self-contained)：负载中包含了所有用户所需要的信息，避免了多次查询数据库\nJWT的主要应用场景 # 身份认证 在这种场景下，一旦用户完成了登陆，在接下来的每个请求中包含JWT，可以用来验证用户身份以及对路由，服务和资源的访问权限进行验证。由于它的开销非常小，可以轻松的在不同域名的系统中传递，所有目前在单点登录（SSO）中比较广泛的使用了该技术。\n信息交换 在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式，由于它的信息是经过签名的，可以确保发送者发送的信息是没有经过伪造的。\nJWT的结构 # JWT包含了使用.分隔的三部分：\nHeader 头部\nPayload 负载\nSignature 签名\n其结构看起来是这样的\nxxxxx.yyyyy.zzzzz Header # 在header中通常包含了两部分：token类型和采用的加密算法。\n{ \u0026#34;alg\u0026#34;: \u0026#34;HS256\u0026#34;, \u0026#34;typ\u0026#34;: \u0026#34;JWT\u0026#34; } 接下来对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分。\nPayload # Token的第二部分是负载，它包含了claim， Claim是一些实体（通常指的用户）的状态和额外的元数据，有三种类型的claim： reserved, public 和 private.\nReserved claims: 这些claim是JWT预先定义的，在JWT中并不会强制使用它们，而是推荐使用，常用的有 iss（签发者）, exp（过期时间戳）, sub（面向的用户）, aud（接收方）, iat（签发时间）。\nPublic claims：根据需要定义自己的字段，注意应该避免冲突\nPrivate claims：这些是自定义的字段，可以用来在双方之间交换信息\n负载使用的例子：\n{ \u0026#34;sub\u0026#34;: \u0026#34;1234567890\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;John Doe\u0026#34;, \u0026#34;admin\u0026#34;: true } 上述的负载需要经过Base64Url编码后作为JWT结构的第二部分。\nSignature # 创建签名需要使用编码后的header和payload以及一个秘钥，使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法，那么签名应该使用下列方式创建：\nHMACSHA256( base64UrlEncode(header) + \u0026#34;.\u0026#34; + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。\n完整的JWT # JWT格式的输出是以.分隔的三段Base64编码，与SAML等基于XML的标准相比，JWT在HTTP和HTML环境中更容易传递。\n下列的JWT展示了一个完整的JWT格式，它拼接了之前的Header， Payload以及秘钥签名：\n如何使用JWT？ # 在身份鉴定的实现中，传统方法是在服务端存储一个session，给客户端返回一个cookie，而使用JWT之后，当用户使用它的认证信息登陆系统之后，会返回给用户一个JWT，用户只需要本地保存该token（通常使用local storage，也可以使用cookie）即可。\n当用户希望访问一个受保护的路由或者资源的时候，通常应该在Authorization头部使用Bearer模式添加JWT，其内容看起来是下面这样：\nAuthorization: Bearer \u0026lt;token\u0026gt; 因为用户的状态在服务端的内存中是不存储的，所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息，如果合法，则允许用户的行为。由于JWT是自包含的，因此减少了需要查询数据库的需要。\nJWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务，甚至是创建一个下载流服务。因为JWT并不使用Cookie的，所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题（CORS）。\n下面的序列图展示了该过程：\n为什么要使用JWT？ # 相比XML格式，JSON更加简洁，编码之后更小，这使得JWT比SAML更加简洁，更加适合在HTML和HTTP环境中传递。\n在安全性方面，SWT只能够使用HMAC算法和共享的对称秘钥进行签名，而JWT和SAML token则可以使用X.509认证的公私秘钥对进行签名。与简单的JSON相比，XML和XML数字签名会引入复杂的安全漏洞。\n因为JSON可以直接映射为对象，在大多数编程语言中都提供了JSON解析器，而XML则没有这么自然的文档-对象映射关系，这就使得使用JWT比SAML更方便。\n原文： Introduction to JSON Web Tokens\n","date":"May 6, 2019","externalUrl":null,"permalink":"/posts/jwt/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eJWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"JWT简介","type":"posts"},{"content":" 起源 # Go语言项目的目标是什么？ # 近十年来程序开发语言没有重大进展，但环境已经发生了巨大的变化。有以下几个趋势：\n软件开发速度并未跟上计算机硬件的发展速度。 依赖管理是当今软件开发的重要组成部分，但C语言中的语言“头文件”与清除依赖关系分析和快速编译背道而驰。 对Java和C++等繁琐的开发语言反思越来越多，人们逐渐靠向动态类型的语言，如Python和JavaScript。 垃圾回收(GC)和并行计算(parallel computation)等一些基本概念并未被流行语言很好的支持。 多核的出现引发了开发者的担忧和困惑。 我们认为在此等情况下建立一门新的开发语言是必要的，这门语言应该能够妥善的支持GC、 并行计算和快速编译：\nGo能够在一台计算机上在几秒内编译一个大型的程序。 Go能提供良好的软件结构模型，使得依赖分析变得简单，同时避免像C一样产生的文件和库的开销。 Go的类型系统没有层次结构，所以不需要花费大量时间来定义类型之间的关系。尽管具有静态 类型，但开发感受比典型OO语言更轻松。 Go支持GC，从语言层面为并发执行和通信提供直接支持。 在设计上，Go提出了一种在多核机器上构建系统软件的方法。 更多有用的内容请查看 在Google内部，Go为软件开发设计提供强力支撑\nGo语言项目现在处于什么状态？ # 2009.10.10起Go成为了一个开源项目。经过几年快速的迭代，2012.3.28日发布了1.0版本。 Go 1包括语言规范，标准库和定制工具，为创建可靠的软件产品提供了稳定的基础。 随着稳定性的确立，我们使用Go语言开发Go的程序，产品和工具，而不是主动地改变语言和库本身。 事实上，Go 1的目的是提供 长期的稳定性。 向后不兼容的更改将不会对任何Go 1点发行版进行。我们希望让Go具有大众需要的特性，而不是止步不前。\n当然，Go的新版本将继续围绕Go本身，但重点将放在性能，可靠性，可移植性以及增加新功能（如改进对国际化的支持）上。\n某一天Go 2会到来，但不会很久。它会受到我们在Go 1中所学的到的经验影响。\n(2018-02-01 Go 1.9.3正式版已经发布，按照Go官方每半年一个版本的时间安排来算，Go 2即将到来。对此我既担忧又期待，虽然官方一再表示不会造成像Python2和3那样的割裂感，但谁知道呢。在网上有这样一张图，能表现出这样的忧虑。)\n为什么把Go语言的徽标定为地鼠？ # 语言的logo由 Renée French设计 ，她也设计了 Glenda——Plan 9的那只兔子。 地鼠(gopher)源自几年前她用于WFMU T恤设计中的一个。 徽标和吉祥物受Creative Commons Attribution 3.0许可证的保护。\nGo语言项目历史 # Robert Griesemer, Rob Pike 和 Ken Thompson于2007年9月21日开始在白板上勾画新语言的目标。 诸神用业余时间在几天之内确定了基本计划和任务。到2008年1月，Ken已经开始编写一个编译器——它能生成C代码作为输出。 到了年中，语言已经变成了一个全职项目，编译器所需前置工作基本完成。2008年5月，Ian Taylor开始让GCC和Go一起干活， Russ Cox于2008年底加入，帮助语言和库脱离原型化。\nGo于2009.11.10成为开源项目以来，社区贡献了大量的想法，讨论和代码。\n你们为什么要创造一门新的开发语言？ # Go源自对现有语言和编程环境的挫折感。编程变得太困难了，语言的选择是部分原因。 一个人不得不从有效的编辑、有效的执行、或简化编程中选一个——没有哪一门主流语言同时提供了这些。 开发者可以通过转向Python或JavaScript等动态类型而不是C++以提高开发速度，或在小范围内使用Java来提高安全性和效率。\nGo尝试将动态类型语言的简便性与静态类型语言的效率及安全性相结合，同时提供对现代化编程、 网络开发和多核计算方面的支持。使用Go的目的是快速——在单个计算机上快速的编译。为了达到这些目标需要解决语言上的问题——具有表达性但轻量级的类型系统；并发和垃圾回收；严格的依赖规范等等。现有语言的库或工具无法很好的解决这些问题——Go因此而诞生。\nGo语言在Google讨论了Go语言设计的背景和动机，并提供了本FAQ中许多答案的更多细节。\nGo语言在设计时参考了哪些语言？ # 主要是C语言（基本语法），来自Pascal/Modula/Oberon（声明，包）的输入方式， 以及由Tony Hoare的CSP（例如Newsqueak和Limbo（并发））启发的一些想法。 但是，这是一门全新的语言。 从各方面来说，语言都是表达了开发者的想法， 与其他语言相比至少我们所做的更有趣和高效。\nGo语言在设计时的指导原则是什么？ # 今天的编程涉及太多重复和文书工作。 正如Dick Gabriel所说的那样：“旧的程序读起来就像一个语音清晰的研究人员和一个 学习良好的机械同事之间的安静对话，而不是与编译器的辩论——谁会想到这些不必要的辩论所带来的杂音呢？“这种复杂性是值得的 - 没有人愿意回到以前的语言 - 但是有更加安静地实现吗？\n在整个设计中，我们试图减少混乱和复杂性。 没有前向声明和头文件; 一切都被明确声明一次。 初始化是表达性的，自动的，且易于使用。关键字语法清晰明了。 通过简单类型派生结构减少类似于这样的（foo.Foo *myFoo = new（foo.Foo））语句。 最根本的是，没有类型层次结构。 这些简化允许Go不牺牲表达性，而且语义强大。\n另一个重要的原则是保持正交的概念。任何类型实现了接口内的方法就等于实现了接口；结构代表数据，接口代表抽象等等。正交性使得理解事物结合后会发生什么更容易。\n使用 # Google内部在使用Go吗？ # 是的。现在Google内部有几个Go项目在进行。例如golang.org，它是在Google App Engine中运行的godoc文档服务器。。\n其他的例子包括用于大规模SQL安装的Vitess系统和Google的下载服务器，dl.google.com，它提供了Chrome二进制文件和其他大型可安装软件，比如apt-get软件包。\nGo项目可以与C/C++项目连接吗？ # 有两个Go编译器实现，gc和gccgo。Gc使用了不同的调用约定和链接器，因此只能使用相同的约定与C程序链接。 Gccgo是一个GCC front-end，可以与GCC编译的C或C++程序链接。\ncgo 提供了一个“外部函数接口”机制，允许使用Go代码安全地调用C库。如果你想用go调用C++，可以使用SWIG。\nGo语言支持Google的protocol buffers吗？ # github.com/golang/protobuf/提供了对库和插件的支持。 什么是Google的protocol buffers。protocol buffers由Google建立，是一种平台无关的，可扩展的序列化数据结构的机制——有点像XML，但更小，更快，更简单。可以定义一次数据的结构，然后使用专门生成的源代码轻松地将结构化数据写入各种数据流，或使用各种语言读取结构化数据。\n我能把Go语言官网页面转换为其他语言吗？ # 绝对可以。我们鼓励开发者把Go语言网站翻译为自己的母语。然而如果你要使用Google的商标或其他品牌标记(虽然并没有出现在golang.org官网上) ，你必须取得授权 www.google.com/permissions/guidelines.html。\n语言设计 # Go有运行时吗？ # Go有一个被广泛使用的标准库runtime。它实现垃圾收集，并发，堆栈管理以及Go语言的其他重要功能。Go的运行时间与C库（libc）类似。\n但是，有一点很重要：Go的运行时不包含虚拟机（例如Java运行时提供的虚拟机）。Go程序会首先编译为本地机器码。因此，尽管这个术语通常用于描述程序运行的虚拟环境， 但是在Go中“运行时”是提供关键语言服务的库的名称。\n关于Unicode编码 # 基于ASCII的范围扩展标识符对于我们来说非常重要。Go规定：标识符必须是由Unicode定义的字母或数字——这很容易理解和实现，但具有限制。结合字符(Combining characters)被因此排除在外。有一个商定的外部定义之前，为了确定标识符可能是什么，加上对标识符进行规范化的定义，最好不要使用混合字符。 因此，我们有一个简单的原则，可以在不破坏程序的情况下进行扩展，避免了因容许模糊标识符的规则而产生错误。\n由于导出的标识符必须以大写字母开头，因此根据定义，某些语言中由“字母”创建的标识符不能导出。现在唯一的解决办法是使用X中文这种写法，这显然无法令人满意，我们正在考虑其他选择。但Go中的大写可见性规则不太可能改变， 这是我们最喜欢的功能之一。\n为什么Go没有某某某特性？ # 每个语言都会有自己独有的特性同时遗漏掉某些\u0026quot;有意思\u0026quot;的特性。 Go的设计注重于程序开发的快捷性，编译速度，概念的正交性， 以及支持诸如并发和垃圾收集等需求。你喜欢的功能可能因为不适合而去除，因为它会影响编译速度 或语言的可读性，或使系统模型变得难以构建。\n如果因缺少某种特性而使得你非常困扰，请原谅并和我们一起探讨Go未来的特性。你可能发现我们 以其他方式提供了某某某特性的实现。\n为什么Go没有泛型？ # 泛型也许会在某个版本添加。尽快我们了解有些开发者非常想要这个特性，但并不觉得这一点非常紧迫。\n泛型非常方便，但会使类型系统和运行时变得复杂，增大开销。我们并没有找到一种能够平衡使用价值与增大 复杂性相平衡的方案——但我们一直在考虑。并且，Go内置了map和slice结构，加上interface，可以在多数情况下替代泛型，虽然你觉得可能不是那么顺手。\n关于泛型的话题讨论依然处于开放状态。你可以在这里看到 讨论过的几个Go的泛型解决方案。\n为什么Go没有异常？ # 我们相信把try-catch-finally与控制结构耦合会使代码错综复杂，它倾向于鼓励开发者标记 太多的普通错误，比如没有打开文件等。\nGo采用了不同的方法。对于普通的错误处理，Go的多值返回可以轻松地报告错误而不会重载返回值。 我们相信 规范的错误类型与Go的其他功能相结合使错误处理变得愉快，这一点与其他语言中的错误处理有很大不同。\nGo也有一些内置的功能来发出信号并从特殊条件中恢复。恢复机制仅作为函数状态的一部分被执行，在错误发生之后被移除，这足以处理灾难情况，而且不需要额外的控制结构，如果使用得当可以产生干净的错误处理代码。\n查看 Defer,Panic,Recover以了解更多。\n为什么Go没有断言？ # 注意：Go是有类型断言的。\nGo不提供断言。虽然断言很方便，但我们的经验是，开发者使用它们来偷懒，以避免考虑正确的错误处理和报告。 正确的错误处理意味着服务器在非致命错误时能够继续运行而不是崩溃。适当的错误报告意味着错误日志是清晰的，开发者不必像扒垃圾堆一样检查异常链。 当开发者看到错误而又不熟悉代码时，精准的错误日志是非常重要的。\n我们明白这是一个争论点。Go语言中有很多不同于现代实践的东西，我们觉得有时尝试一种不同的方法是非常值得的。\n为什么使用CSP的思想构建并发？ # 众所周知并发和多线程编程是很困难的。我们认为这是由于诸如pthreads之类的复杂设计导致， 部分原因在于过分强调底层细节（如互斥锁，条件变量和内存障碍）。高级别的接口可以在存在互斥体等情况下实现更简单的代码。\nHoare的交流顺序过程（CSP）是为并发提供高级语言支持的最成功模式之一。 Occam和Erlang是使用CSP的两个众所周知的语言。 Go的并发原语来源于CSP系语言的不同部分，其主要贡献是提出了channel这一强大概念。 前几种语言的经验表明，CSP模型非常适合程序语言框架。\n为什么使用goroutine替代线程threads？ # Goroutines是简化并发编程操作的一部分。这个想法是将独立执行的函数——coroutines——复用到一组线程上。 当一个coroutine(通过调用一个阻塞的系统调用)阻塞的时候，运行时会自动的将同一个操作系统线程上的其他coroutines移动到一个不同的、可运行的线程上去，这样他们就不会被阻塞。 开发者对这些是无感知的——这一点很重要。我们称之为goroutine的开销可能非常小：除了堆栈的内存外，只有几千字节(just a few kilobytes)。\n为了使堆栈变小，Go的运行时使用可调整大小的有界堆栈。新建的goroutine有几千字节，运行时会自动增长（缩小）用于存储堆栈的内存，从而允许许多协程生存在适量的内存中。CPU平均每个函数调用大约三个开销很小的指令。在同一个地址空间中创建数十万个goroutines是可行的。相对于线程，goroutines使用的系统资源会更少。\n为什么map操作没有被定义为原子的？ # 经过漫长的讨论后，我们决定map的典型使用场景不包含从多个例程安全地访问，因为map可能是已经同步的一些大型数据结构或计算的一部分。 要求所有map操作获取互斥锁将会减慢大多数程序的执行速度，并几乎没有增加安全性。 这不是一个容易的决定，尽管这意味着不受控制的map访问会使程序崩溃。\n注意：Go 1.9已经添加了sync.Map，算是弥补了这一缺憾。\n你们会接受我的语言改进方案吗？ # 很多人向我们提交了改进建议—— 邮件列表包含了这些讨论的丰富历史——但是这些建议只有很少一部分被接受了。\n尽管Go是一个开源项目，但是语言和库受到 兼容性承诺的保护， 可以防止破坏现有程序的更改。如果你的提案违反了Go 1规范，我们甚至不能接受这个想法，不管它的优点如何。 Go的未来主要版本可能与Go 1不兼容，但我们还没准备好开始讨论相关的内容。\n即使你的提议与Go 1规范相兼容，也可能不符合Go的设计目标。发布于Google的文章 软件工程服务中的语言设计解释了Go的起源和设计背后的思想。\n类型 # Go是一门面向对象的语言吗？ # 即是也不是。虽然Go有类型和方法，并且允许面向对象的编程风格，但在语言层面没有类型层次结构支持。 Go中虽然也存在的\u0026quot;接口(interface)\u0026ldquo;概念，但与其他语言相比提供了不同的使用方法。另外还允许将类型嵌入到其他类型，以提供类似于子类化的东西——但 与其他面向对象语言的子类并不完全相同。而且在Go中函数相比C++和Java使用范围更广：函数可以被定义为任何类型 ，甚至是内置类型，例如“非装箱”整数型。它们并不受限于结构(类)。\n此外，缺少类型层次结构使Go中的“对象”感觉比C++或Java等语言更轻量级。\n如何获得动态的方法调度？ # 动态分派方法的唯一方法是通过接口。对结构或其他具体类型的方法总是静态解析的。\n为什么没有类型继承？ # 面向对象的编程，至少在最著名的语言中，涉及到对类型之间的关系的讨论很多，这些关系往往可以自动导出。Go采用了不同的实现方式。\n在Go中不需要开发者提前声明两种类型是相关的。类型可以同时满足多个接口，没有传统多重继承的复杂性。接口可以是非常轻量级的—— 一个甚至是没有任何方法的接口可以也可以表达一个有用的概念。接口可以在事后添加，如果出现新的想法或测试——不需要注释原始类型。 因为在类型和接口之间没有明确的关系，所以没有类型层次来管理或讨论。\n使用这种方式，就能够构建类似于类型安全的Unix管道一样的东西。例如，fmt.Fprintf 可以将格式化打印输出到任何输出而不仅仅是一个文件，bufio包完全独立于文件I/O等等。所有这些实现都来自 代表单个方法(Write)的单个接口(io.Writer)。而这只是表面。Go的接口对整个程序的结构有深刻的影响。\n它需要逐渐去习惯，但这种隐含的类型依赖风格是Go中最有成效的东西之一。\n为什么len是个方法而不是函数？ # 原标题为 Why is len a function and not a method\n我们就这个问题进行了辩论，但是决定将len和其他内置操作实现为方法，因为这样在实践中是最好的，并且不会使关于基本类型的接口（在Go类型意义上）问题复杂化。\n为什么Go不支持函数和操作符重载？ # 在不需要进行类型匹配的情况下，函数的调度会被简化。其他语言的使用经验告诉我们，使用同一个名称但 签名不同的多种方法，偶尔有用，但在实践中可能令人困惑。只与名称匹配并要求类型一致是Go类型系统中一个重要的简化原则。\n没有运算符重载的世界很美好。\nGo为什么没有\u0026quot;implements\u0026quot;关键字？ # Go通过实现接口的方法来满足接口。这允许定义和使用接口而不必修改现有的代码。 它提供了一种结构化的类型，可以促进解耦，并提高代码重用性，能让你在产品的压迫下活的更舒坦。接口的语义是Go灵活轻便的主要原因之一。\n怎样才能保证我的类型满足了接口？ # 可以要求编译器检查T类型能够通过尝试使用T的零值或指向T的指针来实现接口I（视情况而定）：\ntype T struct{} var _ I = T{} // Verify that T implements I. var _ I = (*T)(nil) // Verify that *T implements I. 如果T(或者*T)并没有实现I接口，错误将在编译时被捕获。\n如果希望接口的用户明确声明他们实现它，则可以向接口的方法集添加一个具有描述性名称的方法。例如：\ntype Fooer interface { Foo() ImplementsFooer() } 一个类型必须实现ImplementsFooer方法成为一个Fooer，godoc产生的输出会很清楚地记录该类型实现了它。\ntype Bar struct{} func (b Bar) ImplementsFooer() {} func (b Bar) Foo() {} 大多数代码没有使用这样的约束，因为它们限制了接口思想的实用性。有时候，有必要解决类似接口之间的歧义。\n为什么类型T不能满足Equal接口？ # 考虑这个简单的接口，表示可以执行与另一个值比较的操作：\ntype Equaler interface { Equal(Equaler) bool } 还有类型T\ntype T int func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler 与一些多态类型系统中的类似情况不同，T不执行Equaler。 T.Equal的参数类型是T，而不是字面上所需的类型Equaler。\n在Go中，类型系统不支持Equal的愿意在于——这是开发者的责任，正如类型T2所说明的那样，它实现了Equaler：\ntype T2 int func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler 在Go中，任何满足Equaler的类型都可以作为参数传递给T2.Equal，并且在运行时我们必须检查参数是T2类型。有些语言会在编译时做出保证。\n这里还有个例子\ntype Opener interface { Open() Reader } func (t T3) Open() *os.File 在Go中，T3并不能满足Opener，尽管它在其他语言中可行。\n虽然在这种情况下，Go的类型系统对于开发者来说确实少了一些，但缺乏子类型使得是否满足接口的规则很容易描述——函数的名称和签名是否与接口的名称和签名完全相符。Go的这一规则很容易实现。 我们觉得好处大于使用上的其他不足。如果有一天我们会采用某种形式的多态输入，我们希望能够有一种方法来表达这些例子的思想，并且对它们进行静态检查。\n我能把[]T转换成[]interface{}吗？ # 不能直接这么做。因为它们在内存中没有相同的表现。必须逐个的将元素拷贝到目标切片中去。下面这个例子就是这样的：\nt := []int{1, 2, 3, 4} s := make([]interface{}, len(t)) for i, v := range t { s[i] = v } 如果T1和T2具有相同的基础类型，是否可以将[]T1转换为[]T2？ # 最后一行无法通过编译：\ntype T1 int type T2 int var t1 T1 var x = T2(t1) // OK var st1 []T1 var sx = ([]T2)(st1) // NOT OK 在Go中，类型与方法紧密相关，因为每个命名类型都有一个（可能是空的）方法集。一般的规则是你可以改变被转换类型的名称（因此可能改变它的方法集），但你不能改变组合类型元素的名字（和方法集合）。Go要求你明确地表达类型转换。\n为什么我的nil错误值不等于nil？ # 在底层实现中，接口被实现为两个元素，一个类型和一个值。被称为接口的动态值的值是一个任意的具体值，类型是值的类型。对于int值3，可以认为接口实现为（int，3）。\n只有在内部值和类型都未设置时（nil，nil），接口值才为nil。一个nil接口总是持有nil类型。如果我们在一个接口值中存储一个类型为*int的零指针，那么无论指针的值是什么（*int，nil），内部类型都将是*int。因此即使里面的指针是nil，这样一个接口值也是非nil的。\n这种情况可能会引起混淆，并且在一个接口值（如错误返回值）中存储一个零值时会出现这种情况：\nfunc returnsError() error { var p *MyError = nil if bad() { p = ErrBad } return p // Will always return a non-nil error. } 这个函数返回一个nil的p，所以返回值是一个错误的接口值（*MyError，nil）。\n对于返回错误的函数来说，总是在签名中使用错误类型（如上所述）而不是像MyError这样的具体类型，以帮助保证正确创建错误。例如，os.Open返回一个错误，如果不是nil时，返回具体类型*os.PathError。\n无论何时使用接口，都会出现类似于这里描述的情况。请记住，如果接口中存储了具体的值，接口将不会为nil。更多信息请查看 反射的原则。\n为什么没有类似于C的untagged unions？ # Untagged unions会危害到Go的内存安全。\n为什么Go没有variant type？ # 原标题为 Why does Go not have variant types，此段翻译存疑，特将原文附上，欢迎讨论。\nvariant 是c++里面一种数据结构，变体，这种结构可以变成任何的对象或者函数指针。\nvariant type，也称为代数类型，提供了一种方法来指定一个值可能采用一组类型中的一个。系统编程中的一个常见例子是将错误指定为网络错误，安全错误或应用程序错误，并允许调用者通过检查错误的类型来区分问题的根源。另一个例子是一个语法树，其中每个节点可以是不同的类型：声明，语句，赋值等等。\n我们曾考虑加入这一特性，但经过讨论后我们移除了它——因为它与接口产生交叠并会引发混淆——如果改组类型的元素本身是接口该怎么办？\nVariant types, also known as algebraic types, provide a way to specify that a value might take one of a set of other types, but only those types. A common example in systems programming would specify that an error is, say, a network error, a security error or an application error and allow the caller to discriminate the source of the problem by examining the type of the error. Another example is a syntax tree in which each node can be a different type: declaration, statement, assignment and so on.\nWe considered adding variant types to Go, but after discussion decided to leave them out because they overlap in confusing ways with interfaces. What would happen if the elements of a variant type were themselves interfaces?\nAlso, some of what variant types address is already covered by the language. The error example is easy to express using an interface value to hold the error and a type switch to discriminate cases. The syntax tree example is also doable, although not as elegantly.\n为什么Go没有协变类型？ # 协变类型意味着像这样一个接口\ntype Copyable interface { Copy() interface{} } 会被这个方法所满足\nfunc (v Value) Copy() Value 因为 Value 实现了空的接口。但同时，在Go中，实现接口要求方法类型必须完全匹配，所以Value不会实现Copyable——是不是很矛盾。Go从类型的实现中分离出类型的概念。如果两个方法返回不同的类型，他们不会做同样的事情。希望使用协变类型的开发者经常试图通过接口表达类型层次结构。在Go中，接口和实现之间存在清晰的分离无疑是更加自然的。\n值 # 为什么Go不提供隐式数字转换 # C中的数字类型自动转换的方便性超小于它引发的问题。什么时候表达式是无符号的？ 值有多大？它溢出吗？结果是否可传递？与执行的机器有无关联？这使编译器复杂化。“通常的算术转换”在不同的架构上不易实现。 出于可移植性的原因，我们决定让转换的代价简单明了。\n为什么map是系统内置的？ # 和内置字符串类型一样：它们是如此强大和重要的数据结构，为语法支持提供了一个优秀的实现，使编程更愉快。我们认为Go的map足够强大，可以满足绝大多数用途。如果一个特定的应用程序可以从一个自定义的实现中受益，那么可以编写一个，但是从语法上讲它不是那么方便。这似乎是一个合理的折衷办法。\n为什么map不允许把切片作为键？ # map查找需要一个等号运算符，切片无法实现。切片并没有实现是否相等的判断，因为对这种类型的相等没有很好的定义;在对比时有多个因素需要考虑——浅与深比较，指针与值比较，如何处理递归类型等等。我们可能在将来重新讨论这个问题。现在把它放在一边会让大部分问题变得简单。\n在Go 1中，与以前的版本不同，为结构和数组定义了相等性，因此这种类型可以用作map的键。尽管如此，切片仍然没有相等的定义。\n为什么map，slice和channel是引用而array是值？ # 这个话题已经有很长的历史。早期map和channel是指针，不允许声明或使用非指针实例。同时我们对数组应当如何工作争论不休。最终我们认定指针和值的严格分离使得语言更难以使用。将这些类型更改为对关联的共享数据结构的引用可解决这些问题。这种改变给语言增加了一些令人遗憾的复杂性，但对可用性影响很大：当它被引入时，Go变成了一种更高效，更舒适的语言。\n代码编写 # 库文档如何使用？ # Go有一个用Go语言编写的内置工具 godoc，它从源码中提取包文档，可以在命令行或网络上使用。 一个现成的例子就是 golang.org/pkg/。 事实上，godoc 在golang.org上实现了完整的网站。\ngodoc实例可以被配置为在其显示的程序中提供丰富的交互式静态符号分析;这里列出了 更多细节。\n为了从命令行访问文档，go工具有一个doc子命令，它提供了相同信息的文本接口。\n有没有Go编程风格指南？ # 可能会有少量规则来指导命名，布局和文件组织等事情。在 Go进阶中包含了一些代码风格的建议。 gofmt自动为你提供更直接的帮助；它取代了通常的做法和不该做的解释。存储库中的所有Go代码都已经通过gofmt运行。\nGo Code Review Comments是关于Go的非常短的文章的集合，这些文章经常被开发者遗漏。对于Go项目的代码审查人员来说，这是一个方便的参考。\n如何为Go标准库提交补丁？ # 标准库源码在仓库 src 文件夹下。如果你想提交一个重大的修改，请在着手之前提交至邮件列表讨论。\n查看 为Go项目做贡献以了解更多细节。\n为什么\u0026quot;go get\u0026quot;克隆仓库的时候使用HTTPS？ # 大部分公司通常只允许在开放标准TCP端口80（HTTP）和443（HTTPS），阻止其他端口（包括TCP端口9418（git）和TCP端口22（SSH））。当使用HTTPS代替HTTP时，git默认强制执行证书验证，提供针对中间人，窃听和篡改攻击的保护。因此，get get命令使用HTTPS来确保安全。\n如果你使用git，并且喜欢用你现有的密钥通过SSH推送更改，那很容易解决这个问题。对于GitHub，请尝试以下解决方案：\n在软件包目录中手动克隆存储库： $ cd src/github.com/username $ git clone git@github.com:username/package.git 通过添加以下两行代码至 ~/.gitconfig 强制 git push 使用SSH协议 [url \u0026#34;git@github.com:\u0026#34;] pushInsteadOf = https://github.com/ 如何使用\u0026quot;go get\u0026quot;管理包版本？ # \u0026ldquo;go get\u0026quot;没有任何明确的包版本的概念。版本控制是非常复杂的，特别是在大型代码库中，我们并不知道在多种情况下广泛适用于所有Go用户的大规模运行的方法。\u0026ldquo;go get\u0026quot;和更大的Go工具链提供的方案是，隔离具有不同导入路径的包来做区分。 例如，标准库的html/template和text/template共存，即使它们都是“包模板”。这可以作为包作者及相关使用者的参考。\n供公众使用的软件包应该尽量保持向后兼容性。 Go 1 兼容性标准导读 是一个很好的参考：不要删除导出的名称，鼓励标记的复合文字等等。如果需要不同的功能，请添加一个新名称，而不是更改旧名称。 如果有重大的修改调整，请使用新的导入路径创建新的包。\n如果你使用的是外部提供的软件包，并担心提供者可能会以意想不到的方式进行更改，最简单的解决方案是将其复制到本地——这是Google在内部采取的方法——国内的各种SDK你懂的。 将拷贝存储在新的导入路径下并标识其为本地副本。例如，你可能会将“original.com/pkg”复制到“you.com/external/original.com/pkg”。 gomvpkg工具能帮你自动执行此过程。\nGo 1.5版本为go命令添加了一个工具，通过将其“vendoring”到靠近依赖于它们的包的特定目录中，可以更容易地管理外部依赖关系。查看 govendor介绍以了解更多细节。\n我们正在进行关于 dep的工作——它是一个go的包管理工具。查看 dep FAQ以了解更多。\n指针和内存分配 # 函数参数何时通过值传递？ # 和C系中的所有语言一样，Go中的一切都是通过值传递的。也就是说，函数总是获得被传递的值的副本，就像一个赋值语句赋值给参数一样。 例如，将一个int值传递给一个函数会生成一个int的副本，传递一个指针值会创建指针的副本，但不会复制它所指向的数据(原文是：passing a pointer value makes a copy of the pointer, but not the data it points to)。\nmap和切片类似与指针：它们是指向底层map或切片数据的描述符。复制地图或切片值不会复制指向的数据。复制一个接口值会复制存储在接口值中的东西：如果接口值包含一个结构体，则复制该接口值将生成该结构体的一个副本。如果接口值包含指针，则复制接口值会生成指针的副本，但不会复制指向的数据。\n请注意，这里讨论的是关于操作的语义。实际的实现可以应用优化来避免复制，只要优化不会改变语义。\n应该在什么时候使用接口的指针？ # 几乎从不。使用指向接口值的指针只出现在罕见的，棘手的情况下。\n将一个指针传递给一个接口值的函数是一个常见的错误。编译器会报错，而这种情况很令人困惑，因为有时 需要一个指针来满足接口。尽管指向具体类型的指针可以满足接口，但是，指向接口的指针永远不能满足接口。这段跟绕口令一样，实在是不通，原文：It is however a common mistake to pass a pointer to an interface value to a function expecting an interface. The compiler will complain about this error but the situation can still be confusing, because sometimes a pointer is necessary to satisfy an interface. The insight is that although a pointer to a concrete type can satisfy an interface, with one exception a pointer to an interface can never satisfy an interface.\n考虑如下声明\nvar w io.Writer 打印函数fmt.Fprintf将第一个参数作为满足io.Wirter类型的值——只要这个值实现了规范的Writer方法。所以可以这样：\nfmt.Fprintf(w, \u0026#34;hello, world\\n\u0026#34;) 如果我们传递 w 的指针，程序无法编译：\nfmt.Fprintf(\u0026amp;w, \u0026#34;hello, world\\n\u0026#34;) // Compile-time error. 任何值，甚至指向接口的指针，都可以分配给一个空接口类型的变量（interface{}）。即便如此，如果这个值是一个指向接口的指针，那几乎肯定是一个错误。结果可能会令人困惑。原文：The one exception is that any value, even a pointer to an interface, can be assigned to a variable of empty interface type (interface{}). Even so, it\u0026rsquo;s almost certainly a mistake if the value is a pointer to an interface; the result can be confusing.\n应该把函数的接收者设置为值还是指针？ # 原标题 Should I define methods on values or pointers\nfunc (s *MyStruct) pointerMethod() { } // method on pointer func (s MyStruct) valueMethod() { } // method on value 对于不熟悉指针的开发者来说，这两个例子之间的区别可能会令人困惑，但区别其实非常简单。当在一个类型上定义一个方法时，接收者（在上面的例子中的s）的行为就像它是方法的参数一样。无论是将接收者定义为值还是指针，都是同一个问题，那么函数参数是一个值还是一个指针。有下面几个因素需要考虑。\n首先，最重要的是，该函数是否需要修改接收者的属性。如果需要的话，接收者必须是个指针。(切片和map 有类似与指针的表现，所以他们的使用情况有点微妙。但是如果需要改变一个方法中作为接收者的切片的长度，那么仍然需要是个指针。) 在上面的例子里，如果 pointerMethod 修改了 s 的属性，调用者在调用该函数后会看到这些改变，但是 valueMethod 被 s 的副本调用(值传递)，所以调用后 s 的属性不会发生改变。\n顺便提一句，指针接收器和Java的情况是一样的，尽管在Java中指针是隐藏在底层。\n其次是效率的考虑。如果接收器很大，例如一个大的结构，使用指针接收器会开销会小得多。\n接下来是一致性。如果某些类型的方法必须有指针接收器，其余的也应该如此。(此处存疑，原文有点前后矛盾，另外实际操作中还得看情况：If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used)。\n对于基本类型，切片和小体积结构体等类型，使用值接收开销是很小的，所以除非方法的语义需要指针，值接收是高效和清晰的。\nnew和make的区别是什么？ # 一句话概括，new 分配内存，make 初始化切片，map和channel类型。\n详见[Go进阶 new 关键字](effective#Allocation with new)和[Go进阶 make 关键字](effective#Allocation with make)。\n一个int类型在64位机器上是多少位？ # 原标题 What is the size of an int on a 64 bit machine\nint和uint的大小特定于实现的平台，但在给定平台上彼此相同。依赖于特定大小的值的代码应该使用明确大小的类型，如int64。在Go 1.1之前，64位Go编译器（包括gc和gccgo）都使用了32位的int表示。从Go 1.1开始，他们使用64位表示。\n另一方面，浮点标量和complex类型的大小始终不变（因为没有浮点或complex 的基本类型），开发者在使用浮点数时应该注意精度。用于（无类型）浮点常量的默认类型是float64。因此，foo:= 3.0声明了一个float64类型的变量foo。 对于由（无类型）常量初始化的float32变量，必须在变量声明中明确指定变量类型：\nvar foo float32 = 3.0 或者，常量必须被赋予一个具有转换的类型，如foo:= float32（3.0）。\n如果判断一个变量分配在堆上还是栈上？ # 从某种角度上来说，你不需要考虑这个问题。在Go中只要变量还被引用着，它就会存在。实现存储位置与语言的语义无关。\n但了解存储位置确实对编写高效的程序有益。在可能的情况下，Go编译器将为该函数的堆(function\u0026rsquo;s stack frame)中的函数分配本地变量。但是，如果编译器不能在函数返回后证明变量未被引用，那么编译器必须在垃圾回收堆上分配变量，以避免错过指针错误。另外，如果局部变量非常大，将其存储在堆而不是栈可能更有意义。(原文：The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function\u0026rsquo;s stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.)。\n在当前的编译器中，如果一个变量的地址被占用，那么这个变量就是在堆上分配的候选对象。然而，通过基本的逃逸分析我们会认识到，这些变量不会超过函数的返回，并且可以驻留在堆上(原文：In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.)。\n为什么Go进程使用这么多的虚拟内存？ # Go内存分配器保留大量虚拟内存作为分配备用。该虚拟内存对于特定的Go进程是本地的;预留不会剥夺其他内存进程。原文：The Go memory allocator reserves a large region of virtual memory as an arena for allocations. This virtual memory is local to the specific Go process; the reservation does not deprive other processes of memory.\n要查找分配给Go进程的实际内存量，请使用Unix top命令并查阅RES（Linux）或RSIZE（Mac OS X）列。原文：To find the amount of actual memory allocated to a Go process, use the Unix top command and consult the RES (Linux) or RSIZE (Mac OS X) columns.\n并发 # 什么操作是原子的？互斥体是吗？ # 我们还没有完全定义这一切，但有关原子性的一些细节可以在 Go Memory Model规范中找到。\n在sync包中实现了互斥体。但是我们希望Go编程风格会鼓励一次只有一个goroutine负责某个特定的数据。\n不要通过共享内存进行通信。相反，通过通信实现共享内存。\n请参阅[Share Memory By Communicating](Share Memory By Communicating)和 相关文章\n为什么我的项目没有使用到多核CPU？ # GOMAXPROCS shell环境变量控制着可用于执行goroutine的CPU数量。在Go的早期版本中，默认值为1，但从Go 1.5开始，默认值是系统可用的内核数量。 因此1.5之后编译的程序会默认多个goroutine的并行执行。如果想要更改这一行为，请设置环境变量或使用 标准库的runtime.GOMAXPROCS函数以使用不同的CPU数。\n为什么设置GOMAXPROCS\u0026gt;1有时反而让程序变慢了？ # 这取决于你的项目。使用更多的goroutine并不能加速程序的表现。当问题的本质是并行时，扩大并发数也许是无效的。\n实际上，在操作系统中使用多个线程时，花费更多时间在channel上进行通信可能会降低程序性能。 这是因为在线程之间发送数据涉及切换上下文，成本很高，因此增加GOMAXPROCS更有可能使速度变慢。\nGo的goroutine调度程序并未达到我们预期的要求，尽管在最近的发行版中已经有所改进。 在接下来的版本中我们将持续优化。目前，如果存在性能问题，则应该根据每个应用程序来设置最佳的GOMAXPROCS数量。\n想了解更多细节，请查看 Concurrency is not Parallelism.\n方法和函数 # 为什么T和*T有不同的方法集？ # 在 Go开发规范中\n任何其他命名类型T的方法集合由所有具有接收器类型T的方法组成。 相应的指针类型*T的方法集是所有具有接收者*T或T（也包含T的方法集合）的方法的集合。\n如果一个接口值包含一个指针*T，则方法调用可以通过解引用指针来获得一个值，但是如果一个接口值包含一个值T，那么方法调用就没有用于获取指针的有用方法。\n即使在编译器可以将一个值的地址传递给方法的情况下，如果该方法修改了值，这些更改也将丢失。 例如，如果bytes.Buffer的Write方法使用了一个值接收器而不是一个指针，那么：\nvar buf bytes.Buffer io.Copy(buf, os.Stdin) 会将标准输入复制到buf的副本中，而不是复制到buf本身。\n如果闭包并发运行会发生什么？ # 使用并发的闭包时可能会出现一些混淆。考虑下面的程序：\nfunc main() { done := make(chan bool) values := []string{\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;, \u0026#34;c\u0026#34;} for _, v := range values { go func() { fmt.Println(v) done \u0026lt;- true }() } // wait for all goroutines to complete before exiting for _ = range values { \u0026lt;-done } } 你可能期望看到输出 a，b，c。但你很可能看到输出 c，c，c。这是因为循环的每个迭代使用变量v的同一个实例，所以每个闭包共享单个变量。 当闭包运行时，它会在执行fmt.Println时打印v的值，但是当goroutine启动v可能已被修改。 为了在这些问题发生之前检测到这些问题，请运行 go vet。\n要将v的当前值绑定到每个闭包，必须修改内部循环以便在每次迭代中创建一个新变量。一种方法是将变量作为参数传递给闭包：\nfor _, v := range values { go func(u string) { fmt.Println(u) done \u0026lt;- true }(v) } 可以看到v的值以参数的形式传递给匿名函数。这个值可以在函数里作为变量u访问。\n有更简单的办法，虽然看起来有点奇怪，但在Go中是允许的：\nfor _, v := range values { v := v // create a new \u0026#39;v\u0026#39;. go func() { fmt.Println(v) done \u0026lt;- true }() } 流程控制 # Go有三元运算符?:吗？ # 没有。\n包和测试 # 如何创建一个多文件包？ # 将包的所有源文件放在一个目录中。源文件可以随意引用来自不同文件的项目;不需要前向声明或头文件。除了被分割成多个文件之外，软件包将像单个文件包一样进行编译和测试。\n如何写联合测试？ # 在与源码相同的目录中创建一个以_test.go结尾的新文件。在文件里导入testing包，然后这样写\nfunc TestFoo(t *testing.T) { ... } 打开命令行，在该文件夹目录下运行go test。这个命令会找到测试函数，编译并执行它。\n更多内容，查看 如何编写Go代码\nWhere is my favorite helper function for testing？ # Go的标准库testing使得编写测试很容易，但缺少其他语言测试框架（如断言）提供的功能。 在上面的章节我们说明了为什么没有断言的存在。正确的错误处理方式意味着调试失败的人可以得到完整的错误信息快速定位，而这一点会在时间和精力上给予你回报。\n值得一提的是，测试框架倾向于发展自己的迷你语言，具有自己的条件和控制以及打印机制， 但是Go已经具备了所有这些功能，为什么还要用它们？\n如果编写好的测试所需的额外代码量看起来十分让人难以忍受，那么如果表驱动测试可能会好点， 迭代在数据结构中定义的输入和输出列表（Go对此有很好的支持）。标准库有很多这样的例子，参见 fmt包测试源码\n为什么标准库没有X？ # 标准库的目的是支持运行时，连接到操作系统，并提供Go程序需要的关键功能，例如格式化的I/O和网络。 它还包含对web编程非常重要的内容，包括加密和对HTTP，JSON和XML等标准的支持。\n没有明确的标准定义了标准库里必须有什么，虽然我们在不断添加新的标准，新的内容。\n标准库新增的功能很少，包含的条目经过严格审查。包含在标准库中的代码需要 承担较大的持续维护成本（通常由原作者以外的人承担），受 Go 1兼容性承诺以及 Go版本发布计划的约束，防止因错误产生的修复分支被提供给开发者。\n大多数新代码应该在标准库之外，并且可以通过go工具的get get命令来访问。这样的代码可以有自己的维护者，发布周期和兼容性保证。 用户可以在 godoc.org上找到软件包并阅读文档。\n尽管标准库中有些与标准不符的东西，比如log/syslog，但根据Go 1兼容性承诺，我们继续保有库中的所有内容。\n实现 # 编译器是如何构建的？ # Gccgo有一个用C++编写的front end，递归下降解析器连接到标准的GCC back end。 Gc是使用递归下降解析器编写的，使用一个自定义的加载器，用Go编写，但是基于Plan 9加载器，生成ELF/Mach-O/PE二进制文件。\n我们曾考虑使用LLVM作为gc，但是我们觉得这对于实现我们的性能目标来说太大而且太慢了。\n旧的gc是用C编写的，但是引导困难——你需要一个Go编译器来设置Go环境。但从Go 1.5开始，编译器使用Go编写， 它使用自动翻译工具将C转到Go。因此编译器现在是自举(self-hosting)的，这意味着我们必须面对引导问题。 解决方案是要有一个正常工作的Go安装器，就像通常有一个正常工作的C安装器一样。有兴趣可以 看看这里。\nGo是实现Go编译器的较好的选择。尽管gc并没有使用它们(不确定)，但在go软件包中有一个本地的词法分析器和解析器( 源码在这里)，还有一个类型检查器( 标准库的type包)。\n运行时如何支持编译？ # 还是因为引导问题，运行时代码最初主要是用C编写的(有一点点的汇编)，但是现在已经被翻译成了Go（还是有一点汇编）。 gccgo 的运行时支持使用 glibc。 gccgo 的编译器使用称为分段堆栈（segmented stacks）的技术来实现goroutine，最近对gold链接器进行了修改支持。\n为什么我的代码很少生成的可执行程序却这么大？ # gc工具链中的链接器默认创建静态链接的二进制文件。因此，所有Go二进制文件都包含Go运行时，以及支持动态类型检查，反射以及恐慌时间堆栈跟踪所需的运行时类型信息。\n在Linux上使用gcc静态编译和链接的一个简单的C“hello，world”程序大约是750 kB，包括一个printf的实现。 使用fmt.Printf的等效Go程序大约为1.5 MB，但是包含更强大的运行时支持和类型信息。\n我能禁掉未使用变量及导包的编译错误吗？ # 存在一个未使用的变量的可能表明一个错误，而未使用的包导入只会减慢编译速度，随着时间的推移，烂代码原来越多，这种影响可能会变得很大。 鉴于这些原因，Go拒绝使用未使用的变量或包导入来编译程序，我们在语言层面要求付出短期的便利，以实现长期的编译速度和程序清晰度。\n尽管如此，在开发代码时，通常会暂时存在这些情况，在程序编译之前必须将它们编辑出来是很麻烦的。\n一些人已经要求编译器选项关闭这些检查，或至少减少这些警告。但是，这样的选项还没有被添加，因为编译器选项不应该影响语言的语义——Go编译器不会报告警告，只会报告阻止编译的错误。\nGo编译器不会报告警告有两个原因。首先，我们不想改代码，其次，让编译器生成警告，可能会导致噪音从而掩盖真正的错误。\n解决这个问题很容易。使用空白标识符就能让在开发过程中不用的东西持续存在。\nimport \u0026#34;unused\u0026#34; // This declaration marks the import as used by referencing an // item from the package. var _ = unused.Item // TODO: Delete before committing! func main() { debugData := debug.Profile() _ = debugData // Used only during debugging. .... } 现在，大多数Go开发者使用一个命令行工具 goimports，它会自动重写一个Go源文件以获得正确的导入，并消除未使用的导入问题。 这个程序很容易连接到大多数编辑器，在源文件被写入时自动运行。\n性能 # 为什么Go在benchmark上性能不佳？ # 原文将benchmark比赛称为Benchmark games。\nGo的设计目标之一是为可比较的程序提供C的性能，但在一些基准测试中表现不佳，其中包括 golang.org/x/exp/shootout中的一些。 例如，pidigits.go 采用了多精度数学包，而C版本与Go不同，使用GMP（它是用优化的汇编器编写的）。基于正则表达式的基准测试（例如regex-dna.go）将Go的regexp软件包与PCRE等成熟，高度优化的正则表达式库进行了比较。\n如果衡量可比较的C和Go程序（reverse-complement.go就是一个例子），你会看到这两种语言在原始性能方面比这个游戏所显示的更为接近。\n不过还有改进的空间。编译器效率还可以更高，许多库需要调整性能，gc还不够快。\n无论如何，Go非常有竞争力。随着语言和工具的发展，许多关键点的性能有了显着的提高。查看 剖析Go程序以了解更多的内容\n与C的不同 # 为什么语法上与C差别这么大？ # 除了声明语法外，差异并不是非常大，这是出于两个诉求。首先， 语法应该感觉轻松，没有太多的强制性关键字，重复或难以理解。其次，这个语言被设计为易于分析， 并且可以在没有符号表的情况下被解析。这使得构建诸如调试器，依赖关系分析器，自动文档提取器， IDE插件等工具变得更加容易。C和它的后代在这方面实现起来非常困难。\n为什么将类型声明后置？ # 在C中，这个概念是变量被声明为表示它的类型的表达式， 这是一个好主意，但是类型和表达式语法不能很好地混合， 结果可能会令人困惑。Go主要分离表达式和类型语法，并进行了简化。 在C，声明\nint* a, b; 声明了 a 是个指针而 b 不是，在Go中\nvar a, b *int 声明了两者都是指针。这看起来更清晰。同样，:= 短声明表达式认为，完整的变量声明应该按照如下顺序\nvar a uint64 = 1 和这种写法是一样的\na := uint64(1) 语法分析也被简化为具有不仅仅是表达式语法的类型的独特语法; func和chan等关键字采用这样的方法保持含义清晰。\n查看 Go声明语法了解更多。\n为什么没有指针运算？ # 为了安全。如果没有指针运算，可能会创建一个永远不会导致错误地成功的非法地址。编译器和硬件技术已经发展到使用数组索引的循环与使用指针运算的循环一样有效。另外，抛弃指针运算可以简化垃圾收集器的实现。\n为什么只有++和\u0026ndash;后缀没有前缀？为什么它们是语句而不是表达式？ # 如果没有指针运算，前缀和后缀增量运算符的便利性会下降。通过将它们从表达式层次中全部删除，简化了表达式语法，并且围绕++和\u0026ndash;（考虑f（i ++）和p [i] = q [++i]）的评估顺序的复杂性也被消除。至于后缀与前缀， 后缀版本更传统。\n为什么没有分号？为什么不能把首个大括号放在下一行？ # Go使用括号括起括号，这是一种熟悉C系列中任何语言的编程人员都熟悉的语法。 分号是给解析器看的，而不是人，我们想尽可能地消除分号。为了实现这个目标，Go从BCPL中借鉴了一个技巧： 分隔语句的分号存在于形式语法中，但是在任何可能是语句结束的行末尾，词法分析器都会自动注入分号，而不是向前。 这在实践中效果很好，但是写起来就必须符合一定的规则。 例如，函数的左括号不能单独出现在一行上。\n有些人认为，词法分析器应该向前检查，以便大括号能够存在于下一行。 我们拒绝了这种思路。由于Go代码是由gofmt自动格式化的，因此必须选择一定的风格。 这种风格可能与你在C或Java中使用的风格不同。 最最重要的是，统一代码风格的优点大大超过了任何可察觉的缺点。 还要注意，Go的风格意味着Go的一个交互式实现可以一次使用一行标准语法而不需要制定特殊的规则。\n为什么采用GC？ # 内存管理是系统程序中最大的记录消耗来源之一。我们认为消除开发者的心智负担是至关重要的， 过去几年里垃圾收集技术的进步使我们有信心能够以足够低的开销实现它，并且没有明显的延迟。\n还有一点是并发和多线程编程的难点大部分是内存管理; 随着对象在线程之间传递，保证它们被安全地释放变得麻烦。自动垃圾收集使并发代码更容易编写。 当然，在并发环境中实现垃圾收集本身就是一个挑战，但是一次解决而不是在每个程序中都造轮子能帮助所有人。\n最后，抛开并发，垃圾收集使得接口更简单，因为它们不需要指定如何管理内存。\n目前的做法是实现一个并发的标记和扫描收集器。在 设计文档中 记录的最近的改进已经引入了有限的暂停时间并改善了并行性。未来版本可能会尝试新的方法。\n关于性能的话题，请记住，Go让开发者对内存布局和分配有相当大的控制，远远超过垃圾收集语言的典型特征。 一个细心的开发者可以很好地使用语言来减少垃圾收集开销；请参阅关于对 Go程序进行性能分析，其中包括Go的性能分析工具演示。\n","date":"December 16, 2018","externalUrl":null,"permalink":"/posts/go4/","section":"","summary":"起源 # Go语言项目的目标是什么？ # 近十年来程序开发语言没有重大进展，但环境已经发生了巨大的变化。有以下几个趋势：","title":"Go常见问题及解答","type":"posts"},{"content":" 介绍 # 这篇文档介绍了如何进行简单的Go程序开发，如何获取和构建库，以及安装第三方库和命令行工具的标准方法。\nGo要求你以特定的方式组织代码。请仔细阅读本文档，它解释了使用Go安装启动和运行程序的最简便方法。\n你也可以查看 位于youtube上的视频教学(请自备梯子)。\n组织代码 # 预览 # Go开发者通常将所有的Go代码保存在一个工作区 workspace 中。 工作空间包含多种版本控制存储库（例如，由Git）。 每个存储库都包含一个或多个包 package。 每个包 package 由一个或多个Go源文件组成。 包的目录路径决定了它的导入路径。 请注意，这与开发语言不同，在其他语言中，每个项目都有独立的工作空间，且工作空间与版本控制存储库紧密相关。\n工作空间 Workspaces # 工作空间(本质是一个文件夹)是一个目录层次结构，其根目录有三个目录。\nsrc 目录包含了 .go 源文件 pkg 目录包含了编译后生成的文件 bin 目录包含了生成的可执行文件 src中的源码编译后会安装到pkg和bin目录下。\nsrc目录通常包含多个版本控制存储库（如Git或Mercurial），用于跟踪一个或多个源包的开发\n为了更直观的理解，这里有一个例子：\nbin/ hello # command executable outyet # command executable pkg/ linux_amd64/ github.com/golang/example/ stringutil.a # package object src/ github.com/golang/example/ .git/ # Git repository metadata hello/ hello.go # command source outyet/ main.go # command source main_test.go # test source stringutil/ reverse.go # package source reverse_test.go # test source golang.org/x/image/ .git/ # Git repository metadata bmp/ reader.go # package source writer.go # package source ... (省略了更多的仓库和包) ... 这个树状结构展示了这个工作空间workspace有两个仓库(example 和 image)。example仓库包含hello包、outyet包，和一个stringutil库(你可以在pkg文件夹里看到也有stringutil)。 img仓库包含了bmp包和其他省略的文件。\n原文是：The tree above shows a workspace containing two repositories (example and image). The example repository contains two commands (hello and outyet) and one library (stringutil). The image repository contains the bmp package and several others.\n对于command source和package source的区别，个人认为是outyet包内的main.go中main函数调用了hello.go，编译后生成二进制可执行文件，所以在bin中可以看到他们。 如有错误，还望指正。\n典型的工作空间包含许多包含许多包和命令的源代码库。大多数Go程序员将所有的Go源代码和依赖关系保存在一个工作空间中。\nCommands 和 libraries 是由不同的源代码包构建的。我们稍后会讨论这个区别。\nGOPATH 环境变量 # GOPATH 环境变量指明了你的工作空间 workspace。它默认为一个名为go的主目录，所以各操作系统就会找下面的这些目录：Unix的$HOME/go，Plan 9的 $home/go和%USERPROFILE%\\go，Windows的C:\\Users\\YourName\\go。\n如果你想换一个目录，必须 把GOPATH设置为对应的目录(另一个常见设置是GOPATH=$HOME)。注意GOPATH绝对不能与Go安装文件夹是同一个路径\n在命令行输入 go env GOPATH 会显示出当前有效的GOPATH，如果环境变量未设置，它将打印默认位置。\n为方便起见，将工作空间的bin子目录添加到PATH中：\n$ export PATH=$PATH:$(go env GOPATH)/bin 为了简洁起见，本文剩余部分的脚本使用$ GOPATH而不是$（go env GOPATH）。如果你还没有设置GOPATH，为了使脚本正常运行，你可以用这些命令替换$ HOME / go，或者运行：\n$ export GOPATH=$(go env GOPATH) 想了解更多关于GOPATH环境变量参数，可以查阅 go命令行文档\n想自行设置工作空间workspace的所在位置， 请查阅安装文档\n包导入路径 # 包路径是一个字符串，是一个包的唯一标识。包的导入路径对应于其在工作空间 workspace 内或远程存储库中的位置。\n标准库中的包被赋予了很短的导入路径名称，如 fmt 和 net/http。对于自己的软件包，你必须选择一个基本路径， 这个路径不能在将来添加到标准库或其他外部库时发生冲突(即必须是唯一的)。\n如果将代码保存在某个源代码库中，则应该使用该代码源的根作为基本路径。例如，如果在github.com/user上有GitHub帐户，那应该是你的基本路径。\n请注意，在构建之前，不要将代码发布到远程存储库，在实践中，你可以选择任意路径名称，只要它是标准库和更大的Go生态系统所独有的。\n我们将使用github.com/user作为我们的基本路径。在工作区内创建一个目录，在其中保留源代码：\n$ mkdir -p $GOPATH/src/github.com/user 你的第一个程序 # 要编译和运行一个简单的程序，首先选择一个包路径（我们将使用github.com/user/hello）并在工作空间workspace内创建一个对应的包目录：\n$ mkdir $GOPATH/src/github.com/user/hello 接着在该文件夹下建立名为 hello.go 的文件，包含如下代码:\npackage main import \u0026#34;fmt\u0026#34; func main() { fmt.Printf(\u0026#34;Hello, world.\\n\u0026#34;) } 现在你可以编译并安装它:\n$ go install github.com/user/hello 你可以在系统的任何位置运行这行命令。go编译工具通过在GOPATH指定的工作空间内查找github.com/user/hello包来查找源代码。\n如果从软件包目录运行go install，也可以省略软件包路径：\n$ cd $GOPATH/src/github.com/user/hello $ go install 这条命令编译 hello 文件并产生一个可执行的二进制文件。然后，它将这个二进制文件安装到工作空间的bin目录（在Windows下，是hello.exe文件）。 在当前例子中，是$ GOPATH/bin/hello，即$ HOME/go/bin/hello。\n只有当错误发生时，编译工具才会打印输出，所以如果没有任何输出，说明已经成功执行了。\n你可以通过在命令行键入完整路径来运行该程序：\n$ $GOPATH/bin/hello Hello, world. 如果你已经将$ GOPATH/bin添加到PATH中，只需输入二进制名称即可：\n$ hello Hello, world. 如果你正在使用Git，现在是初始化仓库，添加文件以及提交第一个更改的好时机。\n注意这是可选的：你不一定非得使用版本控制来编写Go代码。\n$ cd $GOPATH/src/github.com/user/hello $ git init Initialized empty Git repository in /home/user/work/src/github.com/user/hello/.git/ $ git add hello.go $ git commit -m \u0026#34;initial commit\u0026#34; [master (root-commit) 0b4507d] initial commit 1 file changed, 1 insertion(+) create mode 100644 hello.go 把代码推到远程仓库上就当作给你留下的小练习吧。\n你的第一个依赖库 # 我们来编写一个库，并在hello程序中使用它。\n第一步是选择一个包路径（我们将使用github.com/user/stringutil）并创建包目录：\n$ mkdir $GOPATH/src/github.com/user/stringutil 接着在stringutil文件夹内创建名为 reverse.go 的文件，包含如下代码：\n// Package stringutil contains utility functions for working with strings. package stringutil // Reverse returns its argument string reversed rune-wise left to right. func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i \u0026lt; len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) } 现在使用go build命令来试着编译一下：\n$ go build github.com/user/stringutil 如果正在当前目录下，你也可以这样：\n$ go build 这将不会产生输出文件(注意，因为代码中并没有main)。为此，你必须使用go install，它将package对象放置在工作区的pkg目录中。\n在确认了stringutil包的构建之后，修改原来的hello.go（在$ GOPATH/src/github.com/user/hello中）\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;github.com/user/stringutil\u0026#34; ) func main() { fmt.Printf(stringutil.Reverse(\u0026#34;!oG ,olleH\u0026#34;)) } 每当使用go命令安装一个包或二进制文件，它也会安装其所具有的任何依赖关系。所以当你安装hello程序，stringutil包也会被自动安装。\n$ go install github.com/user/hello 运行新版本的程序，你应该看到一个新的，反转过来的消息：\n$ hello Hello, Go! 现在你的工作空间应该是这样的目录结构：\nbin/ hello # command executable pkg/ linux_amd64/ # this will reflect your OS and architecture github.com/user/ stringutil.a # package object src/ github.com/user/ hello/ hello.go # command source stringutil/ reverse.go # package source 注意，go install将stringutil.a对象放置在映射其源目录的pkg/linux_amd64内的目录中。这样以后的go 命令可以找到包对象，避免了不必要的重新编译。linux_amd64部分是为了帮助交叉编译，并将反映你的系统的操作系统和体系结构。\ngo命令操作可执行文件是静态链接的;运行go程序时包对象不是必须存在\n包名称 # Go源文件中的第一条语句必须是：\npackage name 其中name是导入包的默认名称。包中的所有文件必须使用相同的名称。\nGo的惯例是包名称是导入路径的最后一个元素：导入为“crypto/rot13”的包应该被命名为rot13。\n入口文件必须总是使用package main。\n没有必要要求包名称在链接到一个二进制文件的所有软件包中是唯一的，只要导入路径（它们的完整文件名）是唯一的。\n查看 Go进阶以了解更多。\n测试 # Go有一个由go test命令和测试包组成的轻量级测试框架。\n可以通过创建名称以_test.go结尾的文件来编写一个测试，该文件包含名为TestXXX并带有签名func（t * testing.T）的函数。测试框架运行每个这样的函数;如果函数调用失败函数，如t.Error或t.Fail，则认为测试失败。\n通过创建包含以下Go代码的$ GOPATH/src/github.com/user/stringutil/reverse_test.go文件，将测试添加到stringutil包中。\npackage stringutil import \u0026#34;testing\u0026#34; func TestReverse(t *testing.T) { cases := []struct { in, want string }{ {\u0026#34;Hello, world\u0026#34;, \u0026#34;dlrow ,olleH\u0026#34;}, {\u0026#34;Hello, 世界\u0026#34;, \u0026#34;界世 ,olleH\u0026#34;}, {\u0026#34;\u0026#34;, \u0026#34;\u0026#34;}, } for _, c := range cases { got := Reverse(c.in) if got != c.want { t.Errorf(\u0026#34;Reverse(%q) == %q, want %q\u0026#34;, c.in, got, c.want) } } } 使用命令执行测试：\n$ go test github.com/user/stringutil ok github.com/user/stringutil 0.165s 与之前一样，如果你正在当前目录下执行此命令，则可以省略软件包路径：\n$ go test ok github.com/user/stringutil 0.165s 查看 测试帮助文档以了解更多。\n远程包 # 包的路径可以描述如何使用Git或Mercurial等版本控制系统获取源代码。go会读取包的路径自动从远程存储库获取软件包。例如，上面的示例也保存在GitHub github.com/golang/example托管的Git存储库中。你可以使用go get命令获取，构建并安装它：\n$ go get github.com/golang/example/hello $ $GOPATH/bin/hello Hello, Go examples! 如果指定的包不在工作空间内，go get将放置在由GOPATH指定的第一个工作区内。（如果包已经存在，则跳过远程抓取，并执行与go install相同的操作。）\n执行go get命令后，工作空间目录树现在应该如下所示：\nbin/ hello # command executable pkg/ linux_amd64/ github.com/golang/example/ stringutil.a # package object github.com/user/ stringutil.a # package object src/ github.com/golang/example/ .git/ # Git repository metadata hello/ hello.go # command source stringutil/ reverse.go # package source reverse_test.go # test source github.com/user/ hello/ hello.go # command source stringutil/ reverse.go # package source reverse_test.go # test source 托管在GitHub上的hello文件依赖于同一个仓库中的stringutil包。 hello.go文件中的导入使用相同的导入路径约定，所以go get命令也能够定位和安装依赖包。\nimport \u0026#34;github.com/golang/example/stringutil\u0026#34; 这个约定是让你的Go软件包供其他人使用的最简单的方法。你可以通过 Go Wiki 和 godoc.org 查看可以引用的Go项目列表。\n你可以查看 go命令行文档以了解更多\n","date":"December 16, 2018","externalUrl":null,"permalink":"/posts/go3/","section":"","summary":"介绍 # 这篇文档介绍了如何进行简单的Go程序开发，如何获取和构建库，以及安装第三方库和命令行工具的标准方法。","title":"Go代码概览","type":"posts"},{"content":" 文档介绍 # Go是一门年轻的语言。虽然它借鉴了现有语言的思想，但自身独有的特性使得与其他语系相近的语言在代码表现上有所不同。 将C++或Java程序直接转换为Go可能会产生令人不太满意的结果——这是思考方式上的不同。 从Go的角度思考问题可能产生一个运行正常但与C++、Java相比完全不同的程序。换句话说，为了写好Go程序， 重点在于理解Go的特性及惯用法，例如命名、格式化、项目结构搭建等，这样你的程序可读性就更强。\n这篇文档着重介绍了编写Go代码的要点。在此之前，强烈建议你了解并熟悉 Go学习指南， 如何编写Go代码和 语言规范\n例子 # Go 标准库源码 不仅作为核心库，还作为如何使用Go语言的例子。 而且，许多包都含有可直接运行的可执行示例。如果你对如何解决某个问题可能存在疑问，标准库的文档，源码和示例也许可以给你提供灵感。\n格式化代码 # 格式问题是最争议性但最不重要的。人们可以适应不同的代码格式，但是如果他们不需要这么做，如果每个人都遵守相同的风格， 那么浪费在这个话题的时间就会减少。\n为此，在Go中我们采取了与其他语言不同的方法——让机器来处理格式问题。gofmt 可以像fmt一样在包级而不是源文件级上运行、读取Go程序， 并以标准的缩进和垂直对齐样式改变源文件，并在必要时重新格式化注释。如果你想知道如何处理一些新的代码格式，运行gofmt； 如果答案看起来不对，重新安排你的程序（或者提交一个关于gofmt的错误），格式的问题别去管它——交给机器就好。\n作为一个例子，没有必要花时间在结构的字段上排列格式。 Gofmt会为你做到这一点。\ntype T struct { name string // name of the object value int // its value } gofmt 会自动帮你排列成这样\ntype T struct { name string // name of the object value int // its value } 标准包中的所有Go代码已经使用gofmt格式化。\n有些格式化的细节你或许感兴趣：\n缩进\ngofmt默认使用制表符缩进。你也可以自己选择使用空格。 行长度\nGo没有行长度的限制。 括号\nGo比C和Java所需要的括号更少：if,for,switch这样的控制结构在语法上不需要括号。另外，与其他语言不同，运算符优先级层次更短，更清晰(间距表明了优先级)。 x\u0026laquo;8 + y\u0026laquo;16\n注释 # Go提供了C风格的 /**/ 块级注释和C++风格的 // 行级注释。块注释大多作为包注释出现，同时也在表达式中或者禁用大量代码时用到。\ngodoc 命令——可以调起一个服务器——能够处理Go源文件中注释以形成文档。写在代码段首行且没有插入换行符的注释，会被 godoc 视作该代码段的解释文本。 注释的内容和风格决定了 godoc 生成的文档质量。\n每个包都应该有个包注释，以块级注释的形式加在 package 代码之上。对于多文件组成的包，包注释只需要存在于一个文件中，任何一个都可以。\n包注释应该提供包的整体介绍及相关信息，它将首先出现在 godoc 生成页面的顶端。下面是个例子：\n/* Package regexp implements a simple library for regular expressions. The syntax of the regular expressions accepted is: regexp: concatenation { \u0026#39;|\u0026#39; concatenation } concatenation: { closure } closure: term \\[ \u0026#39;*\u0026#39; | \u0026#39;+\u0026#39; | \u0026#39;?\u0026#39; \\] term: \u0026#39;^\u0026#39; \u0026#39;$\u0026#39; \u0026#39;.\u0026#39; character \u0026#39;\\[\u0026#39; \\[ \u0026#39;^\u0026#39; \\] character-ranges \u0026#39;\\]\u0026#39; \u0026#39;(\u0026#39; regexp \u0026#39;)\u0026#39; */ package regexp 如果包内容比较简单，包注释也可以写成这样的格式：\n// Package path implements utility routines for // manipulating slash-separated filename paths. 注释不需要额外格式化，例如写成星状条幅。注：有的程序员也许会这么写，但 godoc 不希望这样：\n//********************************************************** // \\_oo0oo\\_ * // o8888888o * // 88\u0026#34; . \u0026#34;88 * // (| -_- |) * // 0\\ = /0 * // ___/`---\u0026#39;\\\\___ * // .\u0026#39; \\\\\\| |// \u0026#39;. * // / \\\\\\||| : |||// \\ * // / _||||| -:- |||||- \\ * // | | \\\\\\\\ - /// | | * // | \\\\_| \u0026#39;\u0026#39;\\\\---/\u0026#39;\u0026#39; |_/ | * // \\ .-\\\\__ \u0026#39;-\u0026#39; ___/-. / * // ___\u0026#39;. .\u0026#39; /--.--\\ `. .\u0026#39;___ * // .\u0026#34;\u0026#34; \u0026#39;\u0026lt; `.___\\\\_\u0026lt;|\u0026gt;_/___.\u0026#39; \u0026gt;\u0026#39; \u0026#34;\u0026#34;. * // | | : `\\- \\\\`.;`\\ _ /`;.`/ - ` : | | * // \\ \\ \\`_. \\\\_ __\\ /__ _/ .-\\` / / * // =====`-.____`.___ \\\\_____/___.-`___.-\u0026#39;===== * // `=---=\u0026#39; * // * // * // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * // * // 佛祖保佑 永无BUG * // * // * //********************************************************** 如果这么写，生成的输出结果可能无法呈现出固定的文字宽度，所以你在使用 godoc 生成文档或 gofmt 格式化代码的时候， 坚决不要用空格做格式对齐。\n注释是纯文本，所以像 HTML 或 this 这样的注释会逐字复制，请不要这样用。\ngodoc 已经调整为以固定宽度的字体显示，非常适合注释中包含代码段。你可以查看标准库中 fmt 包的注释显示效果，看起来挺不错的。\n根据上下文，godoc 可能不会重新格式化注释，所以请确认注释内容直观上没有问题：包括正确的拼写、标点、段落、折行等。\n重复：写在代码段首行且没有插入换行符的注释，会被 godoc 视作该代码段的解释文本，每一个包都应该有自己的包文档。 程序的每个导出(大写)名称(包括结构体、方法等)都应该有一个文档注释。\n文档应该是完整的句子，这样有助于自动化生成和展现。较长的文档段落第一句话应该是一句总结以作为开头(原因在下面)。\n// Compile parses a regular expression and returns, if successful, // a Regexp that can be used to match against text. func Compile(str string) (*Regexp, error) { 如果每个文档注释都以它所描述的项目名称开头，那么 godoc 的输出可以通过 grep 查找。想象一下，你不记得名称 Compile ， 但正在寻找正则表达式的解析函数，所以你可以输入这样的命令行，\n$ godoc regexp | grep -i parse 如果包中的所有文档注释都以 “This function\u0026hellip;” 开始，grep 就没办法帮到你。 但是，由于标准库包会在文档中以项目名称开头，所以您会看到类似这样的内容，这会帮你找到想要查找的单词。\n$ godoc regexp | grep parse Compile parses a regular expression and returns, if successful, a Regexp parsed. It simplifies safe initialization of global variables holding cannot be parsed. It simplifies safe initialization of global variables $ Go的声明语法允许声明分组。一个单独的文档可以引入某一组相关的常量或变量。如果整个声明内容写的很敷衍，那么文档可能就没有什么太大帮助：\n// Error codes returned by failures to parse an expression. var ( ErrInternal = errors.New(\u0026#34;regexp: internal error\u0026#34;) ErrUnmatchedLpar = errors.New(\u0026#34;regexp: unmatched \u0026#39;(\u0026#39;\u0026#34;) ErrUnmatchedRpar = errors.New(\u0026#34;regexp: unmatched \u0026#39;)\u0026#39;\u0026#34;) ... ) 分组声明还可以标明代码内容之间的关系，例如一组变量受锁保护。 var ( countLock sync.Mutex inputCount uint32 outputCount uint32 errorCount uint32 ) 命名规范 # Go的命名规范和其他所有编程语言一样重要。在Go中甚至上升到语义级别：包外的可见性由其第一个字符是否为大写来确定。 因此值得花一些时间讨论Go程序中的命名规范。\n包名 # 在导入包时，包名将成为内容的访问者。例如导入 bytes 包：\nimport \u0026#34;bytes\u0026#34; 导入该包后可直接调用 bytes.Buffer。\n如果每个引用包的人都可以使用相同的名称来引用包内容，这意味着包名称应该足够的简洁、明了。\n在Go中，包名应该是小写、单名词命名， 不应该有下划线或驼峰——简洁的包名有助于他人在引用时书写更方便。\n不要担心包名重复，包名称只是导入的默认名称; 它不需要在所有代码中都是唯一的，在极少数情况下，导入包可以选择不同的名称在本地使用。 相同的包名很罕见，因为导入的文件名决定了正在使用哪个包（这一点见仁见智，毕竟小公司非常多，并不是所有公司流程化做的都很好）。\n在Go中包名的另一个约定是：包名是代码所在目录的名称；位于 src/encoding/base64 目录下的包，被导入时写作 encoding/base64，但其包名是 base64 ，既不是 encoding_base64 也不是 encodingBase64 。\n包的使用者使用包名来调用包的内容，所以好的名称可以在很大程度上避免产生误解（原文是：The importer of a package will use the name to refer to its contents, so exported names in the package can use that fact to avoid stutter） ，不要用 import . 这样的写法，在测试时可能会这么干，但其他情况下请尽量避免。\n例如， buffered reader 类型在 bufio 包下被命名为 Reader，而不是 BufReader， 原因在于使用者将 BufReader 视作 bufio.Reader，这样做更加清晰明了。 此外，由于导入的实体总是使用它们的包名称来寻址，所以bufio.Reader不会与io.Reader冲突。\n类似的，创建 ring.Ring 新实例的函数通常被称为 NewRing ，但由于 Ring 是包导出的唯一类型，并且由于该包称为 ring ，所以它是 叫做 New，包的使用者调用时感觉像 ring.New 。使用包的结构来帮助你选择好的命名来方便他人使用。\n另一个例子是 once.Do; once.Do(setup) 可读性强而且不容易被视作 once.DoOrWaitUntilDone(setup)而产生歧义。 起一个很长名称不会自动使可读性更强(这绝对是在嘲讽OC)。一个有用的文档评论往往比一个额外的长名称更有价值。\nGetters # 在Java中有人将Get方法称为属性获取器，Set方法称为属性设置器。这里的Getters含义与之相对应，Go开发组真是对其他语言怨念颇深。\nGo并不为getter和setter提供自动支持。开发者自己提供getter和setter没有任何问题，这样做通常是合适的，但是把Get放在getter的名字里既不是惯用的也不是必须的。\n如果你有一个属性起名为 owner(小写不可导出)，那么getter方法应该起名为Owner(大写可导出)，而不是GetOwner。 大写名称的可导出机制提供了用于区分字段和方法的钩子。如果需要setter函数的话，可以命名为SetOwner。这样在实践中就具备了很好的可读性：\nowner := obj.Owner() if owner != user { obj.SetOwner(user) } 接口名 # 按照惯例，单方法接口是由方法名加上一个后缀或类似的修饰来构造一个代理名词：Reader, Writer, Formatter, CloseNotifier等等\n在标准库中已经有一系列接口包含了如Read, Write, Close, Flush, String这样的方法名，起到了很好的示范作用。为了避免混乱，不要再给你的函数起这样的名字。 相反，如果你代码中实现了一个与标准库的接口，给它一个与接口中的方法相同的名字和签名;例如把你自己的格式化文本方法起名叫String而不是ToString。\n驼峰命名 # 最后，Go惯例使用MixedCaps或者mixedCaps来替代下划线来分割多个单词\n分号 # 像C一样，Go使用分号来终止语句(原文是:Go\u0026rsquo;s formal grammar uses semicolons to terminate statements)，但与C不同，这些分号不会出现在源代码中。 词法分析器使用一个简单的规则来在扫描时自动插入分号，所以代码中几乎不需要输入分号(for循环是需要的)。\n规则是这样的：如果换行符之前的最后一个标记是一个标识符（包括像int和float64这样的单词），一个基本文字（如数字或字符串常量）或下面这些关键字及符号中的某一个\nbreak continue fallthrough return ++ \u0026ndash; ) }\n那么词法分析器就在这之后插入一个分号。这可以被概括为：“如果换行符出现在可以表明语句结束的标记之后，就插入分号”。\n分号也可以在大括号闭合之前即时省略，如果像这样就不需要分号\ngo func() { for { dst \u0026lt;- \u0026lt;-src } }() Go程序仅在诸如for循环分割子句的地方使用分号，以区分初始化程序，循环条件和循环的元素。如果你用这种方式编写代码，一行中分隔多个语句也需要分号。\n引入分号插入规则的一个后果是，不能在下一行放置if, for, switch, select这些控制结构的开始大括号。如果你这样做了，语法分析器会把分号插入大括号之前， 这可能会导致不必要的错误。应该这样做：\nif i \u0026lt; f() { g() } 而不是这样做： if i \u0026lt; f() // wrong! { // wrong! g() } 控制结构 # Go的控制结构与C语言类似，但在某些方面有极其重要的不同。 没有do或while循环，只是略微泛化的for; switch更灵活; if和switch接受一个可选的初始化语句，这一点类似于for; break和continue可以采用一个可选的标签来确定什么时候跳出或继续; 添加了新的控制结构select。 语法也略有不同：没有括号，而且主体必须始终用大括号分隔。\nif # 在Go中简单的if结构看起来是这样的：\nif x \u0026gt; 0 { return y } if 条件后强制大括号鼓励在多行上写简单的if语句。无论如何，这样做是很好的语法风格，特别是当主体包含控制语句， 例如`return`或`break`时。 由于`if`和`switch`接受一个初始化语句，所以通常可以看到被用来设置局部变量。 if err := file.Chmod(0664); err != nil { log.Print(err) return err } 在Go标准库中，你会发现有些if语句不按照上面那样写，特别是以break，continue，goto或return结尾时， 省略了不必要的else语句。这样做可以减少逻辑嵌套，在出现多个err错误层级判断时，这是很重要的技巧，可以极大的提高可读性。\nf, err := os.Open(name) if err != nil { return err } codeUsing(f) 这是一个处理一系列错误条件的常见例子。由于错误情况往往以return语句结束，所以产生的代码不需要else语句。\n重复声明和赋值 # 在刚才的例子里我们看到了使用:=的短声明细节。这行代码调用os.Open读取文件。\nf, err := os.Open(name) 上面声明了两个变量f和err，在接下来的几行之后又调用了f.Stat获取文件信息。\nd, err := f.Stat() 这行代码看起来像是声明了d和err。注意：err的声明出现在2行代码中，这种重复是合法 的——err是由第一个语句声明的，但是只在第二个语句中重新赋值。这意味着f.Stat这行代码使用了已声 明的err变量，并给它一个新的值。\n在：=声明中，即使已经声明了一个变量v也允许再次出现，只要满足以下条件：\n这个声明与v的现有声明在同一个作用域范围内（如果v已经在外部作用域中声明，声明将创建一个新的变量§）， 在初始化中相应的值可以赋给v， 声明中至少有一个其他变量正在被重新声明。(这一条最重要) 这种不寻常的特性是纯粹的实用主义，使得易于复用单一的值，在较长的if-else代码链中，你会经常遇到这种情况。\n§这里值得注意的是，在Go中，函数参数和返回值的作用域与函数体相同，即使它们出现在包含代码的大括号的外面。\nfor # Go的for关键字与C既有相同也有不同。它将C中的for和while结合了起来，并去除了do-while。有三种形式，其中只有一种是分号的。\n// Like a C for for init; condition; post { } // Like a C while for condition { } // Like a C for(;;) for { } 短声明可以很容易地在循环中声明索引变量。\nsum := 0 for i := 0; i \u0026lt; 10; i++ { sum += i } 如果你循环访问数组，切片slice，字符串或映射map，或者从通道channel读取，range可以很好的帮你管理循环。\nfor key, value := range oldMap { newMap[key] = value } 如果您只需要range中的第一项（键或索引），请扔掉第二项(不写就行，但这里有坑，m的类型你得首先确认好)：\nfor key := range m { if key.expired() { delete(m, key) } } 如果您只需要范围中的第二项（值），请使用空白标识符（下划线）放弃第一个：\nsum := 0 for _, value := range array { sum += value } 空白标识符有很多用途，我们将在下面讨论它。\n对于字符串，range关键字替你做了很多工作，通过解析UTF-8分解出各个Unicode代码点。 错误的编码消耗一个字节并产生替换符文U + FFFD。(内置类型rune是单个Unicode代码点的Go术语， 详细信息请参见 Go语言规范。)\nfor pos, char := range \u0026#34;中文\\\\x80辞海\u0026#34; { // \\\\x80 不是合法的 UTF-8 编码 fmt.Printf(\u0026#34;character %#U starts at byte position %d\\\\n\u0026#34;, char, pos) } } 会打印如下结果：\ncharacter U+4E2D \u0026#39;中\u0026#39; starts at byte position 0 character U+6587 \u0026#39;文\u0026#39; starts at byte position 3 character U+FFFD \u0026#39;�\u0026#39; starts at byte position 6 character U+8F9E \u0026#39;辞\u0026#39; starts at byte position 7 character U+6D77 \u0026#39;海\u0026#39; starts at byte position 10 最后，Go没有逗号运算符，也没有++和--(你可以用+=1或者-=1来替代)。 因此，如果你想在一个for中运行多个变量，你应该使用并行赋值（尽管这排除了++和\u0026ndash; ）。\n// Reverse a for i, j := 0, len(a)-1; i \u0026lt; j; i, j = i+1, j-1 { a[i], a[j] = a[j], a[i] } Switch # Go的switch比C可用性更强。表达式不一定是常数，甚至不一定是整数，从上到下求值，直到找到匹配条件的代码段，如果switch没有表达式， 则相当于if true。因此if-else-if-else链与switch可以看作是等价的。\nfunc unhex(c byte) byte { switch { case \u0026#39;0\u0026#39; \u0026lt;= c \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;9\u0026#39;: return c - \u0026#39;0\u0026#39; case \u0026#39;a\u0026#39; \u0026lt;= c \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;f\u0026#39;: return c - \u0026#39;a\u0026#39; + 10 case \u0026#39;A\u0026#39; \u0026lt;= c \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;F\u0026#39;: return c - \u0026#39;A\u0026#39; + 10 } return 0 } case条件可以使用逗号分隔\nfunc shouldEscape(c byte) bool { switch c { case \u0026#39; \u0026#39;, \u0026#39;?\u0026#39;, \u0026#39;\u0026amp;\u0026#39;, \u0026#39;=\u0026#39;, \u0026#39;#\u0026#39;, \u0026#39;+\u0026#39;, \u0026#39;%\u0026#39;: return true } return false } 尽管与其他类似C的语言几乎没有什么共同之处，但break语句可以用来尽早终止切换。 有时候，有必要打破整个循环，在Go中可以通过在循环中加一个标签并“break”标签来完成。这个例子显示了两种用法。\nLoop: for n := 0; n \u0026lt; len(src); n += size { switch { case src[n] \u0026lt; sizeOne: if validateOnly { break } size = 1 update(src[n]) case src[n] \u0026lt; sizeTwo: if n+1 \u0026gt;= len(src) { err = errShortInput break Loop } if validateOnly { break } size = 2 update(src[n] + src\\[n+1]) } } 当然，continue语句也接受一个可选标签，但是它只适用于循环。\n作为本节的结尾，下面是使用两个switch语句的例子：\n// Compare returns an integer comparing the two byte slices, // lexicographically. // The result will be 0 if a == b, -1 if a \u0026lt; b, and +1 if a \u0026gt; b func Compare(a, b []byte) int { for i := 0; i \u0026lt; len(a) \u0026amp;\u0026amp; i \u0026lt; len(b); i++ { switch { case a[i] \u0026gt; b[i]: return 1 case a[i] \u0026lt; b[i]: return -1 } } switch { case len(a) \u0026gt; len(b): return 1 case len(a) \u0026lt; len(b): return -1 } return 0 } Type switch # switch也可以用来检查接口变量的动态类型，这种用法涉及到类型断言的语法。如果switch在表达式中声明了一个变量， 变量将在每个子句中都有对应的类型。在这种情况下重用名字是常见的惯用法，实际上，是在每个判断条件中声明了一个具有相同名称不同类型的变量。(原文：A switch can also be used to discover the dynamic type of an interface variable. Such a type switch uses the syntax of a type assertion with the keyword type inside the parentheses. If the switch declares a variable in the expression, the variable will have the corresponding type in each clause. It\u0026rsquo;s also idiomatic to reuse the name in such cases, in effect declaring a new variable with the same name but a different type in each case.)\nvar t interface{} t = functionOfSomeType() switch t := t.(type) { default: fmt.Printf(\u0026#34;unexpected type %T\\n\u0026#34;, t) // %T prints whatever type t has case bool: fmt.Printf(\u0026#34;boolean %t\\n\u0026#34;, t) // t has type bool case int: fmt.Printf(\u0026#34;integer %d\\n\u0026#34;, t) // t has type int case *bool: fmt.Printf(\u0026#34;pointer to boolean %t\\n\u0026#34;, \\*t) // t has type \\*bool case *int: fmt.Printf(\u0026#34;pointer to integer %d\\n\u0026#34;, \\*t) // t has type \\*int } 函数 # 多返回值 # Go的一个特性是函数和方法可以返回多个值。这个做法可以用来改进C程序中的一些笨拙的操作方式：同时带内错误返回例如EOF的-1和修改地址传递的参数。\n在C中，一个写入错误通过一个负数标明，并将错误代码分散在一个不同的位置。在Go中，Write方法可以同时返回一个计数值和一个错误值： “是的，你写了一些字节，但不是全部，因为你的设备出现了问题”。标准库os包中的Write方法如下：\nfunc (file *File) Write(b \\[\\]byte) (n int, err error) 如文档所示，当n!=len(b)时这个函数返回写入的字节数，和一个不为nil的error值。这是一种常见的错误处理方式;更多示例请参阅错误处理部分。\n多返回值可以玩出很多的花样。这有一个简单的例子，从一个字节片段中的一个位置抓取一个数字，返回数字和下一个位置。\nfunc nextInt(b []byte, i int) (int, int) { for ; i \u0026lt; len(b) \u0026amp;\u0026amp; !isDigit(b[i]); i++ { } x := 0 for ; i \u0026lt; len(b) \u0026amp;\u0026amp; isDigit(b[i]); i++ { x = x*10 + int(b[i]) - \u0026#39;0\u0026#39; } return x, i } 你可以使用这个函数来遍历切片\nfor i := 0; i \u0026lt; len(b); { x, i = nextInt(b, i) fmt.Println(x) } 为返回值命名 # Go函数的返回值或结果“参数”可以被赋予名称，并用作常规变量，就像传入的参数一样。 当命名时， 它们在函数开始时被初始化为各自类型的零值; 如果函数执行一个不带参数的返回语句，那么结果参数的当前值被用作返回值。\n在声明函数返回值类型时命名并不是强制性的，但是这样做可以使代码更简洁明了：代码即文档。如果我们命名nextInt的结果，那么显而易见明白返回int是哪个\nfunc nextInt(b []byte, pos int) (value, nextPos int) { 如果在声明函数返回值类型时命名，代码的可读性就很强。以下是io包中的一个例子：\nfunc ReadFull(r Reader, buf []byte) (n int, err error) { for len(buf) \u0026gt; 0 \u0026amp;\u0026amp; err == nil { var nr int nr, err = r.Read(buf) n += nr buf = buf[nr:] } return } Defer 关键字 # defer 会在函数正常返回，也就是return之后添加一个函数调用。 因此，defer通常用来释放函数内部资源。这是一种在其他语言不常见但有效的方式。(与Java的finally和python的with不太一样) 典型的例子是解锁互斥或关闭文件。(原文:Go\u0026rsquo;s defer statement schedules a function call (the deferred function) to be run immediately before the function executing the defer returns. It\u0026rsquo;s an unusual but effective way to deal with situations such as resources that must be released regardless of which path a function takes to return. The canonical examples are unlocking a mutex or closing a file.)\n// Contents returns the file\u0026#39;s contents as a string. func Contents(filename string) (string, error) { f, err := os.Open(filename) if err != nil { return \u0026#34;\u0026#34;, err } defer f.Close() // f.Close will run when we\u0026#39;re finished. var result []byte buf := make([]byte, 100) for { n, err := f.Read(buf[0:]) result = append(result, buf\\[0:n]...) // append is discussed later. if err != nil { if err == io.EOF { break } return \u0026#34;\u0026#34;, err // f will be closed if we return here. } } return string(result), nil // f will be closed if we return here. } 像例子中这样使用defer来调用函数关闭文件有两个好处：首先，它保证你永远不会忘记关闭文件，这是一个容易犯的错误， 特别是如果你稍后编辑函数来添加一个新的返回路径。 其次，它靠近开始操作的位置，这比放置在函数结尾要清楚得多。\n当defer执行时会对执行函数的参数进行计算，而不是执行调用时。单个延迟调用可以推迟多个函数的执行。(原文:The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes. Besides avoiding worries about variables changing values as the function executes, this means that a single deferred call site can defer multiple function executions. Here\u0026rsquo;s a silly example.)\nfor i := 0; i \u0026lt; 5; i++ { defer fmt.Printf(\u0026#34;%d \u0026#34;, i) } 被defer修饰的函数会按照LIFO(先进先出)顺序执行，所以这段代码在结束后会打印4 3 2 1 0。 更合理的方式是通过程序追踪函数执行的顺序，\nfunc trace(s string) { fmt.Println(\u0026#34;entering:\u0026#34;, s) } func untrace(s string) { fmt.Println(\u0026#34;leaving:\u0026#34;, s) } // Use them like this: func a() { trace(\u0026#34;a\u0026#34;) defer untrace(\u0026#34;a\u0026#34;) // do something.... } 让我们搞复杂点(这个贱兮兮的例子被人发现出现在面试题上，原来出自官方文档)\nfunc trace(s string) string { fmt.Println(\u0026#34;entering:\u0026#34;, s) return s } func un(s string) { fmt.Println(\u0026#34;leaving:\u0026#34;, s) } func a() { defer un(trace(\u0026#34;a\u0026#34;)) fmt.Println(\u0026#34;in a\u0026#34;) } func b() { defer un(trace(\u0026#34;b\u0026#34;)) fmt.Println(\u0026#34;in b\u0026#34;) a() } func main() { b() } 输出结果\nentering: b in b entering: a in a leaving: a leaving: b 对于习惯于使用其他语言进行块级资源管理的程序员来说，延迟可能看起来很奇特，但其最有趣和最强大的恰恰是它并不基于块， 而是基于功能的。 在关于panic和recover的部分，我们将看到展现其特性的另一个例子。\nData # Allocation with new # 原文标题就长这样，为防止有其他含义就原样放上来。\nGo有两个内存分配原语，即内置函数new和make。他们的实现方式不同，以适用于不同的情况，这可能造成混淆，但规则很简单。\n让我们先来谈谈new，这是一个系统的内置函数，但与其他语言中的new不同，它不初始化内存，只是将其清零。 也就是说，new(T)为一个类型为*T的值分配一块已经归零的地址。在Go术语中，它返回一个指向类型T的新值的指针。(原文是： Let\u0026rsquo;s talk about new first. It\u0026rsquo;s a built-in function that allocates memory, but unlike its namesakes in some other languages it does not initialize the memory, it only zeros it. That is, new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T. In Go terminology, it returns a pointer to a newly allocated zero value of type T.)\nnew返回的内存已经清零，所以在设计数据结构时，其内部的各种属性零值无需进一步初始化。这意味着用户只需要new一下就能正常工作。例如bytes.Buffer的文档指出“Buffer的零值是一个准备使用的空缓冲区”。 同样，sync.Mutex没有显式的构造函数或Init方法。 相反，sync.Mutex的零值被定义为一个解锁的互斥体。(原文：Since the memory returned by new is zeroed, it\u0026rsquo;s helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization. This means a user of the data structure can create one with new and get right to work. For example, the documentation for bytes.Buffer states that \u0026ldquo;the zero value for Buffer is an empty buffer ready to use.\u0026rdquo; Similarly, sync.Mutex does not have an explicit constructor or Init method. Instead, the zero value for a sync.Mutex is defined to be an unlocked mutex.)\n这个特性很有用，考虑这样的数据结构：\ntype SyncedBuffer struct { lock sync.Mutex buffer bytes.Buffer } 类型SyncedBuffer的值可以在分配或刚刚声明时立即使用。在下面的代码片段中，p和v都可以正常工作而无需进一步初始化。\np := new(SyncedBuffer) // type *SyncedBuffer var v SyncedBuffer // type SyncedBuffer Constructors and composite literals # 有时零值不够好用，需要一个初始化构造函数，就像在这个例子(注意，这段代码是摘自系统源码，为包内自调用，故为小写，不是错误。)\nfunc NewFile(fd int, name string) *File { if fd \u0026lt; 0 { return nil } f := new(File) f.fd = fd f.name = name f.dirinfo = nil f.nepipe = 0 return f } File内含有很多个属性，我们可以简单的用composite literal(一种结构体的初始化方式)，每次使用都会创建一个新实例。\nfunc NewFile(fd int, name string) *File { if fd \u0026lt; 0 { return nil } f := File{fd, name, nil, 0} return \u0026amp;f } 请注意，与C不同的是，返回局部变量的地址是完全可以的， 与该变量相关联的存储在函数返回后仍然存在。 实际上，每一次复合文本的地址分配一个新的实例，所以我们可以把这两行合并成一行。\nreturn \u0026amp;File{fd, name, nil, 0} composite literal的属性字段按顺序排列，并且都必须存在。 然而，通过将元素明确地标记为键值对的形式，初始值设定项可以以任何顺序出现， 缺少的元素将保留为其各自的零值。 因此我们可以这样写\nreturn \u0026amp;File{fd: fd, name: name} 有一种极端情况，如果一个composite literal根本不包含任何字段，它会为该类型创建一个零值。new(File)和\u0026amp;File{}是等价的。\n也可以为数组，切片和map集合创建composite literal，标签是索引或key。\n(文档这部分代码有问题，原文也讲不通，实际工作中给map切片赋值也没有这样干的，原文：Composite literals can also be created for arrays, slices, and maps, with the field labels being indices or map keys as appropriate. In these examples, the initializations work regardless of the values of Enone, Eio, and Einval, as long as they are distinct.)\na := [...]string {Enone: \u0026#34;no error\u0026#34;, Eio: \u0026#34;Eio\u0026#34;, Einval: \u0026#34;invalid argument\u0026#34;} s := []string {Enone: \u0026#34;no error\u0026#34;, Eio: \u0026#34;Eio\u0026#34;, Einval: \u0026#34;invalid argument\u0026#34;} m := map[int]string{Enone: \u0026#34;no error\u0026#34;, Eio: \u0026#34;Eio\u0026#34;, Einval: \u0026#34;invalid argument\u0026#34;} Allocation with make # 我们把讨论的话题扯回来。\n内置函数make(T, args)与new(T)的使用目标不同，它仅被用于创建slice切片、map集合和channel通道， 调用后会返回初始化过的(内存不清零)、类型为T(不是*T)的值。调用make创建这三种类型的区别在于，所涉及到的内部数据结构必须在使用前先进行初始化。(原文：It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T). The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use.)\n以切片为例，切片是包含有以下三项内容的描述符：指向内部数据元素(数组内)的指针，长度和容量(capacity)。在这些项被初始化前，切片是nil。(原文：A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil. )\n对于slice切片、map集合和channel通道而言，make初始化了内部的数据结构以备调用。(原文：For slices, maps, and channels, make initializes the internal data structure and prepares the value for use.)\nmake([]int, 10, 100) 这行代码分配了一个含有100个整数的数组，然后创建一个长度为10，容量为100的slice，指向数组的前10个元素。 （当创建一个切片时，容量可以省略;更多信息请参见切片部分）。而new([]int)返回一个指向新分配的、空切片(zeroed slice structure)， 即指向nil的切片指针。\n这个例子说明了new与make的不同：\nvar p *[]int = new([]int) // 分配了 slice 结构; *p == nil; 不常用 var v []int = make([]int, 100) // slice v 指向含有100整数的数组 // 完全不必要的复杂写法 var p *[]int = new([]int) *p = make([]int, 100, 100) // 惯用法 v := make([]int, 100) 重申一遍，make仅支持maps, slices和channels，并且不返回指针。为了显示的获得指针，使用new或使用\u0026amp;手动取地址。\n数组 # 在详细规划内存时，数组非常有用，有时可以帮助避免分配_avoid allocation_。在使用上主要是作为切片的基础，这是下一节我们要讨论的主题。作为前置基础，我们先来简单的了解下。\nGo和C的数组概念有非常大的区别。在Go中：\n数组是值。将一个数组分配给另一个数组会复制所有元素。 尤其是，如果将一个数组传递给一个函数，它将会收到一个数组的拷贝，而不是指向数组的指针。 数组的大小是其类型的一部分。类型[10]int和[20]int是不同的。 值传递的特性非常有用但代价高昂，如果你想像C一样高效的操作数组，就需要传递数组的指针。\nfunc Sum(a *[3]float64) (sum float64) { for _, v := range *a { sum += v } return } array := [...]float64{7.0, 8.5, 9.1} x := Sum(\u0026amp;array) // 注意这里明确的调用指针 以上并不是Go的惯用法，俺们经常用slice来替代。\nSlices # 对于序列化数据而言，切片slice更常用、更强大、更方便。除了具有显式维度的操作（如变换矩阵）外， Go中的大部分序列化操作都是用切片而非数组完成。\n切片持有对底层数组的引用，如果将一个切片分配给另一个切片，则两者都持有相同的数组引用。\n如果一个函数接受一个slice参数，它对slice的元素的改变对调用者来说是可见的，类似于传递了底层数组的指针。\nos包中的Read函数接受slice参数，而非数组指针和数组长度值；切片自带的长度属性会设置该函数读取多少数据的上限。\nfunc (f *File) Read(buf []byte) (n int, err error) 该方法返回读取的字节数和错误值（如果有的话）。要读入较大缓冲区buf的前32个字节，将传入切片参数执行切片操作就行。\nn, err := f.Read(buf[0:32]) 对切片的再切片操作常见且高效，如果暂时把效率放在一边，也可以这样做：\nvar n int var err error for i := 0; i \u0026lt; 32; i++ { nbytes, e := f.Read(buf[i:i+1]) // Read one byte. if nbytes == 0 || e != nil { err = e break } n += nbytes } 一个切片的长度可以改变，只要它仍然在底层数组的范围内；只是把底层数组再分配给自己而已。可以通过内置函数cap查看切片的容量。(原文：The length of a slice may be changed as long as it still fits within the limits of the underlying array; just assign it to a slice of itself. The capacity of a slice, accessible by the built-in function cap, reports the maximum length the slice may assume.)\n下面这个函数将数据添加到切片。如果数据超过容量(capacity)，切片将重新分配(简而言之，超出预设长度时切片会自动扩充容量)。该函数使用len和cap来度量一个值为nil的切片是合法的，返回的结果会是0\nfunc Append(slice, data []byte) []byte { l := len(slice) if l + len(data) \u0026gt; cap(slice) { // reallocate // Allocate double what\u0026#39;s needed, for future growth. newSlice := make([]byte, (l+len(data))*2) // The copy function is predeclared and works for any slice type. copy(newSlice, slice) slice = newSlice } slice = slice[0:l+len(data)] for i, c := range data { slice[l+i] = c } return slice } 尽管Append可以修改slice的元素，但是slice本身（运行时数据结构包含指针，长度和容量）是按值传递的，所以必须返回slice。\n向切片追加数据是非常有用的，内置函数append可以执行这个操作。为了理解这个函数的设计，我们需要了解更多的知识，稍后再进一步讲解。\n二维切片 # Go的数组和切片是一维的。为了实现二维数组和切片，可通过创建数组套数组或切片套切片的结构，像这样：\ntype Transform [3][3]float64 // 3x3 数组 type LinesOfText [][]byte // 每个切片的元素也是切片. 因为切片长度是可变的，所以每个内切片可以具有不同的长度。如示例所示：每行都有一个独立的长度。\ntext := LinesOfText{ []byte(\u0026#34;Now is the time\u0026#34;), []byte(\u0026#34;for all good gophers\u0026#34;), []byte(\u0026#34;to bring some fun to the party.\u0026#34;), } 有时需要分配一个二维切片，例如处理像素的扫描线。有两种方法来实现这一点。 一种是独立分配每个切片; 另一种是分配一个单一的数组，并指向单个切片。 使用哪个取决于你的实际情况。 如果切片长度可能增长或缩小， 它们应该被独立分配以避免覆盖下一行; 如果不是，用单一分配来构造对象会更有效率。 作为参考，这里是两种方法的样例。\n// Allocate the top-level slice. picture := make([][]uint8, YSize) // One row per unit of y. // Loop over the rows, allocating the slice for each row. for i := range picture { picture[i] = make([]uint8, XSize) } 这是另一种方法\n// Allocate the top-level slice, the same as before. picture := make([][]uint8, YSize) // One row per unit of y. // Allocate one large slice to hold all the pixels. pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8. // Loop over the rows, slicing each row from the front of the remaining pixels slice. for i := range picture { picture[i], pixels = pixels[:XSize], pixels[XSize:] } Maps # map是方便且强大的内置数据结构，它将一种类型（键）的值与另一种类型（元素或值）的值相关联。键可以是定义相等运算符的任何类型，例如整数 ，浮点数和复数，字符串，指针，接口（只要动态类型支持比较判断），结构体和数组。切片不能用作映射关键字，因为它们之间没有定义比较关系。 像切片一样，map保存对基础数据结构的引用。如果将map传递给更改map内容的函数，则更改将在函数外部可见。\nmap可以使用通常的复合字面值语法和冒号分隔的键值对来构建，所以在初始化时很容易构建。\nvar timeZone = map[string]int{ \u0026#34;UTC\u0026#34;: 0*60*60, \u0026#34;EST\u0026#34;: -5*60*60, \u0026#34;CST\u0026#34;: -6*60*60, \u0026#34;MST\u0026#34;: -7*60*60, \u0026#34;PST\u0026#34;: -8*60*60, } 分配和提取映射值在语法上看起来就像操作数组和切片一样，只不过索引不一定是整数。\noffset := timeZone[\u0026#34;EST\u0026#34;] 尝试使用map不存在的键获取map值时，将返回map中对应值类型的零值。例如，如果map包含整数，查找一个不存在的键将返回0。 一个集合_set_可以被实现为值类型为bool的映射。将map实体设置为true以将值放入集合_set_中，然后通过简单索引进行测试。\nattended := map[string]bool{ \u0026#34;Ann\u0026#34;: true, \u0026#34;Joe\u0026#34;: true, ... } if attended[person] { // 如果在map中不存在会返回 false fmt.Println(person, \u0026#34;was at the meeting\u0026#34;) } 有时你需要把是否存在与零值区分开，是否存在名为\u0026quot;UTC\u0026quot;的键或改建所对应的值是否为0，你可以这样做：\nvar seconds int var ok bool seconds, ok = timeZone[tz] 这样的判断方式被称为\u0026quot;comma ok\u0026quot;(For obvious reasons this is called the “comma ok” idiom.)。在下面的例子中，如果\u0026quot;tz\u0026quot;在map中存在， \u0026ldquo;seconds\u0026quot;会被赋予键\u0026quot;tz\u0026quot;所对应的值，同时\u0026quot;ok\u0026quot;会被赋值为true，相反，当\u0026quot;tz\u0026quot;在map中不存在，\u0026ldquo;seconds\u0026quot;会被赋予对应类型的零值(在这个例子中是0)，同时\u0026quot;ok\u0026quot;会被赋值为false。\nfunc offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println(\u0026#34;unknown time zone:\u0026#34;, tz) return 0 } 如果只是想测试map中的key是否存在，可以使用空白标识符_来占位。\n_, present := timeZone[tz] 想要删除map中的键值对，可以使用内置函数delete，所需的参数是目标map和要删除的对应的键。即使该键不存在，调用此函数依然是安全的。\ndelete(timeZone, \u0026#34;PDT\u0026#34;) // Now on Standard Time Printing # Go中的格式化打印风格类似于C的printf家族，但是使用方式更丰富功能更强大。这些函数存在于fmt包中，并且有大写的名字：fmt.Printf，fmt.Fprintf，fmt.Sprintf等等。 字符串函数Sprintf返回一个字符串，而不是填充提供的缓冲区。\n你不需要提供格式字符串。对于Printf，Fprintf和Sprintf中的每一个，都有另一组函数与之对应，例如Print和Println。 这些对应的函数为每个参数生成默认格式。 Println在参数之间插入一个空格，并在输出中附加一个换行符。 Print在两侧的操作数都不是字符串时，会添加空格。 在这个例子中，每行的输出相同。\nfmt.Printf(\u0026#34;Hello %d\\n\u0026#34;, 23) fmt.Fprint(os.Stdout, \u0026#34;Hello \u0026#34;, 23, \u0026#34;\\n\u0026#34;) fmt.Println(\u0026#34;Hello\u0026#34;, 23) fmt.Println(fmt.Sprint(\u0026#34;Hello \u0026#34;, 23)) 格式化打印函数fmt.Fprint和fmt包下的其他打印函数将任何实现io.Writer接口的对象作为第一个参数; 变量os.Stdout和os.Stderr就是这样的实例。\n从这里开始与C有所不同。首先，诸如%d这样的数字格式不会将符号或大小作为标志；而是使用参数的类型来决定这些属性。\nvar x uint64 = 1\u0026lt;\u0026lt;64 - 1 fmt.Printf(\u0026#34;%d %x; %d %x\\n\u0026#34;, x, x, int64(x), int64(x)) 会输出\n18446744073709551615 ffffffffffffffff; -1 -1 如果你只是想要默认的打印格式，可以使用%v，它产生的格式化样式与调用Print 和Println相同，此外， 这种方法可以打印任何类型的值，包括数组、切片、结构体、map集合等。上一节打印时区就可以这样做。\nfmt.Printf(\u0026#34;%v\\n\u0026#34;, timeZone) // or just fmt.Println(timeZone) 会输出为\nmap[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200] 对于map来说，键可以以任何顺序输出。打印一个结构体时，使用格式%+v会用结构体的名称来标注结构体的字段， 对于任何值，格式%#v都以完整的Go语法打印出结果。\ntype T struct { a int b float64 c string } t := \u0026amp;T{ 7, -2.35, \u0026#34;abc\\tdef\u0026#34; } fmt.Printf(\u0026#34;%v\\n\u0026#34;, t) fmt.Printf(\u0026#34;%+v\\n\u0026#34;, t) fmt.Printf(\u0026#34;%#v\\n\u0026#34;, t) fmt.Printf(\u0026#34;%#v\\n\u0026#34;, timeZone) 输出结果(请注意\u0026amp;符号)\n\u0026amp;{7 -2.35 abc def} \u0026amp;{a:7 b:-2.35 c:abc def} \u0026amp;main.T{a:7, b:-2.35, c:\u0026#34;abc\\tdef\u0026#34;} map[string] int{\u0026#34;CST\u0026#34;:-21600, \u0026#34;PST\u0026#34;:-28800, \u0026#34;EST\u0026#34;:-18000, \u0026#34;UTC\u0026#34;:0, \u0026#34;MST\u0026#34;:-25200} 当应用于string或[]byte类型的值时，引用的字符串格式也可以通过%q获得。 如果可能，%#q将使用反引号。 %q也适用于整数和runes，会产生一个单引号符号常量(原文是single-quoted rune，可以理解为把一堆runes拆成单个的rune)。 另外，%x能够格式化字符串，字节数组和字节切片以及整数，产生一个十六进制字符串，并在字节之间放置空格。\n另外还有个非常便利的%T，能输出值的类型。\nfmt.Printf(\u0026#34;%T\\n\u0026#34;, timeZone) 会输出为\nmap[string] int 如果你想修改某个自定类型输出格式，只需要为该类型定义方法String() string(类似Java的toString)。例如：\nfunc (t *T) String() string { return fmt.Sprintf(\u0026#34;%d/%g/%q\u0026#34;, t.a, t.b, t.c) } fmt.Printf(\u0026#34;%v\\n\u0026#34;, t) 会输出为\n7/-2.35/\u0026#34;abc\\tdef\u0026#34; 如果你需要打印T类型的值以及指向T的指针，那么String函数的接收者必须是值类型的； 这个例子使用了一个指针，因为这对结构类型来说更有效率。更多信息请查看值与指针类型接收者(pointers vs. value receivers)。\n在String方法中不要用Sprintf来构造返回的字符串，因为打印流程是可以重入的，所以会出现死循环。\ntype MyString string func (m MyString) String() string { return fmt.Sprintf(\u0026#34;MyString=%s\u0026#34;, m) // Error: will recur forever. } 修复的方案也很简单，将无参调用改为传参就行了。\ntype MyString string func (m MyString) String() string { return fmt.Sprintf(\u0026#34;MyString=%s\u0026#34;, string(m)) // OK: note conversion. } 在下面的初始化章节我们可以看到其他的使用方式来避免出现这样的情况。\n另一个格式化输出方案是传入格式化字符串，Printf传递了...interface{}来接收不定长参数，interface接口保证了能够接收任何类型。\nfunc Printf(format string, v ...interface{}) (n int, err error) { 在函数Printf中，v的作用类似[]interface{}类型的变量，但是如果它被传递给另一个可变参数函数，它就像一个常规的参数列表。 函数log.Println将其参数直接传递给fmt.Sprintln。\n// Println prints to the standard logger in the manner of fmt.Println. func Println(v ...interface{}) { std.Output(2, fmt.Sprintln(v...)) // Output takes parameters (int, string) } 我们在嵌套调用Sprintln之后写入...，以告诉编译器将v视为参数列表;否则它只会将v作为单个切片进行参数传递。\n这个对打印输出只是介绍了大概，详情请查看标准库fmt包。\n另外...可以指定特定类型，如下：\nfunc Min(a ...int) int { min := int(^uint(0) \u0026gt;\u0026gt; 1) // largest int for _, i := range a { if i \u0026lt; min { min = i } } return min } Append # 现在我们要补全对内置函数append的介绍。append的函数签名与 上面提到的Append不同：\nfunc append(slice []T, elements ...T) []T 其中T是任何给定类型的占位符。你无法在Go中直接写出这样的函数代码(Go不支持泛型)，这个类型 由函数调用者决定。这就是append内置的原因：它需要编译器支持。\nappend将元素追加到slice切片的尾部并返回最新的切片。返回切片的原因在于， 就跟我们上面提到的Append一样，底层数组可能由于长度不够而完全改变了。\nx := []int{1,2,3} x = append(x, 4, 5, 6) fmt.Println(x) 结果是[1 2 3 4 5 6]。看上去append有点像Printf，可以 传递任意数量的参数。\n但是假如我们想把slice切片拼接在一起呢？很简单，使用...，这段代码生成与 上面相同的结果：\nx := []int{1,2,3} y := []int{4,5,6} x = append(x, y...) fmt.Println(x) //[1 2 3 4 5 6] 不使用...将无法通过编译，会报类型错误，因为y并非int类型。\nInitialization # 常量、变量及全局初始化\n尽管与C或C++相比看起来区别并不大，但是在Go中的初始化功能更强大。在初始化过程中可以构建复杂的结构，并且可以正确处理已初始化对象之间的顺序问题，即使在不同的包中也是如此。\n常量 # Go中的常量就是我们平常说的(其他语言的)常量。它们在编译期创建，即使在函数中被定义为locals,也只能 是数字，字符(runes)，字符串或布尔值。由于编译的限制，定义它们的表达式必须是常量表达式——编译期会进行校验。 例如1\u0026lt;\u0026lt;3是一个常量表达式，而math.Sin(math.Pi/4)不是，因为函数调用 math.Sin发生在运行时。\n在Go中，使用iota枚举器创建枚举常量。由于iota可以是表达式的一部分，并且表达式可以 隐式地重复，所以很容易构建复杂的值集合。\ntype ByteSize float64 const ( _ = iota // ignore first value by assigning to blank identifier KB ByteSize = 1 \u0026lt;\u0026lt; (10 * iota) MB GB TB PB EB ZB YB ) 将函数（如String）附加到任何自定义类型的功能使得任意值可以自动格式化以进行打印。 虽然更常见于结构，但是这种技术对像ByteSize这样的自定义浮点类型也是有用的。\nfunc (b ByteSize) String() string { switch { case b \u0026gt;= YB: return fmt.Sprintf(\u0026#34;%.2fYB\u0026#34;, b/YB) case b \u0026gt;= ZB: return fmt.Sprintf(\u0026#34;%.2fZB\u0026#34;, b/ZB) case b \u0026gt;= EB: return fmt.Sprintf(\u0026#34;%.2fEB\u0026#34;, b/EB) case b \u0026gt;= PB: return fmt.Sprintf(\u0026#34;%.2fPB\u0026#34;, b/PB) case b \u0026gt;= TB: return fmt.Sprintf(\u0026#34;%.2fTB\u0026#34;, b/TB) case b \u0026gt;= GB: return fmt.Sprintf(\u0026#34;%.2fGB\u0026#34;, b/GB) case b \u0026gt;= MB: return fmt.Sprintf(\u0026#34;%.2fMB\u0026#34;, b/MB) case b \u0026gt;= KB: return fmt.Sprintf(\u0026#34;%.2fKB\u0026#34;, b/KB) } return fmt.Sprintf(\u0026#34;%.2fB\u0026#34;, b) } 表达式YB打印为1.00YB，而ByteSize（1e13）打印为9.09TB。\n此处Sprintf用于实现ByteSize的String方法是安全的（避免循环）， 因为它使用%f来调用Sprintf，而不是字符串格式： Sprintf只会在传递字符串时调用String方法，%f传递的是浮点值。\n变量 # 变量可以采取像常量一样的初始化方式，但初始化器可以在运行时计算(一般)表达式(原文并未提到什么情况下的表达式算是\u0026quot;不一般\u0026quot;的表达式)。\nvar ( home = os.Getenv(\u0026#34;HOME\u0026#34;) user = os.Getenv(\u0026#34;USER\u0026#34;) gopath = os.Getenv(\u0026#34;GOPATH\u0026#34;) ) init函数 # 每个源文件都可以定义自己的init初始化函数来设置所需的任何初始化状态，实际上每个文件可以有 多个初始化函数。注意：在所有变量声明都计算了它们的初始化值后才会调用init， 并且只有在所有导入的包已经被初始化后才执行init.(原文：Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.)\n除了不能在初始化中声明之外，初始化函数的一个常见用途是在实际执行开始之前验证或修复 程序状态的正确性。(存疑，原文：Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.)\nfunc init() { if user == \u0026#34;\u0026#34; { log.Fatal(\u0026#34;$USER not set\u0026#34;) } if home == \u0026#34;\u0026#34; { home = \u0026#34;/home/\u0026#34; + user } if gopath == \u0026#34;\u0026#34; { gopath = home + \u0026#34;/go\u0026#34; } // gopath may be overridden by --gopath flag on command line. flag.StringVar(\u0026amp;gopath, \u0026#34;gopath\u0026#34;, gopath, \u0026#34;override default GOPATH\u0026#34;) } Methods # 不同语言对method翻译不同，而c++甚至对函数和方法两个词有不同的解释。为防止造成先入为主的印象，本小结全部使用method而不翻译。\nPointers vs. Values method接收者————指针与值 # 就像我们在上面 ByteSize 例子中看到的，可以为任何指定类型定义method(除了指针和接口)；接收者(receiver)不一定是个结构体。\n在之前关于切片的讨论中，我们提到了Append，可以把这个method改为由切片接收。 首先，我们声明一个类型，然后把该类型的值作为Append的接收者。\ntype ByteSlice []byte func (slice ByteSlice) Append(data []byte) []byte { // 内容与上面定义的Append完全相同 此处省略 } 这里同样需要返回新的silce切片。 但我们可以通过重新定义将ByteSlice指针作为接收者的方式来消除这种笨拙的操作， 以达到更新切片内容的目的。\nfunc (p *ByteSlice) Append(data []byte) { slice := *p // 内容跟上面一样，但是取消了返回值 *p = slice } 实际上我们可以有更好的法子。如果我们按照的Write接口来写的话，\nfunc (p *ByteSlice) Write(data []byte) (n int, err error) { slice := *p // Again as above. *p = slice return len(data), nil } 那么*BytesSlice类型就满足标准库中的io.Writer接口， 可以实现很多便利的操作。例如调用fmt进行打印：\nvar b ByteSlice fmt.Fprintf(\u0026amp;b, \u0026#34;This hour has %d days\\n\u0026#34;, 7) 我们显示的采用取指针的方式传递\u0026amp;b是因为Writemethod的接收者是 *ByteSlice(因此*ByteSlice实现了接口io.Write)。\n对于method的接收者，指针和值的区别在于：前者只能由指针调用，后者可以由指针和值调用。\n之所以会有这条规则，是因为指针作为method接收者时，调用method可以修改接收者(的属性)； 如果在这种情况下，使用接收者的值调用method，会导致产生该接收者值的拷贝， 即menthod对接收者属性的修改会无效化，语言不允许出现这样的错误。\n但也有例外，当接收者的值可寻址的情况下，语言通过自动插入地址运算符来处理值调用接收者为指针 的情况。在我们的例子中，变量b是可寻址的，所以我们用b.Write调用Write——编译器 会把替我们把它改为\u0026amp;b。\n顺便说一下，在一个字节切片上使用Write的是标准库bytes.Buffer的核心。\n并不是所有变量都是可寻址的，map的值就不是。在标准库reflect.CanAddr中是这样解释的： A value is addressable if it is an element of a slice, an element of an addressable array, a field of an addressable struct, or the result of dereferencing a pointer. (此处存疑，有人说这只是针对反射部分的\u0026quot;可寻址\u0026quot;含义进行说明。仅做参考)\n尽管这一节原文很拗口，依然强烈建议比对原文以加深理解：\nWe pass the address of a ByteSlice because only *ByteSlice satisfies io.Writer. The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.\nThis rule arises because pointer methods can modify the receiver; invoking them on a value would cause the method to receive a copy of the value, so any modifications would be discarded. The language therefore disallows this mistake. There is a handy exception, though. When the value is addressable, the language takes care of the common case of invoking a pointer method on a value by inserting the address operator automatically. In our example, the variable b is addressable, so we can call its Write method with just b.Write. The compiler will rewrite that to (\u0026amp;b).Write for us.\n关于Go语言的方法接受者类型什么时候用值类型什么时候用指针类型，讨论的帖子非常多， 建议大家搜索比较下，实际操作中还是挺复杂的。\n接口及其他类型 # Interfaces 接口 # 在Go中提供了一种方式来描述对象的行为，这种方式被称为接口(interface)： 如果能满足某个对象的描述，就可以把它当作这个东西来用。之前我们已经见到了一些实现接口简单的例子； 如上面提到的通过String实现自定义输出。只包含有一或两个方法的接口在Go中很常见，且通常会给出一个 派生自该方法的名称，例如实现Writer接口的io.Writer。\n一个自定义类型可以实现多个接口。例如，一个集合(collection)实现了包含Len(),Less(i,j int) bool,Swap(i,j int) 的排序接口(sort包下的sort.Interface)，同样可以再实现自定义格式化输出：\ntype Sequence []int // Methods required by sort.Interface. func (s Sequence) Len() int { return len(s) } func (s Sequence) Less(i, j int) bool { return s[i] \u0026lt; s[j] } func (s Sequence) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // Method for printing - sorts the elements before printing. func (s Sequence) String() string { sort.Sort(s) str := \u0026#34;[\u0026#34; for i, elem := range s { if i \u0026gt; 0 { str += \u0026#34; \u0026#34; } str += fmt.Sprint(elem) } return str + \u0026#34;]\u0026#34; } Conversions 类型转换 # 上面的String方法中使用Sprint重新设置了输出样式，我们可以 在调用Sprint前将Sequence转换为[]int。\nfunc (s Sequence) String() string { sort.Sort(s) return fmt.Sprint([]int(s)) } 这是另一种在String方法中安全地调用Sprintf的示例，由于Sequence与[]int类型相同，所以在它们之间进行转换是合法的。 转换不会创建一个新的值，这只是暂时的行为，就好像现有的值有一个新的类型。(还有其他合法的转换，例如从整数到浮点，但这样会创建一个新的值。）)\n这是Go中的一个惯用法，用于转换表达式的类型以访问不同的方法集。我们可以使用以下方式来简化：\ntype Sequence []int // Method for printing - sorts the elements before printing func (s Sequence) String() string { sort.IntSlice(s).Sort() return fmt.Sprint([]int(s)) } 现在Sequence不需要再实现sort.Interface，我们通过使用类型转换的方式 也达到了同样的效果。这在实践中并不很常见，但很有效。\n接口类型转换和类型断言 # 我们之前看到的type switch曾经提到了这样的转换用法：对每一种条件(case)，某种意义上 将其转换为符合该条件的类型。这里有一个关于fmt.Printf代码如何使用switch将一个值转换为 字符串输出的简化版本。如果传入的是一个字符串，我们希望直接返回字符串；而如果传入的值实现了String方法，我们就 调用其自行实现的输出方法：\ntype Stringer interface { String() string } var value interface{} // Value provided by caller. switch str := value.(type) { case string: return str case Stringer: return str.String() } 第一个条件找到了具体的值，第二个条件将传入的接口转换为另一个接口。\n那么如果我们只关心单独一种类型呢？如果我们知道传入的接口持有字符串，想提取出内容怎么办？ 单类型断言(可以把上面的例子当作多类型断言)可以解决这个问题。单类型断言从接口中提取指定类型的值。 这个语法借用自type switch但不使用type关键字：\nvalue.(typeName) 返回结果是类型为typeName的全新值。该类型必须是接口所拥有的具体类型， 或者该值可以转换为的其他种接口类型。为了提取我们知道的字符串中的值，可以这样写：\nstr, ok := value.(string) if ok { fmt.Printf(\u0026#34;string value is: %q\\n\u0026#34;, str) } else { fmt.Printf(\u0026#34;value is not a string\\n\u0026#34;) } 如果类型断言失败，str变量依然会是个字符串类型，但是会是一个字符串的零值——即一个空字符串。\n下面这种写法，与上面的type switch相同：\nif str, ok := value.(string); ok { return str } else if str, ok := value.(Stringer); ok { return str.String() } 总结 # 一句话总结：构造时返回接口是最好的。另加密这块术语实在让人发疯，翻的不好请见谅。\n如果一个类型只是为了实现一个接口而存在，并且永远不会有超出这个接口的方法，那么就不需要导出类型本身，除了产生多余文档并没有其他作用。 If a type exists only to implement an interface and will never have exported methods beyond that interface, there is no need to export the type itself. Exporting just the interface makes it clear the value has no interesting behavior beyond what is described in the interface. It also avoids the need to repeat the documentation on every instance of a common method.\n在这种情况下，构造函数应该返回一个接口值而不是实现类型。例如，在标准库中hash包crc32.NewIEEE和adler32.New 返回了hash.Hash32接口。在Go程序中将CRC-32算法替换为Adler-32只需要改变构造函数调用，其余的代码不受算法改变的影响。In such cases, the constructor should return an interface value rather than the implementing type. As an example, in the hash libraries both crc32.NewIEEE and adler32.New return the interface type hash.Hash32. Substituting the CRC-32 algorithm for Adler-32 in a Go program requires only changing the constructor call; the rest of the code is unaffected by the change of algorithm.\nA similar approach allows the streaming cipher algorithms in the various crypto packages to be separated from the block ciphers they chain together. The Block interface in the crypto/cipher package specifies the behavior of a block cipher, which provides encryption of a single block of data. Then, by analogy with the bufio package, cipher packages that implement this interface can be used to construct streaming ciphers, represented by the Stream interface, without knowing the details of the block encryption.\ncrypto/cipher接口如下：\ntype Block interface { BlockSize() int Encrypt(src, dst []byte) Decrypt(src, dst []byte) } type Stream interface { XORKeyStream(dst, src []byte) } 这是计数器(CTR)的定义，它将块cipher转换为流cipher；注意块cipher的细节被抽象了：\n// NewCTR returns a Stream that encrypts/decrypts using the given Block in // counter mode. The length of iv must be the same as the Block\u0026#39;s block size. func NewCTR(block Block, iv []byte) Stream NewCTR不仅适用于一个特定的加密算法和数据源，还适用于Block接口和任何Stream的任何实现。 由于它们返回接口值，因此将CTR加密替换为其他加密模式是本地更改。 构造函数的调用必须是编辑过的，但是由于周围的代码只能将结果视为Stream，所以不会注意到其差异。NewCTR applies not just to one specific encryption algorithm and data source but to any implementation of the Block interface and any Stream. Because they return interface values, replacing CTR encryption with other encryption modes is a localized change. The constructor calls must be edited, but because the surrounding code must treat the result only as a Stream, it won\u0026rsquo;t notice the difference.\n接口和方法 # 由于几乎所有东西都可以附加方法，所以几乎任何东西都可以满足任何你想要实现的接口。 一个极具代表性的例子是http包中的Handler接口。只要实现了这个接口就可以接收HTTP请求。\ntype Handler interface { ServeHTTP(ResponseWriter, *Request) } ResponseWriter本身是一个接口，提供了访问返回给客户端的响应结果的(一些)方法。这些方法 包括标准的Witre方法，所以在使用io.Writer的地方就可以使用http.ResponseWriter。 http请求是一个结构体，包含了来自客户端的解析后的请求内容。\n为简洁起见，让我们忽略POST并假设HTTP请求总是GET; 简化不会影响处理程序的设置方式。 这是一个简单但完整的处理程序实现来计算页面被访问的次数。\n// Simple counter server. type Counter struct { n int } func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctr.n++ fmt.Fprintf(w, \u0026#34;counter = %d\\n\u0026#34;, ctr.n) } 请注意Fprintf如何将内容输出到http.ResponseWriter。作为参考，以下展示如何给服务器添加路由。\nimport \u0026#34;net/http\u0026#34; ... ctr := new(Counter) http.Handle(\u0026#34;/counter\u0026#34;, ctr) 但是为什么建立Counter结构体？我们明明只需要一个整数。(接收器需要是一个指针，这样增量对于调用者可见)\n// Simpler counter server. type Counter int func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { *ctr++ fmt.Fprintf(w, \u0026#34;counter = %d\\n\u0026#34;, *ctr) } 如果程序有一些内部状态，需要根据一个页面是否被访问来修改状态该怎么办？绑定一个channel。\n// A channel that sends a notification on each visit. // (Probably want the channel to be buffered.) type Chan chan *http.Request func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { ch \u0026lt;- req fmt.Fprint(w, \u0026#34;notification sent\u0026#34;) } 最后，假设我们想要显示调用服务器二进制文件时使用的参数。编写一个函数来打印参数很容易。\nfunc ArgServer() { fmt.Println(os.Args) } 既然我们可以为除了指针和接口外的任何类型定义方法，那么我们同样可以为函数编写一个方法，在标准库http 中是这样写的：\n// The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, req). func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) { f(w, req) } HandlerFunc实现了符合ServeHTTP接口的函数，所以f可以 接收HTTP请求。看看这个方法的实现：方法接收方是函数f，方法调用f。这可能看起来很奇怪，但与接收器是一个通道和在通道里发送方法没有什么不同。\n采用统一的思路，我们首先修改ArgServer：\n// Argument server. func ArgServer(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, os.Args) } ArgServer现在与HandlerFunc具有相同的签名了， 所以可以转换类型以达到方法方法的目的，就像我们之前将Sequence转换为IntSlice来访问IntSlice.Sort一样。\nhttp.Handle(\u0026#34;/args\u0026#34;, http.HandlerFunc(ArgServer)) 当有人访问页面/args时，该页面的处理程序会接收到ArgServer和HandlerFunc。 HTTP服务器将调用该类型的ServeHTTP方法，将ArgServer作为接收方，然后调用ArgServer（通过HandlerFunc.ServeHTTP中的调用f（w，req））。这样就实现了我们的目的——将系统参数显示出来。\n在本节中，我们从一个结构体，一个整数，一个通道和一个函数中创建了一个HTTP服务器， 所有这些能够实现，都是因为接口只是一组方法的组合，这些方法可以被定义为（几乎）任何类型。\n空标识符 _ # 我们在循环和map中已经几次提到了空标识符。空标识符可以在分配或声明任何类型的任何值时将其丢弃掉且没有副作用。 (但从业务角度来讲，丢弃err是自寻死路。)这有点像写入Unix的 /dev/null 文件：它代表一个只有写权限的值，用作 该变量的占位符，与实际值不相关。它拥有广泛的用途。\n多返回值中的空标识符 # 在range循环中使用空标识符是很常见的。\n如果需要在左侧分配多个值，但是其中一个值不会被使用，位于左侧的空标识符避免了创建虚拟变量的需要，并且明确指出该值将被丢弃。 例如，当调用一个返回值和错误的函数时，只有错误是重要的，使用空标识符丢弃不需要使用的值：\nif _, err := os.Stat(path); os.IsNotExist(err) { fmt.Printf(\u0026#34;%s does not exist\\n\u0026#34;, path) } 有时候你会看到代码丢弃了错误值以忽略错误，这种行为非常可怕。总是检查错误返回值——这是Go中错误存在的意义。\n// Bad! This code will crash if path does not exist. fi, _ := os.Stat(path) if fi.IsDir() { fmt.Printf(\u0026#34;%s is a directory\\n\u0026#34;, path) } 未使用的包引用和变量 # 如果导包或声明了变量却没有使用就会报错。未使用的导包行为或使程序膨胀编译变慢， 一个被初始化但没有被使用的变量会浪费算力，甚至引发更多的错误。然而，一个程序在开发过程中， 经常会出现未使用的导入和变量，以后可能还会用到，删除它们会很麻烦，为了让编译继续下去， 空标识符提供了很好的解决办法。\n这个写了一半的程序有两个未使用的导入和一个未使用的变量，编译会报告错误：\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; ) func main() { fd, err := os.Open(\u0026#34;test.go\u0026#34;) if err != nil { log.Fatal(err) } // TODO: use fd. } 要使编译器暂时不报unused err，使用空标识符来引用导入的包。类似地， 将未使用的变量fd分配给空标识符，这样就可以正常编译了。\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; ) var _ = fmt.Printf // For debugging; delete when done. var _ io.Reader // For debugging; delete when done. func main() { fd, err := os.Open(\u0026#34;test.go\u0026#34;) if err != nil { log.Fatal(err) } // TODO: use fd. _ = fd } 这样的临时措施应该尽快清理，如果短时间内无法进行，应该添加详细的注释，以便日后查找处理。\nImport for side effect # 为了使用包的副作用而进行导包\n前面例子中的fmt或io等未使用的导入应最终被使用或删除。 但是有时会为了副作用导入一个包。 例如，在其init函数期间，net/http/pprof 包会提供调试信息给HTTP处理程序。 它有一个导出的API，但大多数客户只需要处理程序注册并通过网页访问数据。 要仅为副作用导入包，请将包重命名为空标识符：\nimport _ \u0026#34;net/http/pprof\u0026#34; 在这种使用场景下，被导入的包不需要拥有名称，如果有而我们没有使用，编译器会报错。\n接口检查 # 就像我们在上面看到的，一个类型不需要显示的标明它实现了一个接口(像Java、Python会使用关键字或符号很明确的告诉使用者\u0026quot;看我实现了一个接口\u0026rdquo;)。 大多数接口转换是静态的，因此在编辑期会进行检查。例如，将*os.File传递给期望 os.File类型的函数不会通过编译，除非*os.File实现了io.Reader接口。\n有的接口会在运行时进行类型检查。例如在标准库的encoding/json包中，定义了Marshaler接口， 当JSON编码器接收到实现了该接口的值，编码器会调用marshaling方法把它转换为JSON而不是执行标准转换。 编码器会在运行时做类型断言：\nm, ok := val.(json.Marshaler) 如果确实有必要检查是否一个类型实现了某个接口，而不实际使用接口本身（也许作为错误检查的一部分），则可以使用空标识符来忽略类型声明的值：\nif _, ok := val.(json.Marshaler); ok { fmt.Printf(\u0026#34;value %v of type %T implements json.Marshaler\\n\u0026#34;, val, val) } 出现这种情况的一个地方是在实际满足接口的类型的包内需要保证。 如果一个类型（例如json.RawMessage）需要一个自定义的JSON表示， 它应该实现json.Marshaler，但是没有静态转换会导致编译器自动验证。 如果类型无意中不能满足接口，JSON编码器仍然可以工作，但不会使用自定义实现。 为了保证实现是正确的，可以这样做：\nvar _ json.Marshaler = (*RawMessage)(nil) 这是一个会在运行时检查的类型转换。一旦json.Marshaler接口发生变化， 这个包将无法正常编译，进而我们将会注意到它需要更新。\n在这个构造中空白标识符的出现表明该声明只存在于类型检查中，而不是创建一个变量。 不过，不要为满足接口的每种类型做这个。 按照惯例，只有在代码中不存在静态转换的情况下，才会使用这种声明，这种情况很少见。原文是：The appearance of the blank identifier in this construct indicates that the declaration exists only for the type checking, not to create a variable. Don\u0026rsquo;t do this for every type that satisfies an interface, though. By convention, such declarations are only used when there are no static conversions already present in the code, which is a rare event.\nEmbedding # 结构体的组合嵌入\nGo并不提供典型的，类型驱动的子类化概念，但是它有能力通过在结构或接口中嵌入类型来实现。\n接口的组合十分简单。我们之前提到过 io.Reader 和 io.Writer：\ntype Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } io包同样导出了其他几个接口，这些接口制定了可以实现多个多个这种方法的对象。 例如，io.ReadWriter接口包含了 Read 和 Write。我们可以通过明确地列出两个方法来实现io.ReadWriter，但是嵌入两个接口以形成新的接口更容易和更具启发性，如下所示：\n// ReadWriter is the interface that combines the Reader and Writer interfaces. type ReadWriter interface { Reader Writer } 一个ReadWriter既可以做Reader的任务也可以做Writer的任务，它是两个接口的组合(必须是不想交的方法集合)。只有接口可以嵌入接口。\n同样的思想也可以适用于结构体，但影响更深远。bufio包有两个结构体类型 bufio.Reader 和 bufio.Writer， 每个都实现了来自io包的类似接口。同时bufio实现了带有缓冲的读写器(buffered reader/writer)，它通过将 读写器组合到一个结构体中实现：它在结构体中列出了类型但没有给出它们的字段名：\n// ReadWriter stores pointers to a Reader and a Writer. // It implements io.ReadWriter. type ReadWriter struct { *Reader // *bufio.Reader *Writer // *bufio.Writer } 嵌入的元素是指向结构体的指针，所以必须初始化为指向有效的结构体才能使用。\ntype ReadWriter struct { reader *Reader writer *Writer } 为了满足io接口，我们还需要提供如下方法：\nfunc (rw *ReadWriter) Read(p []byte) (n int, err error) { return rw.reader.Read(p) } 这样做开销很小，而且不仅具有 bufio.Reader 和 bufio.Writer 方法，还 满足三个接口io.Reader, io.Writer, 和 io.ReadWriter。\n当我们嵌入一个类型时，这个类型的方法变成了外部类型的方法，但是当它们被调用时，方法的接收者是内部类型，而不是外部类型。 在我们的例子中，当调用bufio.ReadWriter的Read方法时，其效果与上面写出的转发方法完全相同; 接收器是ReadWriter的reader段，而不是ReadWriter本身。\n这个例子显示了一个常规的命名字段和一个匿名嵌入字段。\nJob类型现在拥有Log, Logf 和 *log.Logger中的函数。我们可以给Logger一个字段名，但 现在没必要这么做。现在初始化后我们可以使用job来执行：\njob.Log(\u0026ldquo;starting now\u0026hellip;\u0026rdquo;)\nLogger是Job中的常规字段，所以我们可以在Job的构造函数中初始化它：\nfunc NewJob(command string, logger *log.Logger) *Job { return \u0026amp;Job{command, logger} } 或者\njob := \u0026amp;Job{command, log.New(os.Stderr, \u0026#34;Job: \u0026#34;, log.Ldate)} 如果我们需要直接引用一个嵌入字段，忽略包限定符的字段的类型名称将作为字段名称，就像我们的ReaderWriter结构的Read方法中那样。\n在这里，如果我们需要访问一个Job变量job的*log.Logger，我们会写job.Logger，如果我们想要改进Logger的方法，这会很有用。\nfunc (job *Job) Logf(format string, args ...interface{}) { job.Logger.Logf(\u0026#34;%q: %s\u0026#34;, job.Command, fmt.Sprintf(format, args...)) } 组合嵌入不同类型引入了名称冲突的问题，但解决它们很简单。首先，字段或方法X在类型的更深层嵌套部分隐藏任何其他项目X.如果log.Logger包含一个名为Command的字段或方法，那么Job的命令字段将会占主导地位。\n其次，如果相同的名字出现在相同的嵌套层次上，通常意味着错误的产生;如果Job结构包含另一个称为Logger的字段或方法，那么嵌入log.Logger会是错误的。 但是，如果在类型定义之外的程序中从不引用到重复的名称，那么就没问题。这提供了一些保护，防止外部嵌入类型的变化; 如果添加的字段与另一个子类型中的一个字段发生冲突（如果两个字段均未曾使用过），则没有问题。\n并发 # Share by communicating # 并发编程是个很大的话题，而Go对支持并发有独到的亮点。\n在许多环境中，通过共享变量的方式，在并发编程中对其进行细致化的访问操作是很困难的。 Go鼓励使用一种不同的方式，即通过channel传递需要共享的值，而非在单独的执行线程中共享。 在任何时候，只有一个goroutine(go协程)可以访问这个值。使用这种方式，数据竞争就不会发生。 为了鼓励这种思维方式，我们把它简化为一个口号：\nDo not communicate by sharing memory; instead, share memory by communicating.\n不要通过共享内存来通讯；应当通过通讯来共享内存\n这种思路可以被应用的地方很多。例如，引用计数可以通过在整数变量周围包裹一个互斥体来完成， 但是作为一个更高层次的方法， 使用channel来控制访问使编写清晰、正确的程序变得更加容易。\n这种模式可以按在一个CPU上运行的典型的单线程程序来考虑——它不需要使用同步原语(synchronization primitives)， 现在在这样的条件下再运行一个同样不需要同步的实例——现在让他们两个通讯，如果通讯过程是同步的， 他们依然不需要采用其他的同步方式或工具。Unix的pipelines就是这样的典型实现。尽管Go的并发 思想源自Hoare的通讯顺序进程(CSP)，但也可以被看作是Unix管道的类型安全的泛化。\nGoroutines # 被称为 goroutines(go协程) 是因为其他已经被大家熟知的名词——线程threads 协程coroutines 进程processes 等等——这些叫法都无法传达goroutines准确的内涵。goroutines有一个简单的模型：它是一个在同一地址空间中 与其他goroutines同时执行的函数。他是轻量级的，仅比堆栈空间的分配开销多一点点。而堆栈在分配初期 占用很小，使用代价低廉，只是随着分配、释放堆存储空间才逐渐增大。\ngoroutines 可以被多路复用到多个OS线程，所以如果某个发生阻塞，比如在等待I/O的时候， 其他的会继续运行。这样的设计隐藏了许多线程创建和管理的复杂性。\n用go关键字在一个函数或方法调用前添加前缀，以在新的goroutine中运行调用。 当调用完毕，goroutine会自动退出，这个效果和在后台运行命令的Unix shell的\u0026amp;符号相似。\ngo list.Sort() // 并发运行 list.Sort; 别等它. 可以在这样的场景下很方便的使用：\nfunc Announce(message string, delay time.Duration) { go func() { time.Sleep(delay) fmt.Println(message) }() //注意这一行的括号，调用匿名函数时必须写 } 在Go中这样可以实现闭包：这样就确保了函数引用的变量正常使用。\n这些例子不太实际，因为函数没有办法传递完成信号。为此，我们需要使用channel。\nChannels # 类似于map,channel使用make来创建，返回的值是底层数据结构的引用。 如果提供了一个可选的整数参数，它将设置通道的缓冲区大小。对于无缓冲或同步通道，默认值为零。\nci := make(chan int) // unbuffered channel of integers cj := make(chan int, 0) // unbuffered channel of integers cs := make(chan *os.File, 100) // buffered channel of pointers to Files 无缓冲的渠道将通信（交换数据）与同步相结合， 确保两个goroutines在计算时处于已知状态。\n对于channel的使用有很多惯用法。在之前的小节我们实现过一个排序。使用channel 可以允许启动的goroutine等待排序结束。\nc := make(chan int) // Allocate a channel. // Start the sort in a goroutine; when it completes, signal on the channel. go func() { list.Sort() c \u0026lt;- 1 // Send a signal; value does not matter. }() doSomethingForAWhile() \u0026lt;-c // Wait for sort to finish; discard sent value. 在有数据可以接收之前，接收器总是阻塞的。如果channel没有缓冲，则发送器也会阻塞， 直到接收器接收到传递的值。如果channel有一个缓冲区，发送器只会在所有值全部被复制到缓冲区 后，才发生阻塞；如果缓冲区已满，则会等待某个接收器检索其中的数据。原文：Receivers always block until there is data to receive. If the channel is unbuffered, the sender blocks until the receiver has received the value. If the channel has a buffer, the sender blocks only until the value has been copied to the buffer; if the buffer is full, this means waiting until some receiver has retrieved a value.\n一个拥有缓冲区的channel可以被当作信号来使用，例如限流。在这个例子里，传入的请求被传递给句柄， 它将一个值发送到通道，处理请求，然后从通道接收一个值，为下一个使用者准备好“信号量”。 channel的缓冲区容量限制了同时处理请求的数量。\nvar sem = make(chan int, MaxOutstanding) func handle(r *Request) { sem \u0026lt;- 1 // Wait for active queue to drain. process(r) // May take a long time. \u0026lt;-sem // Done; enable next request to run. } func Serve(queue chan *Request) { for { req := \u0026lt;-queue go handle(req) // Don\u0026#39;t wait for handle to finish. } } 一旦MaxOutstanding处理程序正在执行process， 将阻塞任何尝试发送到已填充的通道缓冲区的行为， 直到其中一个现有的处理程序完成并从缓冲区被接收。\n这个思路存在一个问题：Serve为每个传入的请求创建新的goroutine。于是，如果请求速度太快， 程序可能会消耗无限的资源。我们可以通过改变Serve的使用门槛来解决这个缺陷。注意这里有一个 我们随后会修复的错误：\nfunc Serve(queue chan *Request) { for req := range queue { sem \u0026lt;- 1 go func() { process(req) // Buggy; see explanation below. \u0026lt;-sem }() } } 这个问题出在for循环——循环变量会被每个迭代重复使用——所以req变量是被所有goroutine 共享的。这里有一个解决方案，将req的值作为参数传递给goroutine中的闭包：：\nfunc Serve(queue chan *Request) { for req := range queue { sem \u0026lt;- 1 go func(req *Request) { process(req) \u0026lt;-sem }(req) } } 比较这个版本和以前的版本，看看在关闭声明和运行方式上的区别。 另一个解决方案就是创建一个新名字相同的变量，如下所示：\nfunc Serve(queue chan *Request) { for req := range queue { req := req // Create new instance of req for the goroutine. sem \u0026lt;- 1 go func() { process(req) \u0026lt;-sem }() } } 这可能看起来很奇怪\nreq := req 但这是合法的，而且是很常见的惯用法。你得到一个新的变量，故意在本地隐藏循环变量， 这个新的变量对每个goroutine来说都是唯一的。\n回到我们之前的Serve问题，另一种管理资源的方法是，启动固定数量的goroutine， 来处理所有从channel中获取到的请求。goroutines的数量限制了同时处理的请求数量。 这个Serve函数还可以添加一个channel参数，它会被告知什么时候退出；在启动goroutine后，它会阻止从该channel继续接收请求。\nfunc handle(queue chan *Request) { for r := range queue { process(r) } } func Serve(clientRequests chan *Request, quit chan bool) { // Start handlers for i := 0; i \u0026lt; MaxOutstanding; i++ { go handle(clientRequests) } \u0026lt;-quit // Wait to be told to exit. } Channels of channels # 在channel中生成channel\nGo最重要的特性之一就是，channel可以被其他channel生成和传递。 这个特性的一个常见用途是实现安全的并行解复用。原文是：One of the most important properties of Go is that a channel is a first-class value that can be allocated and passed around like any other. A common use of this property is to implement safe, parallel demultiplexing.\n在上面的例子中，handle是一个理想化的处理器，但我们并没有定义它处理的类型。 如果这种类型含有包含了返回内容的channel，每个发送请求的客户端都能够提供自己的接收路径。\ntype Request struct { args []int f func([]int) int resultChan chan int } 结构体内包含一个带有参数的函数，以及请求后接收反馈的channel。\nfunc sum(a []int) (s int) { for _, v := range a { s += v } return } request := \u0026amp;Request{[]int{3, 4, 5}, sum, make(chan int)} // Send request clientRequests \u0026lt;- request // Wait for response. fmt.Printf(\u0026#34;answer: %d\\n\u0026#34;, \u0026lt;-request.resultChan) handle函数只需要做很小的改动： func handle(queue chan *Request) { for req := range queue { req.resultChan \u0026lt;- req.f(req.args) } } 在实际场景中，上面的代码还有很多的地方需要完善，现在咱们已经完成了一个简单的、能够限速的、 并行的非阻塞RPC系统，并且代码中没有使用互斥体(这样效率就比较高)。\nParallelization 并行 # 这种思想的另一种实现形式是在多个CPU内核之间进行并行计算。如果计算可以分解成相互独立的单独部分， 则可以并行化，每个部分使用channel发出完成信号。\n假设我们有个计算元素矢量的耗时操作，每一步操作的值是独立的，就像下面这样：\ntype Vector []float64 // Apply the operation to v[i], v[i+1] ... up to v[n-1]. func (v Vector) DoSome(i, n int, u Vector, c chan int) { for ; i \u0026lt; n; i++ { v[i] += u.Op(v[i]) } c \u0026lt;- 1 // signal that this piece is done } 我们独立地启动一个循环，每个CPU一个。他们可以以任何顺序完成，但没关系; 我们只需要在启动所有goroutine之后通过清空channel来计算完成信号。\nconst numCPU = 4 // number of CPU cores func (v Vector) DoAll(u Vector) { c := make(chan int, numCPU) // Buffering optional but sensible. for i := 0; i \u0026lt; numCPU; i++ { go v.DoSome(i*len(v)/numCPU, (i+1)*len(v)/numCPU, u, c) } // Drain the channel. for i := 0; i \u0026lt; numCPU; i++ { \u0026lt;-c // wait for one task to complete } // All done. } 与其为numCPU创建一个常量值，不如直接在运行时查询可以采用CPU数做计算。标准库 runtime.NumCPU返回了硬件CPU数，所以我们可以这样写\nvar numCPU = runtime.NumCPU() 同样还有个函数runtime.GOMAXPROCS，可以获取或设置用户指定的 内核数量以运行程序。其默认值与runtime.NumCPU相同，但是可以 通过设置类似名称的shell环境变量或通过调用具有正数的函数来覆盖。传入0代表只是查询， 所以如果我们使用用户的默认设置，可以这样写：\nvar numCPU = runtime.GOMAXPROCS(0) 一定不要与并发（concurrency）混淆，并行（parallelism）将程序构建为独立的运行组件， 利用多个CPU并行执行计算以提高效率。虽然Go的并发特性可以使一些问题很容易构建为并行计算， Go是并发语言，不是并行语言，并不是所有的并行化问题都适合Go的模型。 有关这种区别的进一步讨论，请参阅 博文中的讨论。原文是：Be sure not to confuse the ideas of concurrency—structuring a program as independently executing components—and parallelism—executing calculations in parallel for efficiency on multiple CPUs. Although the concurrency features of Go can make some problems easy to structure as parallel computations, Go is a concurrent language, not a parallel one, and not all parallelization problems fit Go\u0026rsquo;s model. For a discussion of the distinction, see the talk cited in this blog post.\nA leaky buffer # 并发编程的工具甚至可以使非并行的想法更容易表达。这里有一个取自rpc包的例子。 客户端程序循环接收来自某个源的数据，这个源可能来自网络。为了避免分配和释放缓冲区，我们保留一个空闲列表， 并使用一个缓冲channel来表示它。如果channel是空的，则分配一个新的缓冲区。一旦消息缓冲区准备就绪， 它就会被发送到serverChan。\nvar freeList = make(chan *Buffer, 100) var serverChan = make(chan *Buffer) func client() { for { var b *Buffer // Grab a buffer if available; allocate if not. select { case b = \u0026lt;-freeList: // Got one; nothing more to do. default: // None free, so allocate a new one. b = new(Buffer) } load(b) // Read next message from the net. serverChan \u0026lt;- b // Send to server. } } 服务端循环接收、处理来自客户端的消息，并返回至空闲列表。\nfunc server() { for { b := \u0026lt;-serverChan // Wait for work. process(b) // Reuse buffer if there\u0026#39;s room. select { case freeList \u0026lt;- b: // Buffer on free list; nothing more to do. default: // Free list full, just carry on. } } } 客户端会尝试从freeList中检索缓冲区；如果没有可用的，它分配一个新的。服务器发送给freeList会将b放回空闲列表中，除非列表已满，在这种情况下， 缓冲区被丢弃，被垃圾收集器收回。select语句中的default子句在没有其他情况准备就绪时执行， 这意味着select不会被阻塞。这个实现只需几行就可以建立一个清单_leaky bucket free list_， 依靠缓冲的channel和垃圾收集器进行记录。\n这一节翻译的有点词不达意， 查看leaky bucket更详细的解释， 欢迎大家指正。\n原文是：The client attempts to retrieve a buffer from freeList; if none is available, it allocates a fresh one. The server\u0026rsquo;s send to freeList puts b back on the free list unless the list is full, in which case the buffer is dropped on the floor to be reclaimed by the garbage collector. (The default clauses in the select statements execute when no other case is ready, meaning that the selects never block.) This implementation builds a leaky bucket free list in just a few lines, relying on the buffered channel and the garbage collector for bookkeeping.\n错误处理 # 在标准库的例子中，经常会返回一些错误提示给调用者。就像之前提到的，Go的多返回值机制可以 很容易的既返回操作结果也返回详细的错误信息描述。这是Go语言提倡的风格。例如，我们将看到， 标准库中os.Open不仅在失败时返回一个零指针，还会返回一个描述出错原因的值。\n在标准库中，错误的类型是error，是个很简单的内置接口：\ntype error interface { Error() string } 任何人可以用更丰富的模型自由的实现这个接口，不仅可以查看错误，还可以提供丰富的其他信息。 如前所述，除了通常的*os.File返回值之外，os.Open还会返回一个错误值。 如果文件打开成功，错误将是零，但是当有问题时，它将持有一个os.PathError：\n// PathError records an error and the operation and // file path that caused it. type PathError struct { Op string // \u0026#34;open\u0026#34;, \u0026#34;unlink\u0026#34;, etc. Path string // The associated file. Err error // Returned by the system call. } func (e *PathError) Error() string { return e.Op + \u0026#34; \u0026#34; + e.Path + \u0026#34;: \u0026#34; + e.Err.Error() } PathError内的Error方法生成了这样的字符串：\nopen /etc/passwx: no such file or directory 这种使用方式，包括有问题的文件名称，操作和触发的操作系统错误， 即使多层调用它的错误信息，在排查错误时也是极其有用的。它比普通的“没有这样的文件或目录”更具清晰明了。\n在可行的情况下，错误字符串应该标识它们的来源，例如通过命名产生错误的操作或包的前缀。 例如，在标准库包image中，由于未知格式导致的解码错误的字符串表示是“image：未知格式”。\n关心精确错误细节的调用者可以使用type switch或类型断言来查找特定错误并提取细节。 对于PathErrors，这可能包括检查内部Err字段是否存在可恢复的故障。\nfor try := 0; try \u0026lt; 2; try++ { file, err = os.Create(filename) if err == nil { return } if e, ok := err.(*os.PathError); ok \u0026amp;\u0026amp; e.Err == syscall.ENOSPC { deleteTempFiles() // Recover some space. continue } return } 第二个if使用了类型断言。如果断言失败，ok的值会是false，同时e会是nil。如果断言成功， ok的值是true，这意味着该错误就是*os.PathError类型。这样我们就可以使用e查看到有关错误的更多信息。\nPanic # 向调用者报告错误的常用方法，是将错误作为额外的返回值返回。标准库中的Read 方法是一个众所周知的例子，它返回一个字节数和错误。但是如果错误非常严重导致程序无法继续下去呢？\n系统内置函数panic就是为了解决这个问题而存在的，该函数接受任意类型的单个参数(通常是字符串)， 它会产生一个运行时错误，并停止程序。这是为了表明一种不可能的情况发生了，例如发生了无限循环。\n// A toy implementation of cube root using Newton\u0026#39;s method. func CubeRoot(x float64) float64 { z := x/3 // Arbitrary initial value for i := 0; i \u0026lt; 1e6; i++ { prevz := z z -= (z*z*z-x) / (3*z*z) if veryClose(z, prevz) { return z } } // A million iterations has not converged; something is wrong. panic(fmt.Sprintf(\u0026#34;CubeRoot(%g) did not converge\u0026#34;, x)) } 这只是个例子，真正提供给他人使用的库函数应该避免panic。如果问题可以被掩盖或者解决， 那么让事情继续运行而不是放弃整个程序总是更好的。一个可能的反例就是在初始化过程中： 如果库怎么都无法初始化起来，那真的可以说技术部恐慌业务部惊慌了。\nvar user = os.Getenv(\u0026#34;USER\u0026#34;) func init() { if user == \u0026#34;\u0026#34; { panic(\u0026#34;no value for $USER\u0026#34;) } } Recover # 当手动调用panic或出现隐式运行时错误，例如索引切片超出范围或失败类型断言时， 它立即停止当前函数的执行，并开始展开goroutine的堆栈，运行被defer修饰的延迟函数， 如果到达goroutine堆栈的顶部，程序就会死亡。 但是，可以使用内置函数recover来重新获得goroutine的控制权并恢复正常运行。\n调用recover后停止展开goroutine的堆栈，并把参数传递给panic 函数。由于在展开时运行的唯一代码位于延迟函数内部，因此recover仅在延迟函数中有用。\nrecover的一个常见用法是关闭服务器内的失败的goroutine， 而不杀死其他正在执行的正常goroutines\nfunc server(workChan \u0026lt;-chan *Work) { for work := range workChan { go safelyDo(work) } } func safelyDo(work *Work) { defer func() { if err := recover(); err != nil { log.Println(\u0026#34;work failed:\u0026#34;, err) } }() do(work) } 在这个例子里，如果 do(work) 出现panic，结果会被 log 打印，该 goroutine 会干净的退出而 不干扰到其他的 goroutine 。你不需要做任何其他的事情，只需要考虑recover 接下来的处理逻辑。\n除非直接从延迟函数中调用，否则recover总返回nil。延迟代码可以调用自己使用panic的库例程，并在不失败的情况下恢复。 例如，safeDo中的延迟函数在调用recover之前可能会调用日志记录函数，并且日志代码将不受panic状态的影响。\ndo函数（以及任何它所调用的）都可以通过调用panic来干净地摆脱任何不好的情况。 我们可以使用这个想法来简化复杂软件中的错误处理。 让我们看看regexp包的理想版本，它通过调用本地错误类型panic 来报告解析错误。\n// Error is the type of a parse error; it satisfies the error interface. type Error string func (e Error) Error() string { return string(e) } // error is a method of *Regexp that reports parsing errors by // panicking with an Error. func (regexp *Regexp) error(err string) { panic(Error(err)) } // Compile returns a parsed representation of the regular expression. func Compile(str string) (regexp *Regexp, err error) { regexp = new(Regexp) // doParse will panic if there is a parse error. defer func() { if e := recover(); e != nil { regexp = nil // Clear return value. err = e.(Error) // Will re-panic if not a parse error. } }() return regexp.doParse(str), nil } 如果doParse发生panic，恢复代码块块会将返回值设置为nil。 然后，它会检查，该问题是不是一个解析错误。 否则，断言类型将失败，导致运行时错误继续堆栈展开，就像没有任何中断它一样。 这种检查意味着出现了意想不到的情况，比如索引越界， 那么即使我们使用panic和恢复来处理分析错误，代码也会失败。\n在处理错误的位置，可以很容易地报告解析错误，而不必担心需要手动解析堆栈：\nif pos == 0 { re.error(\u0026#34;\u0026#39;*\u0026#39; illegal at start of expression\u0026#34;) } 虽然这种模式很有用，但只能在一个同包中使用。 上面的处理将内部的panic调用转化为错误值而不是将panic暴露给使用者。 这是一个值得遵循的规则。\n顺便说一下，在实际应用中，这种操作方式会改变panic的价值。 但是，新旧故障都会在崩溃报告中展现，所以问题的根本原因仍然是可见的。 因此，这种简单的处理方法足够用了。 但如果你只想显示原始值，需要编写更多的代码来过滤意外情况，并处理原始错误。 这是留给你的一个练习。\n","date":"December 16, 2018","externalUrl":null,"permalink":"/posts/go2/","section":"","summary":"文档介绍 # Go是一门年轻的语言。虽然它借鉴了现有语言的思想，但自身独有的特性使得与其他语系相近的语言在代码表现上有所不同。 将C++或Java程序直接转换为Go可能会产生令人不太满意的结果——这是思考方式上的不同。 从Go的角度思考问题可能产生一个运行正常但与C++、Java相比完全不同的程序。换句话说，为了写好Go程序， 重点在于理解Go的特性及惯用法，例如命名、格式化、项目结构搭建等，这样你的程序可读性就更强。","title":"Go进阶","type":"posts"},{"content":" Go学习指南\n官方提供了83个代码段，以介绍使用Go所需要用到的语法和概念。\n第一部分介绍基本的语法和数据结构;\n第二部分讨论方法和接口;\n第三部分介绍Go的并发原语。\n可以通过以下方式在本地离线学习,离线使用时所有代码样例均在你的机器上编译和运行，所以速度会更快：\n请先在本地 安装 Go 然后使用go get命令安装 gotour-zh:\ngo get github.com/Go-zh/tour/gotour 然后运行得到的 gotour 程序就可以了(./gotour是Mac系统或linux系统执行程序的方式，在windows系统下你可以在自己设置的 GOPATH工作目录下的bin文件夹内找到它)：\ncd $GOPATH/bin ./gotour 如果不想安装编辑器，可以前往 官方提供的在线编辑器(自备梯子)\nHello, 世界 # package main import \u0026#34;fmt\u0026#34; func main() { fmt.Println(\u0026#34;Hello, 世界\u0026#34;) } 包 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; ) func main() { fmt.Println(\u0026#34;My favorite number is\u0026#34;, rand.Intn(10)) } 每个 Go 程序都是由包组成的。\n程序运行的入口是包 main 。\n这个程序使用并导入了包 \u0026ldquo;fmt\u0026rdquo; 和 \u0026ldquo;math/rand\u0026rdquo; 。\n按照惯例，包名与导入路径的最后一个目录一致。例如，\u0026ldquo;math/rand\u0026rdquo; 包由 package rand 语句开始。\n注意：这个程序的运行环境是确定性的，因此 rand.Intn 每次都会返回相同的数字。 （为了得到不同的随机数，需要提供一个随机数种子，参阅 rand.Seed。）\n导入 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func main() { fmt.Printf(\u0026#34;Now you have %g problems.\u0026#34;, math.Sqrt(7)) } 这个代码用圆括号组合了导入，这是“打包”导入语句。\n同样可以编写多个导入语句，例如：\nimport \u0026#34;fmt\u0026#34; import \u0026#34;math\u0026#34; 不过使用打包的导入语句是更好的形式。\n导出名 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func main() { fmt.Println(math.pi) } 在 Go 中，首字母大写的名称是被导出的。\n在导入包之后，你只能访问包所导出的名字，任何未导出的名字是不能被包外的代码访问的。\nFoo 和 FOO 都是被导出的名称。名称 foo 是不会被导出的。\n执行代码，注意编译器报的错误。\n然后将 math.pi 改名为 math.Pi 再试着执行一下。\n函数 # package main import \u0026#34;fmt\u0026#34; func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 函数可以没有参数或接受多个参数。\n在这个例子中， add 接受两个 int 类型的参数。\n注意类型在变量名 之后 。\n（参考 这篇关于 Go 语法定义了解类型以这种形式出现的原因。）\n函数（续） # package main import \u0026#34;fmt\u0026#34; func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 当两个或多个连续的函数命名参数是同一类型，则除了最后一个类型之外，其他都可以省略。\n在这个例子中 ，\nx int, y int 被缩写为\nx, y int 多值返回 # package main import \u0026#34;fmt\u0026#34; func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap(\u0026#34;hello\u0026#34;, \u0026#34;world\u0026#34;) fmt.Println(a, b) } 函数可以返回任意数量的返回值。\nswap 函数返回了两个字符串。\n命名返回值 # package main import \u0026#34;fmt\u0026#34; func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return } func main() { fmt.Println(split(17)) } Go 的返回值可以被命名，并且就像在函数体开头声明的变量那样使用。\n返回值的名称应当具有一定的意义，可以作为文档使用。\n没有参数的 return 语句返回各个返回变量的当前值。这种用法被称作“裸”返回。\n直接返回语句仅应当用在像下面这样的短函数中。在长的函数中它们会影响代码的可读性\n变量 # package main import \u0026#34;fmt\u0026#34; var c, python, java bool func main() { var i int fmt.Println(i, c, python, java) } var 语句定义了一个变量的列表；跟函数的参数列表一样，类型在后面。\n就像在这个例子中看到的一样， var 语句可以定义在包或函数级别\n初始化变量 # package main import \u0026#34;fmt\u0026#34; var i, j int = 1, 2 func main() { var c, python, java = true, false, \u0026#34;no!\u0026#34; fmt.Println(i, j, c, python, java) } 变量定义可以包含初始值，每个变量对应一个。\n如果初始化是使用表达式，则可以省略类型；变量从初始值中获得类型。\n短声明变量 # package main import \u0026#34;fmt\u0026#34; func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, \u0026#34;no!\u0026#34; fmt.Println(i, j, k, c, python, java) } 在函数中， := 简洁赋值语句在明确类型的地方，可以用于替代 var 定义。\n函数外的每个语句都必须以关键字开始（ var 、 func 、等等）， := 结构不能使用在函数外。\n基本类型 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math/cmplx\u0026#34; ) var ( ToBe bool = false MaxInt uint64 = 1\u0026lt;\u0026lt;64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) func main() { const f = \u0026#34;%T(%v)\\\\n\u0026#34; fmt.Printf(f, ToBe, ToBe) fmt.Printf(f, MaxInt, MaxInt) fmt.Printf(f, z, z) } Go 有以下几种基本类型 Basic types\nbool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // uint8 的别名 rune // int32 的别名 // 代表一个Unicode码 float32 float64 complex64 complex128 这个例子演示了具有不同类型的变量。 同时与导入语句一样，变量的定义“打包”在一个语法块中。\nint，uint 和 uintptr 类型在32位的系统上一般是32位，而在64位系统上是64位。当你需要使用一个整数类型时，你应该首选 int，仅当有特别的理由才使用定长整数类型或者无符号整数类型。\n零值 # package main import \u0026#34;fmt\u0026#34; func main() { var i int var f float64 var b bool var s string fmt.Printf(\u0026#34;%v %v %v %q\\n\u0026#34;, i, f, b, s) } 变量在定义时没有明确的初始化时会赋值为 零值 。\n零值是：\n数值类型为 0 ， 布尔类型为 false ， 字符串为 \u0026quot;\u0026quot; （空字符串）。 类型转换 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func main() { var x, y int = 3, 4 var f float64 = math.Sqrt(float64(x*x + y*y)) var z uint = uint(f) fmt.Println(x, y, z) } 表达式 T(v) 将值 v 转换为类型 T 。\n一些关于数值的转换：\nvar i int = 42 var f float64 = float64(i) var u uint = uint(f) 或者，更加简单的形式：\ni := 42 f := float64(i) u := uint(f) 与 C 不同的是 Go 的在不同类型之间的项目赋值时需要显式转换。 试着移除例子中 float64 或 int 的转换看看会发生什么。\n类型推导 # package main import \u0026#34;fmt\u0026#34; func main() { v := 42 // change me! fmt.Printf(\u0026#34;v is of type %T\\n\u0026#34;, v) } 在定义一个变量却并不显式指定其类型时（使用 := 语法或者 var = 表达式语法）， 变量的类型由（等号）右侧的值推导得出。\n当右值定义了类型时，新变量的类型与其相同：\nvar i int j := i // j 也是一个 int 但是当右边包含了未指名类型的数字常量时，新的变量就可能是 int 、 float64 或 complex128 。 这取决于常量的精度：\ni := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128 尝试修改演示代码中 v 的初始值，并观察这是如何影响其类型的。\n常量 # package main import \u0026#34;fmt\u0026#34; const Pi = 3.14 func main() { const World = \u0026#34;世界\u0026#34; fmt.Println(\u0026#34;Hello\u0026#34;, World) fmt.Println(\u0026#34;Happy\u0026#34;, Pi, \u0026#34;Day\u0026#34;) const Truth = true fmt.Println(\u0026#34;Go rules?\u0026#34;, Truth) } 常量的定义与变量类似，只不过使用 const 关键字。\n常量可以是字符、字符串、布尔或数字类型的值。\n常量不能使用 := 语法定义。\n数值常量 # package main import \u0026#34;fmt\u0026#34; const ( Big = 1 \u0026lt;\u0026lt; 100 Small = Big \u0026gt;\u0026gt; 99 ) func needInt(x int) int { return x * 10 + 1 } func needFloat(x float64) float64 { return x * 0.1 } func main() { fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) } 数值常量是高精度的 值 。\n一个未指定类型的常量由上下文来决定其类型。\n也尝试一下输出 needInt(Big) 吧。\n（int 可以存放最大64位的整数，根据平台不同有时会更少。）\nfor # package main import \u0026#34;fmt\u0026#34; func main() { sum := 0 for i := 0; i \u0026lt; 10; i++ { sum += i } fmt.Println(sum) } Go 只有一种循环结构—— for 循环。\n基本的 for 循环包含三个由分号分开的组成部分：\n初始化语句：在第一次循环执行前被执行 循环条件表达式：每轮迭代开始前被求值 后置语句：每轮迭代后被执行 初始化语句一般是一个短变量声明，这里声明的变量仅在整个 for 循环语句可见。\n如果条件表达式的值变为 false，那么迭代将终止。\n注意：不像 C，Java，或者 Javascript 等其他语言，for 语句的三个组成部分 并不需要用括号括起来，但循环体必须用 { } 括起来。\nfor（续） # package main import \u0026#34;fmt\u0026#34; func main() { sum := 1 for ; sum \u0026lt; 1000; { sum += sum } fmt.Println(sum) } 循环初始化语句和后置语句都是可选的。\nfor 是 Go 的 “while” # package main import \u0026#34;fmt\u0026#34; func main() { sum := 1 for sum \u0026lt; 1000 { sum += sum } fmt.Println(sum) } C 的 while 在 Go 中叫做 for 。\n死循环 # package main func main() { for { } } 如果省略了循环条件，循环就不会结束，因此可以用更简洁地形式表达死循环。\nif # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func sqrt(x float64) string { if x \u0026lt; 0 { return sqrt(-x) + \u0026#34;i\u0026#34; } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) } 就像 for 循环一样，Go 的 if 语句也不要求用 ( ) 将条件括起来，同时， { } 还是必须有的。\nif 的便捷语句 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v \u0026lt; lim { return v } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } 跟 for 一样， if 语句可以在条件之前执行一个简单语句。\n由这个语句定义的变量的作用域仅在 if 范围之内。\n（在最后的 return 语句处使用 v 看看。）\nif 和 else # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v \u0026lt; lim { return v } else { fmt.Printf(\u0026#34;%g \u0026gt;= %g\\n\u0026#34;, v, lim) } // 这里开始就不能使用 v 了 return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } 在 if 的便捷语句定义的变量同样可以在任何对应的 else 块中使用。\n（提示：两个 pow 调用都在 main 调用 fmt.Println 前执行完毕了。）\n练习：循环和函数 # package main import ( \u0026#34;fmt\u0026#34; ) func Sqrt(x float64) float64 { } func main() { fmt.Println(Sqrt(2)) } 作为练习函数和循环的简单途径，用牛顿法实现开方函数。\n在这个例子中，牛顿法是通过选择一个初始点 z 然后重复这一过程求 Sqrt(x) 的近似值：\n为了做到这个，只需要重复计算 10 次，并且观察不同的值（1，2，3，……）是如何逐步逼近结果的。 然后，修改循环条件，使得当值停止改变（或改变非常小）的时候退出循环。观察迭代次数是否变化。结果与 math.Sqrt 接近吗？\n提示：定义并初始化一个浮点值，向其提供一个浮点语法或使用转换：\nz := float64(1) z := 1.0 switch # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;runtime\u0026#34; ) func main() { fmt.Print(\u0026#34;Go runs on \u0026#34;) switch os := runtime.GOOS; os { case \u0026#34;darwin\u0026#34;: fmt.Println(\u0026#34;OS X.\u0026#34;) case \u0026#34;linux\u0026#34;: fmt.Println(\u0026#34;Linux.\u0026#34;) default: // freebsd, openbsd, // plan9, windows... fmt.Printf(\u0026#34;%s.\u0026#34;, os) } } 你可能已经知道 switch 语句会长什么样了。\n除非以 fallthrough 语句结束，否则分支会自动终止。\nswitch 的执行顺序 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { fmt.Println(\u0026#34;When\u0026#39;s Saturday?\u0026#34;) today := time.Now().Weekday() switch time.Saturday { case today + 0: fmt.Println(\u0026#34;Today.\u0026#34;) case today + 1: fmt.Println(\u0026#34;Tomorrow.\u0026#34;) case today + 2: fmt.Println(\u0026#34;In two days.\u0026#34;) default: fmt.Println(\u0026#34;Too far away.\u0026#34;) } } switch 的条件从上到下的执行，当匹配成功的时候停止。\n（例如，\nswitch i { case 0: case f(): } 当 i==0 时不会调用 f 。）\n没有条件的 switch # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { t := time.Now() switch { case t.Hour() \u0026lt; 12: fmt.Println(\u0026#34;Good morning!\u0026#34;) case t.Hour() \u0026lt; 17: fmt.Println(\u0026#34;Good afternoon.\u0026#34;) default: fmt.Println(\u0026#34;Good evening.\u0026#34;) } } 没有条件的 switch 同 switch true 一样。\n这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。\ndefer # package main import \u0026#34;fmt\u0026#34; func main() { defer fmt.Println(\u0026#34;world\u0026#34;) fmt.Println(\u0026#34;hello\u0026#34;) } defer 语句会延迟函数的执行直到上层函数返回。\n延迟调用的参数会立刻生成，但是在上层函数返回前函数都不会被调用。\ndefer 栈 # package main import \u0026#34;fmt\u0026#34; func main() { fmt.Println(\u0026#34;counting\u0026#34;) for i := 0; i \u0026lt; 10; i++ { defer fmt.Println(i) } fmt.Println(\u0026#34;done\u0026#34;) } 延迟的函数调用被压入一个栈中。当函数返回时， 会按照后进先出的顺序调用被延迟的函数调用。\n指针 # package main import \u0026#34;fmt\u0026#34; func main() { i, j := 42, 2701 p := \u0026amp;i // point to i fmt.Println(*p) // read i through the pointer *p = 21 // set i through the pointer fmt.Println(i) // see the new value of i p = \u0026amp;j // point to j *p = *p / 37 // divide j through the pointer fmt.Println(j) // see the new value of j } Go 具有指针。 指针保存了变量的内存地址。\n类型 *T 是指向类型 T 的值的指针。其零值是 nil 。\nvar p *int \u0026amp; 符号会生成一个指向其作用对象的指针。\ni := 42 p = \u0026amp;i * 符号表示指针指向的底层的值。\nfmt.Println(*p) // 通过指针 p 读取 i *p = 21 // 通过指针 p 设置 i 这也就是通常所说的“间接引用”或“非直接引用”。\n与 C 不同，Go 没有指针运算。\n结构体 # package main import \u0026#34;fmt\u0026#34; type Vertex struct { X int Y int } func main() { fmt.Println(Vertex{1, 2}) } 一个结构体（ struct ）就是一个字段的集合。\n（而 type 的含义跟其字面意思相符。）\n结构体字段 # package main import \u0026#34;fmt\u0026#34; type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) } 结构体字段使用点号来访问。\n结构体指针 # package main import \u0026#34;fmt\u0026#34; type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} p := \u0026amp;v p.X = 1e9 fmt.Println(v) } 结构体字段可以通过结构体指针来访问。\n通过指针间接的访问是透明的。\n结构体文法 # package main import \u0026#34;fmt\u0026#34; type Vertex struct { X, Y int } var ( v1 = Vertex{1, 2} // 类型为 Vertex v2 = Vertex{X: 1} // Y:0 被省略 v3 = Vertex{} // X:0 和 Y:0 p = \u0026amp;Vertex{1, 2} // 类型为 *Vertex ) func main() { fmt.Println(v1, p, v2, v3) } 结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。\n使用 Name: 语法可以仅列出部分字段。（字段名的顺序无关。）\n特殊的前缀 \u0026amp; 返回一个指向结构体的指针\n数组 # package main import \u0026#34;fmt\u0026#34; func main() { var a [2]string a[0] = \u0026#34;Hello\u0026#34; a[1] = \u0026#34;World\u0026#34; fmt.Println(a[0], a[1]) fmt.Println(a) } 类型 [n]T 是一个有 n 个类型为 T 的值的数组。\n表达式\nvar a [10]int 定义变量 a 是一个有十个整数的数组。\n数组的长度是其类型的一部分，因此数组不能改变大小。 这看起来是一个制约，但是请不要担心； Go 提供了更加便利的方式来使用数组。\nslice # package main import \u0026#34;fmt\u0026#34; func main() { s := []int{2, 3, 5, 7, 11, 13} fmt.Println(\u0026#34;s ==\u0026#34;, s) for i := 0; i \u0026lt; len(s); i++ { fmt.Printf(\u0026#34;s[%d] == %d\\n\u0026#34;, i, s[i]) } } 一个 slice 会指向一个序列的值，并且包含了长度信息。\n[]T 是一个元素类型为 T 的 slice。\nlen(s) 返回 slice s 的长度。\nslice 的 slice # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;strings\u0026#34; ) func main() { // Create a tic-tac-toe board. game := [][]string{ []string{\u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;}, []string{\u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;}, []string{\u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;, \u0026#34;_\u0026#34;}, } // The players take turns. game[0][0] = \u0026#34;X\u0026#34; game[2][2] = \u0026#34;O\u0026#34; game[2][0] = \u0026#34;X\u0026#34; game[1][0] = \u0026#34;O\u0026#34; game[0][2] = \u0026#34;X\u0026#34; printBoard(game) } slice 可以包含任意的类型，包括另一个 slice。\n对 slice 切片 # package main import \u0026#34;fmt\u0026#34; func main() { s := []int{2, 3, 5, 7, 11, 13} fmt.Println(\u0026#34;s ==\u0026#34;, s) fmt.Println(\u0026#34;s[1:4] ==\u0026#34;, s[1:4]) // 省略下标代表从 0 开始 fmt.Println(\u0026#34;s[:3] ==\u0026#34;, s[:3]) // 省略上标代表到 len(s) 结束 fmt.Println(\u0026#34;s[4:] ==\u0026#34;, s[4:]) } slice 可以重新切片，创建一个新的 slice 值指向相同的数组。\n表达式\ns[lo:hi] 表示从 lo 到 hi-1 的 slice 元素，含前端，不包含后端。因此\ns[lo:lo] 是空的，而\ns[lo:lo+1] 有一个元素。\n构造 slice # package main import \u0026#34;fmt\u0026#34; func main() { a := make([]int, 5) printSlice(\u0026#34;a\u0026#34;, a) b := make([]int, 0, 5) printSlice(\u0026#34;b\u0026#34;, b) c := b[:2] printSlice(\u0026#34;c\u0026#34;, c) d := c[2:5] printSlice(\u0026#34;d\u0026#34;, d) } func printSlice(s string, x []int) { fmt.Printf(\u0026#34;%s len=%d cap=%d %v\\n\u0026#34;, s, len(x), cap(x), x) } slice 由函数 make 创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组：\na := make([]int, 5) // len(a)=5 为了指定容量，可传递第三个参数到 make：\nb := make([]int, 0, 5) // len(b)=0, cap(b)=5 b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4 nil slice # package main import \u0026#34;fmt\u0026#34; func main() { var z []int fmt.Println(z, len(z), cap(z)) if z == nil { fmt.Println(\u0026#34;nil!\u0026#34;) } } slice 的零值是 nil 。\n一个 nil 的 slice 的长度和容量是 0。\n向 slice 添加元素 # package main import \u0026#34;fmt\u0026#34; func main() { var a []int printSlice(\u0026#34;a\u0026#34;, a) // append works on nil slices. a = append(a, 0) printSlice(\u0026#34;a\u0026#34;, a) // the slice grows as needed. a = append(a, 1) printSlice(\u0026#34;a\u0026#34;, a) // we can add more than one element at a time. a = append(a, 2, 3, 4) printSlice(\u0026#34;a\u0026#34;, a) } func printSlice(s string, x []int) { fmt.Printf(\u0026#34;%s len=%d cap=%d %v\\n\u0026#34;, s, len(x), cap(x), x) } 向 slice 的末尾添加元素是一种常见的操作，因此 Go 提供了一个内建函数 append 。 内建函数的文档对 append 有详细介绍。\nfunc append(s []T, vs ...T) []T append 的第一个参数 s 是一个元素类型为 T 的 slice ，其余类型为 T 的值将会附加到该 slice 的末尾。\nappend 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。\n如果 s 的底层数组太小，而不能容纳所有值时，会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。\n（了解更多关于 slice 的内容，参阅文章 Go 切片：用法和本质。）\nrange # package main import \u0026#34;fmt\u0026#34; var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf(\u0026#34;2**%d = %d\\n\u0026#34;, i, v) } } for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。\n当使用 for 循环遍历一个 slice 时，每次迭代 range 将返回两个值。 第一个是当前下标（序号），第二个是该下标所对应元素的一个拷贝。\nrange（续） # package main import \u0026#34;fmt\u0026#34; func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 \u0026lt;\u0026lt; uint(i) } for _, value := range pow { fmt.Printf(\u0026#34;%d\\n\u0026#34;, value) } } 可以通过赋值给 _ 来忽略序号和值。\n如果只需要索引值，去掉 “ , value ” 的部分即可。\n练习：slice # package main import \u0026#34;golang.org/x/tour/pic\u0026#34; func Pic(dx, dy int) [][]uint8 { } func main() { pic.Show(Pic) } 实现 Pic 。它返回一个长度为 dy 的 slice，其中每个元素是一个长度为 dx 且元素类型为8位无符号整数的 slice。当你运行这个程序时， 它会将每个整数作为对应像素的灰度值（好吧，其实是蓝度）并显示这个 slice 所对应的图像。\n计算每个像素的灰度值的方法由你决定；几个有意思的选择包括 (x+y)/2、x*y 和 x^y 。\n（需要使用循环来分配 [][]uint8 中的每个 []uint8 。）\n（使用 uint8(intValue) 来在类型之间进行转换。）\nmap # package main import \u0026#34;fmt\u0026#34; type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m[\u0026#34;Bell Labs\u0026#34;] = Vertex{ 40.68433, -74.39967, } fmt.Println(m[\u0026#34;Bell Labs\u0026#34;]) } map 映射键到值。\nmap 在使用之前必须用 make 来创建；值为 nil 的 map 是空的，并且不能对其赋值。\nmap 的文法 # package main import \u0026#34;fmt\u0026#34; type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ \u0026#34;Bell Labs\u0026#34;: Vertex{ 40.68433, -74.39967, }, \u0026#34;Google\u0026#34;: Vertex{ 37.42202, -122.08408, }, } func main() { fmt.Println(m) } map 的文法跟结构体文法相似，不过必须有键名。\nmap 的文法（续） # package main import \u0026#34;fmt\u0026#34; type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ \u0026#34;Bell Labs\u0026#34;: {40.68433, -74.39967}, \u0026#34;Google\u0026#34;: {37.42202, -122.08408}, } func main() { fmt.Println(m) } 若顶级类型只是一个类型名，你可以在文法的元素中省略它。\n修改 map # package main import \u0026#34;fmt\u0026#34; func main() { m := make(map[string]int) m[\u0026#34;Answer\u0026#34;] = 42 fmt.Println(\u0026#34;The value:\u0026#34;, m[\u0026#34;Answer\u0026#34;]) m[\u0026#34;Answer\u0026#34;] = 48 fmt.Println(\u0026#34;The value:\u0026#34;, m[\u0026#34;Answer\u0026#34;]) delete(m, \u0026#34;Answer\u0026#34;) fmt.Println(\u0026#34;The value:\u0026#34;, m[\u0026#34;Answer\u0026#34;]) v, ok := m[\u0026#34;Answer\u0026#34;] fmt.Println(\u0026#34;The value:\u0026#34;, v, \u0026#34;Present?\u0026#34;, ok) } 在 map m 中插入或修改一个元素：\nm[key] = elem 获得元素：\nelem = m[key] 删除元素：\ndelete(m, key) 通过双赋值检测某个键存在：\nelem, ok = m[key] 如果 key 在 m 中， ok 为 true。否则， ok 为 false，并且 elem 是 map 的元素类型的零值。\n同样的，当从 map 中读取某个不存在的键时，结果是 map 的元素类型的零值。\n练习：map # package main import ( \u0026#34;golang.org/x/tour/wc\u0026#34; ) func WordCount(s string) map[string]int { return map[string]int{\u0026#34;x\u0026#34;: 1} } func main() { wc.Test(WordCount) } 实现 WordCount。它应当返回一个含有 s 中每个 “词” 个数的 map。函数 wc.Test 针对这个函数执行一个测试用例，并输出成功还是失败。\n函数值 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) func compute(fn func(float64, float64) float64) float64 { return fn(3, 4) } func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x * x + y * y) } fmt.Println(hypot(5, 12)) fmt.Println(compute(hypot)) fmt.Println(compute(math.Pow)) } 函数也是值。他们可以像其他值一样传递，比如，函数值可以作为函数的参数或者返回值。\n函数的闭包 # package main import \u0026#34;fmt\u0026#34; func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i \u0026lt; 10; i++ { fmt.Println( pos(i), neg(-2 * i), ) } } Go 函数可以是一个闭包。闭包是一个函数值，它引用了函数体之外的变量。 这个函数可以对这个引用的变量进行访问和赋值；换句话说这个函数被“绑定”在这个变量上。\n例如，函数 adder 返回一个闭包。每个返回的闭包都被绑定到其各自的 sum 变量上。\n练习：斐波纳契闭包 # package main import \u0026#34;fmt\u0026#34; // fibonacci 函数会返回一个返回 int 的函数。 func fibonacci() func() int { } func main() { f := fibonacci() for i := 0; i \u0026lt; 10; i++ { fmt.Println(f()) } } 现在来通过函数做些有趣的事情。\n实现一个 fibonacci 函数，返回一个函数（一个闭包）可以返回连续的斐波纳契数。\n方法 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X * v.X + v.Y * v.Y) } func main() { v := \u0026amp;Vertex{3, 4} fmt.Println(v.Abs()) } Go 没有类。然而，仍然可以在结构体类型上定义方法。\n方法接收者 出现在 func 关键字和方法名之间的参数中。\n方法（续） # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) type MyFloat float64 func (f MyFloat) Abs() float64 { if f \u0026lt; 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) } 你可以对包中的 任意 类型定义任意方法，而不仅仅是针对结构体。\n但是，不能对来自其他包的类型或基础类型定义方法。\n接收者为指针的方法 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X * v.X + v.Y * v.Y) } func main() { v := \u0026amp;Vertex{3, 4} fmt.Printf(\u0026#34;Before scaling: %v, Abs: %v\\n\u0026#34;, v, v.Abs()) v.Scale(5) fmt.Printf(\u0026#34;After scaling: %v, Abs: %v\\n\u0026#34;, v, v.Abs()) } 方法可以与命名类型或命名类型的指针关联。\n刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上，而另一个在 MyFloat 值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值（如果值类型是大的结构体的话会更有效率）。其次，方法可以修改接收者指向的值。\n尝试修改 Abs 的定义，同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。\n当 v 是 Vertex 的时候 Scale 方法没有任何作用。Scale 修改 v。当 v 是一个值（非指针），方法看到的是 Vertex 的副本，并且无法修改原始值。\nAbs 的工作方式是一样的。只不过，仅仅读取 v。所以读取的是原始值（通过指针）还是那个值的副本并没有关系。\n接口 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) type Abser interface { Abs() float64 } func main() { var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // a MyFloat 实现了 Abser a = \u0026amp;v // a *Vertex 实现了 Abser // 下面一行，v 是一个 Vertex（而不是 *Vertex） // 所以没有实现 Abser。 a = v fmt.Println(a.Abs()) } type MyFloat float64 func (f MyFloat) Abs() float64 { if f \u0026lt; 0 { return float64(-f) } return float64(f) } type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X * v.X + v.Y * v.Y) } 接口类型是由一组方法定义的集合。\n接口类型的值可以存放实现这些方法的任何值。\n注意： 示例代码的 22 行存在一个错误。 由于 Abs 只定义在 *Vertex（指针类型）上， 所以 Vertex（值类型）不满足 Abser。\n隐式接口 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; ) type Reader interface { Read(b []byte) (n int, err error) } type Writer interface { Write(b []byte) (n int, err error) } type ReadWriter interface { Reader Writer } func main() { var w Writer // os.Stdout 实现了 Writer w = os.Stdout fmt.Fprintf(w, \u0026#34;hello, writer\\n\u0026#34;) } 类型通过实现那些方法来实现接口。 没有显式声明的必要；所以也就没有关键字“implements“。\n隐式接口解藕了实现接口的包和定义接口的包：互不依赖。\n因此，也就无需在每一个实现上增加新的接口名称，这样同时也鼓励了明确的接口定义。\n包 io 定义了 Reader 和 Writer；其实不一定要这么做。\nStringers # package main import \u0026#34;fmt\u0026#34; type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf(\u0026#34;%v (%v years)\u0026#34;, p.Name, p.Age) } func main() { a := Person{\u0026#34;Arthur Dent\u0026#34;, 42} z := Person{\u0026#34;Zaphod Beeblebrox\u0026#34;, 9001} fmt.Println(a, z) } 一个普遍存在的接口是 fmt 包中定义的 Stringer。\ntype Stringer interface { String() string } Stringer 是一个可以用字符串描述自己的类型。fmt包 （还有许多其他包）使用这个来进行输出。\n练习：Stringers # package main import \u0026#34;fmt\u0026#34; type IPAddr [4]byte // TODO: Add a \u0026#34;String() string\u0026#34; method to IPAddr. func main() { addrs := map[string]IPAddr{ \u0026#34;loopback\u0026#34;: {127, 0, 0, 1}, \u0026#34;googleDNS\u0026#34;: {8, 8, 8, 8}, } for n, a := range addrs { fmt.Printf(\u0026#34;%v: %v\\n\u0026#34;, n, a) } } 让 IPAddr 类型实现 fmt.Stringer 以便用点分格式输出地址。\n例如，IPAddr{1, 2, 3, 4} 应当输出 \u0026ldquo;1.2.3.4\u0026rdquo;。\n错误 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf(\u0026#34;at %v, %s\u0026#34;, e.When, e.What) } func run() error { return \u0026amp;MyError{ time.Now(), \u0026#34;it didn\u0026#39;t work\u0026#34;, } } func main() { if err := run(); err != nil { fmt.Println(err) } } Go 程序使用 error 值来表示错误状态。\n与 fmt.Stringer 类似， error 类型是一个内建接口：\ntype error interface { Error() string } （与 fmt.Stringer 类似，fmt 包在输出时也会试图匹配 error。）\n通常函数会返回一个 error 值，调用的它的代码应当判断这个错误是否等于 nil， 来进行错误处理。\ni, err := strconv.Atoi(\u0026#34;42\u0026#34;) if err != nil { fmt.Printf(\u0026#34;couldn\u0026#39;t convert number: %v\\n\u0026#34;, err) return } fmt.Println(\u0026#34;Converted integer:\u0026#34;, i) error 为 nil 时表示成功；非 nil 的 error 表示错误。\n练习：错误 # package main import ( \u0026#34;fmt\u0026#34; ) func Sqrt(x float64) (float64, error) { return 0, nil } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) } 从先前的练习中复制 Sqrt 函数，并修改使其返回 error 值。\n由于不支持复数，当 Sqrt 接收到一个负数时，应当返回一个非 nil 的错误值。\n创建一个新类型\ntype ErrNegativeSqrt float64 为其实现\nfunc (e ErrNegativeSqrt) Error() string 使其成为一个 error， 该方法就可以让 ErrNegativeSqrt(-2).Error() 返回 \u0026quot;cannot Sqrt negative number: -2\u0026quot;。\n注意： 在 Error 方法内调用 fmt.Sprint(e) 将会让程序陷入死循环。可以通过先转换 e 来避免这个问题：fmt.Sprint(float64(e))。请思考这是为什么呢？\n修改 Sqrt 函数，使其接受一个负数时，返回 ErrNegativeSqrt 值。\nReaders # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;strings\u0026#34; ) func main() { r := strings.NewReader(\u0026#34;Hello, Reader!\u0026#34;) b := make([]byte, 8) for { n, err := r.Read(b) fmt.Printf(\u0026#34;n = %v err = %v b = %v\\n\u0026#34;, n, err, b) fmt.Printf(\u0026#34;b[:n] = %q\\n\u0026#34;, b[:n]) if err == io.EOF { break } } } io 包指定了 io.Reader 接口， 它表示从数据流结尾读取。\nGo 标准库包含了这个接口的许多实现， 包括文件、网络连接、压缩、加密等等。\nio.Reader 接口有一个 Read 方法：\nfunc (T) Read(b []byte) (n int, err error) Read 用数据填充指定的字节 slice，并且返回填充的字节数和错误信息。 在遇到数据流结尾时，返回 io.EOF 错误。\n例子代码创建了一个 strings.Reader。 并且以每次 8 字节的速度读取它的输出。\n练习：Reader # package main import \u0026#34;golang.org/x/tour/reader\u0026#34; type MyReader struct{} // TODO: Add a Read(\\[\\]byte) (int, error) method to MyReader. func main() { reader.Validate(MyReader{}) } 实现一个 Reader 类型，它不断生成 ASCII 字符 \u0026lsquo;A\u0026rsquo; 的流。\n练习：rot13Reader # package main import ( \u0026#34;io\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strings\u0026#34; ) type rot13Reader struct { r io.Reader } func main() { s := strings.NewReader(\u0026#34;Lbh penpxrq gur pbqr!\u0026#34;) r := rot13Reader{s} io.Copy(os.Stdout, \u0026amp;r) } 一个常见模式是 io.Reader 包裹另一个 io.Reader，然后通过某种形式修改数据流。\n例如，gzip.NewReader 函数接受 io.Reader（压缩的数据流）并且返回同样实现了 io.Reader 的 *gzip.Reader（解压缩后的数据流）。\n编写一个实现了 io.Reader 的 rot13Reader， 并从一个 io.Reader 读取， 利用 rot13 代换密码对数据流进行修改。\n已经帮你构造了 rot13Reader 类型。 通过实现 Read 方法使其匹配 io.Reader。\nWeb 服务器 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;log\u0026#34; \u0026#34;net/http\u0026#34; ) type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, \u0026#34;Hello!\u0026#34;) } func main() { var h Hello err := http.ListenAndServe(\u0026#34;localhost:4000\u0026#34;, h) if err != nil { log.Fatal(err) } } 包 http 通过任何实现了 http.Handler 的值来响应 HTTP 请求：\npackage http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) } 在这个例子中，类型 Hello 实现了 http.Handler。\n访问 http://localhost:4000/ 会看到来自程序的问候。\n练习：HTTP 处理 # package main import ( \u0026#34;log\u0026#34; \u0026#34;net/http\u0026#34; ) func main() { // your http.Handle calls here log.Fatal(http.ListenAndServe(\u0026#34;localhost:4000\u0026#34;, nil)) } 实现下面的类型，并在其上定义 ServeHTTP 方法。在 web 服务器中注册它们来处理指定的路径。\ntype String string type Struct struct { Greeting string Punct string Who string } 例如，可以使用如下方式注册处理方法：\nhttp.Handle(\u0026#34;/string\u0026#34;, String(\u0026#34;I\u0026#39;m a frayed knot.\u0026#34;)) http.Handle(\u0026#34;/struct\u0026#34;, \u0026amp;Struct{\u0026#34;Hello\u0026#34;, \u0026#34;:\u0026#34;, \u0026#34;Gophers!\u0026#34;}) 在启动你的 http 服务器后，你将能够访问： http://localhost:4000/string 和 http://localhost:4000/struct.\n图片 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;image\u0026#34; ) func main() { m := image.NewRGBA(image.Rect(0, 0, 100, 100)) fmt.Println(m.Bounds()) fmt.Println(m.At(0, 0).RGBA()) } Package image 定义了 Image 接口：\npackage image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color } 注意：Bounds 方法的 Rectangle 返回值实际上是一个 image.Rectangle， 其定义在 image 包中。\ncolor.Color 和 color.Model 也是接口，但是通常因为直接使用预定义的实现 image.RGBA 和 image.RGBAModel 而被忽视了。这些接口和类型由image/color 包定义。\n练习：图片 # package main import \u0026#34;golang.org/x/tour/pic\u0026#34; type Image struct{} func main() { m := Image{} pic.ShowImage(m) } 还记得之前编写的图片生成器吗？现在来另外编写一个，不过这次将会返回 image.Image 来代替 slice 的数据。\n自定义的 Image 类型，要实现必要的方法，并且调用 pic.ShowImage。\nBounds 应当返回一个 image.Rectangle，例如 image.Rect(0, 0, w, h)。\nColorModel 应当返回 color.RGBAModel。\nAt 应当返回一个颜色；在这个例子里，在最后一个图片生成器的值 v 匹配 `color.RGBA{v, v, 255, 255}`\ngoroutine # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func say(s string) { for i := 0; i \u0026lt; 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say(\u0026#34;world\u0026#34;) say(\u0026#34;hello\u0026#34;) } goroutine 是由 Go 运行时环境管理的轻量级线程。\ngo f(x, y, z) 开启一个新的 goroutine 执行\nf(x, y, z) f，x，y 和 z 是当前 goroutine 中定义的，但是在新的 goroutine 中运行 f。\ngoroutine 在相同的地址空间中运行，因此访问共享内存必须进行同步。sync 提供了这种可能，不过在 Go 中并不经常用到，因为有其他的办法。（在接下来的内容中会涉及到。）\nchannel # package main import \u0026#34;fmt\u0026#34; func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c \u0026lt;- sum // 将和送入 c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := \u0026lt;-c, \u0026lt;-c // 从 c 中获取 fmt.Println(x, y, x+y) } channel 是有类型的管道，可以用 channel 操作符 \u0026lt;- 对其发送或者接收值。\nch \u0026lt;- v // 将 v 送入 channel ch。 v := \u0026lt;-ch // 从 ch 接收，并且赋值给 v。 （“箭头”就是数据流的方向。）\n和 map 与 slice 一样，channel 使用前必须创建：\nch := make(chan int) 默认情况下，在另一端准备好之前，发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。\n缓冲 channel # package main import \u0026#34;fmt\u0026#34; func main() { ch := make(chan int, 2) ch \u0026lt;- 1 ch \u0026lt;- 2 fmt.Println(\u0026lt;-ch) fmt.Println(\u0026lt;-ch) } ch := make(chan int, 100) 向带缓冲的 channel 发送数据的时候，只有在缓冲区满的时候才会阻塞。 而当缓冲区为空的时候接收操作会阻塞。\n修改例子使得缓冲区被填满，然后看看会发生什么。\nrange 和 close # package main import ( \u0026#34;fmt\u0026#34; ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i \u0026lt; n; i++ { c \u0026lt;- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } range 和 close 发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭：当没有值可以接收并且 channel 已经被关闭，那么经过\nv, ok := \u0026lt;-ch 之后 ok 会被设置为 false。\n循环 for i := range c 会不断从 channel 接收值，直到它被关闭。\n注意： 只有发送者才能关闭 channel，而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意： channel 与文件不同；通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭，例如中断一个 range。\nselect # package main import \u0026#34;fmt\u0026#34; func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c \u0026lt;- x: x, y = y, x+y case \u0026lt;-quit: fmt.Println(\u0026#34;quit\u0026#34;) return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i \u0026lt; 10; i++ { fmt.Println(\u0026lt;-c) } quit \u0026lt;- 0 }() fibonacci(c, quit) } select 语句使得一个 goroutine 在多个通讯操作上等待。\nselect 会阻塞，直到条件分支中的某个可以继续执行，这时就会执行那个条件分支。当多个都准备好的时候，会随机选择一个。\n默认选择 # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case \u0026lt;-tick: fmt.Println(\u0026#34;tick.\u0026#34;) case \u0026lt;-boom: fmt.Println(\u0026#34;BOOM!\u0026#34;) return default: fmt.Println(\u0026#34; .\u0026#34;) time.Sleep(50 * time.Millisecond) } } } 当 select 中的其他条件分支都没有准备好的时候，default 分支会被执行。\n为了非阻塞的发送或者接收，可使用 default 分支：\nselect { case i := \u0026lt;-c: // 使用 i default: // 从 c 读取会阻塞 } sync.Mutex # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; \u0026#34;time\u0026#34; ) // SafeCounter 的并发使用是安全的。 type SafeCounter struct { v map[string]int mux sync.Mutex } // Inc 增加给定 key 的计数器的值。 func (c *SafeCounter) Inc(key string) { c.mux.Lock() // Lock 之后同一时刻只有一个 goroutine 能访问 c.v c.v[key]++ c.mux.Unlock() } // Value 返回给定 key 的计数器的当前值。 func (c *SafeCounter) Value(key string) int { c.mux.Lock() // Lock 之后同一时刻只有一个 goroutine 能访问 c.v defer c.mux.Unlock() return c.v[key] } func main() { c := SafeCounter{v: make(map[string]int)} for i := 0; i \u0026lt; 1000; i++ { go c.Inc(\u0026#34;somekey\u0026#34;) } time.Sleep(time.Second) fmt.Println(c.Value(\u0026#34;somekey\u0026#34;)) } 我们已经看到 channel 用来在各个 goroutine 间进行通信是非常合适的了。\n但是如果我们并不需要通信呢？比如说，如果我们只是想保证在每个时刻，只有一个 goroutine 能访问一个共享的变量从而避免冲突？\n这里涉及的概念叫做 互斥，通常使用 互斥锁(mutex) 来提供这个限制。\nGo 标准库中提供了 sync.Mutex 类型及其两个方法：Lock Unlock\n我们可以通过在代码前调用 Lock 方法，在代码后调用 Unlock 方法来保证一段代码的互斥执行。 参见 Inc 方法。\n我们也可以用 defer 语句来保证互斥锁一定会被解锁。参见 Value 方法。\n练习：Web 爬虫 # package main import ( \u0026#34;fmt\u0026#34; ) type Fetcher interface { // Fetch 返回 URL 的 body 内容，并且将在这个页面上找到的 URL 放到一个 slice 中。 Fetch(url string) (body string, urls []string, err error) } // Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面，直到达到最大深度。 func Crawl(url string, depth int, fetcher Fetcher) { // TODO: 并行的抓取 URL。 // TODO: 不重复抓取页面。 // 下面并没有实现上面两种情况： if depth \u0026lt;= 0 { return } body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf(\u0026#34;found: %s %q\\n\u0026#34;, url, body) for _, u := range urls { Crawl(u, depth-1, fetcher) } return } func main() { Crawl(\u0026#34;http://golang.org/\u0026#34;, 4, fetcher) } // fakeFetcher 是返回若干结果的 Fetcher。 type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } func (f fakeFetcher) Fetch(url string) (string, []string, error) { if res, ok := f[url]; ok { return res.body, res.urls, nil } return \u0026#34;\u0026#34;, nil, fmt.Errorf(\u0026#34;not found: %s\u0026#34;, url) } // fetcher 是填充后的 fakeFetcher。 var fetcher = fakeFetcher{ \u0026#34;http://golang.org/\u0026#34;: \u0026amp;fakeResult{ \u0026#34;The Go Programming Language\u0026#34;, []string{ \u0026#34;http://golang.org/pkg/\u0026#34;, \u0026#34;http://golang.org/cmd/\u0026#34;, }, }, \u0026#34;http://golang.org/pkg/\u0026#34;: \u0026amp;fakeResult{ \u0026#34;Packages\u0026#34;, []string{ \u0026#34;http://golang.org/\u0026#34;, \u0026#34;http://golang.org/cmd/\u0026#34;, \u0026#34;http://golang.org/pkg/fmt/\u0026#34;, \u0026#34;http://golang.org/pkg/os/\u0026#34;, }, }, \u0026#34;http://golang.org/pkg/fmt/\u0026#34;: \u0026amp;fakeResult{ \u0026#34;Package fmt\u0026#34;, []string{ \u0026#34;http://golang.org/\u0026#34;, \u0026#34;http://golang.org/pkg/\u0026#34;, }, }, \u0026#34;http://golang.org/pkg/os/\u0026#34;: \u0026amp;fakeResult{ \u0026#34;Package os\u0026#34;, []string{ \u0026#34;http://golang.org/\u0026#34;, \u0026#34;http://golang.org/pkg/\u0026#34;, }, }, } 在这个练习中，将会使用 Go 的并发特性来并行执行 web 爬虫。\n修改 Crawl 函数来并行的抓取 URLs，并且保证不重复。\n提示：你可以用一个 map 来缓存已经获取的 URL，但是需要注意 map 本身并不是并发安全的！\n祝贺你，官方教程已经全部学完了。\n","date":"December 16, 2018","externalUrl":null,"permalink":"/posts/go1/","section":"","summary":"Go学习指南","title":"Go学习指南","type":"posts"},{"content":"","date":"November 30, 2018","externalUrl":null,"permalink":"/tags/java/","section":"","summary":"","title":"Java","type":"tags"},{"content":"","date":"November 30, 2018","externalUrl":null,"permalink":"/categories/java/","section":"","summary":"","title":"Java","type":"categories"},{"content":" Java安装和环境配置\n搭建Java开发环境，第一步我们就需要安装JDK。大家可以在ORACLE官方网站上下载，下载路径：\nhttp://www.oracle.com/technetwork/java/javase/downloads/index.html\n不同平台和系统的安装文件是不一样的，根据自己电脑的情况选择合适的版本进行安装。查看计算机版本的方法：右击计算机，选择属性 。查看系统的类型是64位，选择下载的 JDK 版本也得是64位的。\nwindows 系统下 Java 环境的配置 # 第一步：安装JDK # 在 window 系统下，下载好JDK后，点开安装程序，按照指示安装就可以了。\n验证 JDK 是否安装成功：\n点击开始\n在搜索中输入 cmd，按回车，打开 dos 命令行\n输入命令java -version，按回车\n如果显示了 Java 的版本，那么表示 JDK 安装成功了。 注意：最好不要安装在C盘\n第二步：配置环境变量 # 当我们安装好JDK后，就要配置环境变量了。一般来说，我们会配置这三个环境变量：\nJAVA_HOME 配置 JDK 安装路径 PATH 配置 JDK 命令文件的位置 CLASSPATH 配置类库文件的位置 注意：安装完只要正确配置JAVA_HOME变量值，后面的PATH和CLASSPATH的配置是固定的，不管你的JDK装在什么地方\n步骤一：右击计算机，选择属性\n步骤二：打开高级系统设置\n步骤三：选择高级\n步骤四：选择环境变量\n步骤五：在系统变量 里配置JAVA_HOME\n点击系统变量下的新建\n变量名填JAVA_HOME\n变量值填JDK的安装路径（若是默认安装路径，打开计算机-\u0026gt;C盘-\u0026gt;Program Files-\u0026gt;Java-\u0026gt;jdk1.8.0_192(或者其他版本)，拷贝路径就可以了）\n点击确定\n最终效果：\n$ 变量名:JAVA_HOME $ 变量值：C:\\Program Files\\Java\\jdk1.8.0_192 步骤六：在系统变量里配置PATH变量\n系统变量默认里有Path变量，找到它，点击 编辑\n在变量值最前面添加指定jdk命令文件的位置（jdk1.8.0_191-\u0026gt;bin目录）\n将路径添加到Path变量值前面用;和后面的路径隔开\n最终效果：\n$ 变量名：PATH $ 变量值：%JAVA_HOME%\\bin 步骤七:配置CLASSPATH变量\n点击系统变量下的新建\n变量名填CLASSPATH\n变量值填JAVA的class路径与lib库路径,具体如下，注意开头有一个英文的点和分号！\n点击确定\n最终效果：\n$ 变量名： CLASSPATH $ 变量值：.;%JAVA_HOME%\\lib\\dt.jar;%JAVA_HOME%\\bin\\tools.jar 第三步：验证环境配置是否正确 # 点击开始\n在搜索中输入 cmd，按回车，打开 dos 命令行\n输入命令java，按回车\n下面会出现一系列的命令行，这些提示是Java工具的用法和作用\n再执行一个命令，输入javac，按回车\n如果以上均给出提示，则开发环境配置是没有问题的\nLinux 系统下 Java 环境的配置 # macOS 系统下 Java 环境的配置 # ","date":"November 30, 2018","externalUrl":null,"permalink":"/posts/java/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eJava安装和环境配置\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Java安装和环境配置","type":"posts"},{"content":"","date":"November 18, 2018","externalUrl":null,"permalink":"/tags/unix/","section":"","summary":"","title":"Unix","type":"tags"},{"content":" 为什么编程语言以及数据库要从1970年1月1日开始计算时间\n答案详情见： https://en.wikipedia.org/wiki/Unix_time\n","date":"November 18, 2018","externalUrl":null,"permalink":"/posts/time1970/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e为什么编程语言以及数据库要从1970年1月1日开始计算时间\u003c/p\u003e\n\u003c/blockquote\u003e","title":"为什么编程语言以及数据库要从1970年1月1日开始计算时间","type":"posts"},{"content":"","date":"October 14, 2018","externalUrl":null,"permalink":"/tags/gin/","section":"","summary":"","title":"Gin","type":"tags"},{"content":" Gin使用方法\n安装gin # 直接使用 go get github.com/gin-gonic/gin 即可。\n官方README中提供了非常多的例子。例如最简单的实例代码:\npackage main import \u0026#34;github.com/gin-gonic/gin\u0026#34; func main() { r := gin.Default() r.GET(\u0026#34;/ping\u0026#34;, func(c *gin.Context) { c.JSON(200, gin.H{ \u0026#34;message\u0026#34;: \u0026#34;pong\u0026#34;, }) }) r.Run() // listen and serve on 0.0.0.0:8080 } 路由 # router := gin.Default() // 默认是带有Logger和Recovery中间件的 router := gin.New() // 不带中间件的路由 router.Use(gin.Logger()) // 可以使用这种方式来指明中间件 router.GET(\u0026#34;/test\u0026#34;, MyMiddleware(), testEndpoint) // 也可以用这种方式给指定路由添加中间件 router.GET(\u0026#34;/someGet\u0026#34;, getting) // 支持所有Restful的操作 // 带参数的路由 router.GET(\u0026#34;/user/:name\u0026#34;, func(c *gin.Context) { name := c.Param(\u0026#34;name\u0026#34;) }) // 参数可选/通配符功能 router.GET(\u0026#34;/user/:name/*action\u0026#34;, ...) // 路由分组 v1 := router.Group(\u0026#34;/v1\u0026#34;) { v1.POST(\u0026#34;/login\u0026#34;, loginEndpoint) v1.POST(\u0026#34;/submit\u0026#34;, submitEndpoint) } v1.Use(AuthRequired()) {} // 路由分组单独指定中间件 请求与响应 # 请求\n// 获取路由参数，假设有路由为\u0026#34;/user/:name\u0026#34; c.Params.ByName(\u0026#34;name\u0026#34;) // 获取query参数 c.Query(\u0026#34;name\u0026#34;) c.DefaultQuery(\u0026#34;name\u0026#34;, \u0026#34;Guest\u0026#34;) // 获取表单参数 c.PostForm(\u0026#34;name\u0026#34;) c.DefaultPostForm(\u0026#34;name\u0026#34;) 参数绑定 # 请求验证\n响应\n// 返回简单的字符串 c.String(200, \u0026#34;pong\u0026#34;) // 返回JSON数据 c.JSON(200, gin.H{ \u0026#34;message\u0026#34;: \u0026#34;pong\u0026#34;, }) // 重定向 c.Redirect(http.StatusMovedPermanently, \u0026#34;https://google.com\u0026#34;) 中间件 # 自定义中间件\nBasicAuth中间件\n异步协程\ngin可以借助协程来实现异步任务，但是这时候得手动copy上下文，并且只能是可读取的。\nrouter.GET(\u0026#34;/async\u0026#34;, func(c *gin.Context) { cCp := c.Copy() go func() { time.Sleep(5 * time.Second) log.Println(\u0026#34;Done! in path\u0026#34; + cCp.Request.URL.Path) }() }) ","date":"October 14, 2018","externalUrl":null,"permalink":"/posts/gogin/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eGin使用方法\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Gin使用方法","type":"posts"},{"content":" Go关键字和预定义标识符\nGo 语言中的关键字(25个) # 关键字 描述 break 退出循环 default 选择结构默认项（switch、select） func 定义函数 interface 定义接口 select channel case 选择结构标签 chan 定义channel const 常量 continue 跳过本次循环 defer 延迟执行内容（收尾工作） go 并发执行 map map类型 struct 定义结构体 else 选择结构 goto 跳转语句 package 包 switch 选择结构 fallthrough 流程控制 if 选择结构 range 从slice、map等结构中取元素 type 定义类型 for 循环 import 导入包 return 返回 var 定义变量 预定义标识符(36个) # - - - - - - append bool byte cap close complex complex64 complex128 uint16 copy false float32 float64 imag int int8 int16 uint32 int32 int64 iota len make new nil panic uint64 print println real recover string true uint uint8 uintptr Go语言的语言符号又称为记法元素，共包括5类，标签符(identifier)、关键字(keyword)、操作符(operator)、分隔符(delimiter)、字面量(literal),它们是组成Go语言代码和程序的最基本单位。\nGo语言的所有源代码都必须由Unicode编码规范的UTF-8编码格式进行编码。\n","date":"October 14, 2018","externalUrl":null,"permalink":"/posts/go/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eGo关键字和预定义标识符\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Go关键字和预定义标识符","type":"posts"},{"content":" 免费的编程中文书籍索引\n语言无关类 # 操作系统 # 开源世界旅行手册 鸟哥的Linux私房菜 The Linux Command Line (中英文版) Linux 设备驱动 (第三版) 深入分析Linux内核源码 UNIX TOOLBOX Docker中文指南 Docker —— 从入门到实践 Docker入门实战 Docker Cheat Sheet FreeRADIUS新手入门 Mac 开发配置手册 FreeBSD 使用手册 Linux 命令行(中文版) Linux 构建指南 Linux工具快速教程 Linux Documentation (中文版) 嵌入式 Linux 知识库 (eLinux.org 中文版) 理解Linux进程 命令行的艺术 SystemTap新手指南 操作系统思考 智能系统 # 一步步搭建物联网系统 分布式系统 # 走向分布式 编译原理 # 《计算机程序的结构和解释》公开课 翻译项目 函数式概念 # 傻瓜函数编程 计算机图形学 # OpenGL 教程 WebGL自学网 WEB服务器 # Nginx开发从入门到精通 (淘宝团队出品) Nginx教程从入门到精通(PDF版本，运维生存时间出品) OpenResty最佳实践 Apache 中文手册 版本控制 # Git教程 （本文由 廖雪峰 创作，如果觉得本教程对您有帮助，可以去 iTunes 购买） git - 简易指南 猴子都能懂的GIT入门 Git 参考手册 Pro Git Pro Git 中文版 (整理在gitbook上) Git Magic GotGitHub Git权威指南 Git Community Book 中文版 Mercurial 使用教程 HgInit (中文版) 沉浸式学 Git Git-Cheat-Sheet （感谢 @flyhigher139 翻译了中文版） GitHub秘籍 GitHub帮助文档 git-flow 备忘清单 svn 手册 GitHub漫游指南 编辑器 # exvim\u0026ndash;vim 改良成IDE项目 笨方法学Vimscript 中译本 Vim中文文档 所需即所获：像 IDE 一样使用 vim Atom飞行手册中文版 Markdown·简单的世界 一年成为 Emacs 高手 Emacs 生存指南 Atom官方手册 NoSQL # NoSQL数据库笔谈 Redis 设计与实现 Redis 命令参考 带有详细注释的 Redis 3.0 代码 带有详细注释的 Redis 2.6 代码 The Little MongoDB Book The Little Redis Book Neo4j 简体中文手册 v1.8 Neo4j .rb 中文資源 Disque 使用教程 Apache Spark 设计与实现 PostgreSQL # PostgreSQL 8.2.3 中文文档 PostgreSQL 9.3.1 中文文档 PostgreSQL 9.5.3 中文文档 MySQL # MySQL索引背后的数据结构及算法原理 21分钟MySQL入门教程 管理和监控 # ELKstack 中文指南 Mastering Elasticsearch(中文版) ElasticSearch 权威指南 Elasticsearch 权威指南（中文版） Logstash 最佳实践 Puppet 2.7 Cookbook 中文版 项目相关 # 持续集成（第二版） (译言网) 让开发自动化系列专栏 追求代码质量 selenium 中文文档 Selenium Webdriver 简易教程 Joel谈软件 約耳談軟體(Joel on Software) Gradle 2 用户指南 Gradle 中文使用文档 编码规范 开源软件架构 GNU make 指南 GNU make 中文手册 The Twelve-Factor App 设计模式 # 图说设计模式 史上最全设计模式导学目录 design pattern 包教不包会 设计模式 Java 版 Web # 关于浏览器和网络的 20 项须知 浏览器开发工具的秘密 Chrome 开发者工具中文手册 Chrome扩展开发文档 Grunt中文文档 gulp中文文档 Gulp 入门指南 移动Web前端知识库 正则表达式30分钟入门教程 前端开发体系建设日记 移动前端开发收藏夹 JSON风格指南 HTTP 接口设计指北 前端资源分享（一） 前端资源分享（二） 前端代码规范 及 最佳实践 前端开发者手册 前端工程师手册 w3school教程整理 Wireshark用户手册 一站式学习Wireshark HTTP 下午茶 HTTP/2.0 中文翻译 RFC 7540 - HTTP/2 中文翻译版 http2讲解 3 Web Designs in 3 Weeks 站点可靠性工程 大数据 # 大数据/数据挖掘/推荐系统/机器学习相关资源 面向程序员的数据挖掘指南 大型集群上的快速和通用数据处理架构 数据挖掘中经典的算法实现和详细的注释 Spark 编程指南简体中文版 编程艺术 # 程序员编程艺术 每个程序员都应该了解的内存知识(译)【第一部分】 取悦的工序：如何理解游戏 (豆瓣阅读，免费书籍) 其它 # OpenWrt智能、自动、透明翻墙路由器教程 SAN 管理入门系列 Sketch 中文手册 深入理解并行编程 程序员的自我修养 Growth: 全栈增长工程师指南 语言相关类 # Android # Android Design(中文版) Google Material Design 正體中文版 ( 译本一 译本二) Material Design 中文版 Google Android官方培训课程中文版 Android学习之路 Android开发技术前线(android-tech-frontier) Point-of-Android Android 一些重要知识点解析整理 Android6.0新特性详解 APP # Apache Cordova 开发指南 AWK # awk程序设计语言 awk中文指南 C/C++ # C/C++ 中文参考手册 (欢迎大家参与在线翻译和校对) C 语言编程透视 C++ 并发编程指南 Linux C编程一站式学习 (宋劲杉, 北京亚嵌教育研究中心) CGDB中文手册 100个gdb小技巧 100个gcc小技巧 ZMQ 指南 How to Think Like a Computer Scientist (中英文版) 跟我一起写Makefile(PDF) GNU make中文手册 GNU make 指南 Google C++ 风格指南 C/C++ Primer (by @andycai) 简单易懂的C魔法 Cmake 实践 (PDF版) C++ FAQ LITE(中文版) C++ Primer 5th Answers C++ 并发编程(基于C++11) QT 教程 DevBean的《Qt学习之路2》(Qt5) C++ Template 进阶指南 libuv中文教程 Boost 库中文教程 笨办法学C C# # MSDN C# 中文文档 .NET 类库参考 ASP.NET MVC 5 入门指南 超全面的 .NET GDI+ 图形图像编程教程 .NET控件开发基础 .NET开发要点精讲（初稿） Clojure # Clojure入门教程 CSS/HTML 学习CSS布局 通用 CSS 笔记、建议与指导 CSS参考手册 Emmet 文档 前端代码规范 (腾讯 AlloyTeam 团队) HTML和CSS编码规范 Sass Guidelines 中文 CSS3 Tutorial 《CSS3 教程》 MDN HTML 中文文档 MDN CSS 中文文档 Dart # Dart 语言导览 Elixir # Elixir编程入门 Erlang # 21天学通Erlang Fortran # Fortran77和90/95编程入门 Go # Go编程基础 Go入门指南 学习Go语言 Go Web 编程 (此书已经出版，希望开发者们去购买，支持作者的创作) Go实战开发 (当我收录此项目时，作者已经写完第三章，如果读完前面章节觉得有帮助，可以给作者 捐赠，以鼓励作者的继续创作) Network programming with Go 中文翻译版本 Effective Go Go 语言标准库 Golang标准库文档 Revel 框架手册 Java程序员的Golang入门指南 Go命令教程 Go语言博客实践 Go 官方文档翻译 深入解析Go Go语言圣经(中文版) ( GitBook) Groovy # 实战 Groovy 系列 Haskell # Real World Haskell 中文版 Haskell趣学指南 iOS # iOS开发60分钟入门 iOS7人机界面指南 Google Objective-C Style Guide 中文版 iPhone 6 屏幕揭秘 Apple Watch开发初探 马上着手开发 iOS 应用程序 网易斯坦福大学公开课：iOS 7应用开发字幕文件 Java # Apache Shiro 用户指南 Jersey 2.x 用户指南 Spring Framework 4.x参考文档 Spring Boot参考指南 (翻译中) MyBatis中文文档 MyBatis Generator 中文文档 用jersey构建REST服务 Activiti 5.x 用户指南 Google Java编程风格指南 Netty 4.x 用户指南 Netty 实战(精髓) REST 实战 Java 编码规范 Apache MINA 2 用户指南 H2 Database 教程 Java Servlet 3.1 规范 JSSE 参考指南 Java开源实现及最佳实践 Java 编程要点 Think Java Java 8 简明教程 JavaScript # Google JavaScript 代码风格指南 Google JSON 风格指南 Airbnb JavaScript 规范 JavaScript 标准参考教程（alpha） Javascript编程指南 ( 源码) javascript 的 12 个怪癖 JavaScript 秘密花园 JavaScript核心概念及实践 (PDF) (此书已由人民邮电出版社出版发行，但作者依然免费提供PDF版本，希望开发者们去购买，支持作者) 《JavaScript 模式》 “JavaScript patterns”中译本 命名函数表达式探秘 (注:原文由 为之漫笔翻译，原始地址无法打开，所以此处地址为我博客上的备份) 学用 JavaScript 设计模式 (开源中国) 深入理解JavaScript系列 ECMAScript 5.1 中文版 ECMAScript 6 入门 (作者：阮一峰) JavaScript Promise迷你书 You-Dont-Know-JS (深入JavaScript语言核心机制的系列图书) JavaScript 教程 廖雪峰 MDN JavaScript 中文文档 jQuery jQuery 解构 简单易懂的JQuery魔法 How to write jQuery plugin You Don\u0026rsquo;t Need jQuery 如何实现一个类jQuery？ Node.js Node入门 七天学会NodeJS Nodejs Wiki Book (繁体中文) express.js 中文文档 koa 中文文档 一起学koa 使用 Express + MongoDB 搭建多人博客 Express框架 Node.js 包教不包会 Learn You The Node.js For Much Win! (中文版) Node debug 三法三例 nodejs中文文档 orm2 中文文档 underscore.js Underscore.js中文文档 backbone.js backbone.js中文文档 backbone.js入门教程 (PDF) Backbone.js入门教程第二版 Developing Backbone.js Applications(中文版) AngularJS AngularJS最佳实践和风格指南 AngularJS中译本 AngularJS入门教程 构建自己的AngularJS 在Windows环境下用Yeoman构建AngularJS项目 Zepto.js Zepto.js 中文文档 Sea.js Hello Sea.js React.js React.js 中文文档 React webpack-cookbook React 入门教程 React Native 中文文档(含最新Android内容) Learn React \u0026amp; Webpack by building the Hacker News front page impress.js impress.js的中文教程 CoffeeScript CoffeeScript Cookbook The Little Book on CoffeeScript中文版 CoffeeScript 编码风格指南 TypeScipt TypeScript Handbook ExtJS Ext4.1.0 中文文档 Meteor Discover Meteor Meteor 中文文档 Angular-Meteor 中文教程 Chrome扩展及应用开发 LaTeX # 一份其实很短的 LaTeX 入门文档 一份不太简短的 LATEX 2ε 介绍 （PDF版） LISP # Common Lisp ANSI Common Lisp 中文翻譯版 On Lisp 中文翻译版本 Scheme Yet Another Scheme Tutorial Scheme入门教程 Scheme语言简明教程 Racket Racket book Lua # Lua编程入门 Lua 5.1 参考手册 中文翻译 Lua 5.3 参考手册 中文翻译 Lua源码欣赏 OCaml # Real World OCaml Perl # Modern Perl 中文版 Perl 程序员应该知道的事 PHP # PHP 官方手册 PHP调试技术手册(PDF) PHP之道：php-the-right-way ( @wulijun版 PHPHub版) PHP 最佳实践 PHP 开发者实践 深入理解PHP内核 PHP扩展开发及内核应用 Laravel5.1 中文文档 Laravel 5.1 LTS 速查表 Symfony2 Cookbook 中文版(版本 2.7.0 LTS) Symfony2中文文档 (未译完) YiiBook几本Yii框架的在线教程 深入理解 Yii 2.0 Yii 框架中文文檔 简单易懂的PHP魔法 swoole文档及入门教程 Composer 中文网 Slim 中文文档 Lumen 中文文档 PHPUnit 中文文档 Prolog # 笨办法学Prolog Python # 廖雪峰 Python 2.7 中文教程 廖雪峰 Python 3 中文教程 简明Python教程 零基础学 Python 第一版 零基础学 Python 第二版 可爱的 Python Python 2.7 官方教程中文版 Python 3.3 官方教程中文版 Python Cookbook 中文版 Python3 Cookbook 中文版 深入 Python 深入 Python 3 PEP8 Python代码风格规范 Google Python 风格指南 中文版 Python入门教程 ( PDF) 笨办法学 Python ( PDF EPUB) Python自然语言处理中文版 （感谢陈涛同学的翻译，也谢谢 @shwley 联系了作者） Python 绘图库 matplotlib 官方指南中文翻译 Scrapy 0.25 文档 ThinkPython ThinkPython 2ed Python快速教程 Python 正则表达式操作指南 python初级教程：入门详解 Twisted 与异步编程入门 TextGrocery 中文 API ( 基于svm算法的一个短文本分类 Python 库 ) Requests: HTTP for Humans Pillow 中文文档 PyMOTW 中文版 Python 官方文档中文版 Fabric 中文文档 Beautiful Soup 4.2.0 中文文档 用Python做科学计算 Sphinx 中文文档 精通 Python 设计模式 python 安全编程教程 程序设计思想与方法 知乎周刊·编程小白学Python Scipy 讲义 Python 学习笔记 基础篇 Python 学习笔记 模块篇 Python 标准库 中文版 Python进阶 Python 核心编程 第二版 CPyUG译 Python最佳实践指南 Python 精要教程 Python 量化交易教程 Django Django 1.5 文档中文版 正在翻译中 Diango 1.7 文档中文版 正在翻译中，目前只翻译了目录 Django 1.8.2 文档中文版 正在翻译中 Django 最佳实践 Django搭建简易博客教程 The Django Book 中文版 Django 设计模式与最佳实践 Django 网站开发 Cookbook Django Girls 學習指南 Flask Flask 文档中文版 Jinja2 文档中文版 Werkzeug 文档中文版 Flask之旅 Flask 扩展文档汇总 Flask 大型教程 SQLAlchemy 中文文档 web.py web.py 0.3 新手指南 Web.py Cookbook 简体中文版 Tornado Introduction to Tornado 中文翻译 Tornado源码解析 Tornado 4.3 文档中文版 R # R语言忍者秘笈 Ruby # Ruby 风格指南 Rails 风格指南 笨方法學 Ruby Ruby on Rails 指南 Ruby on Rails 實戰聖經 Ruby on Rails Tutorial 原书第 3 版 (本书网页版免费提供，电子版以 PDF、EPub 和 Mobi 格式提供购买，仅售 9.9 美元) Rails 实践 Rails 5 开发进阶(Beta) Rails 102 编写Ruby的C拓展 Ruby 源码解读 Ruby中的元编程 Rust # rust book 中文翻译 rust primer Scala # Scala课堂 (Twitter的Scala中文教程) Effective Scala(Twitter的Scala最佳实践的中文翻译) Scala指南 Shell # Shell脚本编程30分钟入门 Bash脚本15分钟进阶教程 Linux工具快速教程 shell十三问 Shell编程范例 Swift # The Swift Programming Language 中文版 Swift 语言指南 Stanford 公开课，Developing iOS 8 Apps with Swift 字幕翻译文件 C4iOS - COSMOS 读书笔记及其它 # 编译原理（紫龙书）中文第2版习题答案 把《编程珠玑》读薄 Effective C++读书笔记 Golang 学习笔记、Python 学习笔记、C 学习笔记 (PDF) Jsoup 学习笔记 学习笔记: Vim、Python、memcached 图灵开放书翻译计划\u0026ndash;C++、Python、Java等 蒂姆·奥莱利随笔 （由译言网翻译，电子版免费） SICP 解题集 精彩博客集合 中文文案排版指北 Standard C 语言标准函数库速查 (Cheat Sheet) Git Cheatsheet Chs GitBook简明教程 JavaScript语言精粹 制造开源软件 提问的智慧 Markdown 入门参考 AsciiDoc简明指南 背包问题九讲 老齐的技术资料 前端技能汇总 借助开源项目，学习软件开发 前端工作面试问题 leetcode/lintcode题解/算法学习笔记 前端开发笔记本 测试相关 # 移动APP自动化测试优秀框架Appium API Reference V1.2.0 CN 转载自：https://github.com/Jialin28/free-programming-books-zh_CN\n","date":"October 7, 2018","externalUrl":null,"permalink":"/posts/cnbook/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e免费的编程中文书籍索引\u003c/p\u003e\n\u003c/blockquote\u003e","title":"免费的编程中文书籍索引","type":"posts"},{"content":"","date":"September 18, 2018","externalUrl":null,"permalink":"/tags/django/","section":"","summary":"","title":"Django","type":"tags"},{"content":"","date":"September 18, 2018","externalUrl":null,"permalink":"/tags/gunicorn/","section":"","summary":"","title":"Gunicorn","type":"tags"},{"content":" Python 资源大全中文版\n环境管理 # 管理 Python 版本和环境的工具\np：非常简单的交互式 python 版本管理工具。 官网\npyenv：简单的 Python 版本管理工具。 官网\nVex：可以在虚拟环境中执行命令。 官网\nvirtualenv：创建独立 Python 环境的工具。 官网\nvirtualenvwrapper：virtualenv 的一组扩展。 官网\n包管理 # 管理包和依赖的工具。\npip：Python 包和依赖关系管理工具。 官网\npip-tools：保证 Python 包依赖关系更新的一组工具。 官网\npipenv：Pyhton 官方推荐的新一代包管理工具。 官网\nconda：跨平台，Python 二进制包管理工具。 官网\nCurdling：管理 Python 包的命令行工具。 官网\nwheel：Python 分发的新标准，意在取代 eggs。 官网\n包仓库 # 本地 PyPI 仓库服务和代理。\nwarehouse：下一代 PyPI。 官网\nbandersnatch：PyPA 提供的 PyPI 镜像工具。 官网\ndevpi：PyPI 服务和打包/测试/分发工具。 官网\nlocalshop：本地 PyPI 服务（自定义包并且自动对 PyPI 镜像）。 官网\n分发 # 打包为可执行文件以便分发。\nPyInstaller：将 Python 程序转换成独立的执行文件（跨平台）。 官网\ndh-virtualenv：构建并将 virtualenv 虚拟环境作为一个 Debian 包来发布。 官网\nNuitka：将脚本、模块、包编译成可执行文件或扩展模块。 官网\npy2app：将 Python 脚本变为独立软件包（Mac OS X）。 官网\npy2exe：将 Python 脚本变为独立软件包（Windows）。 官网\npynsist：一个用来创建 Windows 安装程序的工具，可以在安装程序中打包 Python 本身。 官网\n构建工具 # 将源码编译成软件。\nbuildout：一个构建系统，从多个组件来创建，组装和部署应用。 官网\nBitBake：针对嵌入式 Linux 的类似 make 的构建工具。 官网\nfabricate：对任何语言自动找到依赖关系的构建工具。 官网\nPlatformIO：多平台命令行构建工具。 官网\nPyBuilder：纯 Python 实现的持续化构建工具。 官网\nSCons：软件构建工具。 官网\n交互式解析器 # 交互式 Python 解析器。\nIPython：功能丰富的工具，非常有效的使用交互式 Python。 官网\nbpython：界面丰富的 Python 解析器。 官网\nptpython：高级交互式 Python 解析器， 构建于 python-prompt-toolkit 之上。 官网\n文件 # 文件管理和 MIME（多用途的网际邮件扩充协议）类型检测。\naiofiles：基于 asyncio，提供文件异步操作。 官网\nimghdr：（Python 标准库）检测图片类型。 官网\nmimetypes：（Python 标准库）将文件名映射为 MIME 类型。 官网\npath.py：对 os.path 进行封装的模块。 官网\npathlib：（Python3.4+ 标准库）跨平台的、面向对象的路径操作库。 官网\npython-magic：文件类型检测的第三方库 libmagic 的 Python 接口。 官网\nUnipath：用面向对象的方式操作文件和目录。 官网\nwatchdog：管理文件系统事件的 API 和 shell 工具。 官网\n日期和时间 # 操作日期和时间的类库。\narrow：更好的 Python 日期时间操作类库。 官网\nChronyk：Python 3 的类库，用于解析手写格式的时间和日期。 官网\ndateutil：Python datetime 模块的扩展。 官网\ndelorean：解决 Python 中有关日期处理的棘手问题的库。 官网\nmaya：人性化的时间处理库。 官网\nmoment：一个用来处理时间和日期的 Python 库。灵感来自于 Moment.js。 官网\npendulum：一个比 arrow 更具有明确的，可预测的行为的时间操作库。 官网\nPyTime：一个简单易用的 Python 模块，用于通过字符串来操作日期/时间。 官网\npytz：现代以及历史版本的世界时区定义。将时区数据库引入 Python。 官网\nwhen.py：提供用户友好的函数来帮助用户进行常用的日期和时间操作。 官网\n文本处理 # 用于解析和操作文本的库。\n通用\nchardet：字符编码检测器，兼容 Python2 和 Python3。 官网\ndifflib：(Python 标准库)帮助我们进行差异化比较。 官网\nftfy：让 Unicode 文本更完整更连贯。 官网\nfuzzywuzzy：模糊字符串匹配。 官网\nLevenshtein：快速计算编辑距离以及字符串的相似度。 官网\npangu.py：在中日韩语字符和数字字母之间添加空格。 官网\npypinyin：汉字拼音转换工具 Python 版。 官网\nshortuuid：一个生成器库，用以生成简洁的，明白的，URL 安全的 UUID。 官网\nsimplejson：Python 的 JSON 编码、解码器。 官网\nunidecode：Unicode 文本的 ASCII 转换形式 。 官网\nuniout：打印可读的字符，而不是转义的字符串。 官网\nxpinyin：一个用于把汉字转换为拼音的库。 官网\nyfiglet-figlet： pyfiglet -figlet 的 Python 实现。\nSlug 化\nawesome-slugify：一个 Python slug 化库，可以保持 Unicode。 官网\npython-slugify：Python slug 化库，可以把 unicode 转化为 ASCII。 官网\nunicode-slugify：一个 slug 工具，可以生成 unicode slugs ,需要依赖 Django 。 官网\n解析器\nphonenumbers：解析，格式化，储存，验证电话号码。 官网\nPLY：lex 和 yacc 解析工具的 Python 实现。 官网\nPygments：通用语法高亮工具。 官网\npyparsing：生成通用解析器的框架。 官网\npython-nameparser：把一个人名分解为几个独立的部分。 官网\npython-user-agents：浏览器 user agent 解析器。 官网\nsqlparse：一个无验证的 SQL 解析器。 官网\n特殊文本格式处理 # 一些用来解析和操作特殊文本格式的库。\n通用\ntablib：一个用来处理中表格数据的模块。 官网\nOffice\nMarmir：把输入的 Python 数据结构转换为电子表单。 官网\nopenpyxl：一个用来读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件的库。 官网\npyexcel：一个提供统一 API，用来读写，操作 Excel 文件的库。 官网\npython-docx：读取，查询以及修改 Microsoft Word 2007/2008 docx 文件。 官网\nrelatorio：模板化 OpenDocument 文件。 官网\nunoconv：在 LibreOffice/OpenOffice 支持的任意文件格式之间进行转换。 官网\nXlsxWriter：一个用于创建 Excel .xlsx 文件的 Python 模块。 官网\nxlwings：一个使得在 Excel 中方便调用 Python 的库（反之亦然），基于 BSD 协议。 官网\nxlwt：读写 Excel 文件的数据和格式信息。 官网 / xlrd\nPDF\nPDFMiner：一个用于从 PDF 文档中抽取信息的工具。 官网\nPyPDF2：一个可以分割，合并和转换 PDF 页面的库。 官网\nReportLab：快速创建富文本 PDF 文档。 官网\nMarkdown\nMistune：快速并且功能齐全的纯 Python 实现的 Markdown 解析器。 官网\nPython-Markdown：John Gruber’s Markdown 的 Python 版实现。 官网\nPython-Markdown2：纯 Python 实现的 Markdown 解析器，比 Python-Markdown 更快，更准确，可扩展。 官网\nYAML\nPyYAML：Python 版本的 YAML 解析器。 官网\nCSV\ncsvkit：用于转换和操作 CSV 的工具。 官网\nArchive\nunp：一个用来方便解包归档文件的命令行工具。 官网\n自然语言处理 # 用来处理人类语言的库。\nNLTK：一个先进的平台，用以构建处理人类语言数据的 Python 程序。 官网\njieba：中文分词工具。 官网\nlangid.py：独立的语言识别系统。 官网\nPattern：Python 网络信息挖掘模块。 官网\nSnowNLP：一个用来处理中文文本的库。 官网\nTextBlob：为进行普通自然语言处理任务提供一致的 API。 官网\nTextGrocery：一简单高效的短文本分类工具，基于 LibLinear 和 Jieba。 官网\nthulac:清华大学自然语言处理与社会人文计算实验室研制推出的一套中文词法分析工具包 官网\n文档 # 用以生成项目文档的库。\nSphinx：Python 文档生成器。 官网\nawesome-sphinxdoc： 官网\nMkDocs：对 Markdown 友好的文档生成器。 官网\npdoc：一个可以替换 Epydoc 的库，可以自动生成 Python 库的 API 文档。 官网\nPycco：文学编程（literate-programming）风格的文档生成器。 官网\nreadthedocs：一个基于 Sphinx/MkDocs 的在线文档托管系统，对开源项目免费开放使用。 官网\n配置 # 用来保存和解析配置的库。\nconfig： logging 模块作者写的分级配置模块。 官网\nConfigObj：INI 文件解析器，带验证功能。 官网\nConfigParser：(Python 标准库) INI 文件解析器。 官网\nprofig：通过多种格式进行配置，具有数值转换功能。 官网\npython-decouple：将设置和代码完全隔离。 官网\n命令行工具 # 用于创建命令行程序的库。\n命令行程序开发\nasciimatics：跨平台，全屏终端包（即鼠标/键盘输入和彩色，定位文本输出），完整的复杂动画和特殊效果的高级 API。 官网\ncement：Python 的命令行程序框架。 官网\nclick：一个通过组合的方式来创建精美命令行界面的包。 官网\ncliff：一个用于创建命令行程序的框架，可以创建具有多层命令的命令行程序。 官网\nclint：Python 命令行程序工具。 官网\ncolorama：跨平台彩色终端文本。 官网\ndocopt：Python 风格的命令行参数解析器。 官网\nGooey：一条命令，将命令行程序变成一个 GUI 程序。 官网\npython-prompt-toolkit：一个用于构建强大的交互式命令行程序的库。 官网\npython-fire：Google 出品的一个基于 Python 类的构建命令行界面的库。 官网\nPythonpy：在命令行中直接执行任何 Python 指令。 官网\n生产力工具\naws-cli：Amazon Web Services 的通用命令行界面。 官网\nbashplotlib：在终端中进行基本绘图。 官网\ncaniusepython3：判断是哪个项目妨碍你你移植到 Python3。 官网\ncookiecutter：从 cookiecutters（项目模板）创建项目的一个命令行工具。 官网\ndoitlive：一个用来在终端中进行现场演示的工具。 官网\npyftpdlib：一个速度极快和可扩展的 Python FTP 服务库。 官网\nhowdoi：通过命令行获取即时的编程问题解答。 官网\nhttpie：一个命令行 HTTP 客户端，cURL 的替代品，易用性更好。 官网\nPathPicker：从 bash 输出中选出文件。 官网\npercol：向 UNIX shell 传统管道概念中加入交互式选择功能。 官网\nSAWS：一个加强版的 AWS 命令行。 官网\nthefuck：修正你之前的命令行指令。 官网\nmycli：一个 MySQL 命令行客户端，具有自动补全和语法高亮功能。 官网\npgcli：Postgres 命令行工具，具有自动补全和语法高亮功能。 官网\ntry：一个从来没有更简单的命令行工具，用来试用 python 库。 官网\n下载器 # 用来进行下载的库.\ns3cmd：一个用来管理 Amazon S3 和 CloudFront 的命令行工具。 官网\ns4cmd：超级 S3 命令行工具，性能更加强劲。 官网\nyou-get：一个 YouTube/Youku/Niconico 视频下载器，使用 Python3 编写。 官网\nyoutube-dl：一个小巧的命令行程序，用来下载 YouTube 视频。 官网\n图像处理 # 用来操作图像的库.\npillow：Pillow 是一个更加易用版的 PIL。 官网\nhmap：图像直方图映射。 官网\nimgSeek：一个使用视觉相似性搜索一组图片集合的项目。 官网\nnude.py：裸体检测。 官网\npyBarcode：不借助 PIL 库在 Python 程序中生成条形码。 官网\npygram：类似 Instagram 的图像滤镜。 官网\npython-qrcode：一个纯 Python 实现的二维码生成器。 官网\nQuads：基于四叉树的计算机艺术。 官网\nscikit-image：一个用于（科学）图像处理的 Python 库。 官网\nthumbor：一个小型图像服务，具有剪裁，尺寸重设和翻转功能。 官网\nwand： MagickWand的 Python 绑定。MagickWand 是 ImageMagick 的 C API 。 官网\nface_recognition：简单易用的 python 人脸识别库。 官网\nOCR # 光学字符识别库。\npyocr：Tesseract 和 Cuneiform 的一个封装(wrapper)。 官网\npytesseract： Google Tesseract OCR 的另一个封装(wrapper)。 官网\npython-tesseract： Google Tesseract OCR 的一个包装类。\n音频 # 用来操作音频的库\naudiolazy：Python 的数字信号处理包。 官网\naudioread：交叉库 (GStreamer + Core Audio + MAD + FFmpeg) 音频解码。 官网\nbeets：一个音乐库管理工具及 MusicBrainz 标签添加工具。 官网\ndejavu：音频指纹提取和识别。 官网\ndjango-elastic-transcoder：Django + Amazon Elastic Transcoder。 官网\neyeD3：一个用来操作音频文件的工具，具体来讲就是包含 ID3 元信息的 MP3 文件。 官网\nid3reader：一个用来读取 MP3 元数据的 Python 模块。 官网\nm3u8：一个用来解析 m3u8 文件的模块。 官网\nmutagen：一个用来处理音频元数据的 Python 模块。 官网\npydub：通过简单、简洁的高层接口来操作音频文件。 官网\npyechonest： Echo Nest API 的 Python 客户端。 官网\ntalkbox：一个用来处理演讲/信号的 Python 库。 官网\nTimeSide：开源 web 音频处理框架。 官网\ntinytag：一个用来读取 MP3, OGG, FLAC 以及 Wave 文件音乐元数据的库。 官网\nmingus：一个高级音乐理论和曲谱包，支持 MIDI 文件和回放功能。 官网\nVideo # 用来操作视频和 GIF 的库。\nmoviepy：一个用来进行基于脚本的视频编辑模块，适用于多种格式，包括动图 GIFs。 官网\nscikit-video：SciPy 视频处理常用程序。 官网\n地理位置 # 地理编码地址以及用来处理经纬度的库。\nGeoDjango：世界级地理图形 web 框架。 官网\nGeoIP：MaxMind GeoIP Legacy 数据库的 Python API。 官网\ngeojson：GeoJSON 的 Python 绑定及工具。 官网\ngeopy：Python 地址编码工具箱。 官网\npygeoip：纯 Python GeoIP API。 官网\ndjango-countries：一个 Django 应用程序，提供用于表格的国家选择功能，国旗图标静态文件以及模型中的国家字段。 官网\nHTTP # 使用 HTTP 的库。\naiohttp：基于 asyncio 的异步 HTTP 网络库。 官网\nrequests：人性化的 HTTP 请求库。 官网\ngrequests：requests 库 + gevent ，用于异步 HTTP 请求. 官网\nhttplib2：全面的 HTTP 客户端库。 官网\ntreq：类似 requests 的 Python API 构建于 Twisted HTTP 客户端之上。 官网\nurllib3：一个具有线程安全连接池，支持文件 post，清晰友好的 HTTP 库。 官网\n数据库 # Python 实现的数据库。\npickleDB：一个简单，轻量级键值储存数据库。 官网\nPipelineDB：流式 SQL 数据库。 官网\nTinyDB：一个微型的，面向文档型数据库。 官网\nZODB：一个 Python 原生对象数据库。一个键值和对象图数据库。 官网\n数据库驱动 # 用来连接和操作数据库的库。\nMySQL： awesome-mysql 系列\naiomysql：基于 asyncio 的异步 MySQL 数据库操作库。 官网\nmysql-python：Python 的 MySQL 数据库连接器。 官网\nysqlclient： mysql-python 分支，支持 Python 3。\noursql：一个更好的 MySQL 连接器，支持原生预编译指令和 BLOBs。 官网\nPyMySQL：纯 Python MySQL 驱动，兼容 mysql-python。 官网\nPostgreSQL\npsycopg2：Python 中最流行的 PostgreSQL 适配器。 官网\nqueries：psycopg2 库的封装，用来和 PostgreSQL 进行交互。 官网\ntxpostgres：基于 Twisted 的异步 PostgreSQL 驱动。 官网\n其他关系型数据库\napsw：另一个 Python SQLite 封装。 官网\ndataset：在数据库中存储 Python 字典\npymssql：一个简单的 Microsoft SQL Server 数据库接口。 官网\nNoSQL 数据库\ncassandra-python-driver：Cassandra 的 Python 驱动。 官网\nHappyBase：一个为 Apache HBase 设计的，对开发者友好的库。 官网\nPlyvel：一个快速且功能丰富的 LevelDB 的 Python 接口。 官网\npy2neo：Neo4j restful 接口的 Python 封装客户端。 官网\npycassa：Cassandra 的 Python Thrift 驱动。 官网\nPyMongo：MongoDB 的官方 Python 客户端。 官网\nredis-py：Redis 的 Python 客户端。 官网\ntelephus：基于 Twisted 的 Cassandra 客户端。 官网\ntxRedis：基于 Twisted 的 Redis 客户端。 官网\nORM # 实现对象关系映射或数据映射技术的库。\n关系型数据库\nawesome-sqlalchemy 系列\nDjango Models：Django 的一部分。 官网\nSQLAlchemy：Python SQL 工具以及对象关系映射工具。 官网\nPeewee：一个小巧，富有表达力的 ORM。 官网\nPonyORM：提供面向生成器的 SQL 接口的 ORM。 官网\npython-sql：编写 Python 风格的 SQL 查询。 官网\nNoSQL 数据库\ndjango-mongodb-engine：Django MongoDB 后端。 官网\nPynamoDB： Amazon DynamoDB 的一个 Python 风格接口。 官网\nflywheel：Amazon DynamoDB 的对象映射工具。 官网\nMongoEngine：一个 Python 对象文档映射工具，用于 MongoDB。 官网\nhot-redis：为 Redis 提供 Python 丰富的数据类型。 官网\nredisco：一个 Python 库，提供可以持续存在在 Redis 中的简单模型和容器。 官网\n其他\nbutterdb：Google Drive 电子表格的 Python ORM。 官网\nWeb 框架 # 全栈 Web 框架。\nDjango：Python 界最流行的 web 框架。 官网\nawesome-django 系列\nFlask：一个 Python 微型框架。 官网\nawesome-flask 系列\npyramid：一个小巧，快速，接地气的开源 Python web 框架。\nawesome-pyramid 系列\nBottle：一个快速小巧，轻量级的 WSGI 微型 web 框架。 官网\nCherryPy：一个极简的 Python web 框架，服从 HTTP/1.1 协议且具有 WSGI 线程池。 官网\nTurboGears：一个可以扩展为全栈解决方案的微型框架。 官网\nweb.py：一个 Python 的 web 框架，既简单，又强大。 官网\nweb2py：一个全栈 web 框架和平台，专注于简单易用。 官网\nTornado：一个 web 框架和异步网络库。 官网\nsanic：基于 Python3.5+ 的异步网络框架。 官网\n权限 # 允许或拒绝用户访问数据或功能的库。\nCarteblanche：站在用户和设计者角度开发的一个代码对齐模块，很好地处理了代码导航及权限。 官网\ndjango-guardian：Django 1.2+ 实现了单个对象权限。 官网\ndjango-rules：一个小巧但是强大的应用，提供对象级别的权限管理，且不需要使用数据库。 官网\nCMS # 内容管理系统\nodoo-cms: 一个开源的，企业级 CMS，基于 odoo。 官网\ndjango-cms：一个开源的，企业级 CMS，基于 Django。 官网\ndjedi-cms：一个轻量级但却非常强大的 Django CMS ，考虑到了插件，内联编辑以及性能。 官网\nFeinCMS：基于 Django 构建的最先进的内容管理系统之一。 官网\nKotti：一个高级的，Python 范的 web 应用框架，基于 Pyramid 构建。 官网\nMezzanine：一个强大的，持续的，灵活的内容管理平台。 官网\nOpps：一个为杂志，报纸网站以及大流量门户网站设计的 CMS 平台，基于 Django。 官网\nPlone：一个构建于开源应用服务器 Zope 之上的 CMS。 官网\nQuokka：灵活，可扩展的小型 CMS，基于 Flask 和 MongoDB。 官网\nWagtail：一个 Django 内容管理系统。 官网\nWidgy：最新的 CMS 框架，基于 Django。 官网\n电子商务 # 用于电子商务以及支付的框架和库。\ndjango-oscar：一个用于 Django 的开源的电子商务框架。 官网\ndjango-shop：一个基于 Django 的店铺系统。 官网\nCartridge：一个基于 Mezzanine 构建的购物车应用。 官网\nshoop：一个基于 Django 的开源电子商务平台。 官网\nalipay：非官方的 Python 支付宝 API。 官网\nmerchant：一个可以接收来自多种支付平台支付的 Django 应用。 官网\nmoney：一个货币类库。带有可选的 CLDR 后端本地化格式，提供可扩展的货币兑换解决方案。 官网\npython-currencies：显示货币格式以及它的数值。 官网\nRESTful API # 用来开发 RESTful APIs 的库\nDjango\ndjango-rest-framework：一个强大灵活的工具，用来构建 web API。 官网\ndjango-tastypie：为 Django 应用开发 API。 官网\ndjango-formapi：为 Django 的表单验证，创建 JSON APIs 。 官网\nFlask\nflask-api：为 flask 开发的，可浏览 Web APIs 。 官网\nflask-restful：为 flask 快速创建 REST APIs 。 官网\nflask-restless：为 SQLAlchemy 定义的数据库模型创建 RESTful APIs 。 官网\nflask-api-utils：为 Flask 处理 API 表示和验证。 官网\neve：REST API 框架，由 Flask, MongoDB 等驱动。 官网\nPyramid\ncornice：一个 Pyramid 的 REST 框架 。 官网\n与框架无关的\nfalcon：一个用来建立云 API 和 web app 后端的高性能框架。 官网\nsandman：为现存的数据库驱动系统自动创建 REST APIs 。 官网\nrestless：框架无关的 REST 框架 ，基于从 Tastypie 学到的知识。 官网\nripozo：快速创建 REST/HATEOAS/Hypermedia APIs。 官网\n验证 # 实现验证方案的库。\nOAuth\nAuthomatic：简单但是强大的框架，身份验证/授权客户端。 官网\ndjango-allauth：Django 的验证应用。 官网\ndjango-oauth-toolkit：为 Django 用户准备的 OAuth2。 官网\ndjango-oauth2-provider：为 Django 应用提供 OAuth2 接入。 官网\nFlask-OAuthlib：OAuth 1.0/a, 2.0 客户端实现，供 Flask 使用。 官网\nOAuthLib：一个 OAuth 请求-签名逻辑通用、 完整的实现。 官网\npython-oauth2：一个完全测试的抽象接口。用来创建 OAuth 客户端和服务端。 官网\npython-social-auth：一个设置简单的社会化验证方式。 官网\nrauth：OAuth 1.0/a, 2.0, 和 Ofly 的 Python 库。 官网\nsanction：一个超级简单的 OAuth2 客户端实现。 官网\n其他\njose：JavaScript 对象签名和加密草案的实现。 官网\nPyJWT：JSON Web 令牌草案 01。 官网\npython-jws：JSON Web 签名草案 02 的实现。 官网\npython-jwt：一个用来生成和验证 JSON Web 令牌的模块。 官网\n模板引擎 # 模板生成和词法解析的库和工具。\nJinja2：一个现代的，对设计师友好的模板引擎。 官网\nChameleon：一个 HTML/XML 模板引擎。 模仿了 ZPT（Zope Page Templates）, 进行了速度上的优化。 官网\nGenshi：Python 模板工具，用以生成 web 感知的结果。 官网\nMako：Python 平台的超高速轻量级模板。 官网\n队列 # 处理事件以及任务队列的库。\ncelery：一个异步任务队列/作业队列，基于分布式消息传递。 官网\nhuey：小型多线程任务队列。 官网\nmrq：Mr. Queue -一个 Python 的分布式 worker 任务队列， 使用 Redis 和 gevent。 官网\nrq：简单的 Python 作业队列。 官网\nsimpleq：一个简单的，可无限扩张的，基于亚马逊 SQS 的队列。 官网\n搜索 # 对数据进行索引和执行搜索查询的库和软件。\ndjango-haystack：Django 模块化搜索。 官网\nelasticsearch-py：Elasticsearch 的官方底层 Python 客户端。 官网\nelasticsearch-dsl-py：Elasticsearch 的官方高级 Python 客户端。 官网\nsolrpy： solr 的 Python 客户端。 官网\nWhoosh：一个快速的纯 Python 搜索引擎库。 官网\n动态消息 # 用来创建用户活动的库。\ndjango-activity-stream：从你的站点行为中生成通用活动信息流。 官网\nStream-Framework：使用 Cassandra 和 Redis 创建动态消息和通知系统。 官网\n资源管理 # 管理、压缩、缩小网站资源的工具。\ndjango-compressor：将链接和内联的 JavaScript 或 CSS 压缩到一个单独的缓存文件中。 官网\ndjango-storages：一个针对 Django 的自定义存储后端的工具集合。 官网\nfanstatic：打包、优化，并且把静态文件依赖作为 Python 的包来提供。 官网\nFile Conveyor：一个后台驻留的程序，用来发现和同步文件到 CDNs, S3 和 FTP。 官网\nFlask-Assets：帮你将 web 资源整合到你的 Flask app 中。 官网\njinja-assets-compressor：一个 Jinja 扩展，用来编译和压缩你的资源。 官网\nwebassets：为你的静态资源打包、优化和管理生成独一无二的缓存 URL。 官网\n缓存 # 缓存数据的库。\nBeaker：一个缓存和会话库，可以用在 web 应用和独立 Python 脚本和应用上。 官网\ndjango-cache-machine：Django 模型的自动缓存和失效。 官网\ndjango-cacheops：具有自动颗粒化事件驱动失效功能的 ORM。 官网\ndjango-viewlet：渲染模板，同时具有额外的缓存控制功能。 官网\ndogpile.cache：dogpile.cache 是 Beaker 的下一代替代品，由同一作者开发。 官网\nHermesCache：Python 缓存库，具有基于标签的失效和 dogpile effect 保护功能。 官网\njohnny-cache：django 应用缓存框架。 官网\npylibmc： libmemcached 接口的 Python 封装。 官网\n电子邮件 # 用来发送和解析电子邮件的库。\ndjango-celery-ses：带有 AWS SES 和 Celery 的 Django email 后端。 官网\nenvelopes：供人类使用的电子邮件库。 官网\nflanker：一个 email 地址和 Mime 解析库。 官网\nimbox：Python IMAP 库。 官网\ninbox.py：Python SMTP 服务器。 官网\ninbox：一个开源电子邮件工具箱。 官网\nlamson：Python 风格的 SMTP 应用服务器。 官网\nmailjet：Mailjet API 实现，用来提供批量发送邮件，统计等功能。 官网\nmarrow.mailer：高性能可扩展邮件分发框架。 官网\nmodoboa：一个邮件托管和管理平台，具有现代的、简约的 Web UI。 官网\npyzmail：创建，发送和解析电子邮件。 官网\nTalon：Mailgun 库，用来抽取信息和签名。 官网\n国际化 # 用来进行国际化的库。\nBabel：一个 Python 的国际化库。 官网\nKorean：一个韩语词态库。 官网\nURL 处理 # 解析 URLs 的库\nfurl：一个让处理 URL 更简单小型 Python 库。 官网\npurl：一个简单的，不可变的 URL 类，具有简洁的 API 来进行询问和处理。 官网\npyshorteners：一个纯 Python URL 缩短库。 官网\nshorturl：生成短小 URL 和类似 bit.ly 短链的 Python 实现。 官网\nwebargs：一个解析 HTTP 请求参数的库，内置对流行 web 框架的支持，包括 Flask, Django, Bottle, Tornado 和 Pyramid。 官网\nHTML 处理 # 处理 HTML 和 XML 的库。\nBeautifulSoup：以 Python 风格的方式来对 HTML 或 XML 进行迭代，搜索和修改。 官网\nbleach：一个基于白名单的 HTML 清理和文本链接库。 官网\ncssutils：一个 Python 的 CSS 库。 官网\nhtml5lib：一个兼容标准的 HTML 文档和片段解析及序列化库。 官网\nlxml：一个非常快速，简单易用，功能齐全的库，用来处理 HTML 和 XML。 官网\nMarkupSafe：为 Python 实现 XML/HTML/XHTML 标记安全字符串。 官网\npyquery：一个解析 HTML 的库，类似 jQuery。 官网\nrequests-html：人性化的，Pythonic 的 HTML 解析库。 官网\nuntangle：将 XML 文档转换为 Python 对象，使其可以方便的访问。 官网\nxhtml2pdf：HTML/CSS 转 PDF 工具。 官网\nxmltodict：像处理 JSON 一样处理 XML。 官网\n爬取网络站点的库\nScrapy：一个快速高级的屏幕爬取及网页采集框架。 官网\ncola：一个分布式爬虫框架。 官网\nDemiurge：基于 PyQuery 的爬虫微型框架。 官网\nfeedparser：通用 feed 解析器。 官网\nGrab：站点爬取框架。 官网\nMechanicalSoup：用于自动和网络站点交互的 Python 库。 官网\nportia：Scrapy 可视化爬取。 官网\npyspider：一个强大的爬虫系统。 官网\nRoboBrowser：一个简单的，Python 风格的库，用来浏览网站，而不需要一个独立安装的浏览器。 官网\n网页内容提取 # 用于进行网页内容提取的库。\nHaul：一个可以扩展的图像爬取工具。 官网\nhtml2text：将 HTML 转换为 Markdown 格式文本。 官网\nlassie：人性化的网页内容检索库。 官网\nmicawber：一个小型网页内容提取库，用来从 URLs 提取富内容。 官网\nnewspaper：使用 Python 进行新闻提取，文章提取以及内容策展。 官网\nopengraph：一个用来解析开放内容协议(Open Graph Protocol)的 Python 模块。 官网\npython-goose：HTML 内容/文章提取器。 官网\npython-readability：arc90 公司 readability 工具的 Python 高速端口。 官网\nsanitize：为杂乱的数据世界带来调理性。 官网\nsumy：一个为文本文件和 HTML 页面进行自动摘要的模块。 官网\ntextract：从任何格式的文档中提取文本，Word，PowerPoint，PDFs 等等。 官网\n表单 # 进行表单操作的库。\nDeform：Python HTML 表单生成库，受到了 formish 表单生成库的启发。 官网\ndjango-bootstrap3：集成了 Bootstrap 3 的 Django。 官网\ndjango-crispy-forms：一个 Django 应用，他可以让你以一种非常优雅且 DRY（Don\u0026rsquo;t repeat yourself） 的方式来创建美观的表单。 官网\ndjango-remote-forms：一个平台独立的 Django 表单序列化工具。 官网\nWTForms：一个灵活的表单验证和呈现库。 官网\nWTForms-JSON：一个 WTForms 扩展，用来处理 JSON 数据。 官网\n数据验证 # 数据验证库。多用于表单验证。\nCerberus：一个映射验证器（mappings-validator）。支持多种规则，提供归一化功能，可以方便地定制为 Python 风格的 schema 定义。 官网\ncolander：一个用于对从 XML, JSON，HTML 表单获取的数据或其他同样简单的序列化数据进行验证和反序列化的系统。 官网\nkmatch：一种用于匹配/验证/筛选 Python 字典的语言。 官网\nschema：一个用于对 Python 数据结构进行验证的库。 官网\nSchematics：数据结构验证。 官网\nvalideer：轻量级可扩展的数据验证和适配库。 官网\nvoluptuous：一个 Python 数据验证库。主要是为了验证传入 Python 的 JSON，YAML 等数据。 官网\n反垃圾技术 # 帮助你和电子垃圾进行战斗的库。\ndjango-simple-captcha：一个简单、高度可定制的 Django 应用，可以为任何 Django 表单添加验证码。 官网\ndjango-simple-spam-blocker：一个用于 Django 的简单的电子垃圾屏蔽工具。 官网\n标记 # 用来进行标记的库。\ndjango-taggit：简单的 Django 标记工具。 官网 管理面板 # 管理界面库。\nAjenti：一个你的服务器值得拥有的管理面板。 官网\ndjango-suit：Django 管理界面的一个替代品 (仅对于非商业用途是免费的)。 官网\ndjango-xadmin：Django admin 的一个替代品，具有很多不错的功能。 官网\nflask-admin：一个用于 Flask 的简单可扩展的管理界面框架。 官网\nflower：一个对 Celery 集群进行实时监控和提供 web 管理界面的工具。 官网\nGrappelli：Django 管理界面的一个漂亮的皮肤。 官网\nWooey：一个 Django 应用，可以为 Python 脚本创建 web 用户界面。 官网\n静态站点生成器 # 静态站点生成器是一个软件，它把文本和模板作为输入，然后输出 HTML 文件。\nPelican：使用 Markdown 或 ReST 来处理内容， Jinja 2 来制作主题。支持 DVCS, Disqus.。AGPL 许可。 官网\nCactus：为设计师设计的静态站点生成器。 官网\nHyde：基于 Jinja2 的静态站点生成器。 官网\nNikola：一个静态网站和博客生成器。 官网\nTinkerer：Tinkerer 是一个博客引擎/静态站点生成器，由 Sphinx 驱动。 官网\nLektor：一个简单易用的静态 CMS 和博客引擎。 官网\n进程 # 操作系统进程启动及通信库。\nenvoy：比 Python subprocess 模块更人性化。 官网\nsarge：另一 种 subprocess 模块的封装。 官网\nsh：一个完备的 subprocess 替代库。 官网\n并发和并行 # 用以进行并发和并行操作的库。\nmultiprocessing：(Python 标准库) 基于进程的“线程”接口。 官网\nthreading：(Python 标准库)更高层的线程接口。 官网\neventlet：支持 WSGI 的异步框架。 官网\ngevent：一个基于协程的 Python 网络库，使用 greenlet。 官网\nTomorrow：用于产生异步代码的神奇的装饰器语法实现。 官网\nuvloop：在 libuv 之上超快速实现 asyncio 事件循环。 官网\n网络 # 用于网络编程的库。\nasyncio：(Python 标准库) 异步 I/O, 事件循环, 协程以及任务。 官网\nTwisted：一个事件驱动的网络引擎。 官网\npulsar：事件驱动的并发框架。 官网\ndiesel：基于 Greenlet 的事件 I/O 框架。 官网\npyzmq：一个 ZeroMQ 消息库的 Python 封装。 官网\nToapi：一个轻巧，简单，快速的 Flask 库，致力于为所有网站提供 API 服务。 官网\ntxZMQ：基于 Twisted 的 ZeroMQ 消息库的 Python 封装。 官网\nWebSocket # 帮助使用 WebSocket 的库。\nAutobahnPython：给 Python 、使用的 WebSocket \u0026amp; WAMP 基于 Twisted 和 asyncio。 官网\nCrossbar：开源统一应用路由(Websocket \u0026amp; WAMP for Python on Autobahn)。 官网\ndjango-socketio：给 Django 用的 WebSockets。 官网\nWebSocket-for-Python：为 Python2/3 以及 PyPy 编写的 WebSocket 客户端和服务器库。 官网\nWSGI 服务器 # 兼容 WSGI 的 web 服务器\ngunicorn：Pre-forked, 部分是由 C 语言编写的。 官网\nuwsgi：uwsgi 项目的目的是开发一组全栈工具，用来建立托管服务， 由 C 语言编写。 官网\nbjoern：异步，非常快速，由 C 语言编写。 官网\nfapws3：异步 (仅对于网络端)，由 C 语言编写。 官网\nmeinheld：异步，部分是由 C 语言编写的。 官网\nnetius：异步，非常快速。 官网\npaste：多线程，稳定，久经考验。 官网\nrocket：多线程。 官网\nwaitress：多线程, 是它驱动着 Pyramid 框架。 官网\nWerkzeug：一个 WSGI 工具库，驱动着 Flask ，而且可以很方便大嵌入到你的项目中去。 官网\nRPC 服务器 # 兼容 RPC 的服务器。\nSimpleJSONRPCServer：这个库是 JSON-RPC 规范的一个实现。 官网\nSimpleXMLRPCServer：(Python 标准库) 简单的 XML-RPC 服务器实现，单线程。 官网\nzeroRPC：zerorpc 是一个灵活的 RPC 实现，基于 ZeroMQ 和 MessagePack。 官网\n密码学 # cryptography：这个软件包意在提供密码学基本内容和方法提供给 Python 开发者。 官网\nhashids：在 Python 中实现 hashids 。 官网\nParamiko：SSHv2 协议的 Python (2.6+, 3.3+) ，提供客户端和服务端的功能。 官网\nPasslib：安全密码存储／哈希库， 官网\nPyCrypto：Python 密码学工具箱。 官网\nPyNacl：网络和密码学(NaCl) 库的 Python 绑定。 官网\n图形用户界面 # 用来创建图形用户界面程序的库。\ncurses：内建的 ncurses 封装，用来创建终端图形用户界面。 官网\nenaml：使用类似 QML 的 Declaratic 语法来创建美观的用户界面。 官网\nkivy：一个用来创建自然用户交互（NUI）应用程序的库，可以运行在 Windows, Linux, Mac OS X, Android 以及 iOS 平台上。 官网\npyglet：一个 Python 的跨平台窗口及多媒体库。 官网\nPyQt：跨平台用户界面框架 Qt 的 Python 绑定 ，支持 Qt v4 和 Qt v5。 官网\nPySide：跨平台用户界面框架 Qt 的 Python 绑定 ，支持 Qt v4。 官网\nTkinter：Tkinter 是 Python GUI 的一个事实标准库。 官网\nToga：一个 Python 原生的, 操作系统原生的 GUI 工具包。 官网\nurwid：一个用来创建终端 GUI 应用的库，支持组件，事件和丰富的色彩等。 官网\nwxPython：wxPython 是 wxWidgets C++ 类库和 Python 语言混合的产物。 官网\nPyGObject：GLib/GObject/GIO/GTK+ (GTK+3) 的 Python 绑定。 官网\nFlexx：Flexx 是一个纯 Python 语言编写的用来创建 GUI 程序的工具集，它使用 web 技术进行界面的展示。 官网\n游戏开发 # 超赞的游戏开发库。\nCocos2d：cocos2d 是一个用来开发 2D 游戏， 示例和其他图形/交互应用的框架。基于 pyglet。 官网\nPanda3D：由迪士尼开发的 3D 游戏引擎，并由卡内基梅陇娱乐技术中心负责维护。使用 C++ 编写, 针对 Python 进行了完全的封装。 官网\nPygame：Pygame 是一组 Python 模块，用来编写游戏。 官网\nPyOgre：Ogre 3D 渲染引擎的 Python 绑定，可以用来开发游戏和仿真程序等任何 3D 应用。 官网\nPyOpenGL：OpenGL 的 Python 绑定及其相关 APIs。 官网\nPySDL2：SDL2 库的封装，基于 ctypes。 官网\nRenPy：一个视觉小说（visual novel）引擎。 官网\n日志 # 用来生成和操作日志的库。\nlogging：(Python 标准库) 为 Python 提供日志功能。 官网\nlogbook：Logging 库的替代品。 官网\nEliot：为复杂的和分布式系统创建日志。 官网\nRaven：Sentry 的 Python 客户端。 官网\nSentry：实时记录和收集日志的服务器。 官网\n测试 # 进行代码库测试和生成测试数据的库。\n测试框架\nunittest：(Python 标准库) 单元测试框架。 官网\nnose：nose 扩展了 unittest 的功能。 官网\ncontexts：一个 Python 3.3+ 的 BDD 框架。受到 C# – Machine.Specifications 的启发。 官网\nhypothesis：Hypothesis 是一个基于先进的 Quickcheck 风格特性的测试库。 官网\nmamba：Python 的终极测试工具， 拥护 BDD。 官网\nPyAutoGUI：PyAutoGUI 是一个人性化的跨平台 GUI 自动测试模块。 官网\npyshould：Should 风格的断言，基于 PyHamcrest。 官网\npytest：一个成熟的全功能 Python 测试工具。 官网\ngreen：干净，多彩的测试工具。 官网\npyvows：BDD 风格的测试工具，受 Vows.js 的启发。 官网\nRobot Framework：一个通用的自动化测试框架。 官网\nWeb 测试\nSelenium： Selenium WebDriver 的 Python 绑定。 官网\nlocust：使用 Python 编写的，可扩展的用户加载测试工具。 官网\nsixpack：一个和语言无关的 A/B 测试框架。 官网\nsplinter：开源的 web 应用测试工具。 官网\nMock 测试\nmock：(Python 标准库) 一个用于伪造测试的库。 官网\ndoublex：Python 的一个功能强大的 doubles 测试框架。 官网\nfreezegun：通过伪造日期模块来生成不同的时间。 官网\nhttmock：针对 Python 2.6+ 和 3.2+ 生成 伪造请求的库。 官网\nhttpretty：Python 的 HTTP 请求 mock 工具。 官网\nresponses：伪造 Python 中的 requests 库的一个通用库。 官网\nVCR.py：在你的测试中记录和重放 HTTP 交互。 官网\n对象工厂\nfactoryboy：一个 Python 用的测试固件 (test fixtures) 替代库。 官网\nmixer：另外一个测试固件 (test fixtures) 替代库，支持 Django, Flask, SQLAlchemy, Peewee 等。 官网\nmodelmommy：为 Django 测试创建随机固件。 官网\n代码覆盖率\ncoverage：代码覆盖率测量。 官网\nCodecov：一个代码覆盖率测试工具，为开源项目提供免费代码覆盖率测试服务。 官网\n伪数据\nfaker：一个 Python 库，用来生成伪数据。 官网\nfake2db：伪数据库生成器。 官网\nradar：生成随机的日期/时间。 官网\n错误处理\nFuckIt.py：FuckIt.py 使用最先进的技术来保证你的 Python 代码无论对错都能继续运行。 官网\n代码分析和 Lint 工具 # 进行代码分析，解析和操作代码库的库和工具。\n代码分析\ncoala：语言独立和易于扩展的代码分析应用程序。 官网\ncode2flow：把你的 Python 和 JavaScript 代码转换为流程图。 官网\npycallgraph：这个库可以把你的 Python 应用的流程(调用图)进行可视化。 官网\npysonar2：Python 类型推断和检索工具。 官网\nLint 工具\nFlake8：模块化源码检查工具: pep8, pyflakes 以及 co。 官网\nPylint：一个完全可定制的源码分析器。 官网\nYAPF: Google 的 Python 代码格式化工具。 官网\npylama：Python 和 JavaScript 的代码审查工具。 官网\n代码格式化\nautopep8：自动格式化 Python 代码，以使其符合 PEP8 规范。 官网\nblack：一个坚定的 Python 代码格式化工具。 官网\n调试工具 # 用来进行代码调试的库。\n调试器\nipdb：IPython 启用的 pdb。 官网\npudb：全屏，基于控制台的 Python 调试器。 官网\npyringe：可以在 Python 进程中附加和注入代码的调试器。 官网\nwdb：一个奇异的 web 调试器，通过 WebSockets 工作。 官网\nwinpdb：一个具有图形用户界面的 Python 调试器，可以进行远程调试，基于 rpdb2。 官网\ndjango-debug-toolbar：为 Django 显示各种调试信息。 官网\ndjango-devserver：一个 Django 运行服务器的替代品。 官网\nflask-debugtoolbar：django-debug-toolbar 的 flask 版。 官网\n性能分析器\nlineprofiler：逐行性能分析。 官网\nMemory Profiler：监控 Python 代码的内存使用。 官网、 内存\nprofiling：一个交互式 Python 性能分析工具。 官网\n其他\npyelftools：解析和分析 ELF 文件以及 DWARF 调试信息。 官网\npython-statsd： statsd 服务器的 Python 客户端。 官网\n科学计算和数据分析 # 用来进行科学计算和数据分析的库。\nastropy：一个天文学 Python 库。 官网\nbcbio-nextgen：这个工具箱为全自动高通量测序分析提供符合最佳实践的处理流程。 官网\nbccb：生物分析相关代码集合。 官网\nBiopython：Biopython 是一组可以免费使用的用来进行生物计算的工具。 官网\nblaze：NumPy 和 Pandas 的大数据接口。 官网\ncclib：一个用来解析和解释计算化学软件包输出结果的库。 官网\nNetworkX：一个为复杂网络设计的高性能软件。 官网\nNeupy：执行和测试各种不同的人工神经网络算法。 官网\nNumba：Python JIT (just in time) 编译器，针对科学用的 Python ，由 Cython 和 NumPy 的开发者开发。 官网\nNumPy：使用 Python 进行科学计算的基础包。 官网\nOpen Babel：一个化学工具箱，用来描述多种化学数据。 官网\nOpen Mining：使用 Python 挖掘商业情报 (BI) (Pandas web 接口)。 官网\norange：通过可视化编程或 Python 脚本进行数据挖掘，数据可视化，分析和机器学习。 官网\nPandas：提供高性能，易用的数据结构和数据分析工具。 官网\nPyDy：PyDy 是 Python Dynamics 的缩写，用来为动力学运动建模工作流程提供帮助， 基于 NumPy, SciPy, IPython 和 matplotlib。 官网\nPyMC：马尔科夫链蒙特卡洛采样工具。 官网\nRDKit：化学信息学和机器学习软件。 官网\nSciPy：由一些基于 Python ，用于数学，科学和工程的开源软件构成的生态系统。 官网\nstatsmodels：统计建模和计量经济学。 官网\nSymPy：一个用于符号数学的 Python 库。 官网\nzipline：一个 Python 算法交易库。 官网\nBayesian-belief-networks：优雅的贝叶斯信念网络框架。 官网\n数据可视化 # 进行数据可视化的库。 参见: awesome-javascript。\nmatplotlib：一个 Python 2D 绘图库。 官网\nbokeh：用 Python 进行交互式 web 绘图。 官网\nggplot：ggplot2 给 R 提供的 API 的 Python 版本。 官网\nplotly：协同 Python 和 matplotlib 工作的 web 绘图库。 官网\npyecharts：基于百度 Echarts 的数据可视化库。 官网\npygal：一个 Python SVG 图表创建工具。 官网\npygraphviz：Graphviz 的 Python 接口。 官网\nPyQtGraph：交互式实时 2D/3D/ 图像绘制及科学/工程学组件。 官网\nSnakeViz：一个基于浏览器的 Python\u0026rsquo;s cProfile 模块输出结果查看工具。 官网\nvincent：把 Python 转换为 Vega 语法的转换工具。 官网\nVisPy：基于 OpenGL 的高性能科学可视化工具。 官网\n计算机视觉 # 计算机视觉库。\nOpenCV：开源计算机视觉库。 官网\npyocr：Tesseract 和 Cuneiform 的包装库。 官网\npytesseract： Google Tesseract OCR 的另一包装库。 官网\nSimpleCV：一个用来创建计算机视觉应用的开源框架。 官网\n机器学习 # 机器学习库。 参见: awesome-machine-learning.\nCaffe: 一个 Caffe 的 python 接口。 官网\nCaffe2：一个轻量级的，模块化的，可扩展的深度学习框架。 官网\nCrab：灵活、快速的推荐引擎。 官网\ngensim：人性化的话题建模库。 官网\nhebel：GPU 加速的深度学习库。 官网\nkeras: 以 tensorflow/theano/CNTK 为后端的深度学习封装库，快速上手神经网络。 官网\nMXNet：一个高效和灵活的深度学习框架。 官网\nNuPIC：智能计算 Numenta 平台。 官网\npattern：Python 网络挖掘模块。 官网\nPyBrain：另一个 Python 机器学习库。 官网\npydeep：Python 深度学习库。 官网\nPylearn2：一个基于 Theano 的机器学习库。 官网\npython-recsys：一个用来实现推荐系统的 Python 库。 官网\nPytorch：一个具有张量和动态神经网络，并有强大 GPU 加速能力的深度学习框架。 官网\nscikit-learn：基于 SciPy 构建的机器学习 Python 模块。 官网\nskflow：一个 TensorFlow 的简化接口(模仿 scikit-learn)。 官网\nTensorFlow：谷歌开源的最受欢迎的深度学习框架。 官网\nTheano：一个快速数值计算库。 官网\nvowpalporpoise：轻量级 Vowpal Wabbit 的 Python 封装。 官网\nMapReduce # MapReduce 框架和库。\ndpark：Spark 的 Python 克隆版，一个类似 MapReduce 的框架。 官网\ndumbo：这个 Python 模块可以让人轻松的编写和运行 Hadoop 程序。 官网\nluigi：这个模块帮你构建批处理作业的复杂流水线。 官网\nmrjob：在 Hadoop 或 Amazon Web Services 上运行 MapReduce 任务。 官网\nPySpark：Spark 的 Python API 。 官网\nstreamparse：运行针对事实数据流的 Python 代码。集成了 Apache Storm。 官网\n函数式编程 # 使用 Python 进行函数式编程。\nCyToolz：Toolz 的 Cython 实现 : 高性能函数式工具。 官网\nfn.py：在 Python 中进行函数式编程 : 实现了一些享受函数式编程缺失的功能。 官网\nfuncy：炫酷又实用的函数式工具。 官网\nToolz：一组用于迭代器，函数和字典的函数式编程工具。 官网\n第三方 API # 用来访问第三方 API 的库。 参见： List of Python API Wrappers and Libraries。\napache-libcloud：一个为各种云设计的 Python 库。 官网\nboto：Amazon Web Services 的 Python 接口。 官网\ndjango-wordpress：WordPress models and views for Django. 官网\nfacebook-sdk：Facebook 平台的 Python SDK. 官网\nfacepy：Facepy 让和 Facebook\u0026rsquo;s Graph API 的交互变得更容易。 官网\ngmail：Gmail 的 Python 接口。 官网\ngoogle-api-python-client：Python 用的 Google APIs 客户端库。 官网\ngspread：Google 电子表格的 Python API. 官网\ntwython：Twitter API 的封装。 官网\nDevOps 工具 # 用于 DevOps 的软件和库。\nAnsible：一个非常简单的 IT 自动化平台。 官网\nSaltStack：基础设施自动化和管理系统。 官网\nOpenStack：用于构建私有和公有云的开源软件。 官网\nDocker Compose：快速，分离的开发环境，使用 Docker。 官网\nFabric：一个简单的，Python 风格的工具，用来进行远程执行和部署。 官网\ncuisine：为 Fabric 提供一系列高级函数。 官网\nFabtools：一个用来编写超赞的 Fabric 文件的工具。 官网\ngitapi：Git 的纯 Python API。 官网\nhgapi：Mercurial 的纯 Python API。 官网\nhoncho： Foreman 的 Python 克隆版，用来管理基于 Procfile 的应用。 官网\npexpect：Controlling interactive programs in a pseudo-terminal like 在一个伪终端中控制交互程序，就像 GNU expect 一样。 官网\npsutil：一个跨平台进程和系统工具模块。 官网\nsupervisor：UNIX 的进程控制系统。 官网\n任务调度 # 任务调度库。\nAPScheduler：轻巧但强大的进程内任务调度，使你可以调度函数。 官网\ndjango-schedule：一个 Django 排程应用。 官网\ndoit：一个任务执行和构建工具。 官网\ngunnery：分布式系统使用的多用途任务执行工具 ，具有 web 交互界面。 官网\nJoblib：一组为 Python 提供轻量级作业流水线的工具。 官网\nPlan：如有神助地编写 crontab 文件。 官网\nschedule：人性化的 Python 任务调度库。 官网\nSpiff：使用纯 Python 实现的强大的工作流引擎。 官网\nTaskFlow：一个可以让你方便执行任务的 Python 库，一致并且可靠。 官网 * AirFlow：Airflow 是Airbnb公司开源的，是一个工作流分配管理系统，通过有向非循环图的方式管理任务流程，设置任务依赖关系和时间调度。 官方 外来函数接口 # 使用外来函数接口的库。\ncffi：用来调用 C 代码的外来函数接口。 官网\nctypes：(Python 标准库) 用来调用 C 代码的外来函数接口。 官网\nPyCUDA：Nvidia CUDA API 的封装。 官网\nSWIG：简化的封装和接口生成器。 官网\n高性能 # 让 Python 更快的库。\nCython：优化的 Python 静态编译器。使用类型混合使 Python 编译成 C 或 C++ 模块来获得性能的极大提升。 官网\nPeachPy：嵌入 Python 的 x86-64 汇编器。可以被用作 Python 内联的汇编器或者是独立的汇编器，用于 Windows, Linux, OS X, Native Client 或者 Go 。 官网\nPyPy：使用 Python 实现的 Python。解释器使用黑魔法加快 Python 运行速度且不需要加入额外的类型信息。 官网\nPyston：使用 LLVM 和现代 JIT 技术构建的 Python 实现，目标是为了获得很好的性能。 官网\nStackless Python：一个强化版的 Python。 官网\n微软的 Windows 平台 # 在 Windows 平台上进行 Python 编程。\nPython(x,y)：面向科学应用的 Python 发行版，基于 Qt 和 Spyder。 官网\npythonlibs：非官方的 Windows 平台 Python 扩展二进制包。 官网\nPythonNet：Python 与 .NET 公共语言运行库 (CLR)的集成。 官网\nPyWin32：针对 Windows 的 Python 扩展。 官网\nWinPython：Windows 7/8 系统下便携式开发环境。 官网\n网络可视化和 SDN # 用来进行网络可视化和 SDN(软件定义网络)的工具和库。\nMininet：一款流行的网络模拟器以及用 Python 编写的 API。 官网\nPOX：一个针对基于 Python 的软件定义网络应用（例如 OpenFlow SDN 控制器）的开源开发平台。 官网\nPyretic：火热的 SDN 编程语言中的一员，为网络交换机和模拟器提供强大的抽象能力。 官网\nSDX Platform：基于 SDN 的 IXP 实现，影响了 Mininet, POX 和 Pyretic。 官网\nNRU：一个基于组件的软件定义网络框架。 官网\n硬件 # 用来对硬件进行编程的库。\nino：操作 Arduino 的命令行工具。 官网\nPyro：Python 机器人编程库。 官网\nPyUserInput：跨平台的，控制鼠标和键盘的模块。 官网\nscapy：一个非常棒的操作数据包的库。 官网\nwifi：一个 Python 库和命令行工具用来在 Linux 平台上操作 WiFi。 官网\nPingo：Pingo 为类似 Raspberry Pi，pcDuino， Intel Galileo 等设备提供统一的 API 用以编程。 官网\n兼容性 # 帮助从 Python 2 向 Python 3 迁移的库。\nPython-Future：这就是 Python 2 和 Python 3 之间丢失的那个兼容性层。 官网\nPython-Modernize：使 Python 代码更加现代化以便最终迁移到 Python 3。 官网\nSix：Python 2 和 3 的兼容性工具。 官网\n杂项 # 不属于上面任何一个类别，但是非常有用的库。\nblinker：一个快速的 Python 进程内信号/事件分发系统。 官网\nitsdangerous：一系列辅助工具用来将可信的数据传入不可信的环境。 官网\npluginbase：一个简单但是非常灵活的 Python 插件系统。 官网\nPychievements：一个用来创建和追踪成就的 Python 框架。 官网\nTryton：一个通用商务框架。 官网\n算法和设计模式 # Python 实现的算法和设计模式。\nalgorithms：一个 Python 算法模块。 官网\npython-patterns：Python 设计模式的集合。 官网\nsortedcontainers：快速，纯 Python 实现的 SortedList，SortedDict 和 SortedSet 类型。 官网\n编辑器插件 # 编辑器和 IDE 的插件\nEmacs\nElpy：Emacs Python 开发环境。 官网\nSublime Text\nSublimeJEDI：一个 Sublime Text 插件，用来使用超赞的自动补全库 Jedi。 官网\nAnaconda：Anaconda 把你的 Sublime Text 3 变成一个功能齐全的 Python IDE。 官网\nVim\nYouCompleteMe：引入基于 Jedi 的 Python 自动补全引擎。 官网\nJedi-vim：绑定 Vim 和 Jedi 自动补全库对 Python 进行自动补全。 官网\nPython-mode：将 Vim 变成 Python IDE 的一款多合一插件。 官网\nVisual Studio\nPTVS：Visual Studio 的 Python 工具。 官网\n集成开发环境 # 流行的 Python 集成开发环境。\nPyCharm：商业化的 Python IDE ，由 JetBrains 开发。也有免费的社区版提供。 官网\nLiClipse：基于 Eclipse 的免费多语言 IDE 。使用 PyDev 来支持 Python 。 官网\nSpyder：开源 Python IDE。 官网\n自动聊天工具 # 用于开发聊天机器人的库\nErrbot：最简单和最流行的聊天机器人用来实现自动聊天工具。 官网 服务 # 在线工具和简化开发的 API 。\n金融数据 # Tushare ：一个可以提供免费股票、基金、期货、港股等金融数据的 Python 开源数据。 官网\nTa-Lib ：金融数据技术分析库，可以依据原始金融数据计算各种技术指标,计算性能比较优异。 官网\n持续集成 # 参见: awesome-CIandCD.\nTravis CI：一个流行的工具，为你的开源和 私人 项目提供持续集成服务。(仅支持 GitHub) 官网\nCircleCI：一个持续集成工具，可以非常快速的进行并行测试。 (仅支持 GitHub) 官网\nVexor CI：一个为私人 app 提供持续集成的工具，支持按分钟付费。 官网\nWercker：基于 Docker 平台，用来构建和部署微服务。 官网\n代码质量 # Codacy：自动化代码审查，更加快速的发布高质量代码。对于开源项目是免费的。 官网\nQuantifiedCode：一个数据驱动、自动、持续的代码审查工具。 官网\n","date":"September 18, 2018","externalUrl":null,"permalink":"/posts/cnpython/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003ePython 资源大全中文版\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python 资源大全中文版","type":"posts"},{"content":"作者开篇就提到大家总说 Django 性能不行，但是实际上 有很多高性能的站点是使用 Django 开发的。\n第一章：The Big Picture # 作者开篇就提到大家总说 Django 性能不行，但是实际上 有很多高性能的站点是使用 Django 开发的。\nDjango’s scaling success stories are almost too numerous to list at this point. It backs Disqus, Instagram, and Pinterest. Want some more proof? Instagram was able to sustain over 30 million users on Django with only 3 engineers (2 of which had no back-end development experience). Disqus serves over 8 billion page views per month. Those are some huge numbers. These teams have proven Django most certainly does scale. Our experience here at Lincoln Loop backs it up. We’ve built big Django sites capable of spending the day on the Reddit homepage without breaking a sweat.\n在作者的公司，他们开发高性能 Django 站点的准则就是 simplicity :\nUsing as few moving parts as possible to make it all work. “Moving parts” may be servers, services or third-party software. Choosing proven and dependable moving parts instead of the new hotness. Using a proven and dependable architecture instead of blazing your own trail. Deflecting traffic away from complex parts and toward fast, scalable, and simple parts . Simple systems are easier to scale, easier to understand, and easier to develop.\n构建高性能 Web 应用通常需要关注一下几点：\n数据库。关系型数据库通常是整个技术栈中最慢最复杂的部分，一个办法是改用 NoSQL 数据库，不过 大多数情况下都可以通过缓存解决。 模板。我们可以用一个更快的模板引擎替换 Django 自带的模板引擎，不过即便是这样模板仍旧是 整个技术栈中第二慢的部分。我们仍然可以通过缓存解决这个问题。 Python。Python 在通常情况下已经足够快了。我们可以使用 Web 加速器（比如：Varnish）缓存服务器响应， 在请求进入到 Python 那一层之前就返回相应。 这章作者一直在强调缓存，\u0026ldquo;CACHE ALL THE THINGS\u0026rdquo;。无论你怎么优化你的技术栈，没有比缓存更快的优化方案。 说到缓存可能大家可能会顾虑缓存过期的问题，作者说了现在先别关心这个问题，之后会给出解决方案。\n作者提到一般的网站都保护这几层：负载均衡器，Web 加速器，APP 服务器，缓存，数据库。\n顺便提了一个 HTTPS 的负载均衡配置方法：客户端与负载均衡器之间使用 HTTPS，负载均衡器与后端服务之间使用 HTTP。这样既保证了安全又可以减少 HTTPS 对性能的影响。\n第二章 The Build # 小技巧 本地开发环境应该尽可能的与线上环境一致：相同的数据库，相同的操作系统以及相同的软件版本。 组织 settings 文件，创建一个基础的配置文件 settings/base.py ，然后再为开发，测试，部署分别创建一个配置文件，一些重要的配置信息可以通过环境变量获取\nsettings/base.py settings/dev.py settings/deploy.py 这里作者有提到一个小技巧，那就是环境变量的值都是字符串，那么如果将值转换为布尔值，元组甚至字典呢？答案就是可以使用 ast 模块:\n\u0026gt;\u0026gt;\u0026gt; ast.literal_eval(\u0026#39;True\u0026#39;) True \u0026gt;\u0026gt;\u0026gt; ast.literal_eval(\u0026#39;1, 2, 3\u0026#39;) (1, 2, 3) \u0026gt;\u0026gt;\u0026gt; ast.literal_eval(\u0026#39;{\u0026#34;foo\u0026#34;: \u0026#34;bar\u0026#34;}\u0026#39;) {\u0026#39;foo\u0026#39;: \u0026#39;bar\u0026#39;} 小心第三方 APP # 在决定使用某个第三方 APP 之前，先回答下面几个问题：\n它真的符合你的需求吗？还是只是有点相近？ 它是个健康的项目吗？ 维护者有一个好的追踪记录吗？ 文档写的好吗？ 测试覆盖率够吗？ 社区怎样（贡献者，pull requests 等等）？ 还在处于活跃开发吗？ 有很多旧的 issues 和 pull requests 吗？ 性能咋样？ 它会产生多少数据库查询？ 易于缓存吗？ 跟你项目的其他部分有冲突吗？ 它的授权协议跟你的项目兼容吗？ 不再维护以及不稳定的第三方应用很快就会成为你的项目的负债。 可以尝试阅读源代码，然后从中找出你的项目需要的代码然后应用到项目中。\n找出性能问题 # 可以使用下面这些工具\nDjango Debug Toolbar django-debug-panel django-devserver 观察页面性能：\n执行了多少条 SQL 语句？ 有多少时间花费在数据库上？ 执行了什么特殊的查询操作，每次查询花费多长时间？ 这些查询是有什么代码生成的？ 渲染页面都用到了哪些模板？ 冷/热缓存是如果影响性能的？（提示：可以使用 settings 来切换缓存）\n哪里需要优化 # 数据库优化 # 减少查询次数 使用 select_related, prefetch_related, (提示：prefetch_related 要放在查询的最后，不然会没有效果。) 减少查询时间 不要忘记加索引（索引也是有代价的，每次对数据库进行写操作都需要更新索引） 某些情况下 join 查询性能很差，在这种情况下两条查询语句比一条 join 耗时更少。 限制结果数 留意 .all() 只取需要的结果数, queryset[:20] count 查询很慢。可以的话，不要使用 count。比如使用 .exists() 代替 count 进行判断记录是否存在。 generic 外键。generic 外键是个很 cool 的功能，但是它会生成一些特别复杂的查询，所以可能的话，不要使用它。如果你一定要用的话，记得这是个需要缓存的地方。 优化 model 方法。如果某个 model 方法在一个请求内会多次被调用，可以使用 cache_property 缓存方法解决（缓存只在该请求内有效） from django.utils.functional import cached_property class TheModel(models.Model): @cached_property def expensive(self): # ... return result 结果太大了，包含了不需要的字段。使用 defer, only, values, values_list 限制结果大小: posts = Post.objects.all().defer(\u0026#39;body\u0026#39;) posts = Post.objects.all().only(\u0026#39;title\u0026#39;) posts = Post.objects.all().values(\u0026#39;id\u0026#39;) posts = Post.objects.all().values_list(\u0026#39;id\u0026#39;) posts = Post.objects.all().values_list(\u0026#39;id\u0026#39;, flat=True) 缓存查询结果。这里提到两个库: Johnny Cache 和 Cache Machine 这两个库的原理都是在 ORM 和数据库中间加了一个缓存层，将 ORM 生成的 SQL 作为 key 来缓存查询结果。\n只读 replicas。对那些读远远大于写的站点，可以考虑从 只读 replicas 中读取数据，实现读写分离。减少主库的负担优化性能。\nraw 查询。如果感觉 ORM 有点慢话，可以考虑使用 raw 方法执行原生的 SQL 语句。\n反范式。这种方法有个问题就是每次更新的时候都需要同时更新其他表中相关的冗余字段。\n使用其他数据存储方式。比如： Postgres, redis, mongodb，使用 Elasticsearch 进行全文检索等。 需要注意的是，在生产环境下新增一个服务并无法没有代价的。作为开发者我们可以不太在意这个，但是系统需要 支持，配置，监控，备份等。新增服务的时候要考虑到这些代价以及你的系统管理员的意见。\nsharding。99.9% 的网站都不需要用到数据库的 sharding 功能，所以只有在你确信你遇到了那 0.1% 的时候 再使用 sharding 功能。\n模板优化 # 应该缓存模板中一切可以缓存的东西。\n俄罗斯套娃式缓存。在一个模板里缓存嵌套缓存，就像俄罗斯套娃一样，一层套一层。比如: {% cache MIDDLE_TTL \u0026#34;post_list\u0026#34; request.GET.page %} {% include \u0026#34;inc/post/header.html\u0026#34; %} \u0026lt;div class=\u0026#34;post-list\u0026#34;\u0026gt; {% for post in post_list %} {% cache LONG_TTL \u0026#34;post_teaser_\u0026#34; post.id post.last_modified %} {% include \u0026#34;inc/post/teaser.html\u0026#34; %} {% endcache %} {% endfor %} \u0026lt;/div\u0026gt; {% endcache %} 自定义一个支持通过 url 参数刷新模板缓存的 cache 标签，这样就可以随时刷新缓存了 随后处理耗时的任务 # 可以把耗时的，不需要同步知道结果的任务放到类似 celery 的任务队列中异步执行， 从而减少请求——响应的处理时间。下面这些任务可以考虑放到 celery 中：\n第三方 API 的调用 发邮件 非常复杂的计算（视频处理，大量的数字处理等） 对于使用 celery 作者提到了一下小提示：\n不要将 model 实例作为任务的参数，可以改用传主键的方式。因为在这期间 model 的数据可能已经发生改变了， 还有就是那个 model 实例可能不支持序列化。 保持任务小，不要再一个任务中执行太多的工作。把一个任务分割成多个任务，一方面可以使用多核或多 worker 的 方式加速任务执行，另一方面，单个任务可以很快的执行完方便安全快速的重启 worker，因为一个 worker 重启时 会等待正在执行的问题完成，保持任务小巧的话，可以加快部署时间。 可以考虑使用 celery 的 beat 功能去自习定时任务。 前段优化 # 压缩 css 和 javascript（min, gzip, 版本化静态文件）。（个人建议：版本化文件应该类似这样 foo-xxyy.js 而不是这样 foo.js?v=xxyy ，主要是方便使用 CDN，防止出现缓存未过期的情况。） 压缩图片。 使用 CDN 服务静态文件。 文件上传 # 可以考虑使用分布式文件系统或者云存储来存储上传的文件。使用云存储的时候要考虑备用方案，万一服务不可用怎么办。\n测试 # 好的测试用例是健康代码的强有力的基石。测试应该覆盖到你代码中最复杂，最重要，最容易出问题的地方。\n自动化测试以及持续集成 # 一个持续集成系统可以让开发者在开发进度的早期就发现问题，通过持续集成系统来执行 自动化测试以及检查你的代码的健康度。作者提到了他们的检查点：\n单元测试 代码覆盖率 PEP8/linting 使用 Selenium 进行功能测试 所以 Jmeter 进行性能测试 第三章：The Deployment # 先决条件 # 操作系统 # 作者推荐使用 Ubuntu。同时作者给出了选择其他操作系统时，需要考虑的事情：\n能够很容易的就使用 Python 2.7+ 。有些操作系统要安装 2.7+ 版本的 Python 非常的麻烦，如果是这样的话你就要慎重考虑了。 有长久的安全更新支持。 配置管理 # Chef, Puppet, Ansible, Salt 都是比较好的配置管理工具 Fabric 不是配置管理工具，如果你把它当作配置管理工具的话，会有你头疼的时候。你可以在 Fabric 的基础上 构建你自己的配置管理工具。 进程管理 # 系统默认的工具：upstart, systemd 第三方软件：deamontools, supervisord, circus 更新代码 # 更新代码一般需要进行下面几步：\n从版本控制服务器上拉取最新的代码 更新依赖 合并数据库更改(migrate) 收集，压缩，推送静态文件到 CDN reload WSGI 服务器 重启后台 workers 推荐写个脚本自动执行这些操作，这样不容易出错。如果要更新多台服务器的话，可以使用远程执行 框架来做这种事情，比如： Salt, Fabric。\n有一点特别要注意的是，一旦服务上线就应该尽可能的使用平滑 reload 的方式来代替重启进程的暴力方式。\n多个远程环境 # 至少要有两个环境吧：打包/开发环境，生产环境。 同时多个环境尽量保证一致性，尤其是多个生产环境之间（设置，软件，系统，等等）。 不过有些东西还行要区分的，比如：\n第三方服务配置。比如，你肯定不希望在开发环境下触发支付操作或发送文件到生产环境下的 CDN 上。 获取数据的问题。经常看到某些人在开发环境下使用线上数据库的副本，但是这里有几个问题要考虑： 你的开发环境更生产环境一样安全吗？监控松散的开发机器是黑帽黑客经常会攻击的目标。 有可能会从你的应用中发送邮件或通知吗？从你的开发环境下向你的用户发送数千封邮件不仅是非常 尴尬的事情，同时也会影响你们的商业。\n避免单点故障 # 要经常备份，确保你知道所有存储的数据（数据库，用户上传的数据，等等）都有备份。这样出现故障的时候 其他其他机器的时候丢失的数据会少一点。\n高可用是一个可以考虑的方法，如果对你的商业来说他是非常重要的话。 HA 可以保证在出现服务挂掉 的情况下能够无缝自动切换到备用服务或者说不用手动切换。不过需要注意的是通常构建高可用 的花费比服务当掉的花费还高。\n在考虑架构的时候要考虑到如何解决单点故障的问题。举个例子，当使用第三方的 Amazon EC2 的时候， 你是否能够接受某些区域的设备宕机，整个地区呢？如果服务商 Amazone 当了怎么办？ 越早考虑这些问题就能在灾难实际发生的时候更好的应对。\n服务器布局 # 负载均衡器： 可以使用云服务商提供的负载均衡服务或者使用 nginx, Haproxy。对于负载均衡器网络 带宽是个非常重要的注意点。 Web 加速器：网络带宽和内存是值得关注的点 应用：CPU 和内存值得关注。 后台运行的 workers：后台运行的任务通常都是 CPU 密集型的任务并且运行在独立的服务器上。一个服务器上可以运行 多个 workers。 缓存：你的缓存服务器需要更多的内存。另一个需要注意就是网络带宽，它可能会在内存之前成为瓶颈。如果网络开始 拥堵的时候， Django 可以配置多个缓存服务器。 数据库：足够的内存非常重要，最用是足够把你的数据都装载在内存里。 如果你预期会有 64GB 的数据，那么至少要有 64GB 的内存。 磁盘速度也非常重要。购买你能够负担得起的最快的磁盘。 如果你用的是虚拟机的话，你需要留意你其实是在跟你的邻居共享一个物理磁盘，常规的实践是尽可能的买最大的虚拟机。 优化技术栈 # 优化数据库 # 优化 uWSGI # 优化 Django # CACHES\n如果你使用 Memcached，使用 pylibmc 这个库会有更高的性能。 redis 的话可以使用 django-redis 。\n缓存过期是第一个需要面临的问题，一个缓存 key 过期或者被刷新都有可能摧毁你的数据库。 幸运的是有个简单包可以解决这个问题： django-newcache\n还有一个问题就是如果缓存服务器宕机了会到期用户收到 “500 Server Error”的响应。 将使用一个缓存服务器改为三个可以降低出错的几率。 你需要考虑当缓存服务器宕机的时候，你是需要你的网站也跟着宕机，还是希望你的应用继续 运行良好只是把它当作缓存未命中来处理？作者创建了一个 django-ft-cache 包用来 解决这个问题，它会将任何的 memcached 操作用 try/except 包裹，捕获这里的异常， 这样缓存服务器当掉时请求依然可以被正确处理。\nSESSION_ENGINE\n把 session 保存在数据库中非常影响性能，一个好的办法是保存在缓存中。如果用 redis 的话 可以使用 cache backend, 用 memcached 的话 cached_db backend 也还行。 另一个办法就是使用 signed_cookies backend, 让客户端存储 session 数据。\nDATABASES\n可以通过 DATABASES 设置增加 CNN_MAX_AG key 选项来定义重用数据库连接。 比如 300, 这将告诉 Django 保持打开和重用数据库连接 5 分钟。\nLOGGING\n不要定义 LOGGING 将日志保存到文件中，而是应该输出到 STDERR ，让进程管理器来处理日志。\nMIDDLEWARE_CLASSES\n不要轻易自定义中间件，因为中间件会在每个请求中执行。所以确保你知道每个中间件都做了什么， 以及尽可能的不要在中间件中执行数据库查询操作。\n常规安全问题\n需要注意 clickjacking 和 XSS(Cross Site Scripting)。最简单的方法是使用 django-secure 项目来检查安全问题。 另一个安全问题是，确保你的 admin 后台处于保护中，如果你把它开放出去了，确保使用了非常 复杂的管理员密码。最好是把它变成一个内网服务，让它不能从外部互联网访问。\n配置你的服务器 # 安全 # 调整 SSH 配置：禁用 root 登录，禁用密码登录，更改默认端口。 应用安全补丁：关注一下系统安全问题，及时更新 zer-day 补丁（比如，Hearbleed） 使用私有网络：大多数云服务器都提供私有网络服务，只允许访问你帐号下的服务。在私有网络 中访问你的服务器可以加大被人攻击你的难度。 保护内网服务：内网服务包括控制台，开发服务器，持续集成系统。它们会成为你安全网络的一道暗门。 将它们用 VPN 或认证代理锁起来，如果你没有使用 VPN 的话，确保传输数据以及登录时总是使用 SSL/HTTPS。 锁住你的开发环境可以确保 google 不会抓取它从而伤害你的 SEO） 防火墙：只允许指定端口和 IP 访问你的服务器。硬件防火墙很棒，软件防火墙比如 iptables 也不错。 不要在 root 下运行：不在 root 下运行可以防止某些人运用 RCE(remote code execution)获取你服务器的 root 访问权限。使用标准用户登录服务器，只在必要时使用 sudo。 保护你的第三方服务的账户：使用强壮的密码，尽可能的开启两步验证。 备份 # 对于数据库，有一个运行的 replica 可以很方便的执行全备份。推荐在半夜执行全备份。 进行备份的时候有几个问题需要问一下你自己：\n如果有人黑进了你的服务器，他们能够删掉或破坏你的备份吗？ 基于这个原因拉取到备份服务器比推送要更好。 如果有人拿到了你的备份会有多糟糕？加密备份文件并且确保黑客没法在同一个服务器上找到解密的方法（注：比如可以使用公钥进行加密，同时服务器上不要存有私钥，这样就不会被黑客在服务器上找到解密的方法）。 你有测试过备份是否可用吗？定期测试你的备份，验证它们是否真的有效。 监控 # 没有监控的话，线上网站就会成为一个大黑盒。你没法知道实际情况是怎样的，也就没法进行性能优化了。\ninstrumentation # 对于应用，你需要知道下面这个问题：\n我的系统中最慢的部分是什么？ Django 处理响应的平均耗时是多少？ 哪个视图是最慢的？或者花费最多的时间？ 哪个数据库查询是最慢的？或者花费最多的时间？ 这些数据一段时间内是如何变化的？ NewRelic 是个探测这些问题的比较好的服务，它可以很方便的安装。 然而， NewRelic 是闭源的，专有的系统，同时也非常的贵。有一些开源产品可以替代它。 比如：Graphite\n服务器资源 # 关于服务器资源需要监控如下数据：\n平均负载 CPU 负载 物理内存使用情况 磁盘相关数据 网络 I/O 告警 # 下列情况需要发送告警：\n超过 X% 的请求出现错误 服务器宕机 服务器资源占用过高：负载，虚拟内存，磁盘等等 服务未响应\n日志 # 需要收集如下日志：\n从你的负载均衡器都应用的 Apache 风格的日志 任何应用内的日志 相关服务的 syslog 日志 数据库慢查询日志，以及在不会导致数据库连接的 I/O 问题（尤其是磁盘或网络）的前提下收集所有数据库查询的日志。 错误汇报 # 在生成环境下 Django 默认会在出现异常时给网站管理员发送异常邮件。这个功能有时也会导致一些问题：\nEmail 不利于追踪错误 如果你的错误发生在一个高访问的页面的话，你实际上是在 DoS 你的邮件服务器， 可能导致被加入黑名单，或者你的邮件服务商会关掉你的服务（他们不想在几秒内发送超过 10K 的邮件）。 幸运的是，更好的错误汇报方式已经存在了，开源的 Sentry 项目是个非常好的解决方案。 Sentry 并不会发送 10K 的邮件，它之后在第一次出现错误时邮件通知你，之后收集并在 Web 页面上 暂时其他时候的错误用于分析问题。\n还有一个关于错误的主题就是确保有一个漂亮的不引来应用服务的 500.html 文件，并且已经在服务器上配置好了出错是使用 这个页面。\n第四章：预备 # 使用 Jmeter 进行负载测试 # 这一部分主要讲解了 Jmeter 的各种是否方法及配置。\n启动计划 # 启动的时候有些东西需要考虑\n使用负载均衡器在新的和旧的之间分流 使用“dark launch”，这样用户就不会感觉到他们访问的是新的服务器 使用代理功能分一部分流量到新的服务器 使用特性标志来发布一个新的特性。 发送流量到一个新的没有缓存的服务器可能会导致临时的高负载 从而在缓存热和起来前击垮你的服务器。预热你的缓存可以解决这个问题。一个比较简单的办法是使用一个脚本在有真实请求前去抓取你的网站上的热门 URLs。\n不要在一天的最后时间段或周五的时候升启动，除非你想让你的 整个团队在晚上或周末加班。 你应该在大家都在并且有几个小时或几天的时间来处理 启动过程中出现的问题时候启动，同时也要确保你的成员有时间休息。 如果你的网站访问量比较高的话，尝试在访问量比较低的时间段进行升级操作。\n启动前的检查事项 # Django 配置项 # DEBUG 和 TEMPLATE_DEBUG 都设为 False SECRET_KEY 是个非常大的随机字符串并且保密 ALLOWED_HOSTS 包含了访问者可能会使用的有效域名 TEMPLATE_LOADERS: Cached template loader 已启用 SESSION_ENGINE 比默认设置更快 CACHES: 使用 Memcached 或 Redis 后端 MEDIA_ROOT 和 MEDIA_URL 接受并显示文件上传 管理员账户被限制并且有一个强壮的密码 部署 # 通过点击各种页面和链接的方式来确认网站是否按预期的结果 工作（没有挂掉的图片和链接） Django 日志是否正常工作 监控平台是否接收到数据。确保你能看到整个技术栈中各级 的错误信息。 错误被汇报并且触发了通知 第三方服务能够接收到数据（支付，分析等等） 从你的应用服务和 Celery workers 中发出邮件的功能 能够正常运行 自定义的错误页面（400，500）已经在各个级别中被设置（负载均衡器，web 加速器，Django） Django admin 没法通过 /admin/ 公开访问 SSL 证书有效并且设置是安全的。 Django-secure 的 manage.py checksecure 运行起来没有错误输出 基础设施 # 服务器和服务是安全的，已经锁好了大门 有个简单，正式的程序用来部署新的代码 你有一个可以在需要的时候快速水平扩展服务的计划 DNS TTL 可以被修改为 5 分钟或更短的时间在需要更改的时候 第五章 The Launch # 监控整个 Launch # 服务器资源 # 可以使用如下工具参考服务器资源使用情况\nhtop varnishstat varnishhist varnishtop varnishlop uwsgitop celery inspect celery events flower memcache-top pg_top pg_stat_statements pt-query-digest mytop 当灾难来临的时候 # 应用服务器过载 # 最简单的办法就是通过增加服务器的方式进行水平扩展。 不过你需要注意这将导致你的数据库承受更大的压力， 可能会把数据库搞挂。\n当你通过增加服务器的方式把负载降低到可以接受的级别后， 你就需要使用你的低级别的工具来查看为什么会出现负载过高的 情况。你的 web 加速器端缓存命中率过低是一个需要考虑的因素。\n数据库服务器过载 # 如果你的网站是 读多写少的话，可以通过增加 replica 的方式来简单解决响应时间问题。 同时也看看是否在数据库优化时遗漏某些可以优化的项。\n应用和数据库服务器过载 # 你可以从自底向上优化你的数据库，减轻数据库的压力可以让你的应用拥有更高的性能。 你也可以通过优化你的 web 加速器从而减轻应用服务器的压力，进而减轻数据库服务器的压力。\n前方的路 # 恭喜你的网站已经启动了！现在你需要确保它能够持续稳定的运行。在这个战争中你需要与下列事物做斗争：\n你的用户（流量增长） 你的软件（一点点腐烂） 你（错误的决定） 第一个没啥好惊讶的，最后两个可能会让你惊讶同时也是让你网站宕机的一些因素。\n流量增长 # 正常情况下你的网站不应该在技术栈的任何层次占用 100% 的资源，一旦出现超过 70%（CPU,内存，磁盘等等） 的资源占用，那就说明某些地方需要优化了或者增加更多的资源。当流量突增的时候如果你有额外的资源的话就可以很好 的应对。\n有些时候的流量激增可能是商业上进行了某些吸引用户的活动，确保开发者知道这些商业活动，以便应对激增的流量。 一个好的主意是像刚启动时那样，大家在桌子上一起讨论分享这些商业信息。\n一点点腐烂 # 由于高性能网站使用了很多不同的服务，所以需要保持这些服务打上了最新的安全补丁。 “如果没出现问题，就不要去修复它”是个非常危险的准则。\n跳过几个小版本是没什么问题的，但是不要落下太远，不然的话到时就会失去升级动力。 定期小步升级你的依赖（你的操作系统，主要的服务以及 Python 库）。\n作者的小贴士：\n当 review 升级的时候，我们通常不会使用最新的版本，一般最新版本也意味着有新的 bug ，我想 你不希望当小白鼠吧。给新版本几个月的成熟期。 升级到第一个稳定的版本或者你已经确保这个版本没什么大的问题了。\n错误的决定 # 意外的刷新了缓存 # 在流量特别高的时候重启缓存或 web 加速器可能会击垮你的网站。这个问题叫做 dog-piling/cache stampede 所以尽量使用 reload 的方式来更新服务。对于使用 memcached/redis 的缓存可以通过给不同的缓存定义不同的 KEY_PREFIX 的方式来实现逐个清理缓存（通过 VERSION）而不是一下子删除所有缓存的功能。 如果一定要重启的话，进行选择在流量低的时候重启服务。\n数据库锁表 # 数据库锁会临时阻塞主写操作，如果这个操作特别长的话就会成为一个问题。两个比较常见的长时间锁表的场景是： schema migration 和备份。\n在开发的时候， South 和内置的 migration 可以很方便的应用 Model 更改， 但是在生成环境下， migrate 操作可能会导致长时间的锁表现象，这个对你的用户来说就是个糟糕的消息了。 对于 ADD COLUMN 操作，如果你使用的是 MySQL 数据库的话你需要留意一下这个，但是 PostgreSQL 很少会出现这个问题。\n无论你使用哪种数据库，migrations 都需要被 review 并且使用最近的线上数据的副本数据测试过后才能在生产环境使用。\n备份是另一个会导致长时间锁住数据库的操作。最好是在一个只读的 replica 上进行备份操作。\n大量的缓存过期 # 跟刷新缓存差不多糟糕的事情就是有大量的缓存过期，这将导致数据库压力倍增。 如果你不是很确定的话，应该选择在流量比较低的时候进行部署操作，避免击垮你的站点。\n昂贵的 admin 视图 # 构建一个未优化的 admin 视图会导致上千的数据库查询。 如果你正在使用一个查询缓存比如 johnny-cache, 每一次在 admin 中保存都将 导致与所在表相关的缓存都将失效。所以 admin 视图也需要像普通视图那样进行优化。\n昂贵的后台任务 # 未优化，数据库依赖的任务会导致非预期的负载负担。所有应该像优化视图一样优化你的后台任务。\n逐渐恶化 # 随着新功能的增加，网站的性能也在接受不同的考验，你需要随时关注性能问题。\n每次发布新版本的时候都关注一下性能的变化，如果响应时间变长了或者负载变高了， 立马去解决它。这样就不会出现在几个月的提交过去后再去查找问题所在的情况。\ncomplexity creep # 如果你按照前面所说的步骤做了的话，你已经做的非常好了。 随着你的网站的成长你可能会遇到各种各样的新问题，你可以很容易的就构建自己 独有的解决方法。 构建你自己的工具是件很有乐趣的事情，但是 not invented here 对于长时间运行 的站点是非常危险的。你最好去学习如何让 Varnish 更高效而不是丢弃它转而使用你自己的工具。 这个决定衍生的代价是非常巨大的：\n训练新的开发者需要更高的花费。你可以找到一名拥有使用一个比如 Varnish 这种服务经验的开发者， 但是如果使用你自定义的方案的话，你需要训练每一位在门外徘徊的开发者。 开发低级别的基础设施工具将会导致开发时间远离了你的核心产品。使用一个支持良好的开源服务， 你的团队的开发者的效率会很高效。 写你自己的软件代码不是一锤子买卖。所有的软件都需要不断的开发，测试，文档，等等。 最后的想法 # 现在你可以在看看那个老问题“Django doesn\u0026rsquo;t scale”，你是怎么认为的呢？ 如果只用 manage.py runserver，使用 SQLite 在一个非常小的云服务器上跑 当然不会很快。\n让我们回到 2012 年，当 Instagram 只有3个的 Django 团队却支撑起了 1千4百多万的用户的时候， 他们在博客上是 这么说的 ：\n我们选择一个系统的核心宗旨是：\n保持非常简单 不要重复造轮子 可能的话，使用久经考验的成熟的技术 我们非常同意。因此，在你继续你的 Django 旅程的时候，不要忘了你在这里学到的东西。 简单是指导原则。\n注：本文转自 黄黄的博客，并修改了文中大量错误。\n","date":"September 15, 2018","externalUrl":null,"permalink":"/posts/highdjango/","section":"","summary":"\u003cp\u003e作者开篇就提到大家总说 Django 性能不行，但是实际上 有很多高性能的站点是使用 Django 开发的。\u003c/p\u003e","title":"《High Performance Django》阅读笔记","type":"posts"},{"content":"","date":"September 15, 2018","externalUrl":null,"permalink":"/categories/django/","section":"","summary":"","title":"Django","type":"categories"},{"content":" 本项目开发环境是 Python3.5.2+Django1.10.6+Sqlite3+Centos7.5+Nginx1.12.2+Gunicorn\n使用Centos7.5+Nginx+Gunicorn+Django+Python3部署blog项目\n本项目开发环境是 Python3.5.2+Django1.10.6+Sqlite3+Centos7.5+Nginx1.12.2+Gunicorn\nblog虽然已经上线测试有一段时间了(2018.8.7上线)，但是项目部署的笔记由于内容较多，没时间整理。今天抽空整理了一下午，发布出来供需要的同学借鉴参考。文中如有错误请多多指正\n部署前的准备\n服务器 # 要部署项目就要先购买服务器，如腾讯云，阿里云等等\n注意：服务器在国内的话，必须要备案！不然无法使用。\n阿里云优惠券： 点击前往领取 腾讯云代金券： 点击前往领取 腾讯云首页: 点击直达 腾讯云西南区云产品促销链接： 点击直达 腾讯云服务器购买链接： 点击直达\n域名 # 域名购买选择，可以到百度云，腾讯云，阿里云，GoDaddy，namecheap等等\n购买域名后就是解析域名，打开域名解析面板，添加解析记录\n记录类型：A 主机记录：test.markhoo.cn，这个markhoo.cn是你的一级域名，二级域名你可以设置为www.markhoo.cn 解析线路：可以默认也可以选别的 记录值：填写你的服务器IP地址 TTL：域名生效时间，也就是多久生效 设置好以上内容，点击确定。解析完成 主机记录中这个markhoo.cn是你的一级域名，二级域名你也可以设置为 www.markhoo.cn 或者别的 xxx.markhoo.cn 也可以。 注意：域名是否在国内购买不重要，只要服务器不在大陆购买，即使未备案照样可以使用。要是要解析的服务器是大陆服务商则必须要备案。备案后一个域名可以同时接入多个服务商。\n域名解析记录类型说明：\nA：将域名指向一个IPv4地址 AAAA：将域名指向一个IPv6地址 CNAME：将域名指向另外一个域名 NS：将子域名指定其他DNS服务器解析 MX：将域名指向邮件服务器地址 SRV：记录提供特定的服务的服务器 TXT：文本长度限制512，通常做SPF记录(反垃圾邮件) CAA：CA证书颁发机构授权校验 显性URL：将域名302重定向到另外一个地址 隐性URL：与显性URL类似，但是会隐藏真实目标地址 xshell下载与安装 # 主要用于远程登录服务器进行相关操作。在官网注册后发送下载链接到你的注册邮箱，或者到搜索引擎搜索下载。 个人版免费，企业版收费。 下载后一路点击下一步，完成。\n登录服务器步骤：\n点击左上角\u0026rsquo;文件\u0026rsquo;\u0026ndash;\u0026lsquo;新建\u0026rsquo; 然后弹出对话框 常规：名称，这个你可以随便取什么名字 协议：SSH，一般默认 主机：空，填上你的服务器IP地址 端口号：22，默认的就行 填好以上选项内容，点击左侧的\u0026rsquo;用户身份验证\u0026rsquo;，然后看右侧，方法选择\u0026rsquo;Password\u0026rsquo;，在下面输入你的服务器登录用户名和密码，最后点击下面的\u0026rsquo;连接\u0026rsquo;。 登录成功！进入界面。 百度网盘官网软件下载链接： 链接: https://pan.baidu.com/s/19K5q1JkmD9qnYaGRTALyLg 密码: r6hx\n现在官网也是免费使用，也可以直接官网下载\n安装Nginx # 首先用xshell登录服务器 通过 yum 安装 Nginx\n$ yum install nginx 启动 Nginx 服务\n$ systemctl start nginx 或者\n$ systemctl start nginx.service $ systemctl enable nginx.service 访问服务器IP地址，可以看到 nginx 的欢迎界面\nNginx相关命令： 停止nginx\n$ nginx -s stop 重启nginx\n$ nginx -s reload Centos7.5安装Python3 # 由于centos7.5原本默认安装了Python2，而且这个Python2不能被删除，因为有很多系统命令，比如yum都要用到。 以下操作首先要用xshell登录到服务器 输入Python命令，查看可以得知是Python2.7.5版本\n输入\n$ which python 可以查看位置，一般是位于/usr/bin/python目录下。\n下面介绍安装Python3的方法\n首先安装依赖包\n$ yum -y groupinstall \u0026#34;Development tools\u0026#34; $ yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel 然后根据自己需求下载不同版本的Python3，我下载的是Python3.5.2\n注：如果速度不够快，可以直接去官网下载，利用WinSCP等软件传到服务器上指定位置。\n我的存放目录是/usr/local/python3，使用命令：\n$ mkdir /usr/local/python3 建立好一个空文件夹，进入该目录\n$ cd /usr/local/python3 使用一下命令下载压缩包\n$ wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz 然后解压压缩包安装Python3\n$ tar -xvJf Python-3.5.2.tar.xz $ cd Python-3.5.2 $ ./configure --prefix=/usr/local/python3 $ make \u0026amp;\u0026amp; make install 最后创建软链接\n$ ln -s /usr/local/python3/bin/python3 /usr/bin/python3 $ ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3 在命令行中输入python3测试\n升级pip3命令：\n$ pip3 install --upgrade pip 部署前的项目配置 # Django 项目中会有一些 CSS、JavaScript 等静态文件，为了能够方便地让 Nginx 处理这些静态文件的请求，我们把项目中的全部静态文件收集到一个统一的目录下，这个目录通常位于 Django 项目的根目录，并且命名为 static。为了完成这些任务，需要在项目的配置文件里做一些必要的配置：\nmyblog/settings.py # 其他配置... STATIC_URL = \u0026#39;/static/\u0026#39; # 加入下面的配置 STATIC_ROOT = os.path.join(BASE_DIR, \u0026#39;static\u0026#39;) STATIC_ROOT 指明了静态文件的收集目录，即项目根目录（BASE_DIR）下的 static 文件夹。\n为了安全起见，在生产环境下需要关闭 DEBUG 选项以及设置允许访问的域名。打开 settings.py 文件，找到 DEBUG 和 ALLOWED_HOSTS 这两个选项，将它们设置成如下的值：\nmyblog/settings.py DEBUG = False ALLOWED_HOSTS = [\u0026#39;127.0.0.1\u0026#39;, \u0026#39;localhost \u0026#39;, \u0026#39;.markhoo.cn\u0026#39;] ALLOWED_HOSTS 是允许访问的域名列表，127.0.0.1 和 localhost 是本地访问的域名，.markhoo.cn 是访问服务器的域名（换成你自己的域名）。域名前加一个点表示允许访问该域名下的子域名，比如 www.markhoo.cn test.markhoo.cn 等二级域名同样允许访问。如果不加前面的点则只允许访问 markhoo.cn。\n项目还会依赖一些第三方 Python 库，为了方便在服务器上一次性安装，我们将全部依赖写入一个叫 requirements.txt 的文本文件中。激活本地的虚拟环境（如果你使用了虚拟环境的话），并进入项目的根目录，运行 pip freeze \u0026gt; requirements.txt 命令：\n$ (myblog_env) D:\\Djangoprojects\\myblog\u0026gt; $ pip freeze \u0026gt; requirements.txt 这时项目根目录下会生成了一个 requirements.txt 的文本文件，其内容记录了项目的全部依赖。由于有一些依赖包也有先后依赖关系，该调整的需要进去txt文件调整一下。\n如果上传到服务器后修改，可以使用 \u0026lsquo;vi (文件名称)\u0026rsquo; 打开文件，然后点击键盘 \u0026lsquo;i\u0026rsquo; 键切换到输入模式。 修改完需要退出，先按键盘 \u0026lsquo;Esc\u0026rsquo; 键，然后输入 \u0026lsquo;:wq\u0026rsquo; 保存退出。如果不想修改又无法退出可以用 \u0026lsquo;:q!\u0026rsquo; 强制退出，这样不会保存你修改的内容 更多的vi、vim命令使用方法可以自行到搜索引擎搜索学习，这里就不一一讲解了。\n将代码上传到 GitHub 、 Gitee 、 或者其他仓库 # 将代码上传到 GitHub 等代码托管平台，这样我们就可以方便地把代码拉取到服务器了。 如果访问慢或者打不开GitHub的话，可以上传到国内的Gitee或者Coding\nGit的安装和上传代码的简单方法 # 安装Git可以到官网 https://git-scm.com 下载，安装的话可以一直下一步，完成。\n首先你需要一个GitHub 、 Gitee或者其他账号，所以还没有的话先去注册吧！\nhttps://github.com https://gitee.com 1.进入Github首页，点击New repository新建一个项目\n2.填写相应信息后点击create即可\nRepository name: 仓库名称\nDescription(可选): 仓库描述介绍\nPublic, Private : 仓库权限（公开共享，私有或指定合作者）\nInitialize this repository with a README: 添加一个README.md\ngitignore: 不需要进行版本管理的仓库类型，对应生成文件.gitignore\nlicense: 证书类型，对应生成文件LICENSE\n一般可以填写项目仓库名称，点击create就可以了。\n3.创建好项目仓库后，在项目仓库页面点击Clone or dowload会出现一个地址，copy这个地址备用。\n4.接下来就到本地操作了，首先右键你的项目，如果你之前安装git成功的话，右键会出现两个新选项，分别为Git Gui Here,Git Bash Here,这里我们选择Git Bash Here进入界面，我的本地项目名为myblog。\n5.接下来输入如下代码（关键步骤），把github上面的仓库克隆到本地\n$ git clone (粘贴上你之前复制的地址) 6.操作步骤5以后你的本地项目文件夹下面就会多出个文件夹，该文件夹名即为你github上面的项目名，如我多出了个myblogsite文件夹，我们把本地项目文件夹下的所有文件（除了新多出的那个文件夹不用），其余都复制到那个新多出的文件夹下。\n7.接着继续输入命令 cd myblogsite，进入myblogsite文件夹\n8.接下来依次输入以下代码即可完成其他剩余操作：\n$ git add . （注：别忘记后面的.，此操作是把myblogsite文件夹下面的文件都添加进来） $ git commit -m \u0026#34;提交信息\u0026#34; （注：“提交信息”里面换成你需要，如“first commit”） $ git push -u origin master （注：此操作目的是把本地仓库push到github上面，此步骤需要你输入GitHub帐号和密码） 注意： 数据库文件不要上传！要是SQLLITE3里面有你测试的重要数据，将可能会泄露。 配置里面的数据库密码，发送邮件等等配置里面的登录和密码隐私文件也记得删除\n设置服务器目录结构 # 接下来需要把代码上传到服务器了。我服务器上存放代码的目录结构一般是这样的：\n/home/markhoo/ sites/ markhoo.cn/ myblog_env/ myblog/ 一台服务器可能部署多个网站，所有网站代码都放在 sites/ 目录下。markhoo.cn/ 这个文件夹以网站的域名命名，便于区分。myblog_env 是 python 虚拟环境目录。myblog 是 Django 博客项目目录。 创建这个目录结构，注意目录名替换为你自己的域名，以后涉及到 markhoo.cn 的地方通常都要替换你自己的域名，后面就不再一一指出了。\n备注：上面的我的目录结构.可能会有新手看不懂，解释一下,就像下面这样一直创建下去\n/home/markhoo/sites/markhoo.cn/ 最后的 myblog_env 和 myblog 是同级目录位于markhoo.cn下，这里自己先不要创建。\n创建目录的命令为： mkdir (文件夹名称) 删除目录的命令为： rmdir (文件夹名称)\n安装virtualenv # 为了不影响外界环境的清洁，所以我们使用虚拟环境来配置 Django 项目 首先用xshell登录到你的服务器，然后按一下步骤操作\n$ pip3 install virtualenv 接下来创建虚拟环境，先进入到 markhoo.cn 目录下，然后运行 virtualenv 命令创建虚拟环境：\n$ [root@instance-pkb22o0h]# cd /home/markhoo/sites/markhoo.cn $ [root@instance-pkb22o0h markhoo.cn]# virtualenv -p /usr/bin/python3 –-no-site-packages myblog_env 命令说明：\n-p： 指定你要虚拟的Python版本，这里选择了本地的python3 –-no-site-packages：表示在建立虚拟环境时不将原版本中的第三方库拷贝过来，这样就能获得一个纯净的Python环境。 myblog_env:表明在该目录下，建立一个叫做myblog_env的虚拟环境，这样的命名方式，让你一眼就能看出这个虚拟环境是为谁建立的。 virtualenv的命令使用方法：\n创建虚拟环境： $ virtualenv -p /usr/bin/python3.5 –-no-site-packages myblog_env 启动虚拟环境： $ source myblog_env/bin/activate　退出虚拟环境： $ deactivate　删除虚拟环境： $ rm –r myblog_env 检查一下虚拟环境是否创建成功，运行 ls 命令列出当前目录下的文件和文件夹，看到 myblog_env 这个文件夹说明虚拟环境创建成功。\n$ [root@instance-pkb22o0h markhoo.cn]# ls $ myblog_env venv的使用 # 由于我只是测试项目，所以使用了Python自带的venv。 先进入到 markhoo.cn 目录下，然后运行 python3 -m venv myblog_env 创建虚拟环境：\n$ [root@instance-pkb22o0h]# cd /home/markhoo/sites/markhoo.cn $ [root@instance-pkb22o0h markhoo.cn]# python3 -m venv myblog_env 检查一下虚拟环境是否创建成功，运行 ls 命令列出当前目录下的文件和文件夹，看到 myblog_env 这个文件夹说明虚拟环境创建成功。\n$ [root@instance-pkb22o0h markhoo.cn]# ls $ myblog_env 在虚拟环境中安装 django 以及依赖包并初始化项目 # 接着再从代码仓库把项目代码拉取过来，把 git clone 后的地址换成你自己的 GitHub 仓库地址！\n$ [root@instance-pkb22o0h markhoo.cn]# git clone https://github.com/MarkHoo/myblog.git 运行 ls 命令检查一下是否拉取成功：\n$ [root@instance-pkb22o0h markhoo.cn]# ls $ myblog myblog_env 多了 myblog 文件夹（文件夹名称由你的 GitHub 仓库名决定），说明拉取成功了。\n接下来执行命令，启动虚拟环境\n$ [root@instance-pkb22o0h markhoo.cn]# source myblog_env/bin/activate $ (myblog_env) [root@instance-pkb22o0h markhoo.cn]# 安装项目环境依赖\n$ (myblog_env) [root@instance-pkb22o0h markhoo.cn]# cd myblog $ (myblog_env) [root@instance-pkb22o0h myblog]# pip3 install -r requirements.txt 收集静态文件\n运行 python manage.py collectstatic 命令收集静态文件到 static 目录下\n$ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py collectstatic 生成数据库\n$ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py migrate 创建超级用户\n$ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py createsuperuser 然后就会出现以下内容，第一个是用户名，第二个是邮箱，第三和第四是密码（输入时没有任何显示，实际上已经输入了）。最后显示创建成功！\n$ Username (leave blank to use \u0026#39;root\u0026#39;): admin $ Email address: admin@markhoo.cn $ Password: $ Password (again): $ Superuser created successfully. 备注：这里使用的是Django默认生成的sqlite3数据库，我使用了这个，简单写写博客够用了。\n启动 Django $ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py runserver 0.0.0.0:8000 如果没有报错，就说明 Django 已经安装成功了，打开浏览器输入你的服务器IP:8000，就可以看到你的网站了\n退出 Django 按 ctrl+c 退出 Django 服务\n安装 Mysql (使用MySQL数据库同学可以借鉴) # 安装并启动 mariadb\n因为 CentOS 7 之后的版本都不在提供 Mysql 安装源，这里我们使用 mariadb 代替 mysql，依次执行下列命令\n$ yum install mariadb mariadb-server -y $ yum install MySQL-python -y $ systemctl start mariadb 对 mariadb 进行初始化设置\n执行下面命令，根据提示操作 设置新密码为 test 默认密码为空，直接回车即可 $ mysql_secure_installation 使用设置的密码登陆 mariadb\n登陆 db，这里假设密码被设置为 admin123 $ mysql -uroot -ptest 创建一个数据库\n$ create database myblog; 成功后，输入 exit 命令退出 db\n\u0026gt; exit 修改配置文件，与 Mysql 数据库相关联 编辑 myblog/settings.py\n# Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { \u0026#39;default\u0026#39;: { \u0026#39;ENGINE\u0026#39;: \u0026#39;django.db.backends.mysql\u0026#39;, \u0026#39;NAME\u0026#39;: \u0026#39;myblog\u0026#39;, \u0026#39;PASSWORD\u0026#39;:\u0026#39;admin123\u0026#39;, \u0026#39;USER\u0026#39;: \u0026#39;root\u0026#39;, \u0026#39;HOST\u0026#39;:\u0026#39;127.0.0.1\u0026#39;, \u0026#39;PORT\u0026#39;:\u0026#39;3306\u0026#39;, } } 创建 Django 数据库\n$ (myblog_env) [root@instance-pkb22o0h myblog]# cd /home/markhoo/sites/markhoo.cn/myblog $ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py migrate 创建超级用户\n$ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py createsuperuser 然后就会出现以下内容，第一个是用户名，第二个是邮箱，第三和第四是密码（输入时没有任何显示，实际上已经输入了）。最后显示创建成功！\n$ Username (leave blank to use \u0026#39;root\u0026#39;): admin $ Email address: admin@markhoo.cn $ Password: $ Password (again): $ Superuser created successfully. 启动 Django\n$ (myblog_env) [root@instance-pkb22o0h myblog]# python manage.py runserver 0.0.0.0:8000 如果没有报错，就说明 Django 已经安装成功了，并且跟 Mysql 的连接正常。打开浏览器输入你的服务器IP:8000，就可以看到你的网站了\n退出 Django\n按 ctrl+c 退出 Django 服务\n使用 Gunicorn # Gunicorn 一般用来管理多个进程，有进程挂了Gunicorn 可以把它拉起来，防止服务器长时间停止服务，还可以动态调整 worker 的数量，请求多的时候增加 worker 的数量，请求少的时候减少。\n在虚拟环境下，安装 Gunicorn：\n$ (myblog_env) [root@instance-pkb22o0h myblog]# pip3 install gunicorn 测试Gunicorn是否能启动你的项目服务\n$ (myblog_env) [root@instance-pkb22o0h myblog]# gunicorn --bind 0.0.0.0:8000 myblog.wsgi:application 注意：myblog.wsgi:application这里的myblog对应的是 /home/markhoo/sites/markhoo.cn/myblog/myblog, 根据自己的修改。浏览器输入域名，可以看到访问成功了！\n访问ip地址看浏览器是否能正常查看内容（此时没有退出虚拟环境） 完成测试后，按 CTRL-C 停止 Gunicorn 运行\n退出虚拟环境\n$ deactivate 创建一个 Gunicorn Systemd Service 文件，直接运行以下命令\n$ vim /etc/systemd/system/gunicorn.service 修改内容如下：\n[Unit] Description=gunicorn daemon After=network.target [Service] User=root Group=nginx WorkingDirectory=/home/markhoo/sites/markhoo.cn/myblog ExecStart=/home/markhoo/sites/markhoo.cn/myblog_env/bin/gunicorn --workers 3 --bind unix:/home/markhoo/sites/markhoo.cn/myblog/myblog.sock myblog.wsgi:application [Install] WantedBy=multi-user.target 一定要注意自己的项目路径和虚拟环境路径 WorkingDirectory与ExecStart 修改为自己的路径\n开启Gunicorn服务并开机自启,运行以下命令\n$ systemctl start gunicorn $ systemctl enable gunicorn 配置 Nginx # 接下是配置 Nginx 来处理用户请求\n配置nginx代理通过Gunicorn 依次运行以下命令\n$ (myblog_env) [root@instance-pkb22o0h myblog]# cd /etc/nginx/ $ (myblog_env) [root@instance-pkb22o0h nginx]# ls $ conf.d fastcgi.conf fastcgi_params koi-utf mime.types nginx.conf scgi_params uwsgi $ default.d fastcgi.conf.default fastcgi_params.default koi-win mime.types.default nginx.conf.default scgi_params.default uwsgi $ (myblog_env) [root@instance-pkb22o0h nginx]# vi nginx.conf 或者直接运行\n$ vim /etc/nginx/nginx.conf 打开后默认的内容如下：\n# For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { log_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34;\u0026#39;; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /usr/share/nginx/html; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } # Settings for a TLS enabled server. # # server { # listen 443 ssl http2 default_server; # listen [::]:443 ssl http2 default_server; # server_name _; # root /usr/share/nginx/html; # # ssl_certificate \u0026#34;/etc/pki/nginx/server.crt\u0026#34;; # ssl_certificate_key \u0026#34;/etc/pki/nginx/private/server.key\u0026#34;; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 10m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # # # Load configuration files for the default server block. # include /etc/nginx/default.d/*.conf; # # location / { # } # # error_page 404 /404.html; # location = /40x.html { # } # # error_page 500 502 503 504 /50x.html; # location = /50x.html { # } # } } 找到server内容进行修改，具体内容如下：\nserver { listen 80; server_name markhoo.cn 0.0.0.0; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/markhoo/sites/markhoo.cn/myblog; } location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://unix:/home/markhoo/sites/markhoo.cn/myblog/myblog.sock; } } 注意修改自己的IP地址或域名，还有文件路径 server_domain_or_IP 代表你的IP地址或域名\n这里的域名为 markhoo.cn，其次一定要添加 0.0.0.0 否则无法访问，两个之间用空格隔开。 所有URL 带有 /static 的请求均由 Nginx 处理，alias 指明了静态文件的存放目录。 其它请求转发给 Django 处理。proxy_pass 后面使用了 unix 套接字，其作用是防止端口冲突，这里就不再详述。 修改所涉及到的vim命令使用方法：使用 \u0026lsquo;vi (文件名称)\u0026rsquo; 是打开文件，然后点击键盘 \u0026lsquo;i\u0026rsquo; 键切换到输入模式。修改完需要退出vim编辑器，先按键盘 \u0026lsquo;Esc\u0026rsquo; 键，然后输入 \u0026lsquo;:wq\u0026rsquo; 保存退出。如果不想修改又无法退出可以用 \u0026lsquo;:q!\u0026rsquo; 强制退出，这样不会保存你修改的内容。 更多的vi、vim命令的具体使用方法可以自行到搜索引擎搜索学习，这里也不一一讲解了。\n修改nginx的权限\n$ usermod -a -G root nginx $ chmod 710 /home/ $ nginx -t 如果没有报错，就行下一步操作\n开启nginx服务并开机自启\n$ systemctl start nginx $ systemctl enable nginx 现在，一切配置完成！你可以访问你的域名了！\n","date":"August 14, 2018","externalUrl":null,"permalink":"/posts/blogbushu/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e本项目开发环境是 Python3.5.2+Django1.10.6+Sqlite3+Centos7.5+Nginx1.12.2+Gunicorn\u003c/p\u003e\n\u003c/blockquote\u003e","title":"使用Centos7.5+Nginx+Gunicorn+Django+Python3部署blog项目","type":"posts"},{"content":"","date":"August 3, 2018","externalUrl":null,"permalink":"/tags/xadmin/","section":"","summary":"","title":"Xadmin","type":"tags"},{"content":" xadmin 的插件系统架构设计一定程度上借鉴了 wordpress 的设计。 想要了解 |xadmin| 的插件系统架构首先需要了解 |xadmin| AdminView 的概念。\n官网太慢甚至打不开，所以干脆转载过来，方便学习。\n插件原理 # xadmin 的插件系统架构设计一定程度上借鉴了 wordpress 的设计。 想要了解 |xadmin| 的插件系统架构首先需要了解 |xadmin| AdminView 的概念。\n简单来说，就是 |xadmin| 系统中每一个页面都是一个 AdminView 对象返回的 HttpResponse 结果。|xadmin| 的插件系统做的事情其实就是在 AdminView 运行过程中改变其执行的逻辑， 或是改变其返回的结果，起到修改或增强原有功能的效果。下面让我们看看整个插件从制作完成到实际运行的整个过程。\n首先需要创建自己的插件类， 插件类继承 :class:~xadmin.views.BaseAdminPlugin ::\nclass HelloWorldPlugin(BaseAdminPlugin): ... 开发好的插件首先要注册到 |xadmin| 中， 示例代码如下::\n# ListAdminView 是 Model 列表页面 xadmin.site.register_plugin(HelloWorldPlugin, ListAdminView) 其中插件的注册和使用可以参看 :meth:xadmin.sites.AdminSite.register_plugin\n当将插件注册到 |xadmin| 后， |xadmin| 在创建 AdminView 实例的时候会将该插件放入实例的 :attr:plugins 属性。当 AdminView 在处理请求 时，会首先逐个调用 :attr:plugins 中插件的 :meth:~xadmin.views.BaseAdminPlugin.init_request 方法，插件在该方法中一般进行初始化的操作并且返回一个 Boolean 值告诉 AdminView 是否需要加载该插件。当 :meth:~xadmin.views.BaseAdminPlugin.init_request 方法返回值为 False 时， AdminView 不会加载该插件。实例如下::\nclass HelloWorldPlugin(BaseAdminPlugin): say_hello = False # 初始化方法根据 ``say_hello`` 属性值返回 def init_request(self, *args, **kwargs): return bool(self.say_hello) 在以上实例中，插件根据自身的 say_hello 属性来决定是否让自己被加载。您可能会迷惑， say_hello 属性看起来一直会是 False 呀，那样这个插件不是永远不会被加载？ 其实 |xadmin| 在创建插件实例的时候会将 OptionClass 的同名属性替换插件的属性。这样，在不同的 OptionClass 下会有不同的插件结果，实例如下::\nclass SomeModelAdmin(object): say_hello = True ... site.register(SomeModel, SomeModelAdmin) 理解以上内容后，让我们再看看插件具体是如何起作用的。在 AdminView 的执行过程中，可以被插件截获或修改的方法使用 :func:~xadmin.views.base.filter_hook 装饰，实例如下::\nclass ListAdminView(ModelAdminView): # 可以被插件截获或修改的方法使用该装饰器装饰 @filter_hook def get_context(self): ... 使用 :func:~xadmin.views.base.filter_hook 装饰的方法执行过程中会根据一定原则执行插件中的同名方法，具体信息查考该装饰器的文档内容。\n.. autofunction:: xadmin.views.base.filter_hook\n根据该装饰器的执行原则，如果我们想修改上面示例中 ListAdminView 的 get_context 的返回值，可以在插件中实现如下代码::\nclass HelloWorldPlugin(BaseAdminPlugin): # 在插件中加入同名方法，修改 ``ListAdminView`` 的 ``get_context`` 返回的值 def get_context(self, context): context.update({'hello_target': 'World!!'}) return context 如果我们希望插件在 AdminView 的方法前执行，或是完全使用自己的方法替代 AdminView 的方法可以这样::\nclass HelloWorldPlugin(BaseAdminPlugin): # 第一个参数为 ``__`` 。这样 ``__`` 即为 ``ListAdminView`` 的 ``get_context`` 方法本身，注意，这时还没有执行这个方法。 def get_context(self, __): context = {'hello_target': 'World!!'} #我们可以在任何时候执行 ``AdminView`` 的方法，或是根本不执行 context.update(__()) return context 至此，加入的插件就实现了对 AdminView 方法的完全控制。\n模板插件\n我们知道，Django 中一个完整的 View 是包含模板的，模板用来生成 View 最终返回的 HTML 内容。当然，插件也可以在模板中插入自己的内容。我们来看看具体如何实现。\n首先让我们来看看 |xadmin| 中的模板代码示例片段 (change_list.html):\n.. code-block:: html\n{% load xadmin %} ... \u0026lt;form id=\u0026quot;changelist-form\u0026quot; action=\u0026quot;\u0026quot; method=\u0026quot;post\u0026quot;{% view_block 'result_list_form' %}\u0026gt;{% csrf_token %} {% view_block 'results_top' %} \u0026lt;div class=\u0026quot;results\u0026quot;\u0026gt; {% if results %} ... 其中的 view_block Tag 即为插件的 插入点 。插件可以在自己的插件类中使用 block_ + 插入点名称 方法将 HTML 片段插入到页面的这个位置，示例如下:\n.. code-block:: python\nclass HelloWorldPlugin(BaseAdminPlugin): # context 即为 TemplateContext， nodes 参数包含了其他插件的返回内容。 # 您可以直接返回 HTML 片段，或是将内容加入到 nodes 参数中 def block_results_top(self, context, nodes): return s\u0026quot;\u0026lt;div class='info'\u0026gt;Hello %s\u0026lt;/div\u0026gt;\u0026quot; % context['hello_target'] 插件实例 # 下面让我们来看一个 |xadmin| 中完整的插件实例::\nfrom django.template import loader from xadmin.sites import site from xadmin.views import BaseAdminPlugin, ListAdminView REFRESH_VAR = '_refresh' # 该插件实现了一个列表页面刷新器的效果 class RefreshPlugin(BaseAdminPlugin): # 用户可以定制刷新的频率，可以传入多个值。该属性会被 ``OptionClass`` 的同名属性替换 refresh_times = [] def init_request(self, *args, **kwargs): # 根据用户是否制定了刷新器来决定是否启动该插件 return bool(self.refresh_times) # 插件拦截了返回 Media 的方法，加入自己需要的 js 文件。 def get_media(self, media): if self.request.GET.get(REFRESH_VAR): # 放页面处于自动刷新状态时，加入自己的 js 制定刷新逻辑 media.add_js([self.static('xadmin/js/refresh.js')]) return media # Block Views # 在页面中插入 HTML 片段，显示刷新选项。 def block_top_toolbar(self, context, nodes): current_refresh = self.request.GET.get(REFRESH_VAR) context.update({ 'has_refresh': bool(current_refresh), 'clean_refresh_url': self.admin_view.get_query_string(remove=(REFRESH_VAR,)), 'current_refresh': current_refresh, 'refresh_times': [{ 'time': r, 'url': self.admin_view.get_query_string({REFRESH_VAR: r}), 'selected': str(r) == current_refresh, } for r in self.refresh_times], }) # 可以将 HTML 片段加入 nodes 参数中 nodes.append(loader.render_to_string('xadmin/blocks/refresh.html', context_instance=context)) # 注册插件 site.register_plugin(RefreshPlugin, ListAdminView) 最后不要忘记在适当的地方加载该代码， 让其执行。一般情况下，你可以将其写到 adminx.py 文件中，这样，只要您的 APP 加入到 Django Settings 的 INSTALL_APPS 中， |xadmin| 就会自动执行 app 下的 adminx.py 文件。\n插件开发 # 了解了插件的运行原理后我们就可以开发自己的插件了。首先我们需要了解插件类中的实用方法。因为插件是继承 :class:~xadmin.views.BaseAdminPlugin 类，而该类继承自 :class:~xadmin.views.BaseAdminObject，所以这两个类的方法都可以在插件中使用。\n|xadmin| 在创建插件时会自动注入以下属性到插件实例中:\n* request : Http Request * user : 当前 User 对象 * args : View 方法的 args 参数 * kwargs : View 方法的 kwargs 参数 * admin_view : ``AdminView`` 实例 * admin_site : |xadmin| 的 ``admin_site`` 对象实例 如果 AdminView 是 :class:~xadmin.views.ModelAdminView 的子类，还会自动注入以下属性:\n* model : Model 对象 * opts : Model 的 _meta 属性 接下来您应该考虑打算制作什么功能的插件了。不同功能的插件额能需要注册到不同的 AdminView 上，|xadmin| 系统中 主要的 AdminView 有:\n* :class:`~xadmin.views.BaseAdminView` : 所有 ``AdminView`` 的基础类，注册在该 View 上的插件可以影响所有的 ``AdminView`` * :class:`~xadmin.views.CommAdminView` : 用户已经登陆后显示的 View，也是所有登陆后 View 的基础类。该 View主要作用是创建了 |xadmin| 的通用元素，例如：系统菜单，用户信息等。插件可以通过注册该 View 来修改这些信息。 * :class:`~xadmin.views.ModelAdminView` : 基于 Model 的 ``AdminView`` 的基础类，注册的插件可以影响所有基于 Model 的 View。 * :class:`~xadmin.views.ListAdminView` : Model 列表页面 View。 * :class:`~xadmin.views.ModelFormAdminView` : Model 编辑页面 View。 * :class:`~xadmin.views.CreateAdminView` : Model 创建页面 View。 * :class:`~xadmin.views.UpdateAdminView` : Model 修改页面 View。 * :class:`~xadmin.views.DeleteAdminView` : Model 删除页面 View。 * :class:`~xadmin.views.DetailAdminView` : Model 详情页面 View。 选择好目标 AdminView 后就要在自己的插件中编写方法来修改或增强这些 AdminView 。其中每个 AdminView 可以 拦截的方法及其介绍请参看各 AdminView 的文档。\n插件规范 # 文档模板::\n\u0026quot;\u0026quot;\u0026quot; Name ====== 作者 ---- 该插件的作者信息 功能 ---- 描述插件的主要功能 截图 ---- .. image:: /images/plugins/action.png 使用 ---- 描述插件的使用方法, 以及使用示例. 版本 ---- 描述插件的版本信息 API --- .. autoclass:: XXX \u0026quot;\u0026quot;\u0026quot;","date":"August 3, 2018","externalUrl":null,"permalink":"/posts/xadmin/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003exadmin 的插件系统架构设计一定程度上借鉴了 \u003ccode\u003ewordpress\u003c/code\u003e 的设计。 想要了解 |xadmin| 的插件系统架构首先需要了解 |xadmin| \u003ccode\u003eAdminView\u003c/code\u003e 的概念。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"xadmin 插件制作","type":"posts"},{"content":" 安装Django的时候总是安装到一半就报错，而且速度很慢，问题主要是国外的源连接不稳定会中断所致。\n昨晚安装Django的时候总是安装到一半就报错，而且速度很慢，问题主要是国外的源连接不稳定会中断所致。后来就想到了切换到国内源。想到可能还会有同学碰到类似的问题，特此记录分享一下\npip国内的一些镜像 # 公网 http://e.pypi.python.org/simple 教育网 http://pypi.tuna.tsinghua.edu.cn/simple 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/ 豆瓣 http://pypi.doubanio.com/simple/ 阿里云 http://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/ 华中理工大学 http://pypi.hustunique.com/simple 山东理工大学 http://pypi.sdutlinux.org/simple 修改源方法： # 临时使用： # 可以在使用 pip 的时候在后面加上 -i 参数，指定 pip 源\n$ pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple 指定需要安装的库 requirements.txt\n$ pip install -r \u0026#34;/home/admin/$APP_NAME/conf/requirements.txt\u0026#34; -i \u0026#34;http://mirrors.aliyun.com/pypi/simple\u0026#34; --trusted-host \u0026#34;mirrors.aliyun.com\u0026#34; \u0026amp;\u0026amp; \\ #通过阿里云镜像安装三方库 永久修改： # Linux: # 修改 ~/.pip/pip.conf (没有就创建一个)， 内容如下：\n[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple Windows: # 直接在user目录中创建一个pip目录，如：C:\\Users\\xx\\pip，新建文件pip.ini，内容如下\n[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple 参考：https://pip.pypa.io/en/latest/configuration/\n","date":"July 28, 2018","externalUrl":null,"permalink":"/posts/pip-pypi/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e安装Django的时候总是安装到一半就报错，而且速度很慢，问题主要是国外的源连接不稳定会中断所致。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"更换pip源到国内镜像加速python pypi包的安装","type":"posts"},{"content":" GitHub star过万的Python学习资源\nGitHub star过万的Python学习资源。有空慢慢翻译，或者使用翻译插件查看\nA curated list of awesome Python frameworks, libraries, software and resources.\nInspired by awesome-php\nAdmin Panels # Libraries for administrative interfaces.\nAjenti - The admin panel your servers deserve. django-suit - Alternative Django Admin-Interface (free only for Non-commercial use). django-xadmin - Drop-in replacement of Django admin comes with lots of goodies. flask-admin - Simple and extensible administrative interface framework for Flask. flower - Real-time monitor and web admin for Celery. Grappelli - A jazzy skin for the Django Admin-Interface. Wooey - A Django app which creates automatic web UIs for Python scripts. Algorithms and Design Patterns # Python implementation of algorithms and design patterns.\nalgorithms - Minimal examples of data structures and algorithms in Python. PyPattyrn - A simple yet effective library for implementing common design patterns. python-patterns - A collection of design patterns in Python. sortedcontainers - Fast, pure-Python implementation of SortedList, SortedDict, and SortedSet types. SCOOP - Scalable Concurrent Operations in Python. Anti-spam # Libraries for fighting spam.\ndjango-simple-captcha - A simple and highly customizable Django app to add captcha images to any Django form. django-simple-spam-blocker - Simple spam blocker for Django. Asset Management # Tools for managing, compressing and minifying website assets.\ndjango-compressor - Compresses linked and inline JavaScript or CSS into a single cached file. django-pipeline - An asset packaging library for Django. django-storages - A collection of custom storage back ends for Django. fanstatic - Packages, optimizes, and serves static file dependencies as Python packages. fileconveyor - A daemon to detect and sync files to CDNs, S3 and FTP. flask-assets - Helps you integrate webassets into your Flask app. jinja-assets-compressor - A Jinja extension to compile and compress your assets. webassets - Bundles, optimizes, and manages unique cache-busting URLs for static resources. Audio # Libraries for manipulating audio.\naudiolazy - Expressive Digital Signal Processing (DSP) package for Python. audioread - Cross-library (GStreamer + Core Audio + MAD + FFmpeg) audio decoding. beets - A music library manager and MusicBrainz tagger. dejavu - Audio fingerprinting and recognition. django-elastic-transcoder - Django + Amazon Elastic Transcoder. eyeD3 - A tool for working with audio files, specifically MP3 files containing ID3 metadata. id3reader - A Python module for reading MP3 meta data. m3u8 - A module for parsing m3u8 file. mingus - An advanced music theory and notation package with MIDI file and playback support. mutagen - A Python module to handle audio metadata. pyAudioAnalysis - Python Audio Analysis Library: Feature Extraction, Classification, Segmentation and Applications pydub - Manipulate audio with a simple and easy high level interface. pyechonest - Python client for the Echo Nest API. talkbox - A Python library for speech/signal processing. TimeSide - Open web audio processing framework. tinytag - A library for reading music meta data of MP3, OGG, FLAC and Wave files. Authentication # Libraries for implementing authentications schemes.\nOAuth Authomatic - Simple but powerful framework agnostic authentication/authorization client. django-allauth - Authentication app for Django that \u0026ldquo;just works.\u0026rdquo; django-oauth-toolkit - OAuth 2 goodies for Django. Flask-OAuthlib - OAuth 1.0/a, 2.0 implementation of client and provider for Flask. OAuthLib - A generic and thorough implementation of the OAuth request-signing logic. python-oauth2 - A fully tested, abstract interface to creating OAuth clients and servers. python-social-auth - An easy-to-setup social authentication mechanism. rauth - A Python library for OAuth 1.0/a, 2.0, and Ofly. sanction - A dead simple OAuth2 client implementation. Others jose - JavaScript Object Signing and Encryption draft implementation. PyJWT - Implementation of the JSON Web Token draft 01. python-jws - Implementation of JSON Web Signatures draft 02. python-jwt - Module for generating and verifying JSON Web Tokens. Build Tools # Compile software from source code.\nBitBake - A make-like build tool for embedded Linux. buildout - A build system for creating, assembling and deploying applications from multiple parts. PlatformIO - A console tool to build code with different development platforms. PyBuilder - A continuous build tool written in pure Python. SCons - A software construction tool. Built-in Classes Enhancement # Libraries for enhancing Python built-in classes.\nattrs - Replacement for __init__, __eq__, __repr__, etc. boilerplate in class definitions. bidict - Efficient, Pythonic bidirectional map data structures and related functionality.. Box - Python dictionaries with advanced dot notation access. dotted - A library that provides a method of accessing lists and dicts with a dotted path notation. CMS # Content Management Systems.\ndjango-cms - An Open source enterprise CMS based on the Django. djedi-cms - A lightweight but yet powerful Django CMS with plugins, inline editing and performance in mind. FeinCMS - One of the most advanced Content Management Systems built on Django. Kotti - A high-level, Pythonic web application framework built on Pyramid. Mezzanine - A powerful, consistent, and flexible content management platform. Opps - A Django-based CMS for magazines, newspapers websites and portals with high-traffic. Plone - A CMS built on top of the open source application server Zope. Quokka - Flexible, extensible, small CMS powered by Flask and MongoDB. Wagtail - A Django content management system. Widgy - Last CMS framework, based on Django. Caching # Libraries for caching data.\nBeaker - A library for caching and sessions for use with web applications and stand-alone Python scripts and applications. DiskCache - SQLite and file backed cache backend with faster lookups than memcached and redis. django-cache-machine - Automatic caching and invalidation for Django models. django-cacheops - A slick ORM cache with automatic granular event-driven invalidation. django-viewlet - Render template parts with extended cache control. dogpile.cache - dogpile.cache is next generation replacement for Beaker made by same authors. HermesCache - Python caching library with tag-based invalidation and dogpile effect prevention. johnny-cache - A caching framework for Django applications. pylibmc - A Python wrapper around the libmemcached interface. ChatOps Tools # Libraries for chatbot development.\nErrbot - The easiest and most popular chatbot to implement ChatOps. Code Analysis # Tools of static analysis, linters and code quality checkers. See: awesome-static-analysis.\nCode Analysis flake8 - A wrapper around pycodestyle, pyflakes and McCabe. coala - Language independent and easily extendable code analysis application. code2flow - Turn your Python and JavaScript code into DOT flowcharts. prospector - A tool to analyse Python code. pycallgraph - A library that visualises the flow (call graph) of your Python application. pylama - A code audit tool for Python and JavaScript. pylint - A fully customizable source code analyzer. Static Type Checkers mypy - Check variable types during compile time. Pyre - Performant type checking. Static Type Annotations Generators MonkeyType - A system for Python that generates static type annotations by collecting runtime types Command-line Tools # Libraries for building command-line application.\nCommand-line Application Development asciimatics - A package to create full-screen text UIs (from interactive forms to ASCII animations). bashplotlib - Making basic plots in the terminal. cement - CLI Application Framework for Python. click - A package for creating beautiful command line interfaces in a composable way. cliff - A framework for creating command-line programs with multi-level commands. clint - Python Command-line Application Tools. colorama - Cross-platform colored terminal text. docopt - Pythonic command line arguments parser. Gooey - Turn command line programs into a full GUI application with one line Python-Fire - A library for creating command line interfaces from absolutely any Python object. python-prompt-toolkit - A library for building powerful interactive command lines. Productivity Tools aws-cli - A universal command-line interface for Amazon Web Services. cookiecutter - A command-line utility that creates projects from cookiecutters (project templates). doitlive - A tool for live presentations in the terminal. howdoi - Instant coding answers via the command line. httpie - A command line HTTP client, a user-friendly cURL replacement. kube-shell - An integrated shell for working with the Kubernetes CLI. mycli - A Terminal Client for MySQL with AutoCompletion and Syntax Highlighting. PathPicker - Select files out of bash output. percol - Adds flavor of interactive selection to the traditional pipe concept on UNIX. pgcli - Postgres CLI with autocompletion and syntax highlighting. SAWS - A Supercharged AWS CLI. thefuck - Correcting your previous console command. tmuxp - A tmux session manager. try - A dead simple CLI to try out python packages - it\u0026rsquo;s never been easier. Compatibility # Libraries for migrating from Python 2 to 3.\nPython-Future - The missing compatibility layer between Python 2 and Python 3. Python-Modernize - Modernizes Python code for eventual Python 3 migration. Six - Python 2 and 3 compatibility utilities. Computer Vision # Libraries for computer vision.\nOpenCV - Open Source Computer Vision Library. pyocr - A wrapper for Tesseract and Cuneiform. pytesseract - Another wrapper for Google Tesseract OCR. SimpleCV - An open source framework for building computer vision applications. Concurrency and Parallelism # Libraries for concurrent and parallel execution.\neventlet - Asynchronous framework with WSGI support. gevent - A coroutine-based Python networking library that uses greenlet. multiprocessing - (Python standard library) Process-based \u0026ldquo;threading\u0026rdquo; interface. threading - (Python standard library) Higher-level threading interface. Tomorrow - Magic decorator syntax for asynchronous code. uvloop - Ultra fast implementation of asyncio event loop on top of libuv. Configuration # Libraries for storing and parsing configuration options.\nconfig - Hierarchical config from the author of logging. ConfigObj - INI file parser with validation. ConfigParser - (Python standard library) INI file parser. profig - Config from multiple formats with value conversion. python-decouple - Strict separation of settings from code. Cryptography # cryptography - A package designed to expose cryptographic primitives and recipes to Python developers. hashids - Implementation of hashids in Python. Paramiko - A Python (2.6+, 3.3+) implementation of the SSHv2 protocol, providing both client and server functionality. Passlib - Secure password storage/hashing library, very high level. PyNacl - Python binding to the Networking and Cryptography (NaCl) library. Data Analysis # Libraries for data analyzing.\nBlaze - NumPy and Pandas interface to Big Data. Open Mining - Business Intelligence (BI) in Pandas interface. Orange - Data mining, data visualization, analysis and machine learning through visual programming or scripts. Pandas - A library providing high-performance, easy-to-use data structures and data analysis tools. Optimus - Cleansing, pre-processing, feature engineering, exploratory data analysis and easy Machine Learning with a PySpark backend. Data Validation # Libraries for validating data. Used for forms in many cases.\nCerberus - A lightweight and extensible data validation library. colander - Validating and deserializing data obtained via XML, JSON, an HTML form post. jsonschema - An implementation of JSON Schema for Python. schema - A library for validating Python data structures. Schematics - Data Structure Validation. valideer - Lightweight extensible data validation and adaptation library. voluptuous - A Python data validation library. Data Visualization # Libraries for visualizing data. See: awesome-javascript.\nAltair - Declarative statistical visualization library for Python. Bokeh - Interactive Web Plotting for Python. bqplot - Interactive Plotting Library for the Jupyter Notebook ggplot - Same API as ggplot2 for R. Matplotlib - A Python 2D plotting library. Pygal - A Python SVG Charts Creator. PyGraphviz - Python interface to Graphviz. PyQtGraph - Interactive and realtime 2D/3D/Image plotting and science/engineering widgets. Seaborn - Statistical data visualization using Matplotlib. VisPy - High-performance scientific visualization based on OpenGL. Database # Databases implemented in Python.\npickleDB - A simple and lightweight key-value store for Python. TinyDB - A tiny, document-oriented database. ZODB - A native object database for Python. A key-value and object graph database. Database Drivers # Libraries for connecting and operating databases.\nMySQL - awesome-mysql mysqlclient - MySQL connector with Python 3 support ( mysql-python fork). oursql - A better MySQL connector with support for native prepared statements and BLOBs. PyMySQL - A pure Python MySQL driver compatible to mysql-python. PostgreSQL - awesome-postgres psycopg2 - The most popular PostgreSQL adapter for Python. queries - A wrapper of the psycopg2 library for interacting with PostgreSQL. txpostgres - Twisted based asynchronous driver for PostgreSQL. Other Relational Databases apsw - Another Python SQLite wrapper. dataset - Store Python dicts in a database - works with SQLite, MySQL, and PostgreSQL. pymssql - A simple database interface to Microsoft SQL Server. NoSQL Databases cassandra-python-driver - Python driver for Cassandra. HappyBase - A developer-friendly library for Apache HBase. Plyvel - A fast and feature-rich Python interface to LevelDB. py2neo - Python wrapper client for Neo4j\u0026rsquo;s restful interface. pycassa - Python Thrift driver for Cassandra. PyMongo - The official Python client for MongoDB. redis-py - The Redis Python Client. telephus - Twisted based client for Cassandra. txRedis - Twisted based client for Redis. Date and Time # Libraries for working with dates and times.\nChronyk - A Python 3 library for parsing human-written times and dates. dateutil - Extensions to the standard Python datetime module. delorean - A library for clearing up the inconvenient truths that arise dealing with datetimes. moment - A Python library for dealing with dates/times. Inspired by Moment.js. Pendulum - Python datetimes made easy. PyTime - A easy-use Python module which aims to operate date/time/datetime by string. pytz - World timezone definitions, modern and historical. Brings the tz database into Python. when.py - Providing user-friendly functions to help perform common date and time actions. maya - Datetimes for Humans, Maya is mostly built around the headaches and use-cases around parsing datetime data from websites. Debugging Tools # Libraries for debugging code.\npdb-like Debugger ipdb - IPython-enabled pdb. pdb++ - Another drop-in replacement for pdb. pudb - A full-screen, console-based Python debugger. remote-pdb - Remote vanilla PDB (over TCP sockets). wdb - An improbable web debugger through WebSockets. Profiler line_profiler - Line-by-line profiling. memory_profiler - Monitor Memory usage of Python code. Pyflame - Attach this Ptracing Profiler to any processes running Python. Perfect for profiling production webservers. profiling - An interactive Python profiler. vprof - Visual Python profiler. Others IceCream - Inspect variables, expressions, and program execution with a single, simple function call. django-debug-toolbar - Display various debug information for Django. django-devserver - A drop-in replacement for Django\u0026rsquo;s runserver. flask-debugtoolbar - A port of the django-debug-toolbar to flask. hunter - Hunter is a flexible code tracing toolkit. lptrace - strace for Python programs. manhole - Debug service that will accept unix domain socket connections and present the stacktraces for all threads and an interactive prompt. pyelftools - Parsing and analyzing ELF files and DWARF debugging information. pyringe - Debugger capable of attaching to and injecting code into Python processes. Deep Learning # Frameworks for Neural Networks and Deep Learning. See: awesome-deep-learning.\nCaffe - A fast open framework for deep learning.. Keras - A high-level neural networks library and capable of running on top of either TensorFlow or Theano. MXNet - A deep learning framework designed for both efficiency and flexibility. Neupy - Running and testing different Artificial Neural Networks algorithms. Pytorch - Tensors and Dynamic neural networks in Python with strong GPU acceleration. Serpent.AI - Game agent framework. Use any video game as a deep learning sandbox. TensorFlow - The most popular Deep Learning framework created by Google. Theano - A library for fast numerical computation. DevOps Tools # Software and libraries for DevOps.\nAnsible - A radically simple IT automation platform. Cloud-Init - A multi-distribution package that handles early initialization of a cloud instance. cuisine - Chef-like functionality for Fabric. Docker Compose - Fast, isolated development environments using Docker. Fabric - A simple, Pythonic tool for remote execution and deployment. Fabtools - Tools for writing awesome Fabric files. honcho - A Python clone of Foreman, for managing Procfile-based applications. OpenStack - Open source software for building private and public clouds. pexpect - Controlling interactive programs in a pseudo-terminal like GNU expect. psutil - A cross-platform process and system utilities module. SaltStack - Infrastructure automation and management system. supervisor - Supervisor process control system for UNIX. Distribution # Libraries to create packaged executables for release distribution.\ndh-virtualenv - Build and distribute a virtualenv as a Debian package. Nuitka - Compile scripts, modules, packages to an executable or extension module. py2app - Freezes Python scripts (Mac OS X). py2exe - Freezes Python scripts (Windows). PyInstaller - Converts Python programs into stand-alone executables (cross-platform). pynsist - A tool to build Windows installers, installers bundle Python itself. Documentation # Libraries for generating project documentation.\nSphinx - Python Documentation generator. awesome-sphinxdoc MkDocs - Markdown friendly documentation generator. pdoc - Epydoc replacement to auto generate API documentation for Python libraries. Pycco - The literate-programming-style documentation generator. Downloader # Libraries for downloading.\ns3cmd - A command line tool for managing Amazon S3 and CloudFront. s4cmd - Super S3 command line tool, good for higher performance. you-get - A YouTube/Youku/Niconico video downloader written in Python 3. youtube-dl - A small command-line program to download videos from YouTube. E-commerce # Frameworks and libraries for e-commerce and payments.\nalipay - Unofficial Alipay API for Python. Cartridge - A shopping cart app built using the Mezzanine. django-oscar - An open-source e-commerce framework for Django. django-shop - A Django based shop system. merchant - A Django app to accept payments from various payment processors. money - Money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution. python-currencies - Display money format and its filthy currencies. forex-python - Foreign exchange rates, Bitcoin price index and currency conversion. saleor - An e-commerce storefront for Django. shoop - An open source E-Commerce platform based on Django. Editor Plugins and IDEs # Emacs Elpy - Emacs Python Development Environment. Sublime Text Anaconda - Anaconda turns your Sublime Text 3 in a full featured Python development IDE. SublimeJEDI - A Sublime Text plugin to the awesome auto-complete library Jedi. Vim Jedi-vim - Vim bindings for the Jedi auto-completion library for Python. Python-mode - An all in one plugin for turning Vim into a Python IDE. YouCompleteMe - Includes Jedi-based completion engine for Python. Visual Studio PTVS - Python Tools for Visual Studio. Visual Studio Code Python - An extension with rich support for the Python language, with features including linting, IntelliSense, formatting, refactoring, debugging, unit tests, and jupyter support. Magic Python - Cutting edge Python syntax highlighter for Sublime Text, Atom, and Visual Studio Code. Used by GitHub to highlight your Python code! IDE LiClipse - Free polyglot IDE based on Eclipse. Uses PyDev for Python support. PyCharm - Commercial Python IDE by JetBrains. Has free community edition available. Spyder - Open Source Python IDE. Email # Libraries for sending and parsing email.\nenvelopes - Mailing for human beings. flanker - A email address and Mime parsing library. imbox - Python IMAP for Humans. inbox.py - Python SMTP Server for Humans. lamson - Pythonic SMTP Application Server. Marrow Mailer - High-performance extensible mail delivery framework. modoboa - A mail hosting and management platform including a modern and simplified Web UI. Nylas Sync Engine - Providing a RESTful API on top of a powerful email sync platform. yagmail - Yet another Gmail/SMTP client. Environment Management # Libraries for Python version and environment management.\nPipenv - Sacred Marriage of Pipfile, Pip, \u0026amp; Virtualenv. p - Dead simple interactive Python version management. pyenv - Simple Python version management. venv - (Python standard library in Python 3.3+) Creating lightweight virtual environments. virtualenv - A tool to create isolated Python environments. virtualenvwrapper - A set of extensions to virtualenv. Files # Libraries for file manipulation and MIME type detection.\nimghdr - (Python standard library) Determine the type of an image. mimetypes - (Python standard library) Map filenames to MIME types. path.py - A module wrapper for os.path. pathlib - (Python standard library in Python 3.4+) An cross-platform, object-oriented path library. python-magic - A Python interface to the libmagic file type identification library. Unipath - An object-oriented approach to file/directory operations. watchdog - API and shell utilities to monitor file system events. Foreign Function Interface # Libraries for providing foreign function interface.\ncffi - Foreign Function Interface for Python calling C code. ctypes - (Python standard library) Foreign Function Interface for Python calling C code. PyCUDA - A Python wrapper for Nvidia\u0026rsquo;s CUDA API. SWIG - Simplified Wrapper and Interface Generator. Forms # Libraries for working with forms.\nDeform - Python HTML form generation library influenced by the formish form generation library. django-bootstrap3 - Bootstrap 3 integration with Django. django-crispy-forms - A Django app which lets you create beautiful forms in a very elegant and DRY way. django-remote-forms - A platform independent Django form serializer. WTForms - A flexible forms validation and rendering library. Functional Programming # Functional Programming with Python.\nCyToolz - Cython implementation of Toolz: High performance functional utilities. fn.py - Functional programming in Python: implementation of missing features to enjoy FP. funcy - A fancy and practical functional tools. Toolz - A collection of functional utilities for iterators, functions, and dictionaries. GUI # Libraries for working with graphical user interface applications.\ncurses - Built-in wrapper for ncurses used to create terminal GUI applications. Eel - Little library for making simple Electron-like offline HTML/JS GUI apps, with full access to Python capabilities and libraries. enaml - Creating beautiful user-interfaces with Declaratic Syntax like QML. Flexx - Flexx is a pure Python toolkit for creating GUI\u0026rsquo;s, that uses web technology for its rendering. kivy - A library for creating NUI applications, running on Windows, Linux, Mac OS X, Android and iOS. pyglet - A cross-platform windowing and multimedia library for Python. PyGObject - Python Bindings for GLib/GObject/GIO/GTK+ (GTK+3). PyQt - Python bindings for the Qt cross-platform application and UI framework, with support for both Qt v4 and Qt v5 frameworks. PySide - Python bindings for the Qt cross-platform application and UI framework, supporting the Qt v4 framework. pywebview - A lightweight cross-platform native wrapper around a webview component that allows to display HTML content in its own native dedicated window. Tkinter - Tkinter is Python\u0026rsquo;s de-facto standard GUI package. Toga - A Python native, OS native GUI toolkit. urwid - A library for creating terminal GUI applications with strong support for widgets, events, rich colors, etc. wxPython - A blending of the wxWidgets C++ class library with the Python. Game Development # Awesome game development libraries.\nCocos2d - cocos2d is a framework for building 2D games, demos, and other graphical/interactive applications. It is based on pyglet. Panda3D - 3D game engine developed by Disney and maintained by Carnegie Mellon\u0026rsquo;s Entertainment Technology Center. Written in C++, completely wrapped in Python. Pygame - Pygame is a set of Python modules designed for writing games. PyOgre - Python bindings for the Ogre 3D render engine, can be used for games, simulations, anything 3D. PyOpenGL - Python ctypes bindings for OpenGL and it\u0026rsquo;s related APIs. PySDL2 - A ctypes based wrapper for the SDL2 library. RenPy - A Visual Novel engine. Geolocation # Libraries for geocoding addresses and working with latitudes and longitudes.\ndjango-countries - A Django app that provides country choices for use with forms, flag icons static files, and a country field for models. GeoDjango - A world-class geographic web framework. GeoIP - Python API for MaxMind GeoIP Legacy Database. geojson - Python bindings and utilities for GeoJSON. geopy - Python Geocoding Toolbox. pygeoip - Pure Python GeoIP API. HTML Manipulation # Libraries for working with HTML and XML.\nBeautifulSoup - Providing Pythonic idioms for iterating, searching, and modifying HTML or XML. bleach - A whitelist-based HTML sanitization and text linkification library. cssutils - A CSS library for Python. html5lib - A standards-compliant library for parsing and serializing HTML documents and fragments. lxml - A very fast, easy-to-use and versatile library for handling HTML and XML. MarkupSafe - Implements a XML/HTML/XHTML Markup safe string for Python. pyquery - A jQuery-like library for parsing HTML. untangle - Converts XML documents to Python objects for easy access. WeasyPrint - A visual rendering engine for HTML and CSS that can export to PDF. xmldataset - Simple XML Parsing. xmltodict - Working with XML feel like you are working with JSON. HTTP # Libraries for working with HTTP.\ngrequests - requests + gevent for asynchronous HTTP requests. httplib2 - Comprehensive HTTP client library. requests - HTTP Requests for Humans™. treq - Python requests like API built on top of Twisted\u0026rsquo;s HTTP client. urllib3 - A HTTP library with thread-safe connection pooling, file post support, sanity friendly. Hardware # Libraries for programming with hardware.\nino - Command line toolkit for working with Arduino. keyboard - Hook and simulate global keyboard events on Windows and Linux. mouse - Hook and simulate global mouse events on Windows and Linux. Pingo - Pingo provides a uniform API to program devices like the Raspberry Pi, pcDuino, Intel Galileo, etc. PyUserInput - A module for cross-platform control of the mouse and keyboard. scapy - A brilliant packet manipulation library. wifi - A Python library and command line tool for working with WiFi on Linux. Image Processing # Libraries for manipulating images.\nhmap - Image histogram remapping. imgSeek - A project for searching a collection of images using visual similarity. nude.py - Nudity detection. pagan - Retro identicon (Avatar) generation based on input string and hash. pillow - Pillow is the friendly PIL fork. pyBarcode - Create barcodes in Python without needing PIL. pygram - Instagram-like image filters. python-qrcode - A pure Python QR Code generator. Quads - Computer art based on quadtrees. scikit-image - A Python library for (scientific) image processing. thumbor - A smart imaging service. It enables on-demand crop, re-sizing and flipping of images. wand - Python bindings for MagickWand, C API for ImageMagick. Implementations # Implementations of Python.\nCLPython - Implementation of the Python programming language written in Common Lisp. CPython - Default, most widely used implementation of the Python programming language written in C. Cython - Optimizing Static Compiler for Python. Uses type mixins to compile Python into C or C++ modules resulting in large performance gains Grumpy - More compiler than interpreter as more powerful CPython2.7 replacement (alpha). IronPython - Implementation of the Python programming language written in C# targeting the .NET Framework and Mono. Jython - Implementation of Python programming language written in Java for the Java virtual machine (JVM). MicroPython - MicroPython - a lean and efficient Python programming language implementation for microcontrollers and constrained systems Numba - Python JIT compiler to LLVM aimed at scientific Python. PeachPy - x86-64 assembler embedded in Python. Can be used as inline assembler for Python or as a stand-alone assembler for Windows, Linux, OS X, Native Client and Go. Pyjion - A JIT for Python based upon CoreCLR. PyPy - Implementation of the Python programming language written in RPython and translated into C. PyPy focuses on speed, efficiency and compatibility with the original CPython interpreter. The interpreter uses black magic to make Python very fast without having to add in additional type information. PySec - Hardened version of python that makes it easier for security professionals and developers to write applications more resilient to attacks and manipulations. Pyston - A Python implementation built using LLVM and modern JIT techniques with the goal of achieving good performance. Stackless Python - An enhanced version of the Python programming language which allows programmers to reap the benefits of thread-based programming without the performance and complexity problems associated with conventional threads. Interactive Interpreter # Interactive Python interpreters (REPL).\nbpython - A fancy interface to the Python interpreter. Jupyter Notebook (IPython) - A rich toolkit to help you make the most out of using Python interactively. awesome-jupyter ptpython - Advanced Python REPL built on top of the python-prompt-toolkit. Internationalization # Libraries for working with i18n.\nBabel - An internationalization library for Python. PyICU - A wrapper of International Components for Unicode C++ library ( ICU). Job Scheduler # Libraries for scheduling jobs.\nAPScheduler - A light but powerful in-process task scheduler that lets you schedule functions. django-schedule - A calendaring app for Django. doit - A task runner and build tool. gunnery - Multipurpose task execution tool for distributed systems with web-based interface. Joblib - A set of tools to provide lightweight pipelining in Python. Plan - Writing crontab file in Python like a charm. schedule - Python job scheduling for humans. Spiff - A powerful workflow engine implemented in pure Python. TaskFlow - A Python library that helps to make task execution easy, consistent and reliable. Logging # Libraries for generating and working with logs.\nEliot - Logging for complex \u0026amp; distributed systems. logbook - Logging replacement for Python. logging - (Python standard library) Logging facility for Python. raven - Python client for Sentry, a log/error tracking, crash reporting and aggregation platform for web applications. Machine Learning # Libraries for Machine Learning. See: awesome-machine-learning.\nH2O - Open Source Fast Scalable Machine Learning Platform. Metrics - Machine learning evaluation metrics. NuPIC - Numenta Platform for Intelligent Computing. scikit-learn - The most popular Python library for Machine Learning. Spark ML - Apache Spark\u0026rsquo;s scalable Machine Learning library. vowpal_porpoise - A lightweight Python wrapper for Vowpal Wabbit. xgboost - A scalable, portable, and distributed gradient boosting library. MapReduce # Frameworks and libraries for MapReduce.\nPySpark - Apache Spark Python API. luigi - A module that helps you build complex pipelines of batch jobs. mrjob - Run MapReduce jobs on Hadoop or Amazon Web Services. streamparse - Run Python code against real-time streams of data. Integrates with Apache Storm. dask - A flexible parallel computing library for analytic computing. Microsoft Windows # Python programming on Microsoft Windows.\nPython(x,y) - Scientific-applications-oriented Python Distribution based on Qt and Spyder. pythonlibs - Unofficial Windows binaries for Python extension packages. PythonNet - Python Integration with the .NET Common Language Runtime (CLR). PyWin32 - Python Extensions for Windows. WinPython - Portable development environment for Windows 7/8. Miscellaneous # Useful libraries or tools that don\u0026rsquo;t fit in the categories above.\nblinker - A fast Python in-process signal/event dispatching system. itsdangerous - Various helpers to pass trusted data to untrusted environments. pluginbase - A simple but flexible plugin system for Python. Pychievements - A framework for creating and tracking achievements. Tryton - A general purpose business framework. Natural Language Processing # Libraries for working with human languages.\ngensim - Topic Modelling for Humans. Jieba - Chinese text segmentation. langid.py - Stand-alone language identification system. NLTK - A leading platform for building Python programs to work with human language data. Pattern - A web mining module for the Python. polyglot - Natural language pipeline supporting hundreds of languages. SnowNLP - A library for processing Chinese text. spaCy - A library for industrial-strength natural language processing in Python and Cython. TextBlob - Providing a consistent API for diving into common NLP tasks. PyTorch-NLP - A toolkit enabling rapid deep learning NLP prototyping for research. Network Virtualization # Tools and libraries for Virtual Networking and SDN (Software Defined Networking).\nMininet - A popular network emulator and API written in Python. POX - An open source development platform for Python-based Software Defined Networking (SDN) control applications, such as OpenFlow SDN controllers. Pyretic - A member of the Frenetic family of SDN programming languages that provides powerful abstractions over network switches or emulators. SDX Platform - SDN based IXP implementation that leverages Mininet, POX and Pyretic. Networking # Libraries for networking programming.\nasyncio - (Python standard library) Asynchronous I/O, event loop, coroutines and tasks. diesel - Greenlet-based event I/O Framework for Python. pulsar - Event-driven concurrent framework for Python. pyzmq - A Python wrapper for the ZeroMQ message library. Twisted - An event-driven networking engine. txZMQ - Twisted based wrapper for the ZeroMQ message library. NAPALM - Cross-vendor API to manipulate network devices. News Feed # Libraries for building user\u0026rsquo;s activities.\ndjango-activity-stream - Generating generic activity streams from the actions on your site. Stream-Framework - Building newsfeed and notification systems using Cassandra and Redis. ORM # Libraries that implement Object-Relational Mapping or data mapping techniques.\nRelational Databases Django Models - A part of Django. SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper. awesome-sqlalchemy Orator - The Orator ORM provides a simple yet beautiful ActiveRecord implementation. Peewee - A small, expressive ORM. PonyORM - ORM that provides a generator-oriented interface to SQL. pyDAL - A pure Python Database Abstraction Layer. python-sql - Write SQL queries pythonically. NoSQL Databases django-mongodb-engine - Django MongoDB Backend. flywheel - Object mapper for Amazon DynamoDB. hot-redis - Rich Python data types for Redis. MongoEngine - A Python Object-Document-Mapper for working with MongoDB. PynamoDB - A Pythonic interface for Amazon DynamoDB. redisco - A Python Library for Simple Models and Containers Persisted in Redis. Others butterdb - A Python ORM for Google Drive Spreadsheets. dataset - A JSON-based database. Package Management # Libraries for package and dependency management.\npip - The Python package and dependency manager. Python Package Index conda - Cross-platform, Python-agnostic binary package manager. Curdling - Curdling is a command line tool for managing Python packages. pip-tools - A set of tools to keep your pinned Python dependencies fresh. wheel - The new standard of Python distribution and are intended to replace eggs. Package Repositories # Local PyPI repository server and proxies.\nwarehouse - Next generation Python Package Repository (PyPI). Warehouse bandersnatch - PyPI mirroring tool provided by Python Packaging Authority (PyPA). devpi - PyPI server and packaging/testing/release tool. localshop - Local PyPI server (custom packages and auto-mirroring of pypi). Permissions # Libraries that allow or deny users access to data or functionality.\nCarteblanche - Module to align code with thoughts of users and designers. Also magically handles navigation and permissions. django-guardian - Implementation of per object permissions for Django 1.2+ django-rules - A tiny but powerful app providing object-level permissions to Django, without requiring a database. Processes # Libraries for starting and communicating with OS processes.\ndelegator.py - Subprocesses for Humans™ 2.0. sarge - Yet another wrapper for subprocess. sh - A full-fledged subprocess replacement for Python. Queue # Libraries for working with event and task queues.\ncelery - An asynchronous task queue/job queue based on distributed message passing. huey - Little multi-threaded task queue. mrq - Mr. Queue - A distributed worker task queue in Python using Redis \u0026amp; gevent. rq - Simple job queues for Python. simpleq - A simple, infinitely scalable, Amazon SQS based queue. Recommender Systems # Libraries for building recommender systems.\nannoy - Approximate Nearest Neighbors in C++/Python optimized for memory usage. fastFM - A library for Factorization Machines. implicit - A fast Python implementation of collaborative filtering for implicit datasets. libffm - A library for Field-aware Factorization Machine (FFM). LightFM - A Python implementation of a number of popular recommendation algorithms. Spotlight - Deep recommender models using PyTorch. surprise - A scikit for building and analyzing recommender systems. TensorRec - A Recommendation Engine Framework in TensorFlow. RESTful API # Libraries for developing RESTful APIs.\nDjango django-rest-framework - A powerful and flexible toolkit to build web APIs. django-tastypie - Creating delicious APIs for Django apps. Flask eve - REST API framework powered by Flask, MongoDB and good intentions. flask-api-utils - Taking care of API representation and authentication for Flask. flask-api - Browsable Web APIs for Flask. flask-restful - Quickly building REST APIs for Flask. flask-restless - Generating RESTful APIs for database models defined with SQLAlchemy. Pyramid cornice - A RESTful framework for Pyramid. Framework agnostic falcon - A high-performance framework for building cloud APIs and web app backends. hug - A Python3 framework for cleanly exposing APIs over HTTP and the Command Line with automatic documentation and validation. restless - Framework agnostic REST framework based on lessons learned from Tastypie. ripozo - Quickly creating REST/HATEOAS/Hypermedia APIs. sandman - Automated REST APIs for existing database-driven systems. apistar - A smart Web API framework, designed for Python 3. RPC Servers # RPC-compatible servers.\nSimpleJSONRPCServer - This library is an implementation of the JSON-RPC specification. SimpleXMLRPCServer - (Python standard library) Simple XML-RPC server implementation, single-threaded. zeroRPC - zerorpc is a flexible RPC implementation based on ZeroMQ and MessagePack. Science # Libraries for scientific computing.\nastropy - A community Python library for Astronomy. bcbio-nextgen - Providing best-practice pipelines for fully automated high throughput sequencing analysis. bccb - Collection of useful code related to biological analysis. Biopython - Biopython is a set of freely available tools for biological computation. cclib - A library for parsing and interpreting the results of computational chemistry packages. Colour - A colour science package implementing a comprehensive number of colour theory transformations and algorithms. NetworkX - A high-productivity software for complex networks. NIPY - A collection of neuroimaging toolkits. NumPy - A fundamental package for scientific computing with Python. Open Babel - A chemical toolbox designed to speak the many languages of chemical data. ObsPy - A Python toolbox for seismology. PyDy - Short for Python Dynamics, used to assist with workflow in the modeling of dynamic motion. PyMC - Markov Chain Monte Carlo sampling toolkit. QuTiP - Quantum Toolbox in Python. RDKit - Cheminformatics and Machine Learning Software. SciPy - A Python-based ecosystem of open-source software for mathematics, science, and engineering. statsmodels - Statistical modeling and econometrics in Python. SymPy - A Python library for symbolic mathematics. Zipline - A Pythonic algorithmic trading library. SimPy - A process-based discrete-event simulation framework. Search # Libraries and software for indexing and performing search queries on data.\ndjango-haystack - Modular search for Django. elasticsearch-dsl-py - The official high-level Python client for Elasticsearch. elasticsearch-py - The official low-level Python client for Elasticsearch. esengine - ElasticSearch ODM (Object Document Mapper) for Python. pysolr - A lightweight Python wrapper for Apache Solr (incl. SolrCloud awareness). solrpy - A Python client for solr. Whoosh - A fast, pure Python search engine library. Serialization # Libraries for serializing complex data types\nmarshmallow - marshmallow is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes. Serverless Frameworks # Frameworks for developing serverless Python code.\napex - Build, deploy, and manage AWS Lambda functions with ease. python-lambda - A toolkit for developing and deploying Python code in AWS Lambda. Zappa - A tool for deploying WSGI applications on AWS Lambda and API Gateway. Specific Formats Processing # Libraries for parsing and manipulating specific text formats.\nGeneral tablib - A module for Tabular Datasets in XLS, CSV, JSON, YAML. Office Marmir - Takes Python data structures and turns them into spreadsheets. openpyxl - A library for reading and writing Excel 2010 xlsx/xlsm/xltx/xltm files. pyexcel - Providing one API for reading, manipulating and writing csv, ods, xls, xlsx and xlsm files. python-docx - Reads, queries and modifies Microsoft Word 2007/2008 docx files. python-pptx - Python library for creating and updating PowerPoint (.pptx) files. relatorio - Templating OpenDocument files. unoconv - Convert between any document format supported by LibreOffice/OpenOffice. XlsxWriter - A Python module for creating Excel .xlsx files. xlwings - A BSD-licensed library that makes it easy to call Python from Excel and vice versa. xlwt / xlrd - Writing and reading data and formatting information from Excel files. PDF PDFMiner - A tool for extracting information from PDF documents. PyPDF2 - A library capable of splitting, merging and transforming PDF pages. ReportLab - Allowing Rapid creation of rich PDF documents. Markdown Mistune - Fastest and full featured pure Python parsers of Markdown. Python-Markdown - A Python implementation of John Gruber’s Markdown. YAML PyYAML - YAML implementations for Python. CSV csvkit - Utilities for converting to and working with CSV. Archive unp - A command line tool that can unpack archives easily. Static Site Generator # Static site generator is a software that takes some text + templates as input and produces HTML files on the output.\nCactus - Static site generator for designers. Hyde - Jinja2-based static web site generator. Lektor - An easy to use static CMS and blog engine. Nikola - A static website and blog generator. Pelican - Uses Markdown or ReST for content and Jinja 2 for themes. Supports DVCS, Disqus. AGPL. Tinkerer - Tinkerer is a blogging engine/.static website generator powered by Sphinx. Tagging # Libraries for tagging items.\ndjango-taggit - Simple tagging for Django. Template Engine # Libraries and tools for templating and lexing.\nGenshi - Python templating toolkit for generation of web-aware output. Jinja2 - A modern and designer friendly templating language. Mako - Hyperfast and lightweight templating for the Python platform. Testing # Libraries for testing codebases and generating test data.\nTesting Frameworks hypothesis - Hypothesis is an advanced Quickcheck style property based testing library. mamba - The definitive testing tool for Python. Born under the banner of BDD. nose - A nicer unittest for Python. nose2 - The successor to nose, based on unittest2. pytest - A mature full-featured Python testing tool. Robot Framework - A generic test automation framework. unittest - (Python standard library) Unit testing framework. Test Runners green - A clean, colorful test runner. tox - Auto builds and tests distributions in multiple Python versions GUI / Web Testing locust - Scalable user load testing tool written in Python. PyAutoGUI - PyAutoGUI is a cross-platform GUI automation Python module for human beings. Selenium - Python bindings for Selenium WebDriver. sixpack - A language-agnostic A/B Testing framework. splinter - Open source tool for testing web applications. Mock doublex - Powerful test doubles framework for Python. freezegun - Travel through time by mocking the datetime module. httmock - A mocking library for requests for Python 2.6+ and 3.2+. httpretty - HTTP request mock tool for Python. mock - (Python standard library) A mocking and patching library. Mocket - Socket Mock Framework plus HTTP[S]/asyncio/gevent mocking library with recording/replaying capability. responses - A utility library for mocking out the requests Python library. VCR.py - Record and replay HTTP interactions on your tests. Object Factories factory_boy - A test fixtures replacement for Python. mixer - Another fixtures replacement. Supported Django, Flask, SQLAlchemy, Peewee and etc. model_mommy - Creating random fixtures for testing in Django. Code Coverage coverage - Code coverage measurement. Fake Data mimesis - is a Python library that help you generate fake data. fake2db - Fake database generator. faker - A Python package that generates fake data. radar - Generate random datetime / time. Error Handler FuckIt.py - FuckIt.py uses state-of-the-art technology to make sure your Python code runs whether it has any right to or not. Text Processing # Libraries for parsing and manipulating plain texts.\nGeneral chardet - Python 2/3 compatible character encoding detector. difflib - (Python standard library) Helpers for computing deltas. ftfy - Makes Unicode text less broken and more consistent automagically. fuzzywuzzy - Fuzzy String Matching. Levenshtein - Fast computation of Levenshtein distance and string similarity. pangu.py - Spacing texts for CJK and alphanumerics. pyfiglet - An implementation of figlet written in Python. pypinyin - Convert Chinese hanzi to pinyin. shortuuid - A generator library for concise, unambiguous and URL-safe UUIDs. textdistance - Compute distance between sequences. 30+ algorithms, pure python implementation, common interface, optional external libs usage. unidecode - ASCII transliterations of Unicode text. uniout - Print readable chars instead of the escaped string. xpinyin - A library to translate Chinese hanzi (漢字) to pinyin (拼音). Slugify awesome-slugify - A Python slugify library that can preserve unicode. python-slugify - A Python slugify library that translates unicode to ASCII. unicode-slugify - A slugifier that generates unicode slugs with Django as a dependency. Parser phonenumbers - Parsing, formatting, storing and validating international phone numbers. PLY - Implementation of lex and yacc parsing tools for Python. Pygments - A generic syntax highlighter. pyparsing - A general purpose framework for generating parsers. python-nameparser - Parsing human names into their individual components. python-user-agents - Browser user agent parser. sqlparse - A non-validating SQL parser. Third-party APIs # Libraries for accessing third party services APIs. See: List of Python API Wrappers and Libraries.\napache-libcloud - One Python library for all clouds. boto3 - Python interface to Amazon Web Services. django-wordpress - WordPress models and views for Django. facebook-sdk - Facebook Platform Python SDK. facepy - Facepy makes it really easy to interact with Facebook\u0026rsquo;s Graph API gmail - A Pythonic interface for Gmail. google-api-python-client - Google APIs Client Library for Python. gspread - Google Spreadsheets Python API. twython - A Python wrapper for the Twitter API. URL Manipulation # Libraries for parsing URLs.\nfurl - A small Python library that makes parsing and manipulating URLs easy. purl - A simple, immutable URL class with a clean API for interrogation and manipulation. pyshorteners - A pure Python URL shortening lib. short_url - Python implementation for generating Tiny URL and bit.ly-like URLs. webargs - A friendly library for parsing HTTP request arguments, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, and Pyramid. Video # Libraries for manipulating video and GIFs.\nmoviepy - A module for script-based movie editing with many formats, including animated GIFs. scikit-video - Video processing routines for SciPy. WSGI Servers # WSGI-compatible web servers.\nbjoern - Asynchronous, very fast and written in C. fapws3 - Asynchronous (network side only), written in C. gunicorn - Pre-forked, partly written in C. meinheld - Asynchronous, partly written in C. netius - Asynchronous, very fast. rocket - Multi-threaded. uWSGI - A project aims at developing a full stack for building hosting services, written in C. waitress - Multi-threaded, powers Pyramid. Werkzeug - A WSGI utility library for Python that powers Flask and can easily be embedded into your own projects. Web Content Extracting # Libraries for extracting web contents.\nHaul - An Extensible Image Crawler. html2text - Convert HTML to Markdown-formatted text. lassie - Web Content Retrieval for Humans. micawber - A small library for extracting rich content from URLs. newspaper - News extraction, article extraction and content curation in Python. python-goose - HTML Content/Article Extractor. python-readability - Fast Python port of arc90\u0026rsquo;s readability tool. requests-html - Pythonic HTML Parsing for Humans. sanitize - Bringing sanity to world of messed-up data. sumy - A module for automatic summarization of text documents and HTML pages. textract - Extract text from any document, Word, PowerPoint, PDFs, etc. toapi - Every web site provides APIs. Web Crawling \u0026amp; Web Scraping # Libraries to automate data extraction from websites.\ncola - A distributed crawling framework. Demiurge - PyQuery-based scraping micro-framework. feedparser - Universal feed parser. Grab - Site scraping framework. MechanicalSoup - A Python library for automating interaction with websites. portia - Visual scraping for Scrapy. pyspider - A powerful spider system. RoboBrowser - A simple, Pythonic library for browsing the web without a standalone web browser. Scrapy - A fast high-level screen scraping and web crawling framework. Web Frameworks # Full stack web frameworks.\nBottle - A fast, simple and lightweight WSGI micro web-framework. Dash - Built on top of Flask, React and Plotly aimed at analytical web applications. awesome-dash Django - The most popular web framework in Python. awesome-django Flask - A microframework for Python. awesome-flask Pyramid - A small, fast, down-to-earth, open source Python web framework. awesome-pyramid Sanic - Web server that\u0026rsquo;s written to go fast. Tornado - A Web framework and asynchronous networking library. WebSocket # Libraries for working with WebSocket.\nAutobahnPython - WebSocket \u0026amp; WAMP for Python on Twisted and asyncio. Crossbar - Open-source Unified Application Router (Websocket \u0026amp; WAMP for Python on Autobahn). django-channels - Developer-friendly asynchrony for Django django-socketio - WebSockets for Django. WebSocket-for-Python - WebSocket client and server library for Python 2 and 3 as well as PyPy. Services # Online tools and APIs to simplify development.\nContinuous Integration # See: awesome-CIandCD.\nCircleCI - A CI service that can run very fast parallel testing. (GitHub only) Travis CI - A popular CI service for your open source and private projects. (GitHub only) Vexor CI - A continuous integration tool for private apps with pay-per-minute billing model. Wercker - A Docker-based platform for building and deploying applications and microservices. Code Quality # Codacy - Automated Code Review to ship better code, faster. Codecov - Code coverage dashboard. CodeFactor - Automated Code Review for Git. Landscape - Hosted continuous Python code metrics. Resources # Where to discover new Python libraries.\nPodcasts # From Python Import Podcast Podcast.init Python Bytes Python Testing Radio Free Python Talk Python To Me Twitter # @codetengu @getpy @importpython @planetpython @pycoders @pypi @pythontrending @PythonWeekly @TalkPython @realpython Websites # /r/CoolGithubProjects /r/Python Awesome Python @LibHunt Django Packages Full Stack Python PyPI Ranking Python 3 Wall of Superpowers Python Hackers Python ZEEF Python 开发社区 Real Python Trending Python repositories on GitHub today PyXLL Weekly # CodeTengu Weekly 碼天狗週刊 Import Python Newsletter Pycoder\u0026rsquo;s Weekly Python Weekly Python Tricks Other Awesome Lists # List of lists.\nMonty awesome awesomo lists Python pycrumbs python-github-projects python_reference pythonidae Python Podcasts Python for Social Good Contributing # Your contributions are always welcome! Please take a look at the contribution guidelines first.\nI will keep some pull requests open if I\u0026rsquo;m not sure whether those libraries are awesome, you could vote for them by adding 👍 to them. Pull requests will be merged when their votes reach 20.\nIf you have any question about this opinionated list, do not hesitate to contact me @vinta on Twitter or open an issue on GitHub.\n","date":"July 22, 2018","externalUrl":null,"permalink":"/posts/pyziyuan/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eGitHub star过万的Python学习资源\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python学习资源","type":"posts"},{"content":"","date":"July 22, 2018","externalUrl":null,"permalink":"/tags/c/c++/","section":"","summary":"","title":"C/C++","type":"tags"},{"content":"","date":"July 22, 2018","externalUrl":null,"permalink":"/categories/c/c++/","section":"","summary":"","title":"C/C++","type":"categories"},{"content":" C语言资源大全中文版\n构建系统 # 下面是一些 C 项目的自动化构建和测试工具。\naimake：一个被设计来避免复杂配置的构建工具。 GNU GPL3 或更高版本。 官网 Autoconf：一个可扩展的 M4 宏指令包，提供命令行脚本来自动配置软件源码包，是 Autotools 的一部分。 GNU GPL3 或更高版本。 官网 Automake：一个自动生成符合 GNU 代码标准的 Makefile.in 文件的工具。需要与 Autoconf 配合使用，它也是 Autotools 的一部分。 GNU GPL3 或更高版本。 官网 Jam：一个构建系统，它被设计的比make更加易用。隐式地理解 C 的构建规则。 Jam License。 官网 Libtool：一个通用库支持脚本，是 Autotools 的一部分。 GNU GPL3 或更高版本。 官网 Meson：一个非常快，用户友好的构建系统，基于 Ninja。 Apache2.0。 官网 编译器 # Clang：一个基于 LLVM 的 C 的编译器，支持 C11 标准。 NCSA。 官网 CompCert：一个经过充分验证的 C 编译器。支持几乎所有的 C89 标准。 GNU GPL2.1 或更高版本。 官网 GCC：在它的编译器集合中提供了 C 编译器。支持 C11 和 OpenMP 的标准。 GNU GPL3 或更高版本。 官网 PCC：一个值得尊敬的 C 编译器，支持 C99 标准。 Various licenses，完全免费。 官网 TCC：一个迷你，速度快的 C 编译器，支持 C99（除了复杂的类型）。 LGPL2.1。 官网 加密 # GnuTLS：一个安全通信库，实现了 SSL，TLS 和 DTLS。 GNU GPL2.1 或更高版本。 官网 libgcrypt：一个通用的密码库，支持多种加密方法。 GNU LGPL2.1 （代码）， GNU GPL2.1 或更高版本（说明书和工具）。 官网 OpenSSL：一个 SSL 和 TLS 协议的实现，还包括一个加密库。 Dual Licensed under the OpenSSL License and the SSLeay License。 官网 libsodium：一个现代，易用的加密库。 Expat。 官网 libtomcrypt：一个相当全面，模块化和可移植的工具集。公开领域。 官网 mbed TLS：一个 C 实现的加密库。 Apache2.0。 官网 数据库 # 下面列出了基于 C API 的数据库和数据存储\nBerkeleyDB：一个高性能的嵌入式键值对数据库。 GNU AGPLv3。 官网 Hiredis：一个极简的 Redis 客户端。 3-clause BSD。 官网 LMDB：一个极快，极简的嵌入式键值对数据存储系统。 newOpenLDAP。 官网 MariaDB：一个强壮，可扩展和可靠的 SQL 服务器，设计来作为 MySQL 的替代品。 3-clause BSD。 官网 mongo-c-driver：一个高性能的 MongoDB 客户端。 Apache2.0. 官网 PostgreSQL：一个强大的对象关系数据库系统。 PostgreSQL licence。 官网 recutils：一个工具和 C 库的集合，用于访问可编辑，纯文本的名为 recfiles 的数据库文件。 GNU GPL3 或更高版本。 官网 Redis：一个先进的键值对存储系统。 3-clause BSD。 官网 sophia：一个现代，可嵌入的键值对数据库。 FreeBSD。 官网 SQLite：一个自包含，无服务器，零配置，支持事务的 SQL 数据库引擎，包括 C 接口。公开领域。 官网 UnQLite：一个自包含，无服务器，零配置，支持事务的 NoSQL 数据库引擎，包括 C 接口。 FreeBSD。 官网 文档生成 # Cxref：生成 C 程序的文档，支持 LaTeX，HTML， RTF 或者 SGML 的格式。 GPL2.1 官网 DocOnce：一种朴素的标签标记语言，可以用来生成多种格式的文档。 BSD-3clause. 官网 Doxygen：一个事实上的标准工具，用于从注释的代码中生成 C 文档。能够生成多种格式的文档。 GNU GPL2.1。 官网 GTK-Doc：一个从注释的代码中生成 C 文档的工具，支持 Autotools GNU GPL2.1（代码）, GNU FDL1.1。 官网 编辑器 # 这些是特别精致，IDE 类型的编辑器。如果你想要一个程序员的文本编辑器，看别的地方。此外，不管你选择哪一款编辑器，它都支持 C。\nAnjuta DevStudio：GNOME IDE。 GNU GPL2.1。 官网 Code::Blocks：一个可扩展，可配置的 IDE，支持 C。 GNU GPL3。 官网 CodeLite：一个跨平台的 IDE。 GNU GPL2.1。 官网 Eclipse：一个用 Java 写成的 IDE。 EPL。 官网 Geany：一个很小很快的 IDE。 GNU GPL2.1及更高版本。 官网 KDevelop：KDE IDE. GNU GPL2.1。 官网 环境 # 下面列出了被设计来引领 Windows 进入支持 C 的 21 世纪的技术。\nCygwin：被设计用于在 Windows 下模拟 POSIX 兼容环境。 Various licenses, all free。 官网 MinGW-w64：一个 Windows 下极简的 C 开发环境，支持 64 位。 Various licenses, all free。 官网 框架 # 这一节包括提供数据结构的大型库和你所期待“现代”的标准库。\nAPR：Apache Portable Runtime；另一个跨平台的实用函数库。 Apache2.0。 官网 C Algorithms：一个常用算法和数据结构的集合。 官网 CPL：The Common Pipeline Library；一系列详尽，高效和强壮的软件工具包。 GNU GPL2.1。 官网 EFL：一个大型实用数据结构和函数的的集合。多种许可证，完全免费。 官网 GLib：一个便携，高效和强大的实用函数和数据结构库。 GNU LGPL2.1。 官网 GIO：一个现代和易用的 VFS API。[GNU LGPL2.1]。 官网 GObject：一个 C 的面向对象系统和对象模型。 GNU LGPL2.1。 官网 libnih：一个轻量级的 C 函数和数据结构库。 GNU GPL2.1。 官网 libU：一个提供基本实用函数的迷你库，包括内存分配，字符串处理和日志功能。 官网 PBL：一个包括实用函数，特色数据结构等的大型库。 GNU LGPL2.1及更高版本（库）， GNU GPL2.1及更高版本（测试代码）。 官网 qlibc：一个简单且强大的 C 库，当我们想要小且轻的库时，可作为 Glib 的替代品。 qLib license (类似于 FreeBSD)。 官网 stb：一系列单文件 C 库。公共领域。 官网 libcstl：标准C语言通用数据结构和常用算法库。 官网 游戏编程 # 引擎 # 这些作为 C 游戏编程代码的例子。\nCorange：一个纯 C 的游戏引擎。 FreeBSD。 官网 Darkplaces：一个 Quake2 引擎的修改版本。 GNU GPL2.1。 官网 ioquake3：Quake3 引擎，终于免费啦。 GNU GPL2.1。 官网 Orx：一个便携，轻量级，插件化，数据驱动，面向 2D 的游戏引擎。 zlib。 官网 Quake2：Quake2 引擎。 GNU GPL2.1。 官网 Spearmint：一个为 FPS 游戏设计的引擎。 GNU GPL3及更高版本。 官网 资源 # 这些是筛选过的，对游戏编程有用的库。\nAllegro：一个跨平台，视频游戏开发和多媒体库。 zlib。 官网 Chipmunk2D：一个快且轻量级的 2D 游戏物理库。 Expat。 官网 CSFML：一个用 C 封装的 SFML。 zlib。 官网 FreeGLUT：一个替代性的 OpenGL 实用工具包。允许用 OpenGL 上下文创建和管理窗口。 X11。 官网 GLFW：一个使用 OpenGL 上下文创建窗口的多平台库。 zlib。 官网 libao：一个有多种输出的跨平台音频库。 GNU GPL2.1及更高版本。 官网 RetroArch： libretro 的参考前端。 GNU GPL3。 官网 SDL：一个跨平台库，通过 OpenGL 提供音频，键盘，鼠标，操纵杆和图形硬件的底层访问。 zlib。 官网 SDL2：一个跨平台库，通过 OpenGL 提供音频，键盘，鼠标，操纵杆和图形硬件的底层访问。这是最新版本。 zlib。 官网 通用编程 # klib：小且轻量级的常用算法和数据结构实现。 Expat。 官网 图形 # Cairo：一个 2D 图像库。 GNU LGPL2.1 或者 MPLv1.1。 官网 Cogl：一个 GPU 图像和实用 API。 Expat（依赖使用 3-clause BSD 许可，库使用 LGPLv2.1 许可）。 官网 Clutter：一个基于 OpenGL 的 UI 库。 GNU LGPL2.1。 官网 heman：一个迷你图像实用工具库，处理高度图，法线图，距离场等。 Expat。 官网 libcaca：一个基于终端接口的 ASCII 渲染器。 WTFPLv2。 官网 libimagequant：小且轻量级的库，用于高质量的从 RGBA 图像到 8 位像素图的转换。 FreeBSD。 官网 libjpeg-turbo：一个更快的读写 JPEG 文件库。 Various licences。 官网 libpng：官方 PNG 参考库。 libpng license。 官网 libxmi：一个光栅化 2D 位图的函数库。 GNU GPL3 或更高版本。 官网 mozjpeg：一个提升 JPEG 图像质量的编码器。 3-clause BSD。 官网 OpenGL：一个高性能图像的工业标准，提供了原生 C语言 绑定。 Various licenses。 官网 Craft：一个利用OpenGL使用C编写的简单MineCraft的克隆。 官网、 GitHub GUI # 这些具体来说就是 控件工具包\nGTK+：一个跨平台的控件工具包。 GNU LGPL2.1。 官网 IUP：另一个跨平台的控件工具包。 Expat。 官网 Tk：一个基本控件工具包，Tcl/Tk 的一部分。 Tcl/Tk License。 官网 XForms Toolkit：一个为 XWindow 设计的控件工具包。 GNU LGPL2.1。 官网 JSON # Jansson：一个编码，解码和处理 JSON 的 C 库。 Expat。 官网 jsmn：一个极简的 JSON 解析器。 Expat。 官网 json-c：一个处理 JSON 的库。 Expat。 官网 WJElement：高级 JSON 处理库，支持 JSON Schema。任意版本的 LGPL。 官网 YAJL：一个很快的 JSON 流解析 C 库。 ISC。 官网 学习、参考和指南 # 下面列出了学习 C 编程的资源，或者跟 C 编程相关的有用的东西。\n在线 # 参考 # SEI CERT C 编码标准 C FAQ：comp.lang.c 常见问题 GNU/Linux 下 C/POSIX 标准库实现的比较 C11 标准草案 GNU C 参考手册 Robert Pike 的 C 笔记 初级 # 指针教程 C 语言指针 5 分钟教程 C 语言内存地址基础 C 语言函数指针基础 C 语言指针和数组基础 构建 C 项目 C 编程百科全书 “有趣”的 C 介绍 用 GDB 学习 C POSIX 线程编程指南 (有点过时，但大部分还是有效和有用的) GNU C 编程指南 (在线 PDF) C 语言中的模板 中级 # 8 个你应该知道的 GDB 技巧 10 个 C99 技巧 深入并发：尝试互斥锁和原子操作 OpenMP 介绍 (视频) OpenMP 指南 (针对 OpenMP3 标准) memcpy 与 memmove 的比较 MPI 指南 C 语言中一些未知特性或者技巧 失落的 C 语言结构体封装艺术 C 程序员需要了解的内存知识 每个 C 程序员需要知道的未定义行为知识 高级 # C 中的高级元编程 一个快速教程：如何实现和调试 malloc，free，calloc，和 realloc 位操作技巧 我不懂 C 在 C 语言中实现智能指针 C 中的内联函数 C 中的自定义控制结构元编程 用 C 的宏解决临时内存问题 C 语言中一些不为人知的角落 编写高效的 C 和 C 代码优化 自学教程 # C 语言认证协会预备课程 实体书 # 参考资料 # C: A Reference Manual 5E：C99 完全参考手册 C Pocket Reference：C99 简明参考手册 The C Programming Language 2E：第一本 C 语言书籍，由 C 的创造者编写。 初级 # C Primer Plus 6E：一个全面的 C11 编程指南。 C Programming: A Modern Approach：一本极好的学习 C 基础的书。 Head First C：一本“深入浅出”风格的学习 C 的书籍。 中级 # 21st Century C：一本非常好的 C 语言书籍，可作为第二选择。 Understanding and Using C Pointers：一本深入讨论 C 指针的书。 ZeroMQ：一本介绍如何用 C 使用 ZeroMQ 的书。 高级 # Expert C Programming: Deep C Secrets：一本从有趣，深度和娱乐的视角分析 C 内部结构的书籍。 多媒体 # FFMPEG：一个完整，跨平台的录音，转换和流化视频以及音频的解决方案。 GNU LGPL2.1 或更高版本，部分是 GNU GPL2.1 或更高版本。 官网 GStreamer：一个声音和可视化媒体的框架。 GNU LGPL2.1。 官网 lodepng：一个简单的 PNG 图像解码器和编码器，不需要其他依赖。 3-clause BSD. 官网 网络编程 # asnlc：一个把 ASN.1 规范转换为 C 代码的编译器。 FreeBSD。 官网 czmq：一个 ZeroMQ 的高级绑定。 官网 GNU adns：一个高级，易用，异步的 DNS 客户端和实用工具。 GNU GPL3 及更高版本。 官网 GNU SASL：一个简单身份认证和安全层和一些常见 SASL 机制的实现。 GNU GPL3 及更高版本。 官网 GnuTLS：一个安全通信库，实现了 SSL，TLS 和 DTLS。 GNU LGPL2.1 及更高版本。 官网 gumbo-parser：一个 C99 标准的 HTML5 解析库。 Apache2.0。 官网 http-parser：一个 HTTP 请求回应解析器。 Expat。 官网 ldns：一个简化 DNS 编程的库。 3-clause BSD。 官网 libcurl：一个客户端的 URL 转换库，支持多种格式。 curl license。 官网 LibEtPan：一个邮件库，支持 IMAP，SMTP，POP 和 NNTP 网络协议。 3-clause BSD。 官网 libev：一个事件驱动库。 FreeBSD. 官网 libevent：一个在网络服务器中事件驱动库的可替代品。 3-clause BSD。 官网 libgss：通用安全服务。 GNU GPL3 或更高版本。 官网 libhttpd：一个给应用或嵌入式设备添加基本 Web 服务器功能的库。 GNU GPL2。 官网 libidn：Stringprep，Punycode 和 IDNA 规范的实现。 GNU GPL3 或更高版本。 官网 libmicrohttpd：一个迷你库，可作为其他应用的一部分，让运行 HTTP 服务器变得简单。 GNU LGPL2.1 或更高版本。 官网 libsoup：一个 GNOME HTTP 客户端/服务器库，使用了 GObject。 GNU LGPL2.1。 官网 lwan：一个实验性，可扩展，高性能的 HTTP 服务器。 GNU GPL2.1。 官网 mongoose：基于 C 的嵌入式 Web 服务器。 GNU GPL2.1。 官网 nanomsg：一个基于 C 的 ZeroMQ 实现。 Expat。 官网 onion：易于使用的 HTTP 服务器库。 Apache2.0。 官网 OpenSSL：一个 SSL 和 TLS 协议的实现，还包括一个加密库。 Dual Licensed under the OpenSSL License and the SSLeay License。 官网 oSip：一个基于 C 的 SIO 实现，没有额外的依赖。 GNU LGPLv2.1 或更高版本。 官网 s2n：一个 C99 标准的 TLS/SSL 协议的实现，简单，快并且以安全优先。 Apache2.0。 官网 socket99：BSD 套接字 API 的 C99 封装。 ISC。 官网 Tox：一个通信平台，被设计为 Skype 杀手。 GNU GPL3。 官网 twitc：一个与 Twitter OAuth API 交互的迷你 C 库。 Expat。 官网 Web框架 # balde：一个基于 GLib 的 C 微框架。 GNU LGPLv2.1。 官网 数值计算 # apophenia：一个用于统计和科学计算的库。 GNU GPL2.1。 官网 ATLAS：自动调谐线性代数软件。 3-clause BSD。 官网 BLAS：Basic Linear Algebra Subprograms；提供向量和矩阵操作的一系列操作。 BLAS license。 官网 Cuba：一个计算多维的数值积分的库。 GNU LGPLv3。 官网 FFTW：The Fastest Fourier Transform in the West；一个高度优化的快速傅立叶变换例程。 GNU GPL2.1 或更高版本。 官网 FLINT：Fast Library for Number Theory;一个支持算数，多项式，幂级数和矩阵等。 GNU GPL2.1 或更高版本。 官网 GLPK：GNU Linear Programming Kit；一个求解大规模线性规划，混合整数规划和其他相关问题。 GNU GPL3 或更高版本。 官网 GMP：GNU Multple Precision Arithmetic Library；一个支持任意精度计算的库。双重许可 GNU GPL2.1 和 GNU LGPLv3。 官网 GNU MPC：一个支持复杂数字计算的库。 GNU LGPL3 或更高版本。 官网 GNU MPFR：一个支持任意精度的浮点数计算库。 GNU LGPL3 或更高版本（大多数最近版本）， GNU LGPL2.1 或更高版本（2.4.x 之前）。 官网 GNU MPRIA：一个支持多精度合理区间运算的可扩展数学库。 GNU GPL3 或更高版本。 官网 GSL：The GNU Scientific Library；一个精确的数值计算库。 GNU GPL3。 官网 KISS FFT：一个非常快的傅里叶变换库。 3-clause BSD。 官网 LAPACKE：一个 LAPACK 的 C 接口。 3-clause BSD。 官网 PARI/GP：一个数论的计算机代数系统，包括 C 编译器。 GNU GPL2.1 或更高版本。 官网 PETSc：一系列数据结构和例程，用于计算由偏微分方程建模的应用程序的可扩展并行解。 FreeBSD。 官网 SLEPc：一个在并行计算机中的解决大型，稀疏特征值问题的软件库。 LGPL3 官网 Yeppp!：非常快，SIMD 优化的数学库。 3-clause BSD。 官网 并行编程 # cchan：一个线程间通信通道构建的小型库。公共领域。 官网 ck：并发原语，安全内存回收机制和非阻塞数据结构。 FreeBSD。 官网 mill：用 C 写成的 Go 风格并发。 X11 官网 MPICH：MPI 的另一种实现。 MPICH licence。 官网 OpenMP：一组 C 编译指令，使其易于并行化代码。标准（许可不适用）。 官网 OpenMPI：一个消息传输接口实现。 3-clause BSD。 官网 PETSc：一系列数据结构和例程，用于计算由偏微分方程建模的应用程序的可扩展并行解。 FreeBSD。 官网 pth：一个非抢占式优先级调度多线程执行的可扩展实现。 GNU GPL3 或者更高版本。 官网 pthreads：POSIX 线程库。标准（没有适用的许可）。 官网 SLEPc：一个在并行计算机中的解决大型，稀疏特征值问题的软件库。 GNU LGPL3。 官网 TinyCThread：一个可扩展，小型的 C11 标准线程 API 实现。 zlib。 官网 正则表达式 # 有些人遇到问题时就会想：“啊，我知道了，我可以使用正则表达式”。于是现在他们有了两个问题。- Jamie Zawinski。\nPCRE：与 Perl 5 正则表达式完全相同的实现。 3-clause BSD。 官网 SLRE：Super Light Regular Expression library;一个 Perl 正则表达式语法子集的迷你实现。 GNU GPL2.1。 官网 TRE：一个与 POSIX 一致，充满特色的正则表达式库。 FreeBSD。 官网 PS：关于正则表达式，deerchao 有篇文章不错 《 正则表达式 30 分钟入门教程》 序列化 # c-capnproto：一个 Cap\u0026rsquo;n Proto 序列化协议的实现。 Expat。 官网 cmp：一个 MessagePack 序列化协议的实现。 Expat。 官网 libavro：一个 Avro 数据序列化系统的 C 实现。 Apache2.0。 官网 mpack：另一个 MessagePack 序列化协议的实现。 Expat。 官网 protobuf-c：一个 Google Protocol Buffer 的 C 实现。 FreeBSD。 官网 xdr：External Data Representation；数据序列化标准。标准（没有可用许可）。 官网 源码集锦 # 下面包含了一些小型源码集合。如果你想要大型且成熟的，看《框架》那一节。\nCCAN：模仿 Perl 的 CPAN，这是一个大的实用 C 代码集合。完整的列表在 这里。多种许可（所有都是免费软件）。 官网 clib：一个 C 软件包管理器，配备了一堆自己的库。 Expat。 官网 gnulib：通用 GNU 代码的集合。多种许可证，全部免费。 官网 libdjb：做各种事情的库的集合。（显然）公共领域。 官网 标准库 # 下面包括了标准 C 库。\nBionic：谷歌为 Android 开发的 C 标准库。 3-clause BSD。 官网 dietlibc：标准 C 库，可能是最小的二进制文件。 GNU GPL2.1。 官网 glibc：GNU C 库。一个 C 标准库的实现。 GNU LGPL2.1。 官网 musl：标准 C 库，兼容 POSIX 2008 和 C11，为静态链接设计。 Expat。 官网 字符串处理 # bstrlib：更好的字符串处理库。双重许可， 3-clause BSD 或 GNU GPL2.1。 官网 ICU：International Components for Unicode；提供 Unicode 支持的库。 ICU license。 官网 libunistring：处理 Unicode 字符串的 C 库。 GNU LGPL3。 官网 libgiconv：文本转换库。 GNU LGPL2.1（库）， GNU GPL3（编码转换程序）。 官网 SDS：Simple Dynamic Strings；一个用简单方式处理 C 字符串的库，而且兼容标准 C 字符串函数。可通过 clib 使用。 FreeBSD。 官网 shoco：一个小型文本压缩器。 Expat。 官网 smaz：一个高效的字符串压缩库。 3-clause BSD. 官网 测试工具 # CHEAT：一个非常简单的单元测试框架。 FreeBSD。 官网 Check：一个 C 的单元测试框架。 GNU LGPL2.1。 官网 CMock：一个 C 的 mock/stub 生成器。 官网 cmocka：一个支持 mock 对象的单元测试框架。 Apache2.0。 官网 Criterion：一个 KISS 风格，非侵入式的 C 测试框架。 Expat 官网 CUnit：另一个 C 的单元测试框架。 GNU LGPL2.0。 官网 minunit：极小的 C 单元测试框架。 Expat。 官网 Unity：一个简单的 C 单元测试框架。 Expat。 官网 文本编辑器扩展 # 在几乎任何像样的文本编辑器支持 C 之际，有一些扩展，让编辑器更加令人愉快。下面是根据编辑器分目录的。\nEmacs # CEDET：Collection of Emacs Development Environment Tools；给 Emacs 提供一种类似 IDE 的特点，是内建的。 GNU GPL3 或更高版本。 官网 Flycheck：现代语法检查。对于 C，它可以使用 GCC 或者 Clang 作为后端。 GNU GPL3 或更高版本。 官网 Yasnippet：一个模板系统，支持 C 的通用代码片段。 GNU GPL3 或更高版本。 官网 Vim # Syntastic：语法检查和语言分析。 Do What The Fuck You Want To license。 官网 YouCompleteMe：一个 Vim 的代码补全引擎。 GNU GPL3。 官网 工具 # 下面是实用的程序列表，包括了帮助你编写和调试 C 代码的库或者编译器，不包括编辑器。\nadlint：一个静态分析器。支持完全的 C89 标准和部分 C99 标准。 GNU GPL3 或更高版本。 官网 Artistic Style：一个支持 C 的小而快的自动化代码格式化工具。 GNU LGPL3。 官网 address-sanitizer：一个很快的内存错误探测器。 Apache2.0。 官网 biicode：一个现代的 C 依赖管理器。 Expat。 官网 c：在命令行中编译和执行 C 脚本，也支持 shebang。 Expat。 官网 c99sh：用 hash-bang 运行 C 文件。 FreeBSD。 官网 CBMC：C Bounded Model Checker；一个检查数组边界，指针安全和用户自定义断言的工具。 Original BSD。 官网 cdecl：一个在线服务，能够把 C 的声明翻译成英文，反向也可以。公共领域。 官网 cinclude2dot：在 C 项目中使用 Graphviz 的图像包含依赖。任官网 GNU GPL 版本（根据文件中的要求） 官网 Complexity：一个测量 C 代码复杂性的工具。 GNU GPL3 或更高版本。 官网 DDD：各种命令行调试器的图形前端。 GNU GPL3 或更高版本。 官网 fab：让每次构建都最优的构建系统。 GNU GPL3。 官网 GDB：The GNU Project debugger；一个 C 调试器。 GNU GPL3 或更高版本。 官网 Glade：一个让 TK+ GUIs 开发更快的 RAD 工具。 GNU GPL2.1。 官网 GMSL：GNU Make Standard Library；一个 GNU Make 额外功能的集合。 3-clause BSD。 官网 GNU Global：一个 C 的源码标签工具。 GNU GPL3。 官网 Global gprof：一个性能分析工具。GNU binutils 的一部分。 GNU GPL3 或更高版本。 官网 Highlight：把源码转化为高亮的格式化文本。 GNU GPL3。 官网 include-what-you-use：帮助程序员发现不必要的包含和提供解决他们的建议。基于 LLVM/Clang（只能与它一起工作）。 NCSA。 官网 indent：自动格式化 C 代码，让它更容易阅读。也能把一种风格的代码转化为另一种。 GNU GPL3 或更高版本。 官网 Make：一个控制程序的可执行和其他非代码文件的生成的工具。 GNU GPL3 或更高版本（链接到 GNU 的实现)。 官网 qo：无需分开的配置文件的构建系统。 Expat。 官网 rr：记录非确定执行来允许可确定调试的调试器。 FreeBSD。 官网 tup：一个很快，基于文件，跨平台的构建系统。 GNU GPL2.1。 官网 unifdef：移除 #ifdef 和 #if 指令包含的文本，不会改变文件的其他部分。 3-clause BSD 和 FreeBSD。 官网 Valgrind：各种动态分析工具，包括一个内存泄漏检测工具。 GNU GPL2.1。 官网 其他工具 # 下面是包罗万象的目录，主要是那些不适合放在其他目录的东西。\nApeTagLibs：APEv2 标签的 C 库。 Expat。 官网 bfd：处理二进制对象文件的库。GNU binutils 的一部分。 GNU GPL3 或更高版本。 官网 ccv：基于C语言、带缓存机制的现代计算机视觉核心库。 3-clause BSD。 官网 cf4ocl：The C Framework for OpenCL；一个跨平台面向对象框架，用于开发和用基准问题测试 OpenCL 项目。 GNU LGPL3（库）, GNU GPL3（其他代码）。 官网 CommonMark：CommonMark 规范的 C 实现。 Variety of licenses, all free。 官网 CException：异常的 C 实现。 Expat。 官网 docopt.c：命令行选项解析器的 C 实现。 Expat。 官网 dyncall：另一个外部函数接口库。 Expat。 官网 FANN：Fast Artifical Neural Network library；一个神经网络的实现。 GNU GPL2.1。 官网 Firm：一个 C 库，提供了基于图像中间表示，优化和适合编译器的汇编代码生成。配备了 C 的前端例子，使用相同的许可。 GNU LGPLv2.1。 官网 gjrand：随机数字生成的例程库。 GNU GPL2.1 或者 GNU GPLv3（用户的选择）。 官网 GNU FreeIPMI：一个带内和带外的 IPMI 实现。 GNU GPL3。 官网 FreeIPMI GNU gperf：一个完美的哈希函数生成器，提供一系列的字符串。输出 C 代码。 GNU GPL3 或更高版本。 官网 GNU Libffcall：一个构建外部函数接口库的集合。 GNU GPL3 或更高版本。 官网 gperftools：一系列测量和提高性能的实用工具集合。 3-clause BSD。 官网 hammer：二进制格式的解析器组合。 GPL2.1. 官网 Hans Boehm GC：C 的垃圾收集器？如果我用了不要介意。多种许可证，完全免费。 官网 huffandpuff：一个极小的哈夫曼编码器和解码器。公共领域。 官网 iniparser：一个 .ini 文件的解析器。 Expat。 官网 jemalloc：一个 malloc 实现，着重于段错误的避免和可扩展并发支持。 FreeBSD。 官网 jwHash：一个很快的哈希表实现。 Apache2.0。 官网 kdtree：KD-trees 的简单库。 3-clause BSD。 官网 Kitsune：高效，通用的框架，用于软件的动态升级。 GNU LGPL3 或更高版本。 官网 libavl：一个包括各种自平衡二叉树的库。 GNU GPL3 或更高版本。 官网 libbson：BSON 实用库。 Apache2.0。 官网 libCello：引入高级语言给 C 的库。 官网 libcox：一个运行跨平台系统调用和跨系统标准工具的库。 FreeBSD。 官网 libffi：轻量级的外部函数接口库。 Expat。 官网 libgit2：Git 的纯 C 实现。 GNU GPL2 only, with a linking exception。 官网 libimobiledevice：一个跨平台协议库，用于与 iThings 通信。 GNU LGPLv2.1 或更高版本（库）， GNU GPL2.1 或更高版本（工具）。 官网 libmpv：音乐播放库。编译的时候加入 ./waf configure --disable-cplayer --enable-libmpv-shared 来避免生成音乐播放器。 GNU GPL2.1 或更高版本。 官网 libnfc：一个平台独立的 NFC 库。 GNU LGPL3。 官网 libPhenom：事件框架，用于构建高扩展和高性能系统。 Apache2.0。 官网、 GitHub libsoundio：跨平台，实时音频输入输出的库，有很多种后端。 Expat。 官网 libucl：通用配置库解析器。 FreeBSD。 官网 libudp：一个统一设计模式的 C 实现。 GNU GPL3 或更高版本。 官网 libuv：跨平台异步 I/O。 Expat。 官网 libYAML：一个 YAML 1.1 解析器和发射器。 Expat。 官网 lzo：一个很快的数据压缩库。 GNU GPL2.1。 官网 mpc：解析器组合库。 FreeBSD。 官网 ncurses：彩色的终端 UI 库。 GNU GPL3 或更高版本。 官网 nope.c：一个基于 C 语言，超级轻量级的软件平台，用于可扩展服务端和网络应用的开发（想想 C 程序员的 nodejs）。 官网 pbc：一个协议缓冲库。 Expat。 官网 rabbitmq-c： RabbitMQ 的客户端库。 Expat。 官网 Ragel：为编译C的状态机的DSL。 GNU GPL2.1。 官网 uthash：哈希表实现，允许已经存在的数据结构很容易地存在哈希表里面。 1-clause BSD。 官网 Viola：libCello 的简化版本。 Expat。 官网 zlib：一个相当漂亮却精致不张扬的压缩库。 3-clause BSD。 官网 Sundown：一个用C编写的符合标准的、快速的、安全的源码解析器。 官网 XML # “XML 是垃圾。真的，没有任何借口。XML 对人类不友好的，甚至对于电脑来说也是一场灾难。根官网 -Linus Torvalds\nExpat：面向流的 XML 解析器。 Expat。 官网 libxml2：一个符合标准，轻量级的 XML 解析器。 Expat。 官网 mini-xml：小型 XML 读写库。没有 C 标准库的依赖。 GNU LGPL2.1 with static linking exception。 官网 其他资源 # 有影响力的书 # 具有广泛影响且值得阅读的 C 语言经典书籍。\n待补充 知名网站/博客 # 值得关注的 C 语言技术站点和博客。\n中文 # 待补充 英文 # 待补充 微信公众号 # CPP开发者：专注分享 C/C++ 开发相关的技术文章和工具资源。 转自：https://github.com/jobbole/awesome-c-cn\n","date":"July 22, 2018","externalUrl":null,"permalink":"/posts/cl/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eC语言资源大全中文版\u003c/p\u003e\n\u003c/blockquote\u003e","title":"C语言资源大全中文版","type":"posts"},{"content":" Python中的小技巧\n#coding:utf-8 # NO.1 def dispatch_if(operator, x, y): if operator == \u0026#39;add\u0026#39;: return x + y elif operator == \u0026#39;sub\u0026#39;: return x - y elif operator == \u0026#39;mul\u0026#39;: return x * y elif operator == \u0026#39;div\u0026#39;: return x / y else: return None def dispatch_dict(operator, x, y): return { \u0026#39;add\u0026#39;: lambda: x + y, \u0026#39;sub\u0026#39;: lambda: x - y, \u0026#39;mul\u0026#39;: lambda: x * y, \u0026#39;div\u0026#39;: lambda: x / y, }.get(operator, lambda: None)() print(dispatch_if(\u0026#39;sub\u0026#39;, 5, 9)) print(dispatch_dict(\u0026#39;mul\u0026#39;, 3, 7)) print(dispatch_if(\u0026#39;unknown\u0026#39;, 2, 8)) print(dispatch_dict(\u0026#39;unknown\u0026#39;, 2, 8)) # NO.2 def myfunc(a, b): return a + b funcs = [myfunc] print(funcs[0]) print(funcs[0](2, 3)) # NO.3 my_mapping = {\u0026#39;a\u0026#39;: 23, \u0026#39;b\u0026#39;: 42, \u0026#39;c\u0026#39;: 0xc0ffee} print(my_mapping) import json print(json.dumps(my_mapping, indent=4, sort_keys=True)) # json.dumps({all: \u0026#39;yup\u0026#39;}) # NO.4 name_for_userid = { 382: \u0026#34;Alice\u0026#34;, 590: \u0026#34;Bob\u0026#34;, 951: \u0026#34;Dilbert\u0026#34;, } def greeting(userid): return \u0026#34;Hi %s!\u0026#34; % name_for_userid.get(userid, \u0026#34;there\u0026#34;) print(greeting(951)) # NO.5 xs = {\u0026#39;a\u0026#39;: 4, \u0026#39;b\u0026#39;: 3, \u0026#39;c\u0026#39;: 2, \u0026#39;d\u0026#39;: 1} S = (sorted(xs.items(), key=lambda x: x[1])) print(S) # Or: import operator S = sorted(xs.items(), key=operator.itemgetter(1)) print(S) ","date":"July 15, 2018","externalUrl":null,"permalink":"/posts/pyxjq/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003ePython中的小技巧\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python中的小技巧","type":"posts"},{"content":" Python面试题目\n暂时整理这么多，有好的答案可以在下面留言，并注明问题序号 题目有描述错误的也欢迎指正 部分问题答案可参考: 关于Python的面试题以及答案 ======================================\n简要描述Python的垃圾回收机制（garbage collection）？ 这些是什么意思：@classmethod， @staticmethod， @property？ 这两个参数是什么意思： *args， **kwargs？我们为什么要使用它们？ “猴子补丁” （monkey patching）指的是什么？这种做法好吗？ 解释什么是Python？ Python中的模块和包是什么？ Python中的docstring是什么？ 在Python中什么是构造器？ 在Python中什么是silcing？ Python中的unittest是什么？ 为什么lambda没有语句？ 什么是Python的命名空间？ Python都有哪些自带的数据结构？ 字典推导式和列表推导式是什么？ 参数按值传递和引用传递是怎样实现的？ 数组和元组之间的区别是什么？ 什么是Python装饰器？ Python是怎样管理内存的？ Python是如何被解释的？ 什么是pickling和unpickling？ 什么是PEP8？ Python是如何进行内存管理的？ 单引号，双引号，三引号的区别？ Python里关于“堆”这种数据结构的模块是哪个？“堆”有什么优点和缺点？ 写一个类，并让它尽可能多的支持操作符 说说decorator的用法和它的应用场景，如果可以的话，请写一个decorator 有一个长度是101的数组，存在1-100的数字，有一个是重复的，如何把重复的找出来？ Python列表与元组的区别是什么？分别在什么情况下使用？ 介绍一下Python getopt模块？ 打乱一个排好序的list对象alist 有一个dict对象adict，里面包含了一百万个元素，查找其中的某个元素的平均需要多少次比较？ Python代码怎么得到列表list的交集与差集 Python里面如何实现tuple和list的转换？ Python匹配HTML tag的时候，\u0026lt;.*\u0026gt; 和 \u0026lt;.*?\u0026gt; 有什么区别？ Python中如何定义一个函数？ Python里如何反序的迭代一个序列？ 是否遇到过Python的模块间循环引用的问题，如何避免它？ 什么是元类（meta_class）？ 调试Python代码的方法有哪些？ 交叉链表求交点 合并两个有序列表 如何1-\u0026gt;2-\u0026gt;3-\u0026gt;4转换成2-\u0026gt;1-\u0026gt;4-\u0026gt;3？ 用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠的覆盖一个2*n的大矩形，总共有多少种方法？ 一只青蛙一次可以跳上1级台阶，也可以跳上2级\u0026hellip;\u0026hellip;它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法？ 一只青蛙一次可以跳上1级台阶，也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法？ 解释XSRF和XSS 网站用户密码保存问题 Apache和Nginx的区别？ cookie和session的异同点？ urllib和urllib2的区别？ 什么是三次握手？ 解释MylSAM和InnoDB 解释下乐观锁和悲观锁 __new__ 和 __init__ 的区别是什么？ Python中重载是什么？ 什么是鸭子类型？ 什么是自省？ 什么是协程？ 什么是GIL线程全局锁？ Python中的作用域 如何用Python来发送邮件？ Python里面如何实现tuple和list的转换？ Python如何捕获异常？ 写一个简单的Python socket编程 Python如何copy一个文件？ 如何用Python删除一个文件？ 如何在一个function里面设置一个全局的变量？ 有没有一个工具可以帮助查找Python的bug和进行静态的代码分析？ proc，lambda，和Proc的区别？ Python里面如何生成随机数？ Python里面 secarch() 和 match() 的区别？ 如何用Python来进行查询和替换一个文本字符串？ 介绍一下Python下range()函数的用法 如何知道一个Python对象的类型？ Python中pass语句的作用是什么？ 介绍一下except的用法和作用 Python里面如何拷贝一个对象？ 介绍一下Python Date Time方面的类 请写出一段Python代码实现删除一个list里面的重复元素 Python是如何进行类型转换的？ 介绍一下Python的and-or语法 介绍一下Python中webbrowser的用法 如何用Python输出一个Fibonacci数列？ 什么是lambda函数？它有什么好处？ Python下多线程的限制以及多进程中传递参数的方式 alias的用法，alias与alias_method的区别 Python如何实现单例模式？ RE模块中match和search的方法的不同 ","date":"July 14, 2018","externalUrl":null,"permalink":"/posts/pymianshiti/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003ePython面试题目\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python面试题目","type":"posts"},{"content":"","date":"July 14, 2018","externalUrl":null,"permalink":"/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/","section":"","summary":"","title":"面试题","type":"tags"},{"content":"","date":"July 12, 2018","externalUrl":null,"permalink":"/tags/api/","section":"","summary":"","title":"API","type":"tags"},{"content":"Django1.11 学习教程、使用Django REST Framework构建Rest API\nDjango1.11 学习教程 # 使用Django REST Framework构建Rest API # ","date":"July 12, 2018","externalUrl":null,"permalink":"/posts/djangoshipin/","section":"","summary":"\u003cp\u003eDjango1.11 学习教程、使用Django REST Framework构建Rest API\u003c/p\u003e","title":"Django学习视频","type":"posts"},{"content":"","date":"July 12, 2018","externalUrl":null,"permalink":"/tags/rest/","section":"","summary":"","title":"Rest","type":"tags"},{"content":" nginx django 的配置步骤\n升级并安装 pip # $ sudo apt-get update $ sudo apt-get install python-pip 安装虚拟环境 # $ sudo pip install virtualenv virtualenvwrapper 加入到环境变量中 当前用户目录下的 Env为虚拟环境存放的目录\n$ echo \u0026#34;export WORKON_HOME=~/Env\u0026#34; \u0026gt;\u0026gt; ~/.bashrc $ echo \u0026#34;source /usr/local/bin/virtualenvwrapper.sh\u0026#34; \u0026gt;\u0026gt; \\~/.bashrc 激活配置 # $ source \\~/.bashrc 创建虚拟环境 # 环境名为:firstsite\n在用户当前目录下创建 Django工程\n$ cd ~ $ django-admin.py startproject firstsite django 的初始化 # $ cd \\~/firstsite $ ./manage.py migrate $ ./manage.py createsuperuser Django静态文件配置 # $ nano firstsite/settings.py STATIC_ROOT = os.path.join(BASE_DIR, \u0026#34;static/\u0026#34;) $ ./manage.py collectstatic 安装 uWSGI # $ sudo apt-get install python-dev $ sudo pip install uwsgi 测试是否能够正常访问 django 项目 # http 指定进程端口8080 home指定虚拟环境目录 chdir指定 项目工程所在目录 uwsgi --http :8080 --home /home/pi/Env/firstsite --chdir /home/pi/firstsite -w firstsite.wsgi 创建 uwsgi 的配置文件 # $ sudo mkdir -p /etc/uwsgi/sites $ cd /etc/uwsgi/sites uwsgi 的配置相关内容(重点) # [uwsgi] project = firstsite base = /home/pi chdir = %(base)/%(project) home = %(base)/Env/%(project) module = %(project).wsgi:application master = true processes = 5 socket = %(base)/%(project)/%(project).sock chmod-socket = 666 vacuum = true 启动守护进程 $ vim /etc/systemd/system/uwsgi.service [Unit] Description=uWSGI Emperor service After=syslog.target [Service] ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites Restart=always KillSignal=SIGQUIT Type=notify StandardError=syslog NotifyAccess=all [Install] WantedBy=multi-user.target 重载守护进程的配置信息 $ sudo systemctl daemon-reload 保证系统重启后uwsgi守护进程自动重启 $ sudo systemctl enable uwsgi 启动uwsgi进程 $ sudo systemctl start uwsgi nginx 的安装 $ sudo apt-get install nginx nginx的配置 $ sudo nano /etc/nginx/sites-available/firstsite server { listen 80; server_name firstsite.com www.firstsite.com; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/pi/firstsite; } location / { include uwsgi_params; uwsgi_pass unix:/home/pi/firstsite/firstsite.sock; } } nginx配置生效 $ sudo ln -s /etc/nginx/sites-available/firstsite /etc/nginx/sites-enabled 检查配置 $ sudo service nginx configtest 重启nginx $ sudo service nginx restart 非 debug 模式\n非 debug 模式需要重启uwsgi 服务 $ sudo systemctl restart uwsgi ","date":"December 25, 2017","externalUrl":null,"permalink":"/posts/nginx-django/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003enginx django 的配置步骤\u003c/p\u003e\n\u003c/blockquote\u003e","title":"nginx django 的配置步骤","type":"posts"},{"content":" Django使用基础常用命令\n查看django命令：django-admin即可显示django的可用命令 创建项目命令：django-admin startproject [项目名称]若没有报错则创建项目成功 查看manage.py常用命令：python manage.py即可显示manage中的可用命令 启动项目服务器命令：python manage.py runserver启动服务器 启动项目服务器并修改端口：python manage.py runserver 9999使用9999端口来启动服务器 创建django应用需要进入与manage.py的同级目录进行创建 创建命令为：python manage.py startapp [项目名称] 创建完成后将应用名称添加到settings.py中的INSTALLED_APPS中完成应用的创建 models模型创建完毕后使用manage.py来生成数据表，models模型创建需要在应用目录中的models.py中进行创建 创建数据迁移命令：python manage.py makemigrations [应用名称]进行数据迁移的准备步骤，输入以下命令完成数据迁移操作 开始数据迁移命令：python manage.py migrate 注：如果不输入应用名称则默认对django中的所有应用进行数据迁移操作\n数据迁移完成后使用以下命令进行SQL语句查询\n命令为：python manage.py sqlmigrate [应用名称] [文件id] 给django创建一个超级用户\n命令为：python manage.py createsuperuser 浏览器中访问：localhost:8000/admin进行访问admin页面\n在应用下admin.py中引入自身的models模块，使用编辑admin.py添加代码：admin.site.register(models.Article) 将admin管理后台进行配置升级，创建admin配置类class ArticleAdmin(admin.ModelAdmin) 注册：admin.site.register(Article,ArticleAdmin ) ","date":"November 20, 2017","externalUrl":null,"permalink":"/posts/djangoml/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eDjango使用基础常用命令\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Django使用基础常用命令","type":"posts"},{"content":" 两种网易云音乐播放器调用代码\n第一种： # 效果： # \u0026lt;span data-mce-type=\"bookmark\" style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" class=\"mce_SELRES_start\"\u0026gt;\u0026lt;/span\u0026gt; 代码： # \u0026lt;iframe src=\u0026#34;//music.163.com/outchain/player?type=2\u0026amp;amp;id=4341314\u0026amp;amp;auto=1\u0026amp;amp;height=66\u0026#34; width=\u0026#34;100%\u0026#34; height=\u0026#34;86\u0026#34; frameborder=\u0026#34;no\u0026#34; marginwidth=\u0026#34;0\u0026#34; marginheight=\u0026#34;0\u0026#34;\u0026gt;\u0026amp;lt;span data-mce-type=\u0026#34;bookmark\u0026#34; style=\u0026#34;display: inline-block; width: 0px; overflow: hidden; line-height: 0;\u0026#34; class=\u0026#34;mce_SELRES_start\u0026#34;\u0026amp;gt;\u0026amp;lt;/span\u0026amp;gt;\u0026lt;/iframe\u0026gt; 参数说明： # 播放器可修改参数：\nwidth=100% #自适应宽度 height=86 #根据自己喜好修改 id=4341314 #为歌曲的ID http://music.163.com/#/song?id=4341314 auto=0 #0为不自动播放，1为自动播放\n第二种： # 效果： # 代码： # \u0026lt;iframe frameborder=\u0026#34;no\u0026#34; border=\u0026#34;0\u0026#34; marginwidth=\u0026#34;0\u0026#34; marginheight=\u0026#34;0\u0026#34; width=100% height=450 src=\u0026#34;//music.163.com/outchain/player?type=0\u0026amp;id=967047669\u0026amp;auto=0\u0026amp;height=430\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; ","date":"November 18, 2017","externalUrl":null,"permalink":"/posts/wangyi/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e两种网易云音乐播放器调用代码\u003c/p\u003e\n\u003c/blockquote\u003e","title":"两种网易云音乐播放器调用代码 网站可直接调用","type":"posts"},{"content":"","date":"November 18, 2017","externalUrl":null,"permalink":"/tags/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90/","section":"","summary":"","title":"网易云音乐","type":"tags"},{"content":"","date":"November 18, 2017","externalUrl":null,"permalink":"/tags/%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8/","section":"","summary":"","title":"音乐播放器","type":"tags"},{"content":" 关于Python的面试题以及答案\nPython语言特性 # 1 Python的函数参数传递 # 看两个例子:\na = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun(a) print a # [1] 所有的变量都可以理解是内存中一个对象的“引用”，或者，也可以看似c中void*的感觉。\n通过id来看引用a的内存地址可以比较理解：\na = 1 def fun(a): print \u0026#34;func_in\u0026#34;,id(a) # func_in 41322472 a = 2 print \u0026#34;re-point\u0026#34;,id(a), id(2) # re-point 41322448 41322448 print \u0026#34;func_out\u0026#34;,id(a), id(1) # func_out 41322472 41322472 fun(a) print a # 1 注：具体的值在不同电脑上运行时可能不同。\n可以看到，在执行完a = 2之后，a引用中保存的值，即内存地址发生变化，由原来1对象的所在的地址变成了2这个实体对象的内存地址。\n而第2个例子a引用保存的内存值就不会发生变化：\na = [] def fun(a): print \u0026#34;func_in\u0026#34;,id(a) # func_in 53629256 a.append(1) print \u0026#34;func_out\u0026#34;,id(a) # func_out 53629256 fun(a) print a # [1] 这里记住的是类型是属于对象的，而不是变量。而对象有两种,“可更改”（mutable）与“不可更改”（immutable）对象。在python中，strings, tuples, 和numbers是不可更改的对象，而 list, dict, set 等则是可以修改的对象。(这就是这个问题的重点)\n当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.\n如果还不明白的话,这里有更好的解释: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference\n2 Python中的元类(metaclass) # 这个非常的不常用,但是像ORM这种复杂的结构还是会需要的,详情请看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python\n3 @staticmethod和@classmethod # Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下:\ndef foo(x): print \u0026#34;executing foo(%s)\u0026#34;%(x) class A(object): def foo(self,x): print \u0026#34;executing foo(%s,%s)\u0026#34;%(self,x) @classmethod def class_foo(cls,x): print \u0026#34;executing class_foo(%s,%s)\u0026#34;%(cls,x) @staticmethod def static_foo(x): print \u0026#34;executing static_foo(%s)\u0026#34;%x a=A() 这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x)).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好.\n对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.\n\\ 实例方法 类方法 静态方法 a = A() a.foo(x) a.class_foo(x) a.static_foo(x) A 不可用 A.class_foo(x) A.static_foo(x) 更多关于这个问题:\nhttp://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python https://realpython.com/blog/python/instance-class-and-static-methods-demystified/ 4 类变量和实例变量 # 类变量：\n​\t是可在类的所有实例之间共享的值（也就是说，它们不是单独分配给每个实例的）。例如下例中，num_of_instance 就是类变量，用于跟踪存在着多少个Test 的实例。\n实例变量：\n实例化之后，每个实例单独拥有的变量。\nclass Test(object): num_of_instance = 0 def __init__(self, name): self.name = name Test.num_of_instance += 1 if __name__ == \u0026#39;__main__\u0026#39;: print Test.num_of_instance # 0 t1 = Test(\u0026#39;jack\u0026#39;) print Test.num_of_instance # 1 t2 = Test(\u0026#39;lucy\u0026#39;) print t1.name , t1.num_of_instance # jack 2 print t2.name , t2.num_of_instance # lucy 2 补充的例子\nclass Person: name=\u0026#34;aaa\u0026#34; p1=Person() p2=Person() p1.name=\u0026#34;bbb\u0026#34; print p1.name # bbb print p2.name # aaa print Person.name # aaa 这里p1.name=\u0026quot;bbb\u0026quot;是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,p1.name一开始是指向的类变量name=\u0026quot;aaa\u0026quot;,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了.\n可以看看下面的例子:\nclass Person: name=[] p1=Person() p2=Person() p1.name.append(1) print p1.name # [1] print p2.name # [1] print Person.name # [1] 参考:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block\n5 Python自省 # 这个也是python彪悍的特性.\n自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().\na = [1,2,3] b = {\u0026#39;a\u0026#39;:1,\u0026#39;b\u0026#39;:2,\u0026#39;c\u0026#39;:3} c = True print type(a),type(b),type(c) # \u0026lt;type \u0026#39;list\u0026#39;\u0026gt; \u0026lt;type \u0026#39;dict\u0026#39;\u0026gt; \u0026lt;type \u0026#39;bool\u0026#39;\u0026gt; print isinstance(a,list) # True 6 字典推导式 # 可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的:\nd = {key: value for (key, value) in iterable} 7 Python中单下划线和双下划线 # \u0026gt;\u0026gt;\u0026gt; class MyClass(): ... def __init__(self): ... self.__superprivate = \u0026#34;Hello\u0026#34; ... self._semiprivate = \u0026#34;, world!\u0026#34; ... \u0026gt;\u0026gt;\u0026gt; mc = MyClass() \u0026gt;\u0026gt;\u0026gt; print mc.__superprivate Traceback (most recent call last): File \u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; AttributeError: myClass instance has no attribute \u0026#39;__superprivate\u0026#39; \u0026gt;\u0026gt;\u0026gt; print mc._semiprivate , world! \u0026gt;\u0026gt;\u0026gt; print mc.__dict__ {\u0026#39;_MyClass__superprivate\u0026#39;: \u0026#39;Hello\u0026#39;, \u0026#39;_semiprivate\u0026#39;: \u0026#39;, world!\u0026#39;} __foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突，就是例如__init__(),__del__(),__call__()这些特殊方法\n_foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用from module import * 导入，其他方面和公有一样访问；\n__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名,它无法直接像公有成员一样随便访问,通过对象名._类名__xxx这样的方式可以访问.\n详情见:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python\n或者: http://www.zhihu.com/question/19754941\n8 字符串格式化:%和.format # .format在许多方面看起来更便利.对于%最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题:\n\u0026#34;hi there %s\u0026#34; % name 但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:\n\u0026#34;hi there %s\u0026#34; % (name,) # 提供一个单元素的数组而不是一个参数 但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了.\n你为什么不用它?\n不知道它(在读这个之前) 为了和Python2.5兼容(譬如logging库建议使用%( issue #4)) http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format\n9 迭代器和生成器 # 这个是stackoverflow里python排名第一的问题,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python\n这是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html\n这里有个关于生成器的创建问题面试官有考： 问： 将列表生成式中[]改成() 之后数据结构是否改变？ 答案：是，从列表变为生成器\n\u0026gt;\u0026gt;\u0026gt; L = [x*x for x in range(10)] \u0026gt;\u0026gt;\u0026gt; L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] \u0026gt;\u0026gt;\u0026gt; g = (x*x for x in range(10)) \u0026gt;\u0026gt;\u0026gt; g \u0026lt;generator object \u0026lt;genexpr\u0026gt; at 0x0000028F8B774200\u0026gt; 通过列表生成式，可以直接创建一个列表。但是，受到内存限制，列表容量肯定是有限的。而且，创建一个包含百万元素的列表，不仅是占用很大的内存空间，如：我们只需要访问前面的几个元素，后面大部分元素所占的空间都是浪费的。因此，没有必要创建完整的列表（节省大量内存空间）。在Python中，我们可以采用生成器：边循环，边计算的机制—\u0026gt;generator\n10 *args and **kwargs # 用*args和**kwargs只是为了方便并没有强制使用它们.\n当你不确定你的函数里将要传递多少参数时你可以用*args.例如,它可以传递任意数量的参数:\n\u0026gt;\u0026gt;\u0026gt; def print_everything(*args): for count, thing in enumerate(args): ... print \u0026#39;{0}. {1}\u0026#39;.format(count, thing) ... \u0026gt;\u0026gt;\u0026gt; print_everything(\u0026#39;apple\u0026#39;, \u0026#39;banana\u0026#39;, \u0026#39;cabbage\u0026#39;) 0. apple 1. banana 2. cabbage 相似的,**kwargs允许你使用没有事先定义的参数名:\n\u0026gt;\u0026gt;\u0026gt; def table_things(**kwargs): ... for name, value in kwargs.items(): ... print \u0026#39;{0} = {1}\u0026#39;.format(name, value) ... \u0026gt;\u0026gt;\u0026gt; table_things(apple = \u0026#39;fruit\u0026#39;, cabbage = \u0026#39;vegetable\u0026#39;) cabbage = vegetable apple = fruit 你也可以混着用.命名参数首先获得参数值然后所有的其他参数都传递给*args和**kwargs.命名参数在列表的最前端.例如:\ndef table_things(titlestring, **kwargs) *args和**kwargs可以同时在函数的定义中,但是*args必须在**kwargs前面.\n当调用函数时你也可以用*和**语法.例如:\n\u0026gt;\u0026gt;\u0026gt; def print_three_things(a, b, c): ... print \u0026#39;a = {0}, b = {1}, c = {2}\u0026#39;.format(a,b,c) ... \u0026gt;\u0026gt;\u0026gt; mylist = [\u0026#39;aardvark\u0026#39;, \u0026#39;baboon\u0026#39;, \u0026#39;cat\u0026#39;] \u0026gt;\u0026gt;\u0026gt; print_three_things(*mylist) a = aardvark, b = baboon, c = cat 就像你看到的一样,它可以传递列表(或者元组)的每一项并把它们解包.注意必须与它们在函数里的参数相吻合.当然,你也可以在函数定义或者函数调用时用*.\nhttp://stackoverflow.com/questions/3394835/args-and-kwargs\n11 面向切面编程AOP和装饰器 # 这个AOP一听起来有点懵,同学面阿里的时候就被问懵了\u0026hellip;\n装饰器是一个很著名的设计模式，经常被用于有切面需求的场景，较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计，有了装饰器，我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲，装饰器的作用就是为已经存在的对象添加额外的功能。\n这个问题比较大,推荐: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python\n中文: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/3/README.html\n12 鸭子类型 # “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子，那么这只鸟就可以被称为鸭子。”\n我们并不关心对象是什么类型，到底是不是鸭子，只关心行为。\n比如在python中，有很多file-like的东西，比如StringIO,GzipFile,socket。它们有很多相同的方法，我们把它们当作文件使用。\n又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等.\n鸭子类型在动态语言中经常使用，非常灵活，使得python不想java那样专门去弄一大堆的设计模式。\n13 Python中重载 # 引自知乎:http://www.zhihu.com/question/20053359\n函数重载主要是为了解决两个问题。\n可变参数类型。 可变参数个数。 另外，一个基本的设计原则是，仅仅当两个函数除了参数类型和参数个数不同以外，其功能是完全相同的，此时才使用函数重载，如果两个函数的功能其实不同，那么不应当使用重载，而应当使用一个名字不同的函数。\n好吧，那么对于情况 1 ，函数功能相同，但是参数类型不同，python 如何处理？答案是根本不需要处理，因为 python 可以接受任何类型的参数，如果函数的功能相同，那么不同的参数类型在 python 中很可能是相同的代码，没有必要做成两个不同函数。\n那么对于情况 2 ，函数功能相同，但参数个数不同，python 如何处理？大家知道，答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同，那么那些缺少的参数终归是需要用的。\n好了，鉴于情况 1 跟 情况 2 都有了解决方案，python 自然就不需要函数重载了。\n14 新式类和旧式类 # 这个面试官问了,我说了老半天,不知道他问的真正意图是什么.\nstackoverflow\n这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html\n新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类是广度优先,旧式类是深度优先),\u0026lt;Python核心编程\u0026gt;里讲的也很多.\n一个旧式类的深度优先的例子\nclass A(): def foo1(self): print \u0026#34;A\u0026#34; class B(A): def foo2(self): pass class C(A): def foo1(self): print \u0026#34;C\u0026#34; class D(B, C): pass d = D() d.foo1() # A 按照经典类的查找顺序从左到右深度优先的规则，在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1()，从而导致C重写的foo1()被绕过\n15 __new__和__init__的区别 # 这个__new__确实很少见到,先做了解吧.\n__new__是一个静态方法,而__init__是一个实例方法. __new__方法会返回一个创建的实例,而__init__什么都不返回. 只有在__new__返回一个cls的实例时后面的__init__才能被调用. 当创建一个新实例时调用__new__,初始化一个实例时用__init__. stackoverflow\nps: __metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new__和__init__来分别在类创建,实例创建和实例初始化的时候做一些小手脚.\n16 单例模式 # ​\t单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问，从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个，单例模式是最好的解决方案。\n__new__()在__init__()之前被调用，用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象，单例模式设计的类只能实例 这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.\n1 使用__new__方法 # class Singleton(object): def __new__(cls, *args, **kw): if not hasattr(cls, \u0026#39;_instance\u0026#39;): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance class MyClass(Singleton): a = 1 2 共享属性 # 创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.\nclass Borg(object): _state = {} def __new__(cls, *args, **kw): ob = super(Borg, cls).__new__(cls, *args, **kw) ob.__dict__ = cls._state return ob class MyClass2(Borg): a = 1 3 装饰器版本 # def singleton(cls): instances = {} def getinstance(*args, **kw): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return getinstance @singleton class MyClass: ... 4 import方法 # 作为python的模块是天然的单例模式\n# mysingleton.py class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() # to use from mysingleton import my_singleton my_singleton.foo() 单例模式伯乐在线详细解释\n17 Python中的作用域 # Python 中，一个变量的作用域总是由在代码中被赋值的地方所决定的。\n当 Python 遇到一个变量的话他会按照这样的顺序进行搜索：\n本地作用域（Local）→当前作用域被嵌入的本地作用域（Enclosing locals）→全局/模块作用域（Global）→内置作用域（Built-in）\n18 GIL线程全局锁 # 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务，python的多线程起到作用，但对于cpu密集型任务，python的多线程几乎占不到任何优势，还有可能因为争夺资源而变慢。\n见 Python 最难的问题\n解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).\n19 协程 # 知乎被问到了,呵呵哒,跪了\n简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态.\nPython里最常见的yield就是协程的思想!可以查看第九个问题.\n20 闭包 # 闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构，它同样提高了代码的可重复使用性。\n当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:\n必须有一个内嵌函数 内嵌函数必须引用外部函数中的变量 外部函数的返回值必须是内嵌函数 感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料.\n重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上.\n闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样.\n21 lambda函数 # 其实就是一个匿名函数,为什么叫lambda?因为和后面的函数式编程有关.\n推荐: 知乎\n22 Python函数式编程 # 这个需要适当的了解一下吧,毕竟函数式编程在Python中也做了引用.\n推荐: 酷壳\npython中函数式编程支持:\nfilter 函数的功能相当于过滤器。调用一个布尔函数bool_func来迭代遍历每个seq中的元素；返回一个使bool_seq返回值为true的元素的序列。\n\u0026gt;\u0026gt;\u0026gt;a = [1,2,3,4,5,6,7] \u0026gt;\u0026gt;\u0026gt;b = filter(lambda x: x \u0026gt; 5, a) \u0026gt;\u0026gt;\u0026gt;print b \u0026gt;\u0026gt;\u0026gt;[6,7] map函数是对一个序列的每个项依次执行函数，下面是对一个序列每个项都乘以2：\n\u0026gt;\u0026gt;\u0026gt; a = map(lambda x:x*2,[1,2,3]) \u0026gt;\u0026gt;\u0026gt; list(a) [2, 4, 6] reduce函数是对一个序列的每个项迭代调用函数，下面是求3的阶乘：\n\u0026gt;\u0026gt;\u0026gt; reduce(lambda x,y:x*y,range(1,4)) 6 23 Python里的拷贝 # 引用和copy(),deepcopy()的区别\nimport copy a = [1, 2, 3, 4, [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;]] #原始对象 b = a #赋值，传对象的引用 c = copy.copy(a) #对象拷贝，浅拷贝 d = copy.deepcopy(a) #对象拷贝，深拷贝 a.append(5) #修改对象a a[4].append(\u0026#39;c\u0026#39;) #修改对象a中的[\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;]数组对象 print \u0026#39;a = \u0026#39;, a print \u0026#39;b = \u0026#39;, b print \u0026#39;c = \u0026#39;, c print \u0026#39;d = \u0026#39;, d 输出结果： a = [1, 2, 3, 4, [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;], 5] b = [1, 2, 3, 4, [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;], 5] c = [1, 2, 3, 4, [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;]] d = [1, 2, 3, 4, [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;]] 24 Python垃圾回收机制 # Python GC主要使用引用计数（reference counting）来跟踪和回收垃圾。在引用计数的基础上，通过“标记-清除”（mark and sweep）解决容器对象可能产生的循环引用问题，通过“分代回收”（generation collection）以空间换时间的方法提高垃圾回收效率。\n1 引用计数 # PyObject是每个对象必有的内容，其中ob_refcnt就是做为引用计数。当一个对象有新的引用时，它的ob_refcnt就会增加，当引用它的对象被删除，它的ob_refcnt就会减少.引用计数为0时，该对象生命就结束了。\n优点:\n简单 实时性 缺点:\n维护引用计数消耗资源 循环引用 2 标记-清除机制 # 基本思路是先按需分配，等到没有空闲内存的时候从寄存器和程序栈上的引用出发，遍历以对象为节点、以引用为边构成的图，把所有可以访问到的对象打上标记，然后清扫一遍内存空间，把所有没标记的对象释放。\n3 分代技术 # 分代回收的整体思想是：将系统中的所有内存块根据其存活时间划分为不同的集合，每个集合就成为一个“代”，垃圾收集频率随着“代”的存活时间的增大而减小，存活时间通常利用经过几次垃圾回收来度量。\nPython默认定义了三代对象集合，索引数越大，对象存活时间越长。\n举例： 当某些内存块M经过了3次垃圾收集的清洗之后还存活时，我们就将内存块M划到一个集合A中去，而新分配的内存都划分到集合B中去。当垃圾收集开始工作时，大多数情况都只对集合B进行垃圾回收，而对集合A进行垃圾回收要隔相当长一段时间后才进行，这就使得垃圾收集机制需要处理的内存少了，效率自然就提高了。在这个过程中，集合B中的某些内存块由于存活时间长而会被转移到集合A中，当然，集合A中实际上也存在一些垃圾，这些垃圾的回收会因为这种分代的机制而被延迟。\n25 Python的List # 推荐: http://www.jianshu.com/p/J4U6rR\n26 Python的is # is是对比地址,==是对比值\n27 read,readline和readlines # read 读取整个文件 readline 读取下一行,使用生成器方法 readlines 读取整个文件到一个迭代器以供我们遍历 28 Python2和3的区别 # 推荐： Python 2.7.x 与 Python 3.x 的主要差异\n29 super init # super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven\u0026rsquo;t already.\nNote that the syntax changed in Python 3.0: you can just say super().__init__() instead of super(ChildB, self).__init__() which IMO is quite a bit nicer.\nhttp://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods\nPython2.7中的super方法浅见\n30 range and xrange # 都在循环时使用，xrange内存性能更好。 for i in range(0, 20): for i in xrange(0, 20): What is the difference between range and xrange functions in Python 2.X? range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements. xrange is a sequence object that evaluates lazily.\nhttp://stackoverflow.com/questions/94935/what-is-the-difference-between-range-and-xrange-functions-in-python-2-x\n操作系统 # 1 select,poll和epoll # 其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了.\n这个问题可能有点深入了,但相信能回答出这个问题是对I/O多路复用有很好的了解了.其中tornado使用的就是epoll的.\nselec,poll和epoll区别总结\n基本上select有3个缺点:\n连接数受限 查找配对速度慢 数据由内核拷贝到用户态 poll改善了第一个缺点\nepoll改了三个缺点.\n关于epoll的: http://www.cnblogs.com/my_life/articles/3968782.html\n2 调度算法 # 先来先服务(FCFS, First Come First Serve) 短作业优先(SJF, Shortest Job First) 最高优先权调度(Priority Scheduling) 时间片轮转(RR, Round Robin) 多级反馈队列调度(multilevel feedback queue scheduling) 常见的调度算法总结:http://www.jianshu.com/p/6edf8174c1eb\n实时调度算法:\n最早截至时间优先 EDF 最低松弛度优先 LLF 3 死锁 # 原因:\n竞争资源 程序推进顺序不当 必要条件:\n互斥条件 请求和保持条件 不剥夺条件 环路等待条件 处理死锁基本方法:\n预防死锁(摒弃除1以外的条件) 避免死锁(银行家算法) 检测死锁(资源分配图) 解除死锁 剥夺资源 撤销进程 死锁概念处理策略详细介绍:https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/10.html\n4 程序编译与链接 # 推荐: http://www.ruanyifeng.com/blog/2014/11/compiler.html\nBulid过程可以分解为4个步骤:预处理(Prepressing), 编译(Compilation)、汇编(Assembly)、链接(Linking)\n以c语言为例:\n1 预处理 # 预编译过程主要处理那些源文件中的以“#”开始的预编译指令，主要处理规则有：\n将所有的“#define”删除，并展开所用的宏定义 处理所有条件预编译指令，比如“#if”、“#ifdef”、 “#elif”、“#endif” 处理“#include”预编译指令，将被包含的文件插入到该编译指令的位置，注：此过程是递归进行的 删除所有注释 添加行号和文件名标识，以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号 保留所有的#pragma编译器指令。 2 编译 # 编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。\n3 汇编 # 汇编器是将汇编代码转化成机器可以执行的指令，每一条汇编语句几乎都是一条机器指令。经过编译、链接、汇编输出的文件成为目标文件(Object File)\n4 链接 # 链接的主要内容就是把各个模块之间相互引用的部分处理好，使各个模块可以正确的拼接。 链接的主要过程包块 地址和空间的分配（Address and Storage Allocation）、符号决议(Symbol Resolution)和重定位(Relocation)等步骤。\n5 静态链接和动态链接 # 静态链接方法：静态链接的时候，载入代码就会把程序会用到的动态代码或动态代码的地址确定下来 静态库的链接可以使用静态链接，动态链接库也可以使用这种方法链接导入库\n动态链接方法：使用这种方式的程序并不在一开始就完成动态链接，而是直到真正调用动态库代码时，载入程序才计算(被调用的那部分)动态代码的逻辑地址，然后等到某个时候，程序又需要调用另外某块动态代码时，载入程序又去计算这部分代码的逻辑地址，所以，这种方式使程序初始化时间较短，但运行期间的性能比不上静态链接的程序\n6 虚拟内存技术 # 虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储系统.\n7 分页和分段 # 分页: 用户程序的地址空间被划分成若干固定大小的区域，称为“页”，相应地，内存空间分成若干个物理块，页和块的大小相等。可将用户程序的任一页放在内存的任一块中，实现了离散分配。\n分段: 将用户程序地址空间分成若干个大小不等的段，每段可以定义一组相对完整的逻辑信息。存储分配时，以段为单位，段与段在内存中可以不相邻接，也实现了离散分配。\n分页与分段的主要区别 # 页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要. 页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分. 分页的作业地址空间是一维的.分段的地址空间是二维的. 8 页面置换算法 # 最佳置换算法OPT:不可能实现 先进先出FIFO 最近最久未使用算法LRU:最近一段时间里最久没有使用过的页面予以置换. clock算法 9 边沿触发和水平触发 # 边缘触发是指每当状态变化时发生一个 io 事件，条件触发是只要满足条件就发生一个 io 事件\n数据库 # 1 事务 # 数据库事务(Database Transaction) ，是指作为单个逻辑工作单元执行的一系列操作，要么完全地执行，要么完全地不执行。 彻底理解数据库事务: http://www.hollischuang.com/archives/898\n2 数据库索引 # 推荐: http://tech.meituan.com/mysql-index.html\nMySQL索引背后的数据结构及算法原理\n聚集索引,非聚集索引,B-Tree,B+Tree,最左前缀原理\n3 Redis原理 # Redis是什么？ # 是一个完全开源免费的key-value内存数据库 通常被认为是一个数据结构服务器，主要是因为其有着丰富的数据结构 strings、map、 list、sets、 sorted sets Redis数据库 # ​\t通常局限点来说，Redis也以消息队列的形式存在，作为内嵌的List存在，满足实时的高并发需求。在使用缓存的时候，redis比memcached具有更多的优势，并且支持更多的数据类型，把redis当作一个中间存储系统，用来处理高并发的数据库操作\n速度快：使用标准C写，所有数据都在内存中完成，读写速度分别达到10万/20万 持久化：对数据的更新采用Copy-on-write技术，可以异步地保存到磁盘上，主要有两种策略，一是根据时间，更新次数的快照（save 300 10 ）二是基于语句追加方式(Append-only file，aof) 自动操作：对不同数据类型的操作都是自动的，很安全 快速的主\u0026ndash;从复制，官方提供了一个数据，Slave在21秒即完成了对Amazon网站10G key set的复制。 Sharding技术： 很容易将数据分布到多个Redis实例中，数据库的扩展是个永恒的话题，在关系型数据库中，主要是以添加硬件、以分区为主要技术形式的纵向扩展解决了很多的应用场景，但随着web2.0、移动互联网、云计算等应用的兴起，这种扩展模式已经不太适合了，所以近年来，像采用主从配置、数据库复制形式的，Sharding这种技术把负载分布到多个特理节点上去的横向扩展方式用处越来越多。 Redis缺点 # 是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。 Redis较难支持在线扩容，在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题，运维人员在系统上线时必须确保有足够的空间，这对资源造成了很大的浪费。 4 乐观锁和悲观锁 # 悲观锁：假定会发生并发冲突，屏蔽一切可能违反数据完整性的操作\n乐观锁：假设不会发生并发冲突，只在提交操作时检查是否违反数据完整性。\n乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html\n5 MVCC # ​\t全称是Multi-Version Concurrent Control，即多版本并发控制，在MVCC协议下，每个读操作会看到一个一致性的snapshot，并且可以实现非阻塞的读。MVCC允许数据具有多个版本，这个版本可以是时间戳或者是全局递增的事务ID，在同一个时间点，不同的事务看到的数据是不同的。\nMySQL的innodb引擎是如何实现MVCC的 # innodb会为每一行添加两个字段，分别表示该行创建的版本和删除的版本，填入的是事务的版本号，这个版本号随着事务的创建不断递增。在repeated read的隔离级别（ 事务的隔离级别请看这篇文章）下，具体各种数据库操作的实现：\nselect：满足以下两个条件innodb会返回该行数据： 该行的创建版本号小于等于当前版本号，用于保证在select操作之前所有的操作已经执行落地。 该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。 insert：将新插入的行的创建版本号设置为当前系统的版本号。 delete：将要删除的行的删除版本号设置为当前系统的版本号。 update：不执行原地update，而是转换成insert + delete。将旧行的删除版本号设置为当前版本号，并将新行insert同时设置创建版本号为当前版本号。 其中，写操作（insert、delete和update）执行时，需要将系统版本号递增。\n​\t由于旧数据并不真正的删除，所以必须对这些数据进行清理，innodb会开启一个后台线程执行清理工作，具体的规则是将删除版本号小于当前系统版本的行删除，这个过程叫做purge。\n通过MVCC很好的实现了事务的隔离性，可以达到repeated read级别，要实现serializable还必须加锁。\n参考： MVCC浅析\n6 MyISAM和InnoDB # MyISAM 适合于一些需要大量查询的应用，但其对于有大量写操作并不是很好。甚至你只是需要update一个字段，整个表都会被锁起来，而别的进程，就算是读进程都无法操作直到读操作完成。另外，MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。\nInnoDB 的趋势会是一个非常复杂的存储引擎，对于一些小的应用，它会比 MyISAM 还慢。他是它支持“行锁” ，于是在写操作比较多的时候，会更优秀。并且，他还支持更多的高级应用，比如：事务。\nmysql 数据库引擎: http://www.cnblogs.com/0201zcr/p/5296843.html MySQL存储引擎－－MyISAM与InnoDB区别: https://segmentfault.com/a/1190000008227211\n网络 # 1 三次握手 # 客户端通过向服务器端发送一个SYN来创建一个主动打开，作为三次握手的一部分。客户端把这段连接的序号设定为随机数 A。 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK 的确认码应为 A+1，SYN/ACK 包本身又有一个随机序号 B。 最后，客户端再发送一个ACK。当服务端受到这个ACK的时候，就完成了三路握手，并进入了连接创建状态。此时包序号被设定为收到的确认号 A+1，而响应则为 B+1。 2 四次挥手 # 注意: 中断连接端可以是客户端，也可以是服务器端. 下面仅以客户端断开连接举例, 反之亦然.\n客户端发送一个数据分段, 其中的 FIN 标记设置为1. 客户端进入 FIN-WAIT 状态. 该状态下客户端只接收数据, 不再发送数据. 服务器接收到带有 FIN = 1 的数据分段, 发送带有 ACK = 1 的剩余数据分段, 确认收到客户端发来的 FIN 信息. 服务器等到所有数据传输结束, 向客户端发送一个带有 FIN = 1 的数据分段, 并进入 CLOSE-WAIT 状态, 等待客户端发来带有 ACK = 1 的确认报文. 客户端收到服务器发来带有 FIN = 1 的报文, 返回 ACK = 1 的报文确认, 为了防止服务器端未收到需要重发, 进入 TIME-WAIT 状态. 服务器接收到报文后关闭连接. 客户端等待 2MSL 后未收到回复, 则认为服务器成功关闭, 客户端关闭连接. 图解: http://blog.csdn.net/whuslei/article/details/6667471\n3 ARP协议 # 地址解析协议(Address Resolution Protocol)，其基本功能为透过目标设备的IP地址，查询目标的MAC地址，以保证通信的顺利进行。它是IPv4网络层必不可少的协议，不过在IPv6中已不再适用，并被邻居发现协议（NDP）所替代。\n4 urllib和urllib2的区别 # 这个面试官确实问过,当时答的urllib2可以Post而urllib不可以.\nurllib提供urlencode方法用来GET查询字符串的产生，而urllib2没有。这是为何urllib常和urllib2一起使用的原因。 urllib2可以接受一个Request类的实例来设置URL请求的headers，urllib仅可以接受URL。这意味着，你不可以伪装你的User Agent字符串等。 5 Post和Get # GET和POST有什么区别？及为什么网上的多数答案都是错的 知乎回答\nget: RFC 2616 - Hypertext Transfer Protocol \u0026ndash; HTTP/1.1 post: RFC 2616 - Hypertext Transfer Protocol \u0026ndash; HTTP/1.1\n6 Cookie和Session # Cookie Session 储存位置 客户端 服务器端 目的 跟踪会话，也可以保存用户偏好设置或者保存用户名密码等 跟踪会话 安全性 不安全 安全 session技术是要使用到cookie的，之所以出现session技术，主要是为了安全。\n7 apache和nginx的区别 # nginx 相对 apache 的优点：\n轻量级，同样起web 服务，比apache 占用更少的内存及资源 抗并发，nginx 处理请求是异步非阻塞的，支持更多的并发连接，而apache 则是阻塞型的，在高并发下nginx 能保持低资源低消耗高性能 配置简洁 高度模块化的设计，编写模块相对简单 社区活跃 apache 相对nginx 的优点：\nrewrite ，比nginx 的rewrite 强大 模块超多，基本想到的都可以找到 少bug ，nginx 的bug 相对较多 超稳定 8 网站用户密码保存 # 明文保存 明文hash后保存,如md5 MD5+Salt方式,这个salt可以随机 知乎使用了Bcrypy(好像)加密 9 HTTP和HTTPS # 状态码 定义 1xx 报告 接收到请求，继续进程 2xx 成功 步骤成功接收，被理解，并被接受 3xx 重定向 为了完成请求,必须采取进一步措施 4xx 客户端出错 请求包括错的顺序或不能完成 5xx 服务器出错 服务器无法完成显然有效的请求 403: Forbidden 404: Not Found\nHTTPS握手,对称加密,非对称加密,TLS/SSL,RSA\n10 XSRF和XSS # CSRF(Cross-site request forgery)跨站请求伪造 XSS(Cross Site Scripting)跨站脚本攻击 CSRF重点在请求,XSS重点在脚本\n11 幂等 Idempotence # HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。(注意是副作用)\nGET http://www.bank.com/account/123456，不会改变资源的状态，不论调用一次还是N次都没有副作用。请注意，这里强调的是一次和N次具有相同的副作用，而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果，但它本身并没有产生任何副作用，因而是满足幂等性的。\nDELETE方法用于删除资源，有副作用，但它应该满足幂等性。比如：DELETE http://www.forum.com/article/4231，调用一次和N次对系统产生的副作用是相同的，即删掉id为4231的帖子；因此，调用者可以多次调用或刷新页面而不必担心引起错误。\nPOST所对应的URI并非创建的资源本身，而是资源的接收者。比如：POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子，HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源，它们具有不同的URI；所以，POST方法不具备幂等性。\nPUT所对应的URI是要创建或更新的资源本身。比如：PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的；因此，PUT方法具有幂等性。\n12 RESTful架构(SOAP,RPC) # 推荐: http://www.ruanyifeng.com/blog/2011/09/restful.html\n13 SOAP # SOAP（原为Simple Object Access Protocol的首字母缩写，即简单对象访问协议）是交换数据的一种协议规范，使用在计算机网络Web服务（web service）中，交换带结构信息。SOAP为了简化网页服务器（Web Server）从XML数据库中提取数据时，节省去格式化页面时间，以及不同应用程序之间按照HTTP通信协议，遵从XML格式执行资料互换，使其抽象于语言实现、平台和硬件。\n14 RPC # RPC（Remote Procedure Call Protocol）——远程过程调用协议，它是一种通过网络从远程计算机程序上请求服务，而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在，如TCP或UDP，为通信程序之间携带信息数据。在OSI网络通信模型中，RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。\n总结:服务提供的两大流派.传统意义以方法调用为导向通称RPC。为了企业SOA,若干厂商联合推出webservice,制定了wsdl接口定义,传输soap.当互联网时代,臃肿SOA被简化为http+xml/json.但是简化出现各种混乱。以资源为导向,任何操作无非是对资源的增删改查，于是统一的REST出现了.\n进化的顺序: RPC -\u0026gt; SOAP -\u0026gt; RESTful\n15 CGI和WSGI # CGI是通用网关接口，是连接web服务器和应用程序的接口，用户通过CGI来获取动态数据或文件等。 CGI程序是一个独立的程序，它可以用几乎所有语言来写，包括perl，c，lua，python等等。\nWSGI, Web Server Gateway Interface，是Python应用程序或框架和Web服务器之间的一种接口，WSGI的其中一个目的就是让用户可以用统一的语言(Python)编写前后端。\n官方说明： PEP-3333\n16 中间人攻击 # 在GFW里屡见不鲜的,呵呵.\n中间人攻击（Man-in-the-middle attack，通常缩写为MITM）是指攻击者与通讯的两端分别创建独立的联系，并交换其所收到的数据，使通讯的两端认为他们正在通过一个私密的连接与对方直接对话，但事实上整个会话都被攻击者完全控制。\n17 c10k问题 # 所谓c10k问题，指的是服务器同时支持成千上万个客户端的问题，也就是concurrent 10 000 connection（这也是c10k这个名字的由来）。 推荐: https://my.oschina.net/xianggao/blog/664275\n18 socket # 推荐: http://www.360doc.com/content/11/0609/15/5482098_122692444.shtml\nSocket=Ip address+ TCP/UDP + port\n19 浏览器缓存 # 推荐: http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html\n304 Not Modified\n20 HTTP1.0和HTTP1.1 # 推荐: http://blog.csdn.net/elifefly/article/details/3964766\n请求头Host字段,一个服务器多个网站 长链接 文件断点续传 身份认证,状态管理,Cache缓存 HTTP请求8种方法介绍 HTTP/1.1协议中共定义了8种HTTP请求方法，HTTP请求方法也被叫做“请求动作”，不同的方法规定了不同的操作指定的资源方式。服务端也会根据不同的请求方法做不同的响应。\nGET\nGET请求会显示请求指定的资源。一般来说GET方法应该只用于数据的读取，而不应当用于会产生副作用的非幂等的操作中。\nGET会方法请求指定的页面信息，并返回响应主体，GET被认为是不安全的方法，因为GET方法会被网络蜘蛛等任意的访问。\nHEAD\nHEAD方法与GET方法一样，都是向服务器发出指定资源的请求。但是，服务器在响应HEAD请求时不会回传资源的内容部分，即：响应主体。这样，我们可以不传输全部内容的情况下，就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。\nPOST\nPOST请求会 向指定资源提交数据，请求服务器进行处理，如：表单数据提交、文件上传等，请求数据会被包含在请求体中。POST方法是非幂等的方法，因为这个请求可能会创建新的资源或/和修改现有资源。\nPUT\nPUT请求会身向指定资源位置上传其最新内容，PUT方法是幂等的方法。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。\nDELETE\nDELETE请求用于请求服务器删除所请求URI（统一资源标识符，Uniform Resource Identifier）所标识的资源。DELETE请求后指定资源会被删除，DELETE方法也是幂等的。\nCONNECT\nCONNECT方法是HTTP/1.1协议预留的，能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。\nOPTIONS\nOPTIONS请求与HEAD类似，一般也是用于客户端查看服务器的性能。 这个方法会请求服务器返回该资源所支持的所有HTTP请求方法，该方法会用’*’来代替资源名称，向服务器发送OPTIONS请求，可以测试服务器功能是否正常。JavaScript的XMLHttpRequest对象进行CORS跨域资源共享时，就是使用OPTIONS方法发送嗅探请求，以判断是否有对指定资源的访问权限。 允许\nTRACE\nTRACE请求服务器回显其收到的请求信息，该方法主要用于HTTP请求的测试或诊断。\nHTTP/1.1之后增加的方法\n在HTTP/1.1标准制定之后，又陆续扩展了一些方法。其中使用中较多的是 PATCH 方法：\nPATCH\nPATCH方法出现的较晚，它在2010年的RFC 5789标准中被定义。PATCH请求与PUT请求类似，同样用于资源的更新。二者有以下两点不同：\n但PATCH一般用于资源的部分更新，而PUT一般用于资源的整体更新。 当资源不存在时，PATCH会创建一个新的资源，而PUT只会对已在资源进行更新。\n21 Ajax # AJAX,Asynchronous JavaScript and XML（异步的 JavaScript 和 XML）, 是与在不重新加载整个页面的情况下，与服务器交换数据并更新部分网页的技术。\n*NIX # unix进程间通信方式(IPC) # 管道（Pipe）：管道可用于具有亲缘关系进程间的通信，允许一个进程和另一个与它有共同祖先的进程之间进行通信。 命名管道（named pipe）：命名管道克服了管道没有名字的限制，因此，除具有管道所具有的功能外，它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。 信号（Signal）：信号是比较复杂的通信方式，用于通知接受进程有某种事件发生，除了用于进程间通信外，进程还可以发送信号给进程本身；linux除了支持Unix早期信号语义函数sigal外，还支持语义符合Posix.1标准的信号函数sigaction（实际上，该函数是基于BSD的，BSD为了实现可靠信号机制，又能够统一对外接口，用sigaction函数重新实现了signal函数）。 消息（Message）队列：消息队列是消息的链接表，包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息，被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少，管道只能承载无格式字节流以及缓冲区大小受限等缺 共享内存：使得多个进程可以访问同一块内存空间，是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制，如信号量结合使用，来达到进程间的同步及互斥。 内存映射（mapped memory）：内存映射允许任何多个进程间通信，每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。 信号量（semaphore）：主要作为进程间以及同一进程不同线程之间的同步手段。 套接口（Socket）：更为一般的进程间通信机制，可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的，但现在一般可以移植到其它类Unix系统上：Linux和System V的变种都支持套接字。 数据结构 # 1 红黑树 # 红黑树与AVL的比较：\nAVL是严格平衡树，因此在增加或者删除节点的时候，根据不同情况，旋转的次数比红黑树要多；\n红黑是用非严格的平衡来换取增删节点时候旋转次数的降低；\n所以简单说，如果你的应用中，搜索的次数远远大于插入和删除，那么选择AVL，如果搜索，插入删除次数几乎差不多，应该选择RB。\n红黑树详解: https://xieguanglei.github.io/blog/post/red-black-tree.html\n教你透彻了解红黑树: https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/03.01.md\n编程题 # 1 台阶问题/斐波那契 # 一只青蛙一次可以跳上1级台阶，也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。\nfib = lambda n: n if n \u0026lt;= 2 else fib(n - 1) + fib(n - 2) 第二种记忆方法\ndef memo(func): cache = {} def wrap(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrap @memo def fib(i): if i \u0026lt; 2: return 1 return fib(i-1) + fib(i-2) 第三种方法\ndef fib(n): a, b = 0, 1 for _ in xrange(n): a, b = b, a + b return b 2 变态台阶问题 # 一只青蛙一次可以跳上1级台阶，也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。\nfib = lambda n: n if n \u0026lt; 2 else 2 * fib(n - 1) 3 矩形覆盖 # 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形，总共有多少种方法？\n第2*n个矩形的覆盖方法等于第2*(n-1)加上第2*(n-2)的方法。\nf = lambda n: 1 if n \u0026lt; 2 else f(n - 1) + f(n - 2) 4 杨氏矩阵查找 # 在一个m行n列二维数组中，每一行都按照从左到右递增的顺序排序，每一列都按照从上到下递增的顺序排序。请完成一个函数，输入这样的一个二维数组和一个整数，判断数组中是否含有该整数。\n使用Step-wise线性搜索。\ndef get_value(l, r, c): return l[r][c] def find(l, x): m = len(l) - 1 n = len(l[0]) - 1 r = 0 c = n while c \u0026gt;= 0 and r \u0026lt;= m: value = get_value(l, r, c) if value == x: return True elif value \u0026gt; x: c = c - 1 elif value \u0026lt; x: r = r + 1 return False 5 去除列表中的重复元素 # 用集合\nlist(set(l)) 用字典\nl1 = [\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;a\u0026#39;] l2 = {}.fromkeys(l1).keys() print l2 用字典并保持顺序\nl1 = [\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;a\u0026#39;] l2 = list(set(l1)) l2.sort(key=l1.index) print l2 列表推导式\nl1 = [\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;a\u0026#39;] l2 = [] [l2.append(i) for i in l1 if not i in l2] sorted排序并且用列表推导式.\nl = [\u0026lsquo;b\u0026rsquo;,\u0026lsquo;c\u0026rsquo;,\u0026rsquo;d\u0026rsquo;,\u0026lsquo;b\u0026rsquo;,\u0026lsquo;c\u0026rsquo;,\u0026lsquo;a\u0026rsquo;,\u0026lsquo;a\u0026rsquo;] [single.append(i) for i in sorted(l) if i not in single] print single\n6 链表成对调换 # 1-\u0026gt;2-\u0026gt;3-\u0026gt;4转换成2-\u0026gt;1-\u0026gt;4-\u0026gt;3.\nclass ListNode: def __init__(self, x): self.val = x self.next = None class Solution: # @param a ListNode # @return a ListNode def swapPairs(self, head): if head != None and head.next != None: next = head.next head.next = self.swapPairs(next.next) next.next = head return next return head 7 创建字典的方法 # 1 直接创建 # dict = {\u0026#39;name\u0026#39;:\u0026#39;earth\u0026#39;, \u0026#39;port\u0026#39;:\u0026#39;80\u0026#39;} 2 工厂方法 # items=[(\u0026#39;name\u0026#39;,\u0026#39;earth\u0026#39;),(\u0026#39;port\u0026#39;,\u0026#39;80\u0026#39;)] dict2=dict(items) dict1=dict(([\u0026#39;name\u0026#39;,\u0026#39;earth\u0026#39;],[\u0026#39;port\u0026#39;,\u0026#39;80\u0026#39;])) 3 fromkeys()方法 # dict1={}.fromkeys((\u0026#39;x\u0026#39;,\u0026#39;y\u0026#39;),-1) dict={\u0026#39;x\u0026#39;:-1,\u0026#39;y\u0026#39;:-1} dict2={}.fromkeys((\u0026#39;x\u0026#39;,\u0026#39;y\u0026#39;)) dict2={\u0026#39;x\u0026#39;:None, \u0026#39;y\u0026#39;:None} 8 合并两个有序列表 # 知乎远程面试要求编程\n尾递归\ndef _recursion_merge_sort2(l1, l2, tmp): if len(l1) == 0 or len(l2) == 0: tmp.extend(l1) tmp.extend(l2) return tmp else: if l1[0] \u0026lt; l2[0]: tmp.append(l1[0]) del l1[0] else: tmp.append(l2[0]) del l2[0] return _recursion_merge_sort2(l1, l2, tmp) def recursion_merge_sort2(l1, l2): return _recursion_merge_sort2(l1, l2, []) 循环算法\n思路：\n定义一个新的空列表\n比较两个列表的首个元素\n小的就插入到新列表里\n把已经插入新列表的元素从旧列表删除\n直到两个旧列表有一个为空\n再把旧列表加到新列表后面\ndef loop_merge_sort(l1, l2): tmp = [] while len(l1) \u0026gt; 0 and len(l2) \u0026gt; 0: if l1[0] \u0026lt; l2[0]: tmp.append(l1[0]) del l1[0] else: tmp.append(l2[0]) del l2[0] tmp.extend(l1) tmp.extend(l2) return tmp pop弹出\na = [1,2,3,7] b = [3,4,5] def merge_sortedlist(a,b): c = [] while a and b: if a[0] \u0026gt;= b[0]: c.append(b.pop(0)) else: c.append(a.pop(0)) while a: c.append(a.pop(0)) while b: c.append(b.pop(0)) return c print merge_sortedlist(a,b) 9 交叉链表求交点 # 其实思想可以按照从尾开始比较两个链表，如果相交，则从尾开始必然一致，只要从尾开始比较，直至不一致的地方即为交叉点，如图所示\n# 使用a,b两个list来模拟链表，可以看出交叉点是 7这个节点 a = [1,2,3,7,9,1,5] b = [4,5,7,9,1,5] for i in range(1,min(len(a),len(b))): if i==1 and (a[-1] != b[-1]): print \u0026#34;No\u0026#34; break else: if a[-i] != b[-i]: print \u0026#34;交叉节点：\u0026#34;,a[-i+1] break else: pass 另外一种比较正规的方法，构造链表类\nclass ListNode: def __init__(self, x): self.val = x self.next = None def node(l1, l2): length1, lenth2 = 0, 0 # 求两个链表长度 while l1.next: l1 = l1.next length1 += 1 while l2.next: l2 = l2.next length2 += 1 # 长的链表先走 if length1 \u0026gt; lenth2: for _ in range(length1 - length2): l1 = l1.next else: for _ in range(length2 - length1): l2 = l2.next while l1 and l2: if l1.next == l2.next: return l1.next else: l1 = l1.next l2 = l2.next 修改了一下:\n#coding:utf-8 class ListNode: def __init__(self, x): self.val = x self.next = None def node(l1, l2): length1, length2 = 0, 0 # 求两个链表长度 while l1.next: l1 = l1.next#尾节点 length1 += 1 while l2.next: l2 = l2.next#尾节点 length2 += 1 #如果相交 if l1.next == l2.next: # 长的链表先走 if length1 \u0026gt; length2: for _ in range(length1 - length2): l1 = l1.next return l1#返回交点 else: for _ in range(length2 - length1): l2 = l2.next return l2#返回交点 # 如果不相交 else: return 思路: http://humaoli.blog.163.com/blog/static/13346651820141125102125995/\n10 二分查找 # #coding:utf-8 def binary_search(list,item): low = 0 high = len(list)-1 while low\u0026lt;=high: mid = (low+high)/2 guess = list[mid] if guess\u0026gt;item: high = mid-1 elif guess\u0026lt;item: low = mid+1 else: return mid return None mylist = [1,3,5,7,9] print binary_search(mylist,3) 参考: http://blog.csdn.net/u013205877/article/details/76411718\n11 快排 # #coding:utf-8 def quicksort(list): if len(list)\u0026lt;2: return list else: midpivot = list[0] lessbeforemidpivot = [i for i in list[1:] if i\u0026lt;=midpivot] biggerafterpivot = [i for i in list[1:] if i \u0026gt; midpivot] finallylist = quicksort(lessbeforemidpivot)+[midpivot]+quicksort(biggerafterpivot) return finallylist print quicksort([2,4,6,7,1,2,5]) 更多排序问题可见： 数据结构与算法-排序篇-Python描述\n12 找零问题 # #coding:utf-8 #values是硬币的面值values = [ 25, 21, 10, 5, 1] #valuesCounts 钱币对应的种类数 #money 找出来的总钱数 #coinsUsed 对应于目前钱币总数i所使用的硬币数目 def coinChange(values,valuesCounts,money,coinsUsed): #遍历出从1到money所有的钱数可能 for cents in range(1,money+1): minCoins = cents #把所有的硬币面值遍历出来和钱数做对比 for kind in range(0,valuesCounts): if (values[kind] \u0026lt;= cents): temp = coinsUsed[cents - values[kind]] +1 if (temp \u0026lt; minCoins): minCoins = temp coinsUsed[cents] = minCoins print (\u0026#39;面值:{0}的最少硬币使用数为:{1}\u0026#39;.format(cents, coinsUsed[cents])) 思路: http://blog.csdn.net/wdxin1322/article/details/9501163\n方法: http://www.cnblogs.com/ChenxofHit/archive/2011/03/18/1988431.html\n13 广度遍历和深度遍历二叉树 # 给定一个数组，构建二叉树，并且按层次打印这个二叉树\n14 二叉树节点 # class Node(object): def __init__(self, data, left=None, right=None): self.data = data self.left = left self.right = right tree = Node(1, Node(3, Node(7, Node(0)), Node(6)), Node(2, Node(5), Node(4))) 15 层次遍历 # def lookup(root): row = [root] while row: print(row) row = [kid for item in row for kid in (item.left, item.right) if kid] 16 深度遍历 # def deep(root): if not root: return print root.data deep(root.left) deep(root.right) if __name__ == \u0026#39;__main__\u0026#39;: lookup(tree) deep(tree) 17 前中后序遍历 # 深度遍历改变顺序就OK了\n#coding:utf-8 #二叉树的遍历 #简单的二叉树节点类 class Node(object): def __init__(self,value,left,right): self.value = value self.left = left self.right = right #中序遍历:遍历左子树,访问当前节点,遍历右子树 def mid_travelsal(root): if root.left is None: mid_travelsal(root.left) #访问当前节点 print(root.value) if root.right is not None: mid_travelsal(root.right) #前序遍历:访问当前节点,遍历左子树,遍历右子树 def pre_travelsal(root): print (root.value) if root.left is not None: pre_travelsal(root.left) if root.right is not None: pre_travelsal(root.right) #后续遍历:遍历左子树,遍历右子树,访问当前节点 def post_trvelsal(root): if root.left is not None: post_trvelsal(root.left) if root.right is not None: post_trvelsal(root.right) print (root.value) 18 求最大树深 # def maxDepth(root): if not root: return 0 return max(maxDepth(root.left), maxDepth(root.right)) + 1 19 求两棵树是否相同 # def isSameTree(p, q): if p == None and q == None: return True elif p and q : return p.val == q.val and isSameTree(p.left,q.left) and isSameTree(p.right,q.right) else : return False 20 前序中序求后序 # 推荐: http://blog.csdn.net/hinyunsin/article/details/6315502\ndef rebuild(pre, center): if not pre: return cur = Node(pre[0]) index = center.index(pre[0]) cur.left = rebuild(pre[1:index + 1], center[:index]) cur.right = rebuild(pre[index + 1:], center[index + 1:]) return cur def deep(root): if not root: return deep(root.left) deep(root.right) print root.data 21 单链表逆置 # class Node(object): def __init__(self, data=None, next=None): self.data = data self.next = next link = Node(1, Node(2, Node(3, Node(4, Node(5, Node(6, Node(7, Node(8, Node(9))))))))) def rev(link): pre = link cur = link.next pre.next = None while cur: tmp = cur.next cur.next = pre pre = cur cur = tmp return pre root = rev(link) while root: print root.data root = root.next 思路: http://blog.csdn.net/feliciafay/article/details/6841115\n方法: http://www.xuebuyuan.com/2066385.html?mobile=1\n22 两个字符串是否是变位词 # class Anagram: \u0026#34;\u0026#34;\u0026#34; @:param s1: The first string @:param s2: The second string @:return true or false \u0026#34;\u0026#34;\u0026#34; def Solution1(s1,s2): alist = list(s2) pos1 = 0 stillOK = True while pos1 \u0026lt; len(s1) and stillOK: pos2 = 0 found = False while pos2 \u0026lt; len(alist) and not found: if s1[pos1] == alist[pos2]: found = True else: pos2 = pos2 + 1 if found: alist[pos2] = None else: stillOK = False pos1 = pos1 + 1 return stillOK print(Solution1(\u0026#39;abcd\u0026#39;,\u0026#39;dcba\u0026#39;)) def Solution2(s1,s2): alist1 = list(s1) alist2 = list(s2) alist1.sort() alist2.sort() pos = 0 matches = True while pos \u0026lt; len(s1) and matches: if alist1[pos] == alist2[pos]: pos = pos + 1 else: matches = False return matches print(Solution2(\u0026#39;abcde\u0026#39;,\u0026#39;edcbg\u0026#39;)) def Solution3(s1,s2): c1 = [0]*26 c2 = [0]*26 for i in range(len(s1)): pos = ord(s1[i])-ord(\u0026#39;a\u0026#39;) c1[pos] = c1[pos] + 1 for i in range(len(s2)): pos = ord(s2[i])-ord(\u0026#39;a\u0026#39;) c2[pos] = c2[pos] + 1 j = 0 stillOK = True while j\u0026lt;26 and stillOK: if c1[j] == c2[j]: j = j + 1 else: stillOK = False return stillOK print(Solution3(\u0026#39;apple\u0026#39;,\u0026#39;pleap\u0026#39;)) 23 动态规划问题 # 可参考： 动态规划(DP)的整理-Python描述\n","date":"November 13, 2017","externalUrl":null,"permalink":"/posts/pymianshidaan/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003e关于Python的面试题以及答案\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python面试题及答案","type":"posts"},{"content":"","date":"November 13, 2017","externalUrl":null,"permalink":"/tags/%E7%AE%97%E6%B3%95/","section":"","summary":"","title":"算法","type":"tags"},{"content":" CentOS7 常用命令\n文件与目录操作 # 命令解析\ncd /home 进入 /home 目录 cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd - 返回上次所在目录 cp file1 file2 将file1复制为file2 cp -a dir1 dir2 复制一个目录 cp -a /tmp/dir1 . 复制一个目录到当前工作目录（.代表当前目录） ls 查看目录中的文件 ls -a 显示隐藏文件 ls -l 显示详细信息 ls -lrt 按时间显示文件（l表示详细列表，r表示反向排序，t表示按时间排序） pwd 显示工作路径 mkdir dir1 创建 dir1 目录 mkdir dir1 dir2 同时创建两个目录 mkdir -p /tmp/dir1/dir2 创建一个目录树 mv dir1 dir2 移动/重命名一个目录 rm -f file1 删除 file1 rm -rf dir1 删除 dir1 目录及其子目录内容 查看文件内容 # 命令解析\ncat file1 从第一个字节开始正向查看文件的内容 head -2 file1 查看一个文件的前两行 more file1 查看一个长文件的内容 tac file1 从最后一行开始反向查看一个文件的内容 tail -3 file1 查看一个文件的最后三行 vi file 打开并浏览文件 文本内容处理 # 命令解析\ngrep str /tmp/test 在文件 /tmp/test 中查找 “str” grep ^str /tmp/test 在文件 /tmp/test 中查找以 “str” 开始的行 grep [0-9] /tmp/test 查找 /tmp/test 文件中所有包含数字的行 grep str -r /tmp/* 在目录 /tmp 及其子目录中查找 “str” diff file1 file2 找出两个文件的不同处 sdiff file1 file2 以对比的方式显示两个文件的不同 vi file # 操作解析\ni 进入编辑文本模式 Esc 退出编辑文本模式 :w 保存当前修改 :q 不保存退出vi :wq 保存当前修改并退出vi 查询操作 # 命令解析\nfind / -name file1 从 ‘/’ 开始进入根文件系统查找文件和目录 find / -user user1 查找属于用户 ‘user1’ 的文件和目录 find /home/user1 -name *.bin 在目录 ‘/ home/user1’ 中查找以 ‘.bin’ 结尾的文件 find /usr/bin -type f -atime +100 查找在过去100天内未被使用过的执行文件 find /usr/bin -type f -mtime -10 查找在10天内被创建或者修改过的文件 locate *.ps 寻找以 ‘.ps’ 结尾的文件，先运行 ‘updatedb’ 命令 find -name ‘*.[ch]’ | xargs grep -E ‘expr’ 在当前目录及其子目录所有.c和.h文件中查找 ‘expr’ find -type f -print0 | xargs -r0 grep -F ‘expr’ 在当前目录及其子目录的常规文件中查找 ‘expr’ find -maxdepth 1 -type f | xargs grep -F ‘expr’ 在当前目录中查找 ‘expr’ 压缩、解压 # 命令解析\nbzip2 file1 压缩 file1 bunzip2 file1.bz2 解压 file1.bz2 gzip file1 压缩 file1 gzip -9 file1 最大程度压缩 file1 gunzip file1.gz 解压 file1.gz tar -cvf archive.tar file1 把file1打包成 archive.tar（-c: 建立压缩档案；-v: 显示所有过程；-f: 使用档案名字，是必须的，是最后一个参数） tar -cvf archive.tar file1 dir1 把 file1，dir1 打包成 archive.tar tar -tf archive.tar 显示一个包中的内容 tar -xvf archive.tar 释放一个包 tar -xvf archive.tar -C /tmp 把压缩包释放到 /tmp目录下 zip file1.zip file1 创建一个zip格式的压缩包 zip -r file1.zip file1 dir1 把文件和目录压缩成一个zip格式的压缩包 unzip file1.zip 解压一个zip格式的压缩包到当前目录 unzip test.zip -d /tmp/ 解压一个zip格式的压缩包到 /tmp 目录 yum安装器 # 命令解析\nyum -y install [package] 下载并安装一个rpm包 yum localinstall [package.rpm] 安装一个rpm包，使用你自己的软件仓库解决所有依赖关系 yum -y update 更新当前系统中安装的所有rpm包 yum update [package] 更新一个rpm包 yum remove [package] 删除一个rpm包 yum list 列出当前系统中安装的所有包 yum search [package] 在rpm仓库中搜寻软件包 yum clean [package] 清除缓存目录（/var/cache/yum）下的软件包 yum clean headers 删除所有头文件 yum clean all 删除所有缓存的包和头文件 网络相关 # 命令 解析\nifconfig eth0 显示一个以太网卡的配置 ifconfig eth0 192.168.1.1 netmask 255.255.255.0 配置网卡的IP地址 ifdown eth0 禁用 ‘eth0’ 网络设备 ifup eth0 启用 ‘eth0’ 网络设备 iwconfig eth1 显示一个无线网卡的配置 iwlist scan 显示无线网络 ip addr show 显示网卡的IP地址 系统相关 # 命令解析\nsu - 切换到root权限（与su有区别） shutdown -h now 关机 shutdown -r now 重启 top 罗列使用CPU资源最多的linux任务 （输入q退出） pstree 以树状图显示程序 man ping 查看参考手册（例如ping 命令） passwd 修改密码 df -h 显示磁盘的使用情况 cal -3 显示前一个月，当前月以及下一个月的月历 cal 10 1988 显示指定月，年的月历 date –date ‘1970-01-01 UTC 1427888888 seconds’ 把一相对于1970-01-01 00:00的秒数转换成时间 XSheel相关操作:窗体快捷键 # 命令解析\nCtrl + u 删除光标之前到行首的字符 Ctrl + k 删除光标之前到行尾的字符 Ctrl + c 取消当前行输入的命令，相当于Ctrl + Break Ctrl + a 光标移动到行首（ahead of line），相当于通常的Home键 Ctrl + e 光标移动到行尾（end of line） Ctrl + f 光标向前（forward）移动一个字符位置 Ctrl + b 光标往回（backward）移动一个字符位置 Ctrl + l 清屏，相当于执行clear命令 Ctrl + r 显示:号提示，根据用户输入查找相关历史命令（reverse-i-search） Ctrl + w 删除从光标位置前到当前所处单词（word）的开头 Ctrl + t 交换光标位置前的两个字符 Ctrl + y 粘贴最后一次被删除的单词 Ctrl + Alt + d 显示桌面 Alt + b 光标往回（backward）移动到前一个单词 Alt + d 删除从光标位置到当前所处单词的末尾 Alt + F2 运行 Alt + F4 关闭当前窗口 Alt + F9 最小化当前窗口 Alt + F10 最大化当前窗口 Alt + Tab 切换窗口 Alt + 左键 移动窗口（或在最下面的任务栏滚动鼠标滑轮） 操作小技巧 # 鼠标中间键：粘贴突出显示的文本。(使用鼠标左键来选择文本。把光标指向想粘贴文本的地方。点击鼠标中间键来粘贴。)\nTab：命令行自动补全。使用 shell 提示时可使用这一方式。键入命令或文件名的前几个字符，然后按 [Tab] 键，它会自动补全命令或显示匹配键入字符的所有命令。\n在滚动条的空白处点击鼠标中键：屏幕即滚动到那个地方。\n在桌面或文件管理器中直接按 / 就可以输入位置，打开文件管理器。\n在 vi 或 Firefox 中直接按 / 即可进入快速搜索状态。\n网站链接和图片可直接拖放到桌面或者目录，可以马上下载。\n直接将文件管理器中的文件拖到终端中就可以在终端中得到完整的路径名。\n","date":"September 22, 2017","externalUrl":null,"permalink":"/posts/centos7ml/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003eCentOS7 常用命令\u003c/p\u003e\n\u003c/blockquote\u003e","title":"CentOS7 常用命令集合","type":"posts"},{"content":"","date":"July 30, 2017","externalUrl":null,"permalink":"/tags/ide/","section":"","summary":"","title":"IDE","type":"tags"},{"content":" Python学习资料\n编辑器下载： # Python官网下载 https://www.python.org/downloads/ sublimetext官网下载 http://www.sublimetext.com/ Visual Studio Code官网下载 （中文官网） https://code.visualstudio.com/Download Vim官网下载 https://www.vim.org/download.php Atom官网下载 https://atom.io/ PyCharm官网下载 https://www.jetbrains.com/pycharm/ Eclipse官网下载 https://www.eclipse.org/downloads/ Emacs官网下载 http://www.gnu.org/software/emacs/ Linux下载： # Redhat官网下载 https://access.redhat.com/downloads CentOS官网下载 https://www.centos.org/download/ Ubuntu官网下载 https://cn.ubuntu.com/download/ Mint官网下载 https://www.linuxmint.com/download.php Debian官网下载 https://www.debian.org/distrib/ Arch官网下载 https://www.archlinux.org/download/ Fedora官网下载 https://getfedora.org/zh_CN/ deepin官网下载 https://www.deepin.org/download/ 数据库下载： # MYSQL官网下载 https://www.mysql.com/downloads/ MongoDB官网下载 https://www.mongodb.com/ Redis官网下载 https://redis.io/download ","date":"July 30, 2017","externalUrl":null,"permalink":"/posts/pyziliao/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003ePython学习资料\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Python学习资料","type":"posts"},{"content":"","date":"July 30, 2017","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/","section":"","summary":"","title":"数据库","type":"tags"},{"content":" vim常用命令\n在命令状态下对当前行用== （连按=两次）, 或对多行用n==（n是自然数）表示自动缩进从当前行起的下面n行。你可以试试把代码缩进任意打乱再用n==排版，相当于一般IDE里的code format。使用gg=G可对整篇代码进行排版。 vim 选择文本，删除，复制，粘贴 文本的选择，对于编辑器来说，是很基本的东西，也经常被用到，总结如下： v 从光标当前位置开始，光标所经过的地方会被选中，再按一下v结束。 V 从光标当前行开始，光标经过的行都会被选中，再按一下Ｖ结束。 Ctrl + v 从光标当前位置开始，选中光标起点和终点所构成的矩形区域，再按一下Ｃtrl + v结束。 ggVG 选中全部的文本， 其中gg为跳到行首，V选中整行，G末尾 选中后就可以用编辑命令对其进行编辑，如 d 删除 y 复制 （默认是复制到\u0026quot;寄存器） p 粘贴 （默认从\u0026quot;寄存器取出内容粘贴） \u0026ldquo;+y 复制到系统剪贴板(也就是vim的+寄存器） \u0026ldquo;+p 从系统剪贴板粘贴\n=============================================================\nvim命令总结 # 1.删除字符 # 要删除一个字符，只需要将光标移到该字符上按下\u0026quot;x\u0026rdquo;。\n2.删除一行 # 删除一整行内容使用\u0026quot;dd\u0026quot;命令。删除后下面的行会移上来填补空缺。\n3.删除换行符 # 在Vim中你可以把两行合并为一行，也就是说两行之间的换行符被删除了：命令是\u0026quot;J\u0026rdquo;。\n4.撤销 # 如果你误删了过多的内容。显然你可以再输入一遍，但是命令\u0026quot;u\u0026quot; 更简便，它可以撤消上一次的操作。\n5.重做 # 如果你撤消了多次，你还可以用CTRL-R(重做)来反转撤消的动作。换句话说，它是对撤消的撤消。撤消命令还有另一种形式，\u0026ldquo;U\u0026quot;命令，它一次撤消对一行的全部操作。第二次使用该命令则会撤消前一个\u0026quot;U\u0026quot;的操作。用\u0026quot;u\u0026quot;和CTRL-R你可以找回任何一个操作状态。\n6.追加 # \u0026ldquo;i\u0026quot;命令可以在当前光标之前插入文本。 \u0026ldquo;a\u0026quot;命令可以在当前光标之后插入文本。 \u0026ldquo;o\u0026quot;命令可以在当前行的下面另起一行，并使当前模式转为Insert模式。 \u0026ldquo;O\u0026quot;命令(注意是大写的字母O)将在当前行的上面另起一行。\n7.使用命令计数 # 假设你要向上移动9行。这可以用\u0026quot;kkkkkkkkk\u0026quot;或\u0026quot;9k\u0026quot;来完成。事实上，很多命令都可以接受一个数字作为重复执行同一命令的次数。比如刚才的例子，要在行尾追加三个感叹号，当时用的命令是\u0026quot;a!!!\u0026quot;。另一个办法是用\u0026quot;3a!\u0026ldquo;命令。3说明该命令将被重复执行3次。同样，删除3个字符可以用\u0026quot;3x\u0026rdquo;。指定的数字要紧挨在它所要修饰的命令前面。\n8.退出 # 要退出Vim，用命令\u0026quot;ZZ\u0026rdquo;。该命令保存当前文件并退出Vim。\n9.放弃编辑 # 丢弃所有的修改并退出，用命令\u0026rdquo;:q!\u0026quot;。用\u0026rdquo;:e!\u0026ldquo;命令放弃所有修改并重新载入该文件的原始内容。\n###10.以Word为单位的移动 使用\u0026quot;w\u0026quot;命令可以将光标向前移动一个word的首字符上；比如\u0026quot;3w\u0026quot;将光标向前移动3个words。\u0026ldquo;b\u0026quot;命令则将光标向后移动到前一个word的首字符上。 \u0026ldquo;e\u0026quot;命令会将光标移动到下一个word的最后一个字符。命令\u0026quot;ge\u0026rdquo;，它将光标移动到前一个word的最后一个字符上。、\n11.移动到行首或行尾 # \u0026ldquo;$\u0026ldquo;命令将光标移动到当前行行尾。如果你的键盘上有一个键，它的作用也一样。\u0026quot;^\u0026ldquo;命令将光标移动到当前行的第一个非空白字符上。\u0026ldquo;0\u0026quot;命令则总是把光标移动到当前行的第一个字符上。键也是如此。\u0026quot;$\u0026ldquo;命令还可接受一个计数，如\u0026quot;1$\u0026ldquo;会将光标移动到当前行行尾，\u0026ldquo;2$\u0026ldquo;则会移动到下一行的行尾，如此类推。\u0026ldquo;0\u0026quot;命令却不能接受类似这样的计数，命令\u0026rdquo;^\u0026ldquo;前加上一个计数也没有任何效果。\n12.移动到指定字符上 # 命令\u0026quot;fx\u0026quot;在当前行上查找下一个字符x（向右方向），可以带一个命令计数\u0026quot;F\u0026quot;命令向左方向搜索。\u0026ldquo;tx\u0026quot;命令形同\u0026quot;fx\u0026quot;命令，只不过它不是把光标停留在被搜索字符上，而是在它之前的一个字符上。提示：\u0026ldquo;t\u0026quot;意为\u0026quot;To\u0026rdquo;。该命令的反方向版是\u0026quot;Tx\u0026rdquo;。这4个命令都可以用\u0026rdquo;;\u0026ldquo;来重复。以\u0026rdquo;,\u0026ldquo;也是重复同样的命令，但是方向与原命令的方向相反。\n13.以匹配一个括号为目的移动 # 用命令\u0026rdquo;%\u0026ldquo;跳转到与当前光标下的括号相匹配的那一个括号上去。如果当前光标在\u0026rdquo;(\u0026ldquo;上，它就向前跳转到与它匹配的\u0026rdquo;)\u0026ldquo;上，如果当前在\u0026rdquo;)\u0026ldquo;上，它就向后自动跳转到匹配的\u0026rdquo;(\u0026ldquo;上去.\n14.移动到指定行 # 用\u0026quot;G\u0026quot;命令指定一个命令计数，这个命令就会把光标定位到由命令计数指定的行上。比如\u0026quot;33G\u0026quot;就会把光标置于第33行上。没有指定命令计数作为参数的话, \u0026ldquo;G\u0026quot;会把光标定位到最后一行上。\u0026ldquo;gg\u0026quot;命令是跳转到第一行的快捷的方法。 另一个移动到某行的方法是在命令\u0026rdquo;%\u0026ldquo;之前指定一个命令计数比如\u0026quot;50%\u0026ldquo;将会把光标定位在文件的中间. \u0026ldquo;90%\u0026ldquo;跳到接近文件尾的地方。 命令\u0026quot;H\u0026rdquo;,\u0026ldquo;M\u0026rdquo;,\u0026ldquo;L\u0026rdquo;,分别将光标跳转到第一行，中间行，结尾行部分。\n15.告诉你当前的位置 # 使用CTRL-G命令。\u0026ldquo;set number\u0026quot;在每行的前面显示一个行号。相反关闭行号用命令\u0026rdquo;:set nonumber\u0026rdquo;。\u0026quot;:set ruler\u0026quot;在Vim窗口的右下角显示当前光标位置。\n16.滚屏 # CTRL-U显示文本的窗口向上滚动了半屏。CTRL-D命令将窗口向下移动半屏。一次滚动一行可以使用CTRL-E(向上滚动)和CTRL-Y(向下滚动)。要向前滚动一整屏使用命令CTRL-F。另外CTRL-B是它的反向版。\u0026ldquo;zz\u0026quot;命令会把当前行置为屏幕正中央，\u0026ldquo;zt\u0026quot;命令会把当前行置于屏幕顶端，\u0026ldquo;zb\u0026quot;则把当前行置于屏幕底端.\n17.简单搜索 # \u0026ldquo;/string\u0026quot;命令可用于搜索一个字符串。要查找上次查找的字符串的下一个位置,使用\u0026quot;n\u0026quot;命令。如果你知道你要找的确切位置是目标字符串的第几次出现，还可以在\u0026quot;n\u0026quot;之前放置一个命令计数。\u0026ldquo;3n\u0026quot;会去查找目标字符串的第3次出现。 \u0026ldquo;?\u0026ldquo;命令与\u0026rdquo;/\u0026ldquo;的工作相同，只是搜索方向相反.\u0026ldquo;N\u0026quot;命令会重复前一次查找，但是与最初用\u0026rdquo;/\u0026ldquo;或\u0026rdquo;?\u0026ldquo;指定的搜索方向相反。 如果查找内容忽略大小写，则用命令\u0026quot;set ignorecase\u0026rdquo;, 返回精确匹配用命令\u0026quot;set noignorecase\u0026rdquo; 。\n18.在文本中查找下一个word # 把光标定位于这个word上然后按下\u0026quot;\u0026ldquo;键。Vim将会取当前光标所在的word并将它作用目标字符串进行搜索。\u0026rdquo;#\u0026ldquo;命令是\u0026quot;\u0026ldquo;的反向版。还可以在这两个命令前加一个命令计数:\u0026ldquo;3*\u0026ldquo;查找当前光标下的word的第三次出现。\n19.查找整个word # 如果你用\u0026rdquo;/the\u0026quot;来查找Vim也会匹配到\u0026quot;there\u0026rdquo;。要查找作为独立单词的\u0026quot;the\u0026quot;使用如下命令：\u0026quot;/the\u0026gt;\u0026quot;。\u0026quot;\u0026gt;\u0026ldquo;是一个特殊的记法，它只匹配一个word的结束处。近似地，\u0026quot;\u0026lt;\u0026ldquo;匹配到一个word的开始处。这样查找作为一个word的\u0026quot;the\u0026quot;就可以用:\u0026rdquo;/\u0026quot;。\n20.高亮显示搜索结果 # 开启这一功能用\u0026rdquo;:set hlsearch\u0026rdquo;，关闭这一功能：\u0026quot;:set nohlsearch\u0026rdquo;。如果只是想去掉当前的高亮显示，可以使用下面的命令：\u0026quot;:nohlsearch\u0026rdquo;(可以简写为noh)。\n21.匹配一行的开头与结尾 # ^ 字符匹配一行的开头。$字符匹配一行的末尾。 所以\u0026rdquo;/was$\u0026ldquo;只匹配位于一行末尾的单词was，所以\u0026rdquo;/^was\u0026quot;只匹配位于一行开始的单词was。\n22.匹配任何的单字符 # .这个字符可以匹配到任何字符。比如\u0026quot;c.m\u0026quot;可以匹配任何前一个字符是c，后一个字符是m的情况，不管中间的字符是什么。\n23.匹配特殊字符 # 放一个反斜杠在特殊字符前面。如果你查找\u0026quot;ter。\u0026quot;，用命令\u0026rdquo;/ter。\u0026rdquo;\n24.使用标记 # 当你用\u0026quot;G\u0026quot;命令从一个地方跳转到另一个地方时，Vim会记得你起跳的位置。这个位置在Vim中是一个标记。使用命令\u0026rdquo; \u0026quot;可以使你跳回到刚才的出发点。命令可以在两点之间来回跳转。CTRL-O命令是跳转到你更早些时间停置光标的位置(提示:O意为older). CTRL-I则是跳回到后来停置光标的更新的位置(提示：I在键盘上位于O前面)。 注:使用CTRL-I 与按下键一样。\n25.具名标记 # 命令\u0026quot;ma\u0026quot;将当前光标下的位置名之为标记\u0026quot;a\u0026rdquo;。从a到z一共可以使用26个自定义的标记。要跳转到一个你定义过的标记，使用命令\u0026rdquo; marks \u0026ldquo;marks就是定义的标记的名字。命令\u0026rdquo; \u0026lsquo;a \u0026ldquo;使你跳转到a所在行的行首，\u0026rdquo; a \u0026ldquo;会精确定位a所在的位置。命令：\u0026quot;:marks\u0026quot;用来查看标记的列表。 命令delm！删除所有标记。\n26.操作符命令和位移 # \u0026ldquo;dw\u0026quot;命令可以删除一个word，\u0026ldquo;d4w\u0026quot;命令是删除4个word，依此类推。类似有\u0026quot;d2e\u0026rdquo;、\u0026ldquo;d$\u0026quot;。此类命令有一个固定的模式：操作符命令+位移命令。首先键入一个操作符命令。比如\u0026quot;d\u0026quot;是一个删除操作符。接下来是一个位移命。比如\u0026quot;w\u0026rdquo;。这样任何移动光标命令所及之处，都是命令的作用范围。\n27.改变文本 # 操作符命令是\u0026quot;c\u0026rdquo;，改变命令。它的行为与\u0026quot;d\u0026quot;命令类似，不过在命令执行后会进入Insert模式。比如\u0026quot;cw\u0026quot;改变一个word。或者，更准确地说，它删除一个word并让你置身于Insert模式。 \u0026ldquo;cc\u0026quot;命令可以改变整行。不过仍保持原来的缩进。 \u0026ldquo;c$\u0026ldquo;改变当前光标到行尾的内容。 快捷命令：x 代表dl(删除当前光标下的字符) X 代表dh(删除当前光标左边的字符) D 代表d$(删除到行尾的内容) C 代表c$(修改到行尾的内容) s 代表cl(修改一个字符) S 代表cc(修改一整行) 命令\u0026quot;3dw\u0026quot;和\u0026quot;d3w\u0026quot;都是删除3个word。第一个命令\u0026quot;3dw\u0026quot;可以看作是删除一个word的操作执行3次；第二个命令\u0026quot;d3w\u0026quot;是一次删除3个word。这是其中不明显的差异。事实上你可以在两处都放上命令记数，比如，\u0026ldquo;3d2w\u0026quot;是删除两个word，重复执行3次，总共是6个word。\n28.替换单个字符 # \u0026ldquo;r\u0026quot;命令不是一个操作符命令。它等待你键入下一个字符用以替换当前光标下的那个字符。\u0026ldquo;r\u0026quot;命令前辍以一个命令记数是将多个字符都替换为即将输入的那个字符。要把一个字符替换为一个换行符使用\u0026quot;r\u0026rdquo;。它会删除一个字符并插入一个换行符。在此处使用命令记数只会删除指定个数的字符：\u0026ldquo;4r\u0026quot;将把4个字符替换为一个换行符。\n29.重复改动 # \u0026ldquo;.\u0026ldquo;命令会重复上一次做出的改动。\u0026rdquo;.\u0026ldquo;命令会重复你做出的所有修改，除了\u0026quot;u\u0026quot;命令CTRL-R和以冒号开头的命令。\u0026rdquo;.\u0026ldquo;需要在Normal模式下执行，它重复的是命令，而不是被改动的内容，\n30.Visual模式 # 按\u0026quot;v\u0026quot;可以进入Visual模式。移动光标以覆盖你想操纵的文本范围。同时被选中的文本会以高亮显示。最后键入操作符命令。\n31.移动文本 # 以\u0026quot;d\u0026quot;或\u0026quot;x\u0026quot;这样的命令删除文本时，被删除的内容还是被保存了起来。你还可以用p命令把它取回来。\u0026ldquo;P\u0026quot;命令是把被去回的内容放在光标之前，\u0026ldquo;p\u0026quot;则是放在光标之后。对于以\u0026quot;dd\u0026quot;删除的整行内容，\u0026ldquo;P\u0026quot;会把它置于当前行的上一行。\u0026ldquo;p\u0026quot;则是至于当前行的后一行。也可以对命令\u0026quot;p\u0026quot;和\u0026quot;P\u0026quot;命令使用命令记数。它的效果是同样的内容被取回指定的次数。这样一来\u0026quot;dd\u0026quot;之后的\u0026quot;3p\u0026quot;就可以把被删除行的3 份副本放到当前位置。 命令\u0026quot;xp\u0026quot;将光标所在的字符与后一个字符交换。\n32.复制文本（VIM编辑器内复制） # \u0026ldquo;y\u0026quot;操作符命令会把文本复制到一个寄存器3中。然后可以用\u0026quot;p\u0026quot;命令把它取回。因为\u0026quot;y\u0026quot;是一个操作符命令，所以你可以用\u0026quot;yw\u0026quot;来复制一个word. 同样可以使用命令记数。如下例中用\u0026quot;y2w\u0026quot;命令复制两个word，\u0026ldquo;yy\u0026quot;命令复制一整行，\u0026ldquo;Y\u0026quot;也是复制整行的内容，复制当前光标至行尾的命令是\u0026quot;y$\u0026quot;。\n33.文本对象 # \u0026ldquo;diw\u0026rdquo; 删除当前光标所在的word(不包括空白字符) \u0026ldquo;daw\u0026rdquo; 删除当前光标所在的word(包括空白字符)\n34.快捷命令 # x 删除当前光标下的字符(\u0026ldquo;dl\u0026quot;的快捷命令) X 删除当前光标之前的字符(\u0026ldquo;dh\u0026quot;的快捷命令) D 删除自当前光标至行尾的内容(\u0026ldquo;d$\u0026ldquo;的快捷命令) dw 删除自当前光标至下一个word的开头 db 删除自当前光标至前一个word的开始 diw 删除当前光标所在的word(不包括空白字符) daw 删除当前光标所在的word(包括空白字符) dG 删除当前行至文件尾的内容 dgg 删除当前行至文件头的内容 如果你用\u0026quot;c\u0026quot;命令代替\u0026quot;d\u0026quot;这些命令就都变成更改命令。使用\u0026quot;y\u0026quot;就是yank命令，如此类推。\n35.编辑另一个文件 # 用命令\u0026rdquo;:edit foo.txt\u0026rdquo;，也可简写为\u0026rdquo;:e foo.txt\u0026rdquo;。\n36.文件列表 # 可以在启动Vim时就指定要编辑多个文件，用命令\u0026quot;vim one.c two.c three.c\u0026rdquo;。Vim将在启动后只显示第一个文件，完成该文件的编辑后，可以用令：\u0026quot;:next\u0026quot;或\u0026rdquo;:n\u0026quot;要保存工作成果并继续下一个文件的编辑，命令：\u0026quot;:wnext\u0026quot;或\u0026rdquo;:wn\u0026quot;可以合并这一过程。\n37.显示当前正在编辑的文件 # 用命令\u0026rdquo;:args\u0026rdquo;。\n38.移动到另一个文件 # 用命令\u0026rdquo;:previous\u0026rdquo; \u0026ldquo;:prev\u0026quot;回到上一个文件,合并保存步骤则是\u0026rdquo;:wprevious\u0026rdquo; \u0026ldquo;:wprev\u0026rdquo;。要移到最后一个文件\u0026rdquo;:last\u0026rdquo;,到第一个\u0026rdquo;:first\u0026rdquo;.不过没有\u0026rdquo;:wlast\u0026quot;或者\u0026rdquo;:wfirst\u0026quot;这样的命令。可以在\u0026rdquo;:next\u0026quot;和\u0026rdquo;:previous\u0026quot;命令前面使用一个命令计数。\n39.编辑另一个文件列表 # 不用重新启动Vim，就可以重新定义一个文件列表。命令\u0026rdquo;:args five.c six.c seven.h\u0026quot;定义了要编辑的三个文件。\n39.自动存盘 # 命令\u0026rdquo;:set autowrite\u0026rdquo;,\u0026ldquo;set aw\u0026rdquo;。自动把内容写回文件: 如果文件被修改过，在每个:next、:rewind、:last、:first、:previous、:stop、:suspend、:tag、:!、:make、CTRL-] 和 CTRL-^命令时进行。 命令\u0026rdquo;:set autowriteall\u0026rdquo;,\u0026ldquo;set awa\u0026rdquo;。和 \u0026lsquo;autowrite\u0026rsquo; 类似，但也适用于\u0026rdquo;:edit\u0026rdquo;、\u0026quot;:enew\u0026rdquo;、\u0026quot;:quit\u0026rdquo;、\u0026quot;:qall\u0026rdquo;、\u0026quot;:exit\u0026rdquo;、\u0026quot;:xit\u0026rdquo;、\u0026quot;:recover\u0026rdquo; 和关闭 Vim 窗口。置位本选项也意味着 Vim 的行为就像打开 \u0026lsquo;autowrite\u0026rsquo; 一样。\n40.切换到另一文件 # 要在两个文件间快速切换，使用CTRL-^。\n41.文件标记 # 以大写字母命名的标记。它们是全局标记，它们可以用在任何文件中。比如，正在编辑\u0026quot;fab1.Java\u0026rdquo;,用命令\u0026quot;50%mF\u0026quot;在文件的中间设置一个名为F的标记。然后在\u0026quot;fab2.java\u0026quot;文件中，用命令\u0026quot;GnB\u0026quot;在最后一行设置名为B的标记。在可以用\u0026quot;F\u0026quot;命令跳转到文件\u0026quot;fab1.java\u0026quot;的半中间。或者编辑另一个文件，\u0026quot;\u0026lsquo;B\u0026quot;命令会再把你带回文件\u0026quot;fab2.java\u0026quot;的最后一行。 要知道某个标记所代表的位置是什么，可以将该标记的名字作为\u0026quot;marks\u0026quot;命令的参数\u0026rdquo;:marks M\u0026quot;或者连续跟上几个参数\u0026quot;:marks MJK\u0026quot; 可以用CTRL-O和CTRL-I可以跳转到较早的位置和靠后的某位置。\n42.查看文件 # 仅是查看文件，不向文件写入内容，可以用只读形式编辑文件。用命令： vim -R file。如果是想强制性地避免对文件进行修改，可以用命令： vim -M file。\n43.更改文件名 # 将现有文件存成新的文件，用命令\u0026quot;:sav(eas) move.c\u0026quot;。如果想改变当前正在编辑的文件名，但不想保存该文件，就可以用命令：\u0026quot;:f(ile) move.c\u0026quot;。\n44.分割一个窗口 # 打开一个新窗口最简单的办法就是使用命令：\u0026quot;:split\u0026quot;。CTRL-W 命令可以切换当前活动窗口。\n45.关闭窗口 # 用命令：\u0026ldquo;close\u0026rdquo;.可以关闭当前窗口。实际上,任何退出文件编辑的命令\u0026quot;:quit\u0026quot;和\u0026quot;ZZ\u0026quot;都会关闭窗口，但是用\u0026quot;:close\u0026quot;可以阻止你关闭最后一个Vim，以免以意外地整个关闭了Vim。\n46.关闭除当前窗口外的所有其他窗口 # 用命令：\u0026quot;:only\u0026quot;,关闭除当前窗口外的所有其它窗口。如果这些窗口中有被修改过的，你会得到一个错误信息，同时那个窗口会被留下来。\n47.为另一个文件分隔出一个窗口 # 命令\u0026quot;:split two.c\u0026quot;可以打开第二个窗口同时在新打开的窗口中开始编辑作为 参数的文件。如果要打开一个新窗口并开始编辑一个空的缓冲区，使用命令:\u0026quot;:new\u0026quot;。\n48.垂直分割 # 用命令\u0026quot;:vsplit或：:vsplit two.c\u0026quot;。同样有一个对应的\u0026quot;:vnew\u0026quot;命令，用于垂直分隔窗口并在其中打开一个新的空缓冲区。\n49.切换窗口 # CTRL-W h 到左边的窗口 CTRL-W j 到下面的窗口 CTRL-W k 到上面的窗口 CTRL-W l 到右边的窗口 CTRL-W t 到顶部窗口 CTRL-W b 到底部窗口\n50.针对所有窗口操作的命令 # \u0026ldquo;:qall\u0026quot;放弃所有操作并退出，\u0026quot;:wall\u0026quot;保存所有，\u0026quot;:wqall\u0026quot;保存所有并退出。\n51.为每一个文件打开一个窗口 # 使用\u0026rdquo;-o\u0026quot;选项可以让Vim为每一个文件打开一个窗口： \u0026ldquo;vim -o one.txt two.txt three.txt\u0026rdquo;。\n52.使用vimdiff查看不同 # \u0026ldquo;vimdiff main.c~ main.c\u0026rdquo;,另一种进入diff模式的办法可以在Vim运行中操作。编辑文件\u0026quot;main.c\u0026quot;，然后打开另一个分隔窗口显示其不同: \u0026ldquo;:edit main.c\u0026rdquo; \u0026ldquo;:vertical diffpatch main.c.diff\u0026rdquo;。\n53.页签 # 命令\u0026quot;:tabe(dit) thatfile\u0026quot;在一个窗口中打开\u0026quot;thatfile\u0026quot;，该窗口占据着整个的Vim显示区域。命令\u0026quot;:tab split/new\u0026quot;结果是新建了一个拥有一个窗口的页签。以用\u0026quot;gt\u0026quot;命令在不同的页签间切换。 这是我总结的一些基本用法,可能对初用者会有帮助,独乐乐不如众乐乐,是吧! 说明：以下黑色为vi和vim均有的一般功能，而红色为Vim（Vi Improved）所特有功能。Vim一般的Unix和Linux下均有安装。\n三种状态 # Command： 任何输入都会作为编辑命令，而不会出现在屏幕上，任何输入都引起立即反映 Insert： 任何输入的数据都置于编辑寄存器，按ESC，可跳回command方式 Escape： 以“：”或者“/”为前导的指令，出现在屏幕的最下一行，任何输入都被当成特别指令。\n离开vi # :q! 离开vi，并放弃刚在缓冲区内编辑的内容。 :wq 将缓冲区内的资料写入磁盘中，并离开vi。\n","date":"September 11, 2016","externalUrl":null,"permalink":"/posts/vimml/","section":"","summary":"\u003cblockquote\u003e\n\u003cp\u003evim常用命令\u003c/p\u003e\n\u003c/blockquote\u003e","title":"vim常用命令总结","type":"posts"},{"content":" About me # 博客名称：希言の博客\n联系邮箱：\nmarkhoo911#gmail.com markhoo#foxmail.com 技术栈：\nElixir开发者，Rust尝鲜者 Vim、VSCode、IDEA、PyCharm、Sublime MySQL、MongoDB、Redis、PostgreSQL、Elasticsearch等数据库 大模型\u0026amp;区块链尝鲜者 ","date":"January 1, 0001","externalUrl":null,"permalink":"/about/","section":"","summary":"About me # 博客名称：希言の博客","title":"","type":"about"},{"content":" ","date":"January 1, 0001","externalUrl":null,"permalink":"/books/","section":"","summary":" ","title":"","type":"books"},{"content":" 小伙伴们 # 博客信息 # 博客名称：希言の博客\n博客网址： https://b.foresai.com\n博客头像： https://b.foresai.com/img/android-chrome-192x192_hu2cd8897914ec65893453b87131cde6d5_42520_288x288_fill_box_center_3.png\n邮箱：\nmarkhoo911#gmail.com markhoo#foxmail.com 博客介绍：专注，自律，坚持\n友链申请开放中！想友链的小伙伴请在评论区参考我上面的格式留言，我看到会第一时间友链！\n声明： # 申请友链前为表诚意请自觉先添加友链，否则不加 友链后单方面取消本站友链的，直接删除！恕不另行通知 不接受免费域名友链 长期死链将会直接清除 小伙伴 # 名称 介绍 Bgods Bgods 凉风有信 责难无以成事 米米的博客 米米的博客!! Joinsen的博客 Joinsen的博客 FROYO\u0026rsquo;s Blog 将来的你，一定会感谢现在努力的自己。 周信的博客 记录站长生活及编程学习笔记 Mr. 童的博客 Mr. 童的博客 琛苏设计 北海轻歌 Ailoli 永远年轻，永远热泪盈眶 Abalone 鼓在 0 与 1 之间的憨憨 不稽一格 不稽一格 华安自媒体博客 华安自媒体博客 Deepzz 唯爱与美食不可辜负也 :-D 是可可呀~ 简单不先于复杂，而在于复杂之后。 追梦人物 追梦人物 访问异常或未友链 # 名称 介绍 丁D博客 你根本不知道哪片云彩有彩虹。 林阿三 一个非常喜欢前端并且热爱后端的前端人员 打怪升级 主要记录个人学习成长轨迹，打怪升级，自学成菜 ILY’s Blog I Love Y 刘志军的博客 凡心所向，素履所往。生如逆旅，一苇以航。 熊猫小A 持续中二，偶尔 Geek。 阿里云产品优惠券： 点击领取\n","date":"January 1, 0001","externalUrl":null,"permalink":"/links/","section":"","summary":"小伙伴们 # 博客信息 # 博客名称：希言の博客","title":"","type":"links"},{"content":"","date":"January 1, 0001","externalUrl":null,"permalink":"/archive/","section":"Archives","summary":"","title":"Archives","type":"archive"},{"content":"","date":"January 1, 0001","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","date":"January 1, 0001","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"January 1, 0001","externalUrl":null,"permalink":"/archive/archive/","section":"Archives","summary":"","title":"归档","type":"archive"}]