1. 程式人生 > >WSL (Windows Subsystem for Linux) 的 VSLAM (Visual Simultaneous Localization and Mapping) 道路

WSL (Windows Subsystem for Linux) 的 VSLAM (Visual Simultaneous Localization and Mapping) 道路

# **WSL 的 VSLAM 道路** --- > 以 **Windows Subsystem for Linux** 闖入 **Visual Simultaneous Localization and Mapping** 世界的艱難道路... 這裡包含各種各樣的 WSL 中可能用到的包,美化方案,以及相關軟體的一些使用小技巧,用於自己踩坑記錄的同時,希望能夠幫到同為使用 Windows 踩坑的 CVer 們... ... ## **安裝好 Windows Subsystem for Linux 後要做的** ### **解除安裝原裝 Vim** 一波強硬操作把好刪的地方先咔嚓掉(讓輸密碼就老實輸,讓確認就直接'Y'): ```bash sudo apt-get remove vim sudo apt-get remove vim-runtime sudo apt-get remove vim -tiny sudo apt-get remove vim-common sudo apt-get remove vim-doc sudo apt-get remove vim-scripts ``` 然後檢查還剩什麼帶 "vim" 字眼的還活著,輸入: ```bash dpkg -l | grep vim ``` 得到下面這三個東西: ```shell rc vim-common 2:8.1.2269-1ubuntu5 all Vi IMproved - Common files rc vim-runtime 2:8.1.2269-1ubuntu5 all Vi IMproved - Runtime files rc vim-tiny 2:8.1.2269-1ubuntu5 amd64 Vi IMproved - enhanced vi editor - compact version ``` 扯掉它最後的救命稻草,用: ```bash sudo dpkg -P vim-tiny vim-common vim-run ``` ### **安裝 Nano** 這樣就成功 remove 了原裝的 vim,下面用 apt-get 命令安裝 nano,也是一種編輯器: ```bash sudo apt-get install nano ``` ### **Linux換源** 進入[**阿里雲 Ubuntu 映象**](https://developer.aliyun.com/mirror/ubuntu?spm=a2c6h.13651102.0.0.3e221b11ICRBHL),選擇 ubuntu 20.04 (focal) 配置所對應的源: ```css deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse ``` 在 source.list 檔案中新增這些源並儲存(按,Ctrl+K): ```bash sudo nano /etc/apt/source.list # Use nano to edit the .list file ``` ## **一些小型的 Tips** - “右鍵”在 WSL 中意味著 “Paste”; - 訪問源列表中每個網址並讀取**軟體列表**,最後把這個更新後的列表儲存在本地電腦,用的命令為: ```bash sudo apt-get update ``` 要想把本地已安裝的**軟體**與軟體列表中對應軟體進行對比,發現需要更新的檔案,用下面這個: ```bash sudo apt-get upgrade ``` - 回到 home 目錄是用: ```bash cd ~ ``` - WSL 似乎不支援這東西 ```bash # sudo apt-get install yum ``` - 要想鑑定一個網站是否正常連線,使用如下的命令: ```bash ping XXX.com # "Packet Internet Groper", as an example, use 'ping github.com' ``` 這是工作在 TCP/IP 網路體系結構中應用層的一個服務命令, 主要是向特定的目的主機發送 ICMP(Internet Control Message Protocol 因特網報文控制協議)Echo 請求報文,測試目的站是否可達及瞭解其有關狀態。 等待一段時間後(你認為差不多到了統計的總時長了),使用以下命令停止: ```bash ctrl+C ``` 最終輸出基本上是下面這個樣子(github 有一定的丟包 a... ...): ```css PING github.com (140.82.113.3) 56(84) bytes of data. 64 bytes from github.com (140.82.113.3): icmp_seq=1 ttl=47 time=565 ms 64 bytes from github.com (140.82.113.3): icmp_seq=2 ttl=47 time=1062 ms 64 bytes from github.com (140.82.113.3): icmp_seq=3 ttl=47 time=389 ms 64 bytes from github.com (140.82.113.3): icmp_seq=4 ttl=47 time=513 ms 64 bytes from github.com (140.82.113.3): icmp_seq=5 ttl=47 time=434 ms 64 bytes from github.com (140.82.113.3): icmp_seq=6 ttl=47 time=607 ms 64 bytes from github.com (140.82.113.3): icmp_seq=7 ttl=47 time=373 ms 64 bytes from github.com (140.82.113.3): icmp_seq=8 ttl=47 time=401 ms ^C --- github.com ping statistics --- 9 packets transmitted, 8 received, 11.1111% packet loss, time 8026ms rtt min/avg/max/mdev = 372.733/542.963/1062.033/212.073 ms, pipe 2 ``` - 只有 g++ 不足以在 Linux 上面開心地做開發,最好在一切的開始先裝一下 `build-essential` 這個包,裡面有很多依賴,能夠使預設的 Ubuntu 具備 C/C++ 的編譯環境,執行: ```bash sudo apt-get install build-essential ``` - Node Package Manager(npm)能夠加快下載軟體包的速度(?): ```bash sudo apt install npm ``` - 想要看看可以升級的軟體以及它們的版本資訊,使用: ```bash apt list --upgradable -a ``` - 直接 Kill 當前程序: ```bash ctrl+C ``` - 一種臨時給予許可權的方法: ```bash sudo chmod 777 /dev/ttyUSB* ``` 相應的,如果想要給串列埠一個永久許可權,使用: ```bash sudo usermod -a -G dialout user_name ``` - 尋找包的位置,`mlocate` 可以用來定位軟體包,使用 `locate` 命令: ```bash sudo apt-get mlocate sudo apt-get install mlocate sudo updatedb locate eigen3 ``` 就可以輸出 `eigen3` 的位置。 - doxygen 編譯文件的方法,下載並 `make doc` 就可以編譯出一個 `doxygen` 文件: ```bash sudo apt-get install doxygen make doc ``` ## **配置一個比較漂亮的Shell** 可以通過 `cat` 來檢視當前的 Linux 系統是否有可用的 shell: ```bash cat /etc/shells sudo apt-get install zsh ``` 安裝完後,shells 中將出現 `zsh`: ```shell # /etc/shells: valid login shells /bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /bin/dash /usr/bin/dash /usr/bin/tmux /usr/bin/screen /bin/zsh /usr/bin/zsh ``` emmmm... ... 要安裝的就是叫做 zsh 的東西,可以把命令列弄得好看一些,~~對於提高工作效率有很好的促進作用~~,安裝步驟很簡單,直接執行: ```bash sudo apt-get install zsh ``` 然後切換當前 shell 至 zsh 就成功了: ```bash chsh -s /bin/zsh ``` 更新後會輸出一個: ```bash # Updated process:[oh-my-zsh] Would you like to update? [Y/n] Y Updating Oh My Zsh ... remote: Enumerating objects: 154, done. remote: Counting objects: 100% (154/154), done. remote: Compressing objects: 100% (78/78), done. remote: Total 116 (delta 69), reused 84 (delta 38), pack-reused 0 Receiving objects: 100% (116/116), 27.17 KiB | 11.00 KiB/s, done. Resolving deltas: 100% (69/69), completed with 24 local objects. From https://github.com/ohmyzsh/ohmyzsh * branch master -> FETCH_HEAD 93c837f..c549387 master -> origin/master README.md | 61 +++--- lib/functions.zsh | 4 +- lib/termsupport.zsh | 12 +- plugins/aws/aws.plugin.zsh | 1 - plugins/bgnotify/bgnotify.plugin.zsh | 2 +- plugins/brew/README.md | 7 +- plugins/brew/brew.plugin.zsh | 1 + plugins/bundler/README.md | 79 +++++--- plugins/bundler/bundler.plugin.zsh | 116 ++++++----- plugins/compleat/compleat.plugin.zsh | 7 +- plugins/composer/composer.plugin.zsh | 2 + plugins/drush/drush.plugin.zsh | 2 - plugins/git/git.plugin.zsh | 14 +- plugins/ipfs/LICENSE | 22 ++ plugins/ipfs/README.md | 17 ++ plugins/ipfs/_ipfs | 717 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/laravel5/laravel5.plugin.zsh | 19 +- plugins/minikube/minikube.plugin.zsh | 10 +- plugins/pip/pip.plugin.zsh | 6 +- plugins/stack/stack.plugin.zsh | 1 - plugins/sublime-merge/README.md | 17 ++ plugins/sublime-merge/sublime-merge.plugin.zsh | 55 +++++ plugins/terraform/README.md | 3 +- plugins/terraform/terraform.plugin.zsh | 2 + plugins/wp-cli/wp-cli.plugin.zsh | 1 - themes/agnoster.zsh-theme | 2 +- themes/avit.zsh-theme | 4 +- 27 files changed, 1017 insertions(+), 167 deletions(-) create mode 100644 plugins/ipfs/LICENSE create mode 100644 plugins/ipfs/README.md create mode 100644 plugins/ipfs/_ipfs create mode 100644 plugins/sublime-merge/README.md create mode 100644 plugins/sublime-merge/sublime-merge.plugin.zsh First, rewinding head to replay your work on top of it... Fast-forwarded master to c549387745205d7fa8e91c1e6dcdae6901d9dd1d. __ __ ____ / /_ ____ ___ __ __ ____ _____/ /_ / __ \/ __ \ / __ `__ \/ / / / /_ / / ___/ __ \ / /_/ / / / / / / / / / / /_/ / / /_(__ ) / / / \____/_/ /_/ /_/ /_/ /_/\__, / /___/____/_/ /_/ /____/ Hooray! Oh My Zsh has been updated and/or is at the current version. To keep up on the latest news and updates, follow us on Twitter: https://twitter.com/ohmyzsh Want to get involved in the community? Join our Discord: https://discord.gg/ohmyzsh Get your Oh My Zsh swag at: https://shop.planetargon.com/collections/oh-my-zsh ``` 開啟檔案 `.zshrc`,更改 `ZSH_THEME=` 至 `ZSH_THEME="agnoster"`,開啟的方法如下(用 Nano 編輯): ```bash sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" nano ~/.zshrc ``` 實際上,此時 `reboot` 後看到的是奇怪的樣式,需要調整字型才能讓它正常起來,進入官網下載相應的字型,直接雙擊安裝後,在 WSL 命令列視窗直接 `右鍵->屬性->字型` 並更改字型的樣式即可。 ## **輸出檔案樹檢視到.txt(Windows 版本)** 開啟"執行"對話方塊(Win+R),輸入 cmd,開啟控制檯命令視窗... ```bash C:\Users\a1020>g: G:\>cd \__BiBliOthèQuE__ G:\__BiBliOthèQuE__>tree /f > saved_names.txt ``` Linux 中做這件事似乎需要下面這個東西: ```bash sudo apt-get install tree ``` 這個弄出來也蠻好看的,在路徑中輸入: ```bash tree ``` 得到了下面一棵很 happy 的樹圖: ![](https://img2020.cnblogs.com/blog/2137921/202102/2137921-20210211215839887-1386898562.jpg) ## **藉助 WSL 走進 VSLAM的世界** ### **WSL 掛載 Windows檔案** WSL 是可以訪問 Windows 中的檔案的,除了軟體要安裝在根目錄(?),其它的檔案例如各種程式碼、資料檔案什麼的都希望可以放在別的地方,由此,可以切換路徑到自己喜歡的目錄裡面: ```bash cd /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo ``` ### **下載 SLAMdemo 檔案、安裝 cmake、Hello SLAM** 從 Gitee 中把目標檔案 clone 下來: ```bash git clone https://gitee.com/wenyawei/slambook.git ``` 安裝 cmake 的過程還是像以往一樣: ```bash sudo apt-get install cmake ``` 完事之後隨便來一下 `cmake` 命令,可以輸出一些奇奇怪怪的東西: ```shell Usage cmake [options] cmake [options] cmake [options] -S -B Specify a source directory to (re-)generate a build system for it in the current working directory. Specify an existing build directory to re-generate its build system. Run 'cmake --help' for more information. ``` 接下來把路徑切到 `slambook/ch2`,用g++編譯一下 `helloSLAM.cpp`,並執行生成的可執行檔案 `a.out`: ```bash cd slambook/ch2 g++ helloSLAM.cpp ./a.out ``` 得到如下所示 `Hello SLAM!` 的輸出: ![](https://img2020.cnblogs.com/blog/2137921/202102/2137921-20210211215853342-1417613654.jpg) ### **安裝 Eigen、檢測是否成功** 直接採用 `apt-get` 安裝: ```bash sudo apt-get install libeigen3-dev ``` 從前面的路徑回退一位,並切換到 `ch3/useEigen` 的路徑: ```bash cd .. cd ch3/useEigen ``` 使用 `cmake` 進行編譯,並執行: ```bash cmake . make ./eigenMatrix ``` 輸出為(...): ```shell 1 2 3 4 5 6 1 2 3 4 5 6 10 28 32 77 0.680375 0.59688 -0.329554 -0.211234 0.823295 0.536459 0.566198 -0.604897 -0.444451 0.680375 -0.211234 0.566198 0.59688 0.823295 -0.604897 -0.329554 0.536459 -0.444451 1.61307 1.05922 6.80375 5.9688 -3.29554 -2.11234 8.23295 5.36459 5.66198 -6.04897 -4.44451 -0.198521 2.22739 2.8357 1.00605 -0.555135 -1.41603 -1.62213 3.59308 3.28973 0.208598 Eigen values = 0.0242899 0.992154 1.80558 Eigen vectors = -0.549013 -0.735943 0.396198 0.253452 -0.598296 -0.760134 -0.796459 0.316906 -0.514998 time use in normal inverse is 0ms time use in Qr decomposition is 0ms ``` ### **安裝 Pangolin、檢測是否成功** 詳細安裝步驟以及檢測見我的 [**Pangolin Installation & Examination**](https://www.cnblogs.com/optics-css/p/14374766.html) 教程。 ### **安裝 Sophus、檢測是否成功** 和之前一樣,直接切入軟體包的目錄進行編譯,指令如下: ```bash cd /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/Sophus mkdir build cd build cmake .. make ``` 然後~~開心地~~發現竟然有編譯錯誤!!具體錯誤為: ```shell /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/sophus/so2.cpp: In constructor ‘Sophus::SO2::SO2()’: /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/sophus/so2.cpp:32:26: error: lvalue required as left operand of assignment 32 | unit_complex_.real() = 1.; | ^~ /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/sophus/so2.cpp:33:26: error: lvalue required as left operand of assignment 33 | unit_complex_.imag() = 0.; | ^~ make[2]: *** [CMakeFiles/Sophus.dir/build.make:66: CMakeFiles/Sophus.dir/sophus/so2.cpp.o] Error 1 make[2]: Leaving directory '/mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/build' make[1]: *** [CMakeFiles/Makefile2:91: CMakeFiles/Sophus.dir/all] Error 2 make[1]: Leaving directory '/mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/build' make: *** [Makefile:144: all] Error 2 ``` 於是去尋找路徑下名為 `so2.cpp` 的原始檔,找到如下程式碼: ```c++ SO2::SO2() { unit_complex_.real() = 1.; unit_complex_.imag() = 0.; } ``` 顯然這種賦值方式是 XXX 的,應該用實參的形式賦值,也就是說,改成下面的形式: ```c++ SO2::SO2() { //unit_complex_.real() = 1.; //unit_complex_.imag() = 0.; unit_complex_.real(1.); unit_complex_.imag(0.); } ``` 然後重新編譯一下,就通過了: ```bash /usr/bin/cmake -E cmake_progress_start /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/sophus/build/CMakeFiles 0 ``` 接著的使用中會遇到的麻煩事是找不到 `Sophus` 庫,需要把 `CMakeLists.txt` 裡面寫上你安裝的 `Sophus` 庫的標頭檔案路徑以及共享庫的路徑: ```cmake # Use the set(·) command to input the directories of Sophus set(Sophus_INCLUDE_DIRS "/mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/Sophus/sophus") set(Sophus_LIBS "/mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/Sophus/build/libSophus.so") find_package(Sophus REQUIRED) include_directories( ${Sophus_INCLUDE_DIRS} ) target_link_libraries(useSophus ${Sophus_LIBRARIES}) ``` 咳咳,上面是非模板 `Sophus` 的安裝和使用,,下面才是模板庫的,命令列如下(記得不要回滾): ```bash git clone https://github.com/strasdat/Sophus.git cd /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/SophusTemplate mkdir build cd build cmake .. make sudo make install ``` 並沒有任何編譯錯誤,這樣一下子安裝下來就是隻有標頭檔案的 `Sophus`,就像 `eigen3` 一樣的,它和非模板庫的區別在於,這個裡面的型別定義都是基於模板的,也就是說,有更強的泛化效能。 ### **安裝 Ceres、g2o、檢測是否成功** 同前,首先應當安裝依賴: ```bash sudo apt-get install qt5-qmake qt5-default libqglviewer-dev-qt5 libsuitesparse-dev libcxsparse3 libcholmod3 sudo apt-get install liblapack-dev libsuitesparse-dev libcxsparse3 libgflags-dev libgoogle-glog-dev libgtest-dev ``` 然後進入編譯: ```bash git clone https://github.com/RainerKuemmerle/g2o.git sudo apt-get install libsuitesparse-dev qtdeclarative5-dev qt5-qmake libqglviewer-dev-qt5 sudo su cd g2o mkdir build cd build cmake ../ make -j8 sudo make install ``` 遇到了編譯錯誤: ```bash /usr/lib/qt5/bin/uic: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory make[2]: *** [g2o/apps/g2o_viewer/CMakeFiles/viewer_library.dir/build.make:62: g2o/apps/g2o_viewer/ui_base_main_window.h] Error 127 make[1]: *** [CMakeFiles/Makefile2:1580: g2o/apps/g2o_viewer/CMakeFiles/viewer_library.dir/all] Error 2 ``` 查了一下,這個錯誤名為 `error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory`,還好還好,有個[**解決思路**](https://github.com/dnschneid/crouton/wiki/Fix-error-while-loading-shared-libraries:-libQt5Core.so.5)幫助我成功解決了這個問題,似乎為 WSL 特有的,執行如下語句後再進行編譯就可以了: ```bash sudo strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 ``` 開了7個執行緒,花了得有接近 3~4 min 叭才弄完,但是完了就好完了就好,最後別忘了 `sudo make install` 一下。 用 `g2o_viewer` run 一下 `sphere.g2o` 來證明一下已經下載完成: ```bash ./g2o/bin/g2o_viewer /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/3rdparty/ch10/sphere.g2o ``` 一個需要注意的點是,最好別在別的位置編譯 `g2o` 庫,可能會報錯告訴你路徑裡有中文。 ### **安裝 OpenCV4.4、檢測是否成功** 詳細安裝步驟以及檢測見我的 [**OpenCV4.4.0 Installation & Examination**](https://www.cnblogs.com/optics-css/p/14374694.html) 教程。 ## **Cmake、g++** 在 WSL 中使用 g++ 列印虛表的操作如下,會自動寫到一個檔案裡: ```bash g++ -fdump-class-hierarchy XXX.cpp ``` 使用 g++ 匯出繼承結構的指令如下: ```bash g++ -fdump-class-hierarchy -c XXX.cpp ``` ### **Cmake 的簡單用法** 構建 CMakeList.txt 如下,注意一定要把儲存的編碼格式改成 `UTF8`: ```cmake # The lowest version required cmake_minimum_required( VERSION 2.8 ) # Project name project( HelloSLAM ) # The executable file addition add_executable( helloSLAM helloSLAM.cpp ) ``` 在終端輸入: ```bash # Path to slambook/ch2 cd /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/Demolist/slamdemo/slambook/ch2 # CMake, the current path cmake . make # Run the executable file ./helloSLAM ``` 可以通過新建並切換到 `build` 資料夾,並對於上一個資料夾進行編譯,就可以將 cmake 編譯時產生的檔案生成在 `build` 裡面,這樣有利於原始碼的釋出: ```bash mkdir build cd build cmake .. make ``` 注意每次編譯時都記得把以前的 Makefile 刪除。 ## **Python 的簡單用例** 學會用命令列呼叫 `Python` 標準庫的 `doctest`,從而生成程式碼的分析檔案,首先自然是切換到相應的路徑: ```bash cd /mnt/c/Users/a1020/Desktop/Reinforcement_ELyou/#Pythonic/Fluent-Python/01-data-model ``` 執行下面的指令,呼叫 `doctest`,可以獲得一個名為 `frenchdeck.doctest` 的檔案: ```bash python3 -m doctest frenchdeck.py ``` 檔案內容為程式碼的分析: ```python >>> from frenchdeck import FrenchDeck, Card >>> beer_card = Card('7', 'diamonds') >>> beer_card Card(rank='7', suit='diamonds') >>> deck = FrenchDeck() >>> len(deck) 52 >>> deck[:3] [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')] >>> deck[12::13] [Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')] >>> Card('Q', 'hearts') in deck True >>> Card('Z', 'clubs') in deck False >>> for card in deck: # doctest: +ELLIPSIS ... print(card) Card(rank='2', suit='spades') Card(rank='3', suit='spades') Card(rank='4', suit='spades') ... >>> for card in reversed(deck): # doctest: +ELLIPSIS ... print(card) Card(rank='A', suit='hearts') Card(rank='K', suit='hearts') Card(rank='Q', suit='hearts') ... >>> for n, card in enumerate(deck, 1): # doctest: +ELLIPSIS ... print(n, card) 1 Card(rank='2', suit='spades') 2 Card(rank='3', suit='spades') 3 Card(rank='4', suit='spades') ... >>> suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) >>> def spades_high(card): ... rank_value = FrenchDeck.ranks.index(card.rank) ... return rank_value * len(suit_values) + suit_values[card.suit] Rank test: >>> spades_high(Card('2', 'clubs')) 0 >>> spades_high(Card('A', 'spades')) 51 >>> for card in sorted(deck, key=spades_high): # doctest: +ELLIPSIS ... print(card) Card(rank='2', suit='clubs') Card(rank='2', suit='diamonds') Card(rank='2', suit='hearts') ... Card(rank='A', suit='diamonds') Card(rank='A', suit='hearts') Card(rank='A', suit='spade