menu

在 Mac 正确的配置 python 环境还是件很头疼的事

pyenv

最终选择 pyenv/pyenv: Simple Python version management,不过还是要 brew install python 作为其他第三方工具的依赖。

  • 确保安装了 xcode 命令行工具:xcode-select --install
  • 安装 pyenvbrew install pyenv
  • 安装编译依赖

      brew install zlib sqlite
      export LDFLAGS="-L/usr/local/opt/zlib/lib -L/usr/local/opt/sqlite/lib"
      export CPPFLAGS="-I/usr/local/opt/zlib/include -I/usr/local/opt/sqlite/include"
    
  • 安装 python pyenv install 3.9.0
  • 配置环境变量: .zshenv 可以替换 .zshrc 如果仅要在 shell 中生效的话,见 https://unix.stackexchange.com/a/71258

      echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshenv
      echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshenv
      echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.zshenv
    
  • pyenv global 3.9.0 设置默认的 python 版本
  • pyenv local 3.8.6 对于特定目录使用 3.8.6,会在当前目录生成一个 .python_version 文件

问题

openpose 编译 python api 不支持 pyenv,需自己手动指定。

阅读 pybind11/tools/FindPythonLibsNew.cmake 后发现,需要手动指定 PYTHON_EXECUTABLES,其他相应变量都会自动生成:

cmake -D BUILD_PYTHON:BOOL=ON -DPYTHON_EXECUTABLE=$HOME/.pyenv/versions/3.9.0/bin/python3.9 -S . -B ./build

FYI: FindPython — CMake 3.19.1 Documentation

虚拟环境

pyenv/pyenv-virtualenv: a pyenv plugin to manage virtualenv (a.k.a. python-virtualenv)

  • git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
  • echo ‘eval “$(pyenv virtualenv-init -)”’ » ~/.zshrc

例子

比如 scikit-learn-0.23.2 不支持 python 3.9。创建一个 3.8.6 的虚拟环境

pyenv virtualenv 3.8.6 ml-venv
pyenv local ml-venv # 项目目录下执行

Emacs/lsp-mode

目前的方式还需要手动,选择虚拟环境的路径,先配置 pyvenv

(use-package pyvenv
  :ensure t
  :init
  (setenv "WORKON_HOME" "~/.pyenv/versions")) ;;

然后可通过 pyvenv-workon 选择 pyenv 所有版本,选择后(或者直接 pyvenv-activate 目录),会将虚拟环境的python目录设置到环境变量 VIRTUAL_ENV。 lsp-mode 的 pyls 会读取这个变量,切换到这个 python 环境。通过这样曲线的形式来实现 lsp-mode 对虚拟环境的支持。

问题

环境 MacOS 11.0

安装 numpy

遇到 :#15947 (comment)

安装 scipy

遇到:https://stackoverflow.com/questions/41241468/error-pip-install-scipy

解决

brew install openblas
brew install gcc # 为了 gfortran,直接安装 gfortran 会报错 `[ld cannot find -lgcc_s](https://bugsfixes.blogspot.com/2016/02/mac-ld-library-not-found-for-lgccs104.html)`
brew install swig

~/.numpy-site.cfg

[DEFAULT]
library_dirs = /usr/local/lib
include_dirs = /usr/local/include


[openblas]
libraries = openblas
library_dirs = /usr/local/opt/openblas/lib
include_dirs = /usr/local/opt/openblas/include

这是最重要的步骤,主要的报错就是找不到头文件和共享库

scikit-image

没有一个没坑的:#13371#5051

brew install libomp

export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
export CPPFLAGS="$CPPFLAGS -Xpreprocessor -fopenmp"
export CFLAGS="$CFLAGS -I/usr/local/opt/libomp/include -Wno-implicit-function-declaration"
export CXXFLAGS="$CXXFLAGS -I/usr/local/opt/libomp/include"
export LDFLAGS="$LDFLAGS -L/usr/local/opt/libomp/lib -lomp"
export DYLD_LIBRARY_PATH=/usr/local/opt/libomp/lib

tensorflow

Big Surf 需手动安装 whl No Matching Distribution Found for Tensorflow · Issue #31058 · tensorflow/tensorflow

安装 tensorflow 2.3.1,其依赖的 grpcio、h5py、scipy 版本太旧编译报错,也都需全部手动安装 whl。

其他方案

numpy 这样也能安装成功

brew install openblas
OPENBLAS="$(brew --prefix openblas)" pip install scipy

import

https://blog.csdn.net/weixin_38256474/article/details/81228492 https://www.devdungeon.com/content/python-import-syspath-and-pythonpath-tutorial

概念

  • 模块 module,.py、.pyo、.pyc、.pyd、.so、.dll
  • 包 package, 包含有 module 的目录,通过 init.py 标示
  • init.py 包的描述文件

从 sys.path 和 PYTHONPATH 查找模块或包

conda

Package, dependency and environment management for any language

相当于 pip + virtualenv

合理是预编译好的包,不同的 cpu 架构可能有支持上的问题

Miniconda

Anaconda

  • 预装了 1500 个科学计算包,占 3GB 空间

mamba

conda 加强版 https://github.com/mamba-org/mamba

  1. 多线程下载
  2. 更快的依赖解析
  3. c++ 实现

conda-forge

社区维护的 conda channel,相当于社区版 anaconda?

Resources

https://realpython.com/effective-python-environment/#package-management

Basic

Scope

  1. 本地作用域,function 或 lambda 表达式
  2. 外部作用域(Enclosing scope),比如闭包,或上层作用域
  3. 全局作用域(Global),在程序或模块的最外层定义的变量
  4. 内置作用域(Build-in),比如关键字,内置函数

python 查找变量也是按照这个顺序。值得注意的是 python 没有块作用域,即除了 functionlambda 之外的代码块是没有本地作用域,比如 while try-except,特别是 for 语句中声明的变量,仍然在本地作用域。

Python 作用域通过 dictionaries 实现的,这些 dictionaries 通常也叫 namespaces。 module 的变量名存放与 __dict__,比如: sys.__dict__,如果当前程序的话:globals()dir()

在非全局作用域,为全局变量赋值是需要 global 关键字,访问全局变量则不用。 同样的,嵌套作用域要修改,外部作用域的变量,需要用 nonlocal,这里可以发现使用来 nonlocal 关键字表示要为闭包保存外部环境的引用。

几个特殊情况

  • python 没有块作用域,列表推导式(Comprehension)却是有的。
  • except 表达式中声明的变量
  • 类,拥有自己的本地作用域 __dict__
  • 实例,也拥有自己的 __dict__,所以在类的定义中,要访问实例的作用域需要 self
  • 另外,类的作用域并不是方法的外部作用域,类的方法需要通过类的引用才能访问到类的作用域。
  • 通过实例变量查找变量,会先从实例本地作用域,到类本地作用域,如果找不到会抛出 AttributeError.

列表推导式(Comprehension)

通过表达式生成新的列表:

new_list = [expression for member in iterable]

expression 可以访问 member,还支持 filter:

new_list = [expression for member in iterable if conditional]

集合(元素具有唯一性)推导式:

new_set =  {expression for member in iterable}

字典推导式:

squares = {i: i * i for i in range(10)}

生成器(reduce):

sum(i * i for i in range(1000000000))

模块与包

三种定义 python 模块的方式:

  1. python 声明,如名为 mod 的python 模块,即文件名为 mod.py 的 python 脚本
  2. c 实现,运行时动态加载
  3. 解析器的内置模块,如 itertools

模块搜索路径

  1. 当前路径
  2. PYTHONPATH 环境变量
  3. 安装时设置的特殊位置,如:site-package

一个文件夹以树状结构(文件系统)组织模块的集合:

import pkg.mod1, pkg.mod2

import pkg 不会自动把 pkg 内的模块导入。而是执行 __init__.py

from pkg import *

能把 __init__.py 中声明的模块导入:

__all__ = [
        'mod1',
        'mod2',
        'mod3',
        'mod4'
        ]

Thread

由于与GIL的交互实际上限制了一次运行一个Python线程。还有一个 multiprocessing 使用子进程并发的包代替。

基本的启动线程的形式:

x = threading.Thread(target=thread_function, args=(1,))
x.start()

线程池:

import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        executor.map(thread_function, range(3))

加锁:

lock = threading.Lock()
with lock:
    ....
    ....
  • 信号:event = threading.Event()
  • 信号量:threading.Semaphore
  • 定时器:threading.Timer

GIL

Global Interpreter Lock

python 用引用计数来做 GC,GIL 就是来保证引用计数过程中的线程安全。

Coroutines

同步代码实现并发,并不一定是多线程、多进程。 asyncio 是一个单线程单进程模型。

  • async def 声明 native coroutine 或 asynchronous generator
  • await 挂起当前函数,等待指定函数执行完成
keyboard_arrow_up