Python中你可能不知道的platform
某次在查看測試機(Ubuntu)發行版本時,發現得到的結果並不準確;本應得到Ubuntu,結果顯示的卻是Debian,大致代碼如下
...distribution_name = ["centos", "ubuntu", "redhat", "debian", "fedora"]class_name = {"centos": "CentOS", "ubuntu": "Ubuntu", "redhat": "RedHat", "debian": "Debian", "fedora": "Fedora"}...for name in distribution_name: if name in platform.platform.lower(): _platform = class_name[name] break...
項目使用的是可移植版的python(Anaconda),第一反應是用交互模式驗證一下。
>>> import platform>>> platform.platform()"Linux-4.4.0-62-generic-x86_64-with-debian-stretch-sid"
得出的結果卻為Debian。既沒有報錯,也沒有異常,那麼問題是出在哪裡了呢?
遂又使用系統自帶的python驗證。
>>> import platform>>> platform.platform()"Linux-4.4.0-62-generic-x86_64-with-Ubuntu-16.04-xenial"
然而系統得到的卻是正確的結果,難道是移植版本的bug?
在同事的提醒下,意識到應該看一下platform模塊的源碼,看看問題是否出在這裡。首先,查看platform模塊中的platform方法
...def platform(aliased=0, terse=0): result = _platform)_cache.get((aliased, terse), None) if result is not None: return resut system, node, release, version, machine, processor = uname() ... elif system in ("Linux", ): disname, distversion, distid = dist("") ... def uname(): ... try: system, node, release, version, machine = os.uname() ...def dist(distname="", version="", id="", supported_dists=_supported_dists): return linux_distribution(distname, version, id, supported_dists=supported_dists, full_distribution_name=0)...
當調用platform方法時,首先它回去模塊緩存信息中查找,若有則直接返回。因為是第一次調用,緩存中肯定不會存有相應信息,這裡可以跳過。
接著,通過uname方法獲取system, node, release等信息,而uname方法主要是調用os.uname()獲得相應信息。>>> import os>>> os.uname()("Linux", "uyun-VirtualBox", "4.4.0-62-generic", "#83-Ubuntu SMP Wed Jan 18 14:10:15 UTC 2017", "x86_64")
嘗試使用os.uname()後,除了能得到系統版本外發現並沒有期望得到的相應發行版信息,跳過。
然後則是dist()方法。發現dist()方法實際上調用的python_implementation()。最終確定,獲取系統版本的關鍵就在python_implementation()方法中。以下為比較可移植版的python和Ubuntu自帶的python源碼(具體行號可能存在些許偏差)
...259 _supported_dists = (260 "SuSE", "debian", "fedora", "redhat", "centos",261 "mandrake", "mandriva", "rocks", "slackware", "yellowdog", "gentoo",262 "UnitedLinux", "turbolinux")......291 def linux_distribution(distname="", version="", id="",292 supported_dists=_supported_dists,293 full_distribution_name=1):...315 try:316 etc = os.listdir("/etc")317 except os.error:318 # Probably not a Unix system319 return distname, version, id ......
以上為移植版python的platform模塊源碼。
···261 _supported_dists = (262 "SuSE", "debian", "fedora", "redhat", "centos",263 "mandrake", "mandriva", "rocks", "slackware", "yellowdog", "gentoo",264 "UnitedLinux", "turbolinux", "Ubuntu")...293 _distributor_id_file_re = re.compile("(?:DISTRIB_IDs*=)s*(.*)", re.I)294 _release_file_re = re.compile("(?:DISTRIB_RELEASEs*=)s*(.*)", re.I)295 _codename_file_re = re.compile("(?:DISTRIB_CODENAMEs*=)s*(.*)", re.I)296297 def linux_distribution(distname="", version="", id="",298 supported_dists=_supported_dists,299 full_distribution_name=1):...321 # check for the LSB /etc/lsb-release file first, needed so322 # that the distribution doesn"t get identified as Debian.323 try:324 with open("/etc/lsb-release", "rU") as etclsbrel:325 for line in etclsbrel:326 m = _distributor_id_file_re.search(line)327 if m:328 _u_distname = m.group(1).strip()329 m = _release_file_re.search(line)330 if m:331 _u_version = m.group(1).strip()332 m = _codename_file_re.search(line)333 if m:334 _u_id = m.group(1).strip()335 if _u_distname and _u_version:336 return (_u_distname, _u_version, _u_id)337 except (EnvironmentError, UnboundLocalError):338 pass339340 try:341 etc = os.listdir("/etc")342 except os.error:343 # Probably not a Unix system344 return distname,version,id ......
以上為Ubuntu系統自帶python的platform模塊源碼。
首先可以看到,Ubuntu版本中platform的_supported_dists元組中多了一個Ubuntu元素,並且在linux_destribution方法中,首先會嘗試讀取/etc/lsb-release文件;接著通過正則匹配(_distributor_id_file_re, _release_file_re, _codename_file_re),查找相應的值,如果都有結果,則直接返回。
讀取/etc/lsb-release,發現裡面存了一些Ubuntu系統版本信息。
DISTRIB_ID=UbuntuDISTRIB_RELEASE=16.04DISTRIB_CODENAME=xenialDISTRIB_DESCRIPTION="Ubuntu 16.04 LTS"
那麼顯然,三個正則都將匹配到對應的值,返回(Ubuntu, 16.04, xenial)。
最終,正確的獲取到Ubuntu發行版本。推薦閱讀:
※Kaggle HousePrice : LB 0.11666(前15%), 用搭積木的方式(2.實踐-特徵工程部分)
※八大排序演算法python實現合輯
※[Python + SQL] NBA史上最弱的球隊是哪一個
※簡單三步,用 Python 發郵件