使用bandit對(duì)目標(biāo)python代碼進(jìn)行安全函數(shù)掃描的案例分析
技術(shù)背景
在一些對(duì)python開源庫代碼的安全掃描中,我們有可能需要分析庫中所使用到的函數(shù)是否會(huì)對(duì)代碼的執(zhí)行環(huán)境造成一些非預(yù)期的影響。典型的例如python的沙箱逃逸問題,通過一些python的第三方庫可以執(zhí)行系統(tǒng)shell命令,而這就不在python的沙箱防護(hù)范圍之內(nèi)了。關(guān)于python的沙箱逃逸問題,這里不作展開,這也是困擾業(yè)界多年的一個(gè)問題,連python官方也提過python的沙箱是沒有完美的防護(hù)方案的,這里僅作為一個(gè)背景案例使用:
# subprocess_Popen.py
import subprocess
import uuid
subprocess.Popen('touch ' + str(uuid.uuid1()) +'.txt', shell = True)
這里演示的功能是使用subprocess函數(shù)庫開啟一個(gè)系統(tǒng)shell,并執(zhí)行一個(gè)touch的指令,可以生成一個(gè)指定文件名的文件,類似于mkdir產(chǎn)生一個(gè)文件夾。我們可以看到這個(gè)文件成功執(zhí)行后會(huì)在當(dāng)前的目錄下生成一個(gè)uuid隨機(jī)命名的txt文件:
[dechin@dechin-manjaro bandit_test]$ python3 subprocess_Popen.py [dechin@dechin-manjaro bandit_test]$ ll 總用量 4 -rw-r--r-- 1 dechin dechin 0 1月 26 23:03 b7aa0fc8-5fe7-11eb-b5d3-058313e110e4.txt -rw-r--r-- 1 dechin dechin 123 1月 26 23:03 subprocess_Popen.py
然而,本次的關(guān)注點(diǎn)并不在與這個(gè)函數(shù)執(zhí)行了什么功能,而是這個(gè)函數(shù)中用到了subprocess這個(gè)函數(shù)庫。按照python的語言特點(diǎn),當(dāng)你的系統(tǒng)中如果存在這樣的一個(gè)模塊引用了subprocess庫,那么任何可以調(diào)用該功能模塊的函數(shù),都可以調(diào)用到subprocess這個(gè)函數(shù),以下是另外一個(gè)惡意用戶的python代碼:
# bad.py
from subprocess_Popen import subprocess as subprocess
subprocess.Popen('touch bad.txt', shell = True)
該代碼的目的是在不直接import subprocess的條件下,通過前面創(chuàng)建好的subprocess_Popen.py來進(jìn)行搭橋調(diào)用subprocess的功能函數(shù)。這個(gè)腳本的執(zhí)行結(jié)果如下:
[dechin@dechin-manjaro bandit_test]$ python3 bad.py [dechin@dechin-manjaro bandit_test]$ ll 總用量 12 -rw-r--r-- 1 dechin dechin 0 1月 26 23:13 0fda7ede-5fe9-11eb-80a8-ad279ab4e0a6.txt -rw-r--r-- 1 dechin dechin 0 1月 26 23:03 b7aa0fc8-5fe7-11eb-b5d3-058313e110e4.txt -rw-r--r-- 1 dechin dechin 113 1月 26 23:13 bad.py -rw-r--r-- 1 dechin dechin 0 1月 26 23:13 bad.txt drwxr-xr-x 2 dechin dechin 4096 1月 26 23:13 __pycache__ -rw-r--r-- 1 dechin dechin 123 1月 26 23:03 subprocess_Popen.py
這個(gè)結(jié)果意味著,我們成功的使用bad.py調(diào)用了subprocess_Popen.py中所引用的subprocess,成功touch了一個(gè)bad.txt的文件。
到這里我們的背景案例演示結(jié)束,但我們需要重新梳理這些案例中所包含的邏輯:我們?cè)臼窍M谧约旱南到y(tǒng)中不引入python的沙箱逃逸問題,我們會(huì)對(duì)其他人傳遞過來的代碼進(jìn)行掃描,如使用下文中將要介紹的bandit工具來屏蔽subprocess等"危險(xiǎn)函數(shù)"。而如果我們?cè)谧约簩懙膒ython庫或者引入的第三方python庫中存在類似于subprocess的引用,這就會(huì)導(dǎo)致我們的屏蔽失效,用戶可以任意的通過這些引用的搭橋直接調(diào)用subprocess的函數(shù)功能。因此,在特殊的條件要求下,我們需要對(duì)自己的代碼進(jìn)行安全函數(shù)掃描,以免為其他人的系統(tǒng)帶來不可預(yù)期的安全風(fēng)險(xiǎn)。bandit只是其中的一種安全函數(shù)掃描的工具,接下來我們介紹一下其基本安裝和使用方法。
用pip安裝bandit
這里直接使用pip來安裝bandit,有需要的也可以從源碼直接安裝。關(guān)于在pip的使用中配置國內(nèi)鏡像源的方法,可以參考這篇博客中對(duì)python安裝第三方庫的介紹。
[dechin@dechin-manjaro bandit_test]$ python3 -m pip install bandit Collecting bandit Downloading bandit-1.7.0-py3-none-any.whl (115 kB) |████████████████████████████████| 115 kB 101 kB/s Requirement already satisfied: PyYAML>=5.3.1 in /home/dechin/anaconda3/lib/python3.8/site-packages (from bandit) (5.3.1) Collecting GitPython>=1.0.1 Downloading GitPython-3.1.12-py3-none-any.whl (159 kB) |████████████████████████████████| 159 kB 28 kB/s Requirement already satisfied: six>=1.10.0 in /home/dechin/anaconda3/lib/python3.8/site-packages (from bandit) (1.15.0) Collecting stevedore>=1.20.0 Downloading stevedore-3.3.0-py3-none-any.whl (49 kB) |████████████████████████████████| 49 kB 25 kB/s Collecting gitdb<5,>=4.0.1 Downloading gitdb-4.0.5-py3-none-any.whl (63 kB) |████████████████████████████████| 63 kB 28 kB/s Collecting pbr!=2.1.0,>=2.0.0 Downloading pbr-5.5.1-py2.py3-none-any.whl (106 kB) |████████████████████████████████| 106 kB 26 kB/s Collecting smmap<4,>=3.0.1 Downloading smmap-3.0.5-py2.py3-none-any.whl (25 kB) Installing collected packages: smmap, gitdb, GitPython, pbr, stevedore, bandit Successfully installed GitPython-3.1.12 bandit-1.7.0 gitdb-4.0.5 pbr-5.5.1 smmap-3.0.5 stevedore-3.3.0
安裝結(jié)束之后,可以通過以下指令驗(yàn)證是否安裝成功:
[dechin@dechin-manjaro bandit_test]$ bandit -h
usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i] [-f {csv,custom,html,json,screen,txt,xml,yaml}] [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q]
[--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] [--ini INI_PATH] [--exit-zero] [--version]
[targets [targets ...]]
Bandit - a Python source code security analyzer
positional arguments:
targets source file(s) or directory(s) to be tested
optional arguments:
-h, --help show this help message and exit
-r, --recursive find and process files in subdirectories
-a {file,vuln}, --aggregate {file,vuln}
aggregate output by vulnerability (default) or by filename
-n CONTEXT_LINES, --number CONTEXT_LINES
maximum number of code lines to output for each issue
-c CONFIG_FILE, --configfile CONFIG_FILE
optional config file to use for selecting plugins and overriding defaults
-p PROFILE, --profile PROFILE
profile to use (defaults to executing all tests)
-t TESTS, --tests TESTS
comma-separated list of test IDs to run
-s SKIPS, --skip SKIPS
comma-separated list of test IDs to skip
-l, --level report only issues of a given severity level or higher (-l for LOW, -ll for MEDIUM, -lll for HIGH)
-i, --confidence report only issues of a given confidence level or higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)
-f {csv,custom,html,json,screen,txt,xml,yaml}, --format {csv,custom,html,json,screen,txt,xml,yaml}
specify output format
--msg-template MSG_TEMPLATE
specify output message template (only usable with --format custom), see CUSTOM FORMAT section for list of available values
-o [OUTPUT_FILE], --output [OUTPUT_FILE]
write report to filename
-v, --verbose output extra information like excluded and included files
-d, --debug turn on debug mode
-q, --quiet, --silent
only show output in the case of an error
--ignore-nosec do not skip lines with # nosec comments
-x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS
comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default:
.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg)
-b BASELINE, --baseline BASELINE
path of a baseline report to compare against (only JSON-formatted files are accepted)
--ini INI_PATH path to a .bandit file that supplies command line arguments
--exit-zero exit with 0, even with results found
--version show program's version number and exit
CUSTOM FORMATTING
-----------------
Available tags:
{abspath}, {relpath}, {line}, {test_id},
{severity}, {msg}, {confidence}, {range}
Example usage:
Default template:
bandit -r examples/ --format custom --msg-template \
"{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}"
Provides same output as:
bandit -r examples/ --format custom
Tags can also be formatted in python string.format() style:
bandit -r examples/ --format custom --msg-template \
"{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}"
See python documentation for more information about formatting style:
https://docs.python.org/3/library/string.html
The following tests were discovered and loaded:
-----------------------------------------------
B101 assert_used
B102 exec_used
B103 set_bad_file_permissions
B104 hardcoded_bind_all_interfaces
B105 hardcoded_password_string
B106 hardcoded_password_funcarg
B107 hardcoded_password_default
B108 hardcoded_tmp_directory
B110 try_except_pass
B112 try_except_continue
B201 flask_debug_true
B301 pickle
B302 marshal
B303 md5
B304 ciphers
B305 cipher_modes
B306 mktemp_q
B307 eval
B308 mark_safe
B309 httpsconnection
B310 urllib_urlopen
B311 random
B312 telnetlib
B313 xml_bad_cElementTree
B314 xml_bad_ElementTree
B315 xml_bad_expatreader
B316 xml_bad_expatbuilder
B317 xml_bad_sax
B318 xml_bad_minidom
B319 xml_bad_pulldom
B320 xml_bad_etree
B321 ftplib
B323 unverified_context
B324 hashlib_new_insecure_functions
B325 tempnam
B401 import_telnetlib
B402 import_ftplib
B403 import_pickle
B404 import_subprocess
B405 import_xml_etree
B406 import_xml_sax
B407 import_xml_expat
B408 import_xml_minidom
B409 import_xml_pulldom
B410 import_lxml
B411 import_xmlrpclib
B412 import_httpoxy
B413 import_pycrypto
B501 request_with_no_cert_validation
B502 ssl_with_bad_version
B503 ssl_with_bad_defaults
B504 ssl_with_no_version
B505 weak_cryptographic_key
B506 yaml_load
B507 ssh_no_host_key_verification
B601 paramiko_calls
B602 subprocess_popen_with_shell_equals_true
B603 subprocess_without_shell_equals_true
B604 any_other_function_with_shell_equals_true
B605 start_process_with_a_shell
B606 start_process_with_no_shell
B607 start_process_with_partial_path
B608 hardcoded_sql_expressions
B609 linux_commands_wildcard_injection
B610 django_extra_used
B611 django_rawsql_used
B701 jinja2_autoescape_false
B702 use_of_mako_templates
B703 django_mark_safe
從這個(gè)列表中的屏蔽函數(shù)我們可以看出所謂的"危險(xiǎn)函數(shù)"到底都有哪些,比如常用的subprocess和random都被包含在內(nèi)。subprocess是因?yàn)槠鋵?duì)shell的調(diào)用而被列為"危險(xiǎn)函數(shù)",而random則是因?yàn)槠鋫坞S機(jī)數(shù)的性質(zhì)(這里簡單說明一下,現(xiàn)在一般推薦使用secrets中的所謂安全隨機(jī)數(shù),但是實(shí)際上只有量子疊加測量才能夠真正實(shí)現(xiàn)真隨機(jī)數(shù))。
bandit常用使用方法
直接對(duì)py文件進(jìn)行掃描:
[dechin@dechin-manjaro bandit_test]$ bandit subprocess_Popen.py
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: subprocess_Popen.py
Run started:2021-01-26 15:31:00.425603
Test results:
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
Severity: Low Confidence: High
Location: subprocess_Popen.py:3
More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
2
3 import subprocess
4 import uuid
--------------------------------------------------
>> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue.
Severity: High Confidence: High
Location: subprocess_Popen.py:6
More Info: https://bandit.readthedocs.io/en/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html
5
6 subprocess.Popen('touch ' + str(uuid.uuid1()) +'.txt', shell = True)
--------------------------------------------------
Code scanned:
Total lines of code: 3
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0.0
Low: 1.0
Medium: 0.0
High: 1.0
Total issues (by confidence):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 2.0
Files skipped (0):
通過對(duì)剛才所創(chuàng)建的調(diào)用了危險(xiǎn)函數(shù)subprocess的py文件subprocess_Popen.py的掃描,我們識(shí)別出了其中的"危險(xiǎn)函數(shù)",注意這里的Issue編號(hào)是602,定級(jí)是Severity: Low Confidence: High。但是如果我們用bandit去掃描利用了其他函數(shù)對(duì)危險(xiǎn)函數(shù)的調(diào)用搭橋來二次調(diào)用的bad.py文件,我們發(fā)現(xiàn)是另外一種結(jié)果:
[dechin@dechin-manjaro bandit_test]$ bandit bad.py
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: bad.py
Run started:2021-01-26 15:30:47.370468
Test results:
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
Severity: Low Confidence: High
Location: bad.py:3
More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
2
3 from subprocess_Popen import subprocess as subprocess
4
5 subprocess.Popen('touch bad.txt', shell = True)
--------------------------------------------------
>> Issue: [B604:any_other_function_with_shell_equals_true] Function call with shell=True parameter identified, possible security issue.
Severity: Medium Confidence: Low
Location: bad.py:5
More Info: https://bandit.readthedocs.io/en/latest/plugins/b604_any_other_function_with_shell_equals_true.html
4
5 subprocess.Popen('touch bad.txt', shell = True)
--------------------------------------------------
Code scanned:
Total lines of code: 2
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0.0
Low: 1.0
Medium: 1.0
High: 0.0
Total issues (by confidence):
Undefined: 0.0
Low: 1.0
Medium: 0.0
High: 1.0
Files skipped (0):
注意這里雖然實(shí)現(xiàn)的功能跟上面那個(gè)例子是一樣的,但是這里的Issue編號(hào)為604,定級(jí)也變成了Severity: Medium Confidence: Low。這里的關(guān)鍵并不是定級(jí)變成了什么,而是定級(jí)被改變了,這是因?yàn)?code>bandit是通過對(duì)字符串的處理來識(shí)別危險(xiǎn)函數(shù)的,因此對(duì)于這種二次調(diào)用的特殊場景,bandit不一定都能夠準(zhǔn)確的識(shí)別出來對(duì)危險(xiǎn)函數(shù)的調(diào)用,甚至可能出現(xiàn)二次調(diào)用后,完全無法識(shí)別風(fēng)險(xiǎn)函數(shù)的使用的可能性。
2.掃描一個(gè)目錄下的所有py文件,并將結(jié)果寫入txt文件
[dechin@dechin-manjaro bandit_test]$ bandit *.py -o test_bandit.txt -f txt [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.8.5 [node_visitor] INFO Unable to find qualified name for module: bad.py [node_visitor] INFO Unable to find qualified name for module: subprocess_Popen.py [text] INFO Text output written to file: test_bandit.txt
該案例就掃描了當(dāng)前目錄下的所有py文件,其實(shí)就是bad.py和subprocess_Popen.py這兩個(gè),并且將最終的掃描結(jié)果保存至test_bandit.txt文件中,這里我們就不展示txt文件的具體內(nèi)容,大概就是將上一章節(jié)的兩個(gè)執(zhí)行結(jié)果進(jìn)行了整合。
3.掃描一個(gè)目錄下的多層文件夾中的py文件,并將結(jié)果寫入html文件
假如我們有如下所示的一個(gè)目錄結(jié)構(gòu)需要進(jìn)行掃描測試:
[dechin@dechin-manjaro bandit_test]$ tree . ├── bad.py ├── bad.txt ├── level2 │ └── test_random.py ├── subprocess_Popen.py ├── test_bandit.html └── test_bandit.txt 1 directory, 6 files [dechin@dechin-manjaro bandit_test]$ cat level2/test_random.py # test_bandit.py import random a = random.random()
我們可以在當(dāng)前目錄下執(zhí)行如下指令:
[dechin@dechin-manjaro bandit_test]$ bandit -r . -f html -o test_bandit.html [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.8.5 [html] INFO HTML output written to file: test_bandit.html
這里我們得到的結(jié)果是一個(gè)test_bandit.html文件,文件內(nèi)容如下圖所示:

4.使用配置文件禁用部分Issue
在執(zhí)行目錄下創(chuàng)建一個(gè).bandit文件,作如下配置就可以避免對(duì)B404的審查:
[bandit] skips: B404
執(zhí)行的掃描結(jié)果如下圖所示,我們可以看到B404相關(guān)的Issue已經(jīng)不在列表中了:

5.在py文件中直接逃避bandit審計(jì)
在待掃描的py文件的對(duì)應(yīng)風(fēng)險(xiǎn)函數(shù)后加上如下注釋,即可在bandit審計(jì)過程中自動(dòng)忽略:
# bad.py
from subprocess_Popen import subprocess as sb
sb.Popen('touch bad.txt', shell = 1) # nosec
這里我們可以看到最終的審計(jì)結(jié)果中,B604也隨之而不見了,如下圖所示。從這個(gè)案例中我們也可以知悉,bandit并不是一個(gè)用來作安全防護(hù)的工具,僅僅是用來做比較初步的python代碼安全函數(shù)使用規(guī)范的審查工作,而掃描出來的問題是否處理,其實(shí)最終還是取決于開發(fā)者自己。

bandit簡單性能測試
眾所周知python語言的性能是極其受限的,因此bandit的性能也有可能十分的低下,這里讓我們來定量的測試一下bandit的性能到底在什么水準(zhǔn)。首先我們創(chuàng)建一個(gè)10000行的py文件,內(nèi)容全部為危險(xiǎn)函數(shù)的使用:
# gen.py
import os
with open('test_bandit_power.py', 'w') as py_file:
py_file.write('import subprocess as sb\n')
for i in range(10000):
py_file.write('sb.Popen(\'whoami\', shell = 1)\n')
通過執(zhí)行python3 gen.py就可以生成一個(gè)10000行的危險(xiǎn)函數(shù)文件test_bandit_power.py,大約300KB的大小。此時(shí)我們針對(duì)這單個(gè)的文件進(jìn)行bandit掃描測試,我們發(fā)現(xiàn)這個(gè)過程極為漫長,并且生成了大量的錯(cuò)誤日志:
[dechin@dechin-manjaro bandit_test]$ time bandit test_bandit_power.py -f html -o test_power.html [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.8.5 [node_visitor] INFO Unable to find qualified name for module: test_bandit_power.py [html] INFO HTML output written to file: test_power.html real 0m6.239s user 0m6.082s sys 0m0.150s
我們可以簡單估算,如果10000行的代碼都需要6s的時(shí)間來進(jìn)行掃描,那么對(duì)于比較大的項(xiàng)目的1000000+的代碼的掃描時(shí)間,則有可能達(dá)到10min往上,這個(gè)時(shí)間雖然也不是特別長,但是對(duì)于大型的項(xiàng)目而言這絕對(duì)不是一個(gè)非常高效的選擇。
總結(jié)概要
在一些對(duì)安全性要求較高的開發(fā)項(xiàng)目中,有可能會(huì)禁止使用危險(xiǎn)函數(shù),如subprocess等。而bandit的作用旨在通過對(duì)代碼的掃描自動(dòng)化的給出安全危險(xiǎn)函數(shù)分析意見,至于是否采納,還是以不同項(xiàng)目的管理者需求為準(zhǔn)。同時(shí)經(jīng)過我們的測試發(fā)現(xiàn),bandit在實(shí)際使用場景下性能表現(xiàn)并不如意,因此在大型項(xiàng)目中我們并不推薦使用,如果一定要使用也可以考慮進(jìn)行針對(duì)性的配置。
版權(quán)聲明
本文首發(fā)鏈接為:https://www.cnblogs.com/dechinphy/p/bandit.html
作者ID:DechinPhy
更多原著文章請(qǐng)參考:https://www.cnblogs.com/dechinphy/
- Python通過kerberos安全認(rèn)證操作kafka方式
- 線程安全及Python中的GIL原理分析
- python線程安全及多進(jìn)程多線程實(shí)現(xiàn)方法詳解
- 詳解python實(shí)現(xiàn)線程安全的單例模式
- 利用python批量給云主機(jī)配置安全組的方法教程
- Python簡單實(shí)現(xiàn)安全開關(guān)文件的兩種方式
- python smtplib模塊發(fā)送SSL/TLS安全郵件實(shí)例
- Python操作sqlite3快速、安全插入數(shù)據(jù)(防注入)的實(shí)例
- 詳細(xì)總結(jié)Python常見的安全問題
相關(guān)文章
python實(shí)現(xiàn)從pdf文件中提取文本,并自動(dòng)翻譯的方法
今天小編就為大家分享一篇python實(shí)現(xiàn)從pdf文件中提取文本,并自動(dòng)翻譯的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-11-11
Python多進(jìn)程庫multiprocessing中進(jìn)程池Pool類的使用詳解
這篇文章主要介紹了Python多進(jìn)程庫multiprocessing中進(jìn)程池Pool類的使用詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
教你兩步解決conda安裝pytorch時(shí)下載速度慢or超時(shí)的問題
這篇文章主要介紹了教你兩步解決conda安裝pytorch時(shí)下載速度慢or超時(shí)的問題,使用清華鏡像源可以大大減少安裝的時(shí)間,需要的朋友可以參考下2023-03-03
Python虛擬環(huán)境的創(chuàng)建和使用詳解
這篇文章主要給大家介紹了關(guān)于Python虛擬環(huán)境的創(chuàng)建和使用的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
pytorch 把MNIST數(shù)據(jù)集轉(zhuǎn)換成圖片和txt的方法
這篇文章主要介紹了pytorch 把MNIST數(shù)據(jù)集轉(zhuǎn)換成圖片和txt的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Django migrations 默認(rèn)目錄修改的方法教程
這篇文章主要介紹了Django migrations 默認(rèn)目錄修改的方法教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
django+celery+RabbitMQ自定義多個(gè)消息隊(duì)列的實(shí)現(xiàn)
本文主要介紹了django+celery+RabbitMQ自定義多個(gè)消息隊(duì)列的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02

