溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python?3.x踩坑的示例分析

發(fā)布時間:2022-03-22 15:03:59 來源:億速云 閱讀:419 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要為大家展示了“Python 3.x踩坑的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Python 3.x踩坑的示例分析”這篇文章吧。

處處有坑

1. 文件讀取 open

# 我們打開文件使用 open 方法
xml = open("demo.xml")
# 使用 open 命令讀取文件時,經(jīng)常會出現(xiàn)下列錯誤
Traceback (most recent call last):
  File "TempConvert.py", line 84, in <module>
    for line in xml:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x8d in position 38: illegal multibyte sequence
# 出現(xiàn)這個錯誤的原因是系統(tǒng)默認(rèn)打開的編碼方式和文件不一致,需要通過帶格式參數(shù)的方式打開
# 比如,文件如果是 utf-8 格式文件,則需要采用下列格式參數(shù):
xml = open("demo.xml", encoding="utf-8")

2. 正則表達(dá)式 \S 與 \\S

首先提出一個問題,使用正則表達(dá)式獲取到字符串中的郵箱列表。 例:A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM

# 我們可以通過一個簡單的正則表達(dá)式,這里不考慮其他復(fù)雜條件
import re
str = 'A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM'
lst1 = re.findall('\S+@\S+', s)
print(lst1) # ['csev@umich.edu', 'cwen@iupui.edu']

# 然而我們發(fā)現(xiàn),下列正則表達(dá)式也有同樣的結(jié)果
lst2 = re.findall('\\S+@\\S+', s)
print(lst2)

這就比較奇怪了,因為在其他語言的正則表達(dá)式中,\S\\S 代表的含義并不相同,\S 表示一個非空字符,而 \\S 表示匹配字符串 \S,于是我們作下列嘗試:

'\S' == '\\S' # True
len('\\S') # 2
len('\S') # 2

是不是驚呆了!于是我又嘗試

'\s' == '\\s' # True
len('\\s') # 2
len('\s') # 2

'\n' == '\\n' # False
len('\\n') # 2
len('\n') # 1

我們發(fā)現(xiàn) \s\n 的情況并不相同,通過一番查詢,找到了下面的文章:

Python regex '\s' vs '\s'

文中提到

Don't confuse python-level string-escaping and regex-level string-escaping. Since s is not an escapable character at python-level, the interpreter understand a string like \s as the two characters \ and s. Replace s with n, and it understands it as the newline character.
不要混淆 Python 中的字符串轉(zhuǎn)義和正則表達(dá)式級別的字符串轉(zhuǎn)義。由于 s 在 Python 不是可轉(zhuǎn)義字符,解釋器將 \s 這樣的字符串理解為兩個字符 \ 和 s。將 s 替換為 n,它將其理解為換行符。

雖然沒有提及到更權(quán)威的說法,但是也反應(yīng)出了,如果是 \s 會被當(dāng)做是兩個字符,如果是 \\s 因為 \\ 是可轉(zhuǎn)義字符,被當(dāng)做了 \ 一個字符,\\s 也就被當(dāng)做了 \s 兩個字符。所以才會出現(xiàn)這種情況。

'\s' == '\\s' # True

3. 正則表達(dá)式匹配方法 match

在學(xué)習(xí)正則表達(dá)式匹配規(guī)則時候發(fā)現(xiàn),Python 正則匹配的方式和其他的稍有不同,比如上一條提到的 \S\\S 的問題,然后還有下面的:

Python 的正則匹配是從頭匹配,舉個例子,如果我們要匹配一個字符串中的電話號碼

在 JS 中你可以用下列的正則匹配

// 使用 JS 的方式,我們可以有下列的寫法
'我的手機(jī)號碼是15900000000不要告訴別人,否則我就把你號碼是13900000000告訴別人'.match(/1[0-9]{10}/g)
// (2) ['15900000000', '13900000000']

但是如果你把同樣的正則放到 Python 中則不那么好使

import re
str = '我的手機(jī)號碼是15900000000不要告訴別人,否則我就把你號碼是13900000000告訴別人'

# 錯誤的寫法
mah = re.match('1[0-9]{10}', str)
print(mah)
# None

因為 Python 的匹配 match 默認(rèn)是從開頭開始匹配的,而 1 并不一定是給定的字符串的首字母。

# 應(yīng)該使用另一個方法 findall 代替
mah = re.findall('1[0-9]{10}', str)
print(mah)
# ['15900000000', '13900000000']

從這一點可以看出,Python 的很多庫都提供用不同于其他語言的方法,作為其他語言轉(zhuǎn)學(xué) Python 的小伙伴要實際測試過方法或者熟知的情況下使用,而不應(yīng)該不加思考的定式思維,一廂情愿的覺得 Python 就和其他的語言一樣。

4. 幫助文檔 pydoc

Python 中對庫或者方法的幫助查看可以用下列的方式進(jìn)行:

  • 【可選】在命令行環(huán)境下輸入 python 即可進(jìn)入 Python 編譯環(huán)境

  • 使用 dir(庫、對象) 的方式查看庫或者對象可以提供的方法

dir('字符串') # 查看字符串有哪些操作方法

import re
dir(re) # 查看正則表達(dá)式庫有哪些操作方法
  • 使用 help(庫、對象) 的方式查看庫或者對象的幫助信息

import re
help(re) # 查看正則表達(dá)式庫的幫助文檔
dir(re.match) # 查看正則表達(dá)式的 `match` 的幫助信息

如果我們是想把幫助文檔寫入文本文件中,可以在 命令行中 使用命令:

# 將 re 庫的幫助信息到 html 文檔
python -m pydoc -w re

# windows 下可以用下列方法輸出到文本文件
python -m pydoc re > d:\re.txt

更多關(guān)于 pydoc 的信息可以參考官方文檔 pydoc

5. 字符串 encode base64 編碼

一些教程上對字符串的 base64 編碼的方式是這樣的:

str = "this is string example....wow!!!";
print "Encoded String: " + str.encode('base64','strict')

# 預(yù)計輸出結(jié)果
Encoded String: dGhpcyBpcyBzdHJpbmcgZXhhbXBsZS4uLi53b3chISE=

但是這個代碼卻會報錯:

LookupError: 'base64' is not a text encoding; use codecs.encode() to handle arbitrary codecs

據(jù)了解,這種錯誤的寫法其實是來源于 Python 2.x 的寫法,但是在 Python 3.x 中寫法發(fā)生了變化,字符串的 base64 正確編碼方式應(yīng)該是:

import base64
str = "this is string example....wow!!!"

# 返回原字符串編碼為字節(jié)串對象的版本
strb = str.encode()
base64b = base64.b64encode(strb)
base64 = base64b.decode()
print(base64)

6. Python 調(diào)用 C# 動態(tài)鏈接庫

在百度搜索了很多關(guān)于 Python 調(diào)用 C# 動態(tài)鏈接庫的方式,大多是如下代碼:

import clr
# clr.FindAssembly('DotNetWithPython.dll') # dll在當(dāng)前目錄
clr.AddReferenceToFile('DotNetWithPython.dll') # dll在當(dāng)前目錄

from DotNetWithPython import * # 導(dǎo)入動態(tài)鏈接庫中的所有類

if __name__ == '__main__':
    mainapp = MainForm() # 初始化 MainForm 類對象

可惜啊,沒有能正常使用的,我也不清楚到底是哪里出了問題,為什么都沒有效果呢,難不成這些都是 Python 2.x 的用法嗎?(我學(xué)的是 Python 3.x)

作了如下思考:

python 的 clr 即 PythonNet,那么是否直接到 PythonNet 官方或者 github 上查找相關(guān)代碼呢?

于是搜索到了下列地址:pythonnet.github.io/按照里面給出的代碼逐個嘗試,首先是這個:

from System import String
from System.Collections import *

我們發(fā)現(xiàn)會報錯:

Traceback (most recent call last):
  File "d:/Temp/PythonProjects/Demos/DllDo.py", line 10, in <module>
    from System import String
ModuleNotFoundError: No module named 'System'

我們嘗試把代碼修改為:

import clr
from System import String
from System.Collections import *

可以確定,我們對 .NET 相關(guān)類的調(diào)用必須要 import clr 我們繼續(xù)嘗試,當(dāng)嘗試到下列代碼時:

import clr
from System.Drawing import Point
p = Point(5, 5)

又報錯了:

d:/Temp/PythonProjects/Demos/DllDo.py:11: DeprecationWarning: The module was found, but not in a referenced namespace.
Implicit loading is deprecated. Please use clr.AddReference('System.Drawing').
  from System.Drawing import Point

從給出的錯誤信息中,我們可以看出,我們需要對空間進(jìn)行引用:

import clr
clr.AddReference('System.Drawing')
from System.Drawing import Point

p = Point(5, 5)
print(p)
# {X=5,Y=5}

到了這一步,我們基本確定 Python 調(diào)用 C# 是沒有問題的,那么如果才能調(diào)用自己定義的 dll 動態(tài)鏈接庫呢?我們嘗試按照前文系統(tǒng)類的引用方式:

import clr
clr.AddReference('DotNetWithPython')
from DotNetWithPython import MainForm

mainapp = MainForm()

結(jié)果報錯:

Traceback (most recent call last):
  File "d:/Temp/PythonProjects/Demos/DllDo.py", line 12, in <module>
    from DotNetWithPython import MainForm
ModuleNotFoundError: No module named 'DotNetWithPython'

于是我又想:

clr 可以正常調(diào)用 .NET 本身提供的類對象,調(diào)用不到我的 自己寫的動態(tài)鏈接庫和 .NET 本身提供的差異在于不在系統(tǒng)環(huán)境中,自己的 dll 在當(dāng)前目錄或者其他目錄

于是我們使用 dir(clr) 確定了一下是否有什么方法可用

import clr
dir(clr)

# ['AddReference', 'FindAssembly', 'GetClrType', 'ListAssemblies', 'Microsoft', 'Python', 'System', '_AtExit', '__class__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', '_extras', 'clrModule', 'clrmethod', 'clrproperty', 'e__NativeCall', 'getPreload', 'setPreload']

我們發(fā)現(xiàn)了方法 FindAssembly 感覺很像,于是我們按照前文系統(tǒng)類的引用方式及這一句進(jìn)行測試:

import clr
clr.FindAssembly('DotNetWithPython.dll')
clr.AddReference('DotNetWithPython')
from DotNetWithPython import MainForm

mainapp = MainForm()

還是一樣的錯誤,我都要哭了,于是我只能到 PythonNet Github 的 issues 中尋找答案,發(fā)現(xiàn)提出這個問題的人很多,并且問題被鎖定在了 .net core、.net 5,而 .Net Framework 中沒有出現(xiàn)這種問題,我于是新建了一個基于 .Net Framework 4.x 的項目進(jìn)行簡單測試,發(fā)現(xiàn)確實不會報錯。

現(xiàn)在問題很明確了,但是并沒有得到解決,于是我只能一條條看那難懂的 issues 列表,功夫不負(fù)有心人,我找到了這個帖子 issues 1536,明確的給出了說法,Pythonnet 2.5 does not support .NET 5、It is supported in v3 previews.。

好的吧,于是我用 pip list 查看所有 Python 第三方庫的版本

C:\Users\Administrator>pip list
Package          Version
---------------- ----------
click            7.1.2
pip              22.0.3
pycparser        2.21
PyQt5            5.15.4
pyqt5-plugins    5.15.4.2.2
PyQt5-Qt5        5.15.2
PyQt5-sip        12.9.1
pyqt5-tools      5.15.4.3.2
python-dotenv    0.19.2
pythonnet        2.5.2
qt5-applications 5.15.2.2.2
qt5-tools        5.15.2.1.2
setuptools       41.2.0

果然,pythonnet 的版本是 2.5.2,我對項目進(jìn)行降級測試,發(fā)現(xiàn) .net core 僅在版本為 net core 1.x 時候支持,2.x-3.x、.NET 5 均不支持。

所以你如果使用的是 pythonnet 2.x 版本,就不要嘗試使用更高版本的 .net core 實現(xiàn)你的功能了,否則需要更新 pythonnet 到更高版本

繼續(xù)看 issues 1536,發(fā)現(xiàn)即使更新了版本還是會存在問題,并跟蹤到了 issues 1473 我嘗試將 pythonnet 升級到 3.x previews 版本但是出現(xiàn)的錯誤,沒有升級成功,所以并沒有繼續(xù)測試后續(xù)的功能。

以上是“Python 3.x踩坑的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI