用Python制作檢測Linux運行信息的工具的教程
在這篇文章里,我們將會探索如何使用Python語言作為一個工具來檢測Linux系統(tǒng)各種運行信息。讓我們一起來學(xué)習(xí)吧。
哪種Python?
當(dāng)我提到Python時,我一般是指CPython 2(準(zhǔn)確來說是2.7)。當(dāng)同樣的代碼不能在CPython3(3.3)運行時,我們明確地把它指出并給出替代的代碼,解釋它們之間的不同點。請確保你已經(jīng)安裝了CPython,在終端輸入python或者python3你會看到Python提示符出現(xiàn)在你的終端里。
請注意,所有的腳本程序都會以#!/usr/bin/env python作為第一行,意味著我們要Python解析器去運行這些腳本。因此,如果你使用 chmod +x your-script.py 命令給你的腳本添加可執(zhí)行的權(quán)限,你可以使用./your-script.py命令直接運行你的腳本(你將會在這篇文章里看到這種操作)
探索platform模塊
在標(biāo)準(zhǔn)庫中的platform模塊有大量的函數(shù)讓我們?nèi)z查各種系統(tǒng)信息。我們一起來打開Python解釋器(譯者:直接在命令行輸入python即可打開)并探索其中的一部分函數(shù)。我們先從platform.uname()函數(shù)開始:
>>> import platform >>> platform.uname() ('Linux', 'fedora.echorand', '3.7.4-204.fc18.x86_64', '#1 SMP Wed Jan 23 16:44:29 UTC 2013', 'x86_64')
如果你知道Linux上的uname命令,你會意識到這個函數(shù)就是uname命令的一個接口。在Python 2,這個函數(shù)會返回一個由系統(tǒng)類型(或者內(nèi)核類型),主機名,版本號,發(fā)行版號,主機硬件架構(gòu)和處理器類型組成的元組。你可以使用索引來獲取單個屬性,像這樣:
>>> platform.uname()[0] 'Linux'
在Python 3,這個函數(shù)會返回一個默認(rèn)命名的元組:
>>> platform.uname() uname_result(system='Linux', node='fedora.echorand', release='3.7.4-204.fc18.x86_64', version='#1 SMP Wed Jan 23 16:44:29 UTC 2013', machine='x86_64', processor='x86_64')
因為返回值是個默認(rèn)命名的元組,所以我們可以輕易地通過變量名來獲取單個屬性而不用去記住各個屬性的下標(biāo),像這樣:
>>> platform.uname().system 'Linux'
platfrom模塊還提供了一些直接的接口來獲取上面的屬性值,像這些:
>>> platform.system() 'Linux' >>> platform.release() '3.7.4-204.fc18.x86_64'
函數(shù)linx_distribution()返回你正在使用的Linux發(fā)行版的詳細(xì)信息。舉個例子,在Fedora 18系統(tǒng)中,這條命令會返回下面的信息:
>>> platform.linux_distribution() ('Fedora', '18', 'Spherical Cow')
返回值是一個由發(fā)行版本名,版本號,代號組成的元組。你可以通過_supported_dists屬性來打印你所用的Python版本支持哪些發(fā)行版本:
>>> platform._supported_dists ('SuSE', 'debian', 'fedora', 'redhat', 'centos', 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', 'UnitedLinux', 'turbolinux')
如果你的Linux發(fā)行版本不是上面那些的其中一個(或者是其中一個的衍生版),那么你調(diào)用上面的函數(shù)時不會看到任何有用的信息。
最后一個我們要探索的platfrom函數(shù)是architecture()函數(shù)。當(dāng)你不添加任何的參數(shù)來調(diào)用這個函數(shù)時,這個函數(shù)會返回一個由位架構(gòu)和Python可執(zhí)行文件格式組成的元組。比如:
>>> platform.architecture() ('64bit', 'ELF')
在32位的Linux系統(tǒng)中,你會看到:
>>> platform.architecture() ('32bit', 'ELF')
如果你指定其他任意的系統(tǒng)可執(zhí)行程序作為參數(shù),你會得到類似的結(jié)果:
>>> platform.architecture(executable='/usr/bin/ls') ('64bit', 'ELF')
我們鼓勵你去探索platfrom模塊中的其他函數(shù),讓你找到你當(dāng)前使用的Python的版本。如果你非常想知道這個模塊是怎樣獲取這些信息的,你可以去看Python源代碼目錄下的Lib/platfrom.py文件。
os和sys模塊同樣是一個獲取統(tǒng)屬性的有用模塊就像本地BYTEORDER一樣。下一步,我們將會不使用Python你標(biāo)準(zhǔn)庫模塊來探索一些獲取Linux系統(tǒng)信息的通用方法,這次我們通過proc和sys文件系統(tǒng)來實現(xiàn)。要注意的是,通過這些文件系統(tǒng)獲取的信息在不同的硬件架構(gòu)里會有所不同。因此,在閱讀這篇文章和寫腳本從這些文件里獲取系統(tǒng)信息時要它記住。
CPU信息
/proc/cpuinfo這個文件包含了你系統(tǒng)的處理單元信息。比如,這里有一個與在命令行輸入cat /proc/cpuinfo 具備同樣功能的Python腳本
#! /usr/bin/env python """ print out the /proc/cpuinfo file """ from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: print(line.rstrip('n'))
當(dāng)你用Python 2或者Python 3運行這個腳本時,你會看到/proc/cpuinfo文件的所有內(nèi)容都在你的屏幕上顯示出來。(在上面這個腳本,rstrip()方法把每一行的換行符去掉)
下個代碼清單使用了startwith()這個字符串方法來顯示你電腦的處理單元型號
#! /usr/bin/env python """ Print the model of your processing units """ from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: # Ignore the blank line separating the information between # details about two processing units if line.strip(): if line.rstrip('n').startswith('model name'): model_name = line.rstrip('n').split(':')[1] print(model_name)
當(dāng)你運行這個腳本,你會看到你機器的所有處理單元的型號。比如,下面是我在我計算機里看到的:
Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
到目前為止我們已經(jīng)有好幾種方法用來獲取我們的計算機系統(tǒng)構(gòu)架了。從技術(shù)的角度準(zhǔn)確地說,所有的這些方法實際上是呈現(xiàn)了你運行的系統(tǒng)內(nèi)核的架構(gòu)。因此,如果你計算機實際上是64位的機器,但是運行著32位的內(nèi)核,那么上面的方法將會顯現(xiàn)你的計算機是32位架構(gòu)的。為了找出計算機的正確架構(gòu),你可以查看在/proc/cpuinfo中的屬性列表的lm屬性。1m屬性代表著長模式(Long mode)并且只會在64位架構(gòu)的計算機上出現(xiàn)。下面的腳本跟你展示是怎樣做的:
#! /usr/bin/env python """ Find the real bit architecture """ from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: # Ignore the blank line separating the information between # details about two processing units if line.strip(): if line.rstrip('n').startswith('flags') or line.rstrip('n').startswith('Features'): if 'lm' in line.rstrip('n').split(): print('64-bit') else: print('32-bit')
正如目前我們所看到的,我們能夠訪問/proc/cpuinfo文件并且使用簡單的文本處理技術(shù)去讀取我們在查找的信息。為了友好地提供數(shù)據(jù)給其他程序使用,最好的方法可能是把從/proc/cpuinfo里獲取的內(nèi)容轉(zhuǎn)換為標(biāo)準(zhǔn)的數(shù)據(jù)機構(gòu),比如轉(zhuǎn)換為字典類型。方法很簡單:如果你看了這個文件,你會發(fā)現(xiàn)對于每一個處理單元都是以鍵值對形式存在(在之前的一個例子中,我們打印的處理器機型名時,這里的model name就是一個鍵。)每個不同處理器單元的信息都會用空行來分開。這使我們能方便地以每個處理單元數(shù)據(jù)為鍵來構(gòu)建字典數(shù)據(jù)結(jié)構(gòu)。這些鍵(key)都有一個值(value),每個值又對應(yīng)著每一個處理單元在/proc/cupinfo文件中的所有信息。下一個代碼清單告訴你怎樣做:
#!/usr/bin/env/ python """ /proc/cpuinfo as a Python dict """ from __future__ import print_function from collections import OrderedDict import pprint def cpuinfo(): ''' Return the information in /proc/cpuinfo as a dictionary in the following format: cpu_info['proc0']={...} cpu_info['proc1']={...} ''' cpuinfo=OrderedDict() procinfo=OrderedDict() nprocs = 0 with open('/proc/cpuinfo') as f: for line in f: if not line.strip(): # end of one processor cpuinfo['proc%s' % nprocs] = procinfo nprocs=nprocs+1 # Reset procinfo=OrderedDict() else: if len(line.split(':')) == 2: procinfo[line.split(':')[0].strip()] = line.split(':')[1].strip() else: procinfo[line.split(':')[0].strip()] = '' return cpuinfo if __name__=='__main__': cpuinfo = cpuinfo() for processor in cpuinfo.keys(): print(cpuinfo[processor]['model name'])
這段代碼使用了一個OrderedDict(有序的字典)代替常用的字典類型,目的是先對在文件中找到的鍵值對排序后再保存。因此,先展示第一個處理單元的數(shù)據(jù)信息其次是第二個,以此類推。如果你調(diào)用這個函數(shù),它會返回一個字典類型給你。字典的每一個鍵都是一個處理單元。然后你可以使用鍵來篩選要找的信息(就像if __name='__main__'語句塊里展示的一樣)。當(dāng)上面的腳本運行時會再次打印出每個處理單元的model name(通過print(cpuinfo[processor]['model name']語句來展示)
Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
內(nèi)存信息
與/proc/cpuinfo類似,/proc/meminfo文件包含了你計算機的主存信息。下一個腳本產(chǎn)生一個包含這個文件內(nèi)容的字典并把它輸出。
#!/usr/bin/env python from __future__ import print_function from collections import OrderedDict def meminfo(): ''' Return the information in /proc/meminfo as a dictionary ''' meminfo=OrderedDict() with open('/proc/meminfo') as f: for line in f: meminfo[line.split(':')[0]] = line.split(':')[1].strip() return meminfo if __name__=='__main__': #print(meminfo()) meminfo = meminfo() print('Total memory: {0}'.format(meminfo['MemTotal'])) print('Free memory: {0}'.format(meminfo['MemFree']))
和前面看到的一樣,你同樣可以使用特定的鍵去獲取任意你想要的信息(在if __name__=='__main__'語句快里有展示)。當(dāng)你運行這個腳本,你可以看到類似下面的輸出:
Total memory: 7897012 kB Free memory: 249508 kB
網(wǎng)絡(luò)統(tǒng)計
下面,我們會探索我們計算機系統(tǒng)的網(wǎng)絡(luò)設(shè)備。我們將會檢索系統(tǒng)的網(wǎng)絡(luò)接口和系統(tǒng)開啟后發(fā)送和接收到的字節(jié)數(shù)據(jù)。這些信息可以在/proc/net/dev文件中獲取。如果你審查過這個文件的內(nèi)容,你會發(fā)現(xiàn)前兩行包含了頭信息-i.e.文件中的第一列是網(wǎng)絡(luò)接口名,第二和第三列展示了接收和傳輸?shù)淖止?jié)信息(比如,總發(fā)送字節(jié),數(shù)據(jù)包數(shù)量,錯誤統(tǒng)計,等等)。我們感興趣的是如何獲取不同的網(wǎng)絡(luò)設(shè)備的總數(shù)據(jù)發(fā)送和接受量。下一個代碼清單展示了我們?nèi)绾螐?proc/net/dev提出這些信息:
#!/usr/bin/env python from __future__ import print_function from collections import namedtuple def netdevs(): ''' RX and TX bytes for each of the network devices ''' with open('/proc/net/dev') as f: net_dump = f.readlines() device_data={} data = namedtuple('data',['rx','tx']) for line in net_dump[2:]: line = line.split(':') if line[0].strip() != 'lo': device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0), float(line[1].split()[8])/(1024.0*1024.0)) return device_data if __name__=='__main__': netdevs = netdevs() for dev in netdevs.keys(): print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))
當(dāng)你運行上面的腳本時,會以MiB為單位輸出從你最近的一次重啟后你的網(wǎng)絡(luò)設(shè)備接受和發(fā)送的數(shù)據(jù)。正如下面展示的:
em1: 0.0 MiB 0.0 MiB wlan0: 2651.40951061 MiB 183.173976898 MiB
你可能會利用一個持久性存儲機制和這個腳本來寫一個你自己的數(shù)據(jù)使用監(jiān)控程序。
進程
/proc目錄同樣包含了每個運行進程的目錄。這些目錄的名稱以相應(yīng)的進程ID來命名。因此,如果你遍歷/proc目錄下的所有以數(shù)字命名的目錄,你會得到一個所有當(dāng)前運行進程的ID列表。下面的代碼清單里的process_list()函數(shù)返回一個包含所有當(dāng)前運行進程ID的列表。這個列表的長度等于系統(tǒng)運行進程的總數(shù),正如你運行這個腳本看到的一樣:
#!/usr/bin/env python """ List of all process IDs currently active """ from __future__ import print_function import os def process_list(): pids = [] for subdir in os.listdir('/proc'): if subdir.isdigit(): pids.append(subdir) return pids if __name__=='__main__': pids = process_list() print('Total number of running processes:: {0}'.format(len(pids)))
運行上面的腳本時,輸出結(jié)果和下面輸出類似:
每個進程目錄都包含了大量的其他文件和目錄,這些目錄包含了各種關(guān)于進程調(diào)用命令,使用的共享庫和其他的信息。
塊設(shè)備
接下來的腳本通過訪問sysfs虛擬文件系統(tǒng)列出了所有的塊設(shè)備信息。你能夠在/sys/block目錄下找到系統(tǒng)上的所有塊設(shè)備。因此,你的系統(tǒng)上會有/sys/block/sda,/sys/block/sdb和其他的類似目錄。為了找到這些設(shè)備,我們可以遍歷/sys/block目錄然后通過簡單的正則表達式去匹配我們要查找的內(nèi)容。
#!/usr/bin/env python """ Read block device data from sysfs """ from __future__ import print_function import glob import re import os # Add any other device pattern to read from dev_pattern = ['sd.*','mmcblk*'] def size(device): nr_sectors = open(device+'/size').read().rstrip('n') sect_size = open(device+'/queue/hw_sector_size').read().rstrip('n') # The sect_size is in bytes, so we convert it to GiB and then send it back return (float(nr_sectors)*float(sect_size))/(1024.0*1024.0*1024.0) def detect_devs(): for device in glob.glob('/sys/block/*'): for pattern in dev_pattern: if re.compile(pattern).match(os.path.basename(device)): print('Device:: {0}, Size:: {1} GiB'.format(device, size(device))) if __name__=='__main__': detect_devs()
如果你運行了這個腳本,你將會看到與下面類似的輸出結(jié)果:
Device:: /sys/block/sda, Size:: 465.761741638 GiB Device:: /sys/block/mmcblk0, Size:: 3.70703125 GiB
當(dāng)我運行這個腳本時,我額外插入了一張SD卡。所以你會看到這個腳本檢測到了它(上面輸出的第二行,譯者注)。你同樣可以擴展這個腳本去識別其他的塊設(shè)備(比如虛擬硬盤)。
構(gòu)建命令行工具
允許用戶指定命令行參數(shù)去自定義程序的默認(rèn)行為是所有Linux命令行工具的一個普遍特征。argparse模塊能使你程序擁有與內(nèi)置工具界面類似的界面。下一個代碼清單展示了一個獲取你系統(tǒng)上所有用戶并把它們相應(yīng)的登陸shell打印出來的程序。
#!/usr/bin/env python """ Print all the users and their login shells """ from __future__ import print_function import pwd # Get the users from /etc/passwd def getusers(): users = pwd.getpwall() for user in users: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) if __name__=='__main__': getusers()
當(dāng)運行上面的腳本時,它會打印出你系統(tǒng)上所有的用戶和它們的登陸shell
現(xiàn)在,我們假設(shè)你想讓腳本使用者能夠選擇是否想看到系統(tǒng)的其他用戶(比如daemon,apache)。我們通過使用argparse模塊擴展之前的代碼來實現(xiàn)這個功能,就像下面的代碼。
#!/usr/bin/env python """ Utility to play around with users and passwords on a Linux system """ from __future__ import print_function import pwd import argparse import os def read_login_defs(): uid_min = None uid_max = None if os.path.exists('/etc/login.defs'): with open('/etc/login.defs') as f: login_data = f.readlines() for line in login_data: if line.startswith('UID_MIN'): uid_min = int(line.split()[1].strip()) if line.startswith('UID_MAX'): uid_max = int(line.split()[1].strip()) return uid_min, uid_max # Get the users from /etc/passwd def getusers(no_system=False): uid_min, uid_max = read_login_defs() if uid_min is None: uid_min = 1000 if uid_max is None: uid_max = 60000 users = pwd.getpwall() for user in users: if no_system: if user.pw_uid >= uid_min and user.pw_uid <= uid_max: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) else: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) if __name__=='__main__': parser = argparse.ArgumentParser(description='User/Password Utility') parser.add_argument('--no-system', action='store_true',dest='no_system', default = False, help='Specify to omit system users') args = parser.parse_args() getusers(args.no_system)
使用–help選項來運行上面的腳本,你會看到一個帶有可選項(和作用)的友好幫助信息
$ ./getusers.py --help usage: getusers.py [-h] [--no-system] User/Password Utility optional arguments: -h, --help show this help message and exit --no-system Specify to omit system users
上面腳本的一個例子調(diào)用如下:
$ ./getusers.py --no-system gene:/bin/bash
當(dāng)你傳遞一個無效的參數(shù)時,腳本會報錯:
$ ./getusers.py --param usage: getusers.py [-h] [--no-system] getusers.py: error: unrecognized arguments: --param
讓我們一起來簡單地了解下在上面的腳本中我們是如何使用argparse模塊的parser=argparse.ArgumentParser(description='User/Password Utility')這行代碼使用一個描述腳本作用的可選參數(shù)創(chuàng)建了一個新的ArgumentParser對象。
然后,我們在下一行代碼:parser.add_argument(‘–no-system', action='store_true', dest='no_system', default = False, help='Specify to omit system users')里使用了add_argument()方法添加一些參數(shù),讓腳本能夠識別命令行選項。這個方法的第一個參數(shù)是腳本使用者在調(diào)用腳本時作為參數(shù)提供的選項名。下一個參數(shù)action=store_true表明了這是一個布爾選項。也就是說這個參數(shù)的有無在一定程度上會對程序產(chǎn)生影響。dest參數(shù)指定了一個變量來保存選項值并提供給腳本使用。如果用戶沒有提供選項,通過參數(shù)default=False可以設(shè)置默認(rèn)值為False。最后一個參數(shù)是腳本展示關(guān)于這個選項的幫助信息。最后,使用parse_args()方法處理參數(shù):args=parser.parse_args()。一旦處理完畢,使用args.option_dest可以獲取到用戶提供的選項值,這里的option_dest就是你設(shè)置參數(shù)時指定的dest變量。這行代碼getusers(args.no_system)使用用戶提供的no_system選項值作為參數(shù)調(diào)用了getusers()。
接下來的腳本展示了你該如何在你的腳本里提供用戶一個非布爾值的選項。這個腳本重寫了代碼清單6,添加了額外的選項讓你能夠指定檢測你感興趣的網(wǎng)絡(luò)設(shè)備。
#!/usr/bin/env python from __future__ import print_function from collections import namedtuple import argparse def netdevs(iface=None): ''' RX and TX bytes for each of the network devices ''' with open('/proc/net/dev') as f: net_dump = f.readlines() device_data={} data = namedtuple('data',['rx','tx']) for line in net_dump[2:]: line = line.split(':') if not iface: if line[0].strip() != 'lo': device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0), float(line[1].split()[8])/(1024.0*1024.0)) else: if line[0].strip() == iface: device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0), float(line[1].split()[8])/(1024.0*1024.0)) return device_data if __name__=='__main__': parser = argparse.ArgumentParser(description='Network Interface Usage Monitor') parser.add_argument('-i','--interface', dest='iface', help='Network interface') args = parser.parse_args() netdevs = netdevs(iface = args.iface) for dev in netdevs.keys(): print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))
當(dāng)你不提供任何參數(shù)運行這個腳本時,它的運行結(jié)果實際上和之前的版本一樣。但是,你同樣可以指定你可能感興趣的網(wǎng)絡(luò)設(shè)備。舉個例子:
$ ./net_devs_2.py em1: 0.0 MiB 0.0 MiB wlan0: 146.099492073 MiB 12.9737148285 MiB virbr1: 0.0 MiB 0.0 MiB virbr1-nic: 0.0 MiB 0.0 MiB $ ./net_devs_2.py --help usage: net_devs_2.py [-h] [-i IFACE] Network Interface Usage Monitor optional arguments: -h, --help show this help message and exit -i IFACE, --interface IFACE Network interface $ ./net_devs_2.py -i wlan0 wlan0: 146.100307465 MiB 12.9777050018 MiB
使你的腳本能在任意地方運行
在這篇文章的幫助下,你可能已經(jīng)能夠給自己寫一個或者更多的有用腳本,這些腳本你想每天都使用,就像Linux的其他命令一樣。去實現(xiàn)這個愿望最簡單的方式就是使這些腳本能夠運行(給腳本添加運行權(quán)限,譯者注)并且給這些命令設(shè)置BASH別名(BASH alias)。你同樣可以刪除.py后綴并且把這個文件放到一個標(biāo)準(zhǔn)位置比如/usr/local/sbin。
其他的有用標(biāo)準(zhǔn)庫模塊
除了到目前為止我們在這篇文章里看到過的標(biāo)準(zhǔn)庫外,這里還有很多其它可能有用的標(biāo)準(zhǔn)庫:subprocess,ConfigParser, readline 和 curses。
下一步?
在這個階段,根據(jù)你自己的Python經(jīng)驗和深入探索Linux,你選擇以下方式的一種。如果你已經(jīng)寫了大量的shell腳本或者命令管道去深入探索各種Linux,請試一下Python。如果你想以一種更簡易的方式去寫你自己的腳本工具去執(zhí)行各種任務(wù),請試一下Python。最后,如果你已經(jīng)使用其他種類的Python在Linux上編程,祝你使用Python深入探索Linux愉快。
相關(guān)文章
關(guān)于不懂Chromedriver如何配置環(huán)境變量問題解決方法
這篇文章主要介紹了關(guān)于不懂Chromedriver如何配置環(huán)境變量問題解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-06-06pyspark連接mysql數(shù)據(jù)庫報錯的解決
本文主要介紹了pyspark連接mysql數(shù)據(jù)庫報錯的解決,因為spark中缺少連接MySQL的驅(qū)動程序,下面就來介紹一下解決方法,感興趣的可以了解一下2023-11-11從0到1使用python開發(fā)一個半自動答題小程序的實現(xiàn)
這篇文章主要介紹了從0到1使用python開發(fā)一個半自動答題小程序的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05python實現(xiàn)學(xué)生信息管理系統(tǒng)(精簡版)
這篇文章主要為大家詳細(xì)介紹了python實現(xiàn)學(xué)生信息管理系統(tǒng)的精簡版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-11-11Python編程functools模塊創(chuàng)建修改的高階函數(shù)解析
本篇文章主要為大家介紹functools模塊中用于創(chuàng)建、修改函數(shù)的高階函數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2021-09-09