开发者

Python 3.x踩坑实战汇总

目录
  • 纪要
  • 处处有坑
    • 1. 文件读取 open
    • 2. 正则表达式 \S 与 \\S
    • 3. 正则表达式匹配方法 match
    • 4. 帮助文档 pydoc
    • 5. 字符串 encode base64 编码
    • 6. python 调用 C# 动态链接库
  • 总结

    纪要

    本文用于记录学习 Python 过程中遇到的一些小问题,如果遇到的是比较大的问题会单独开页面分析学习

    处处有坑

    1. 文件读取 open

    # 我们打开文件使用 open 方法
    xml = open("demo.xml")
    # 使用 open 命令读取文件时,经常会出现下列错误
    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
    # 出现这个错误的原因是系统默认打开的编码方式和文件不一致,需要通过带格式参数的方式打开
    # 比如,文件如果是 utf-8 格式文件,则需要采用下列格式参数:
    xml = open("demo.xml", encoding="utf-8")

    2. 正则表达式 \S 与 \\S

    首先提出一个问题,使用正则表达式获取到字符串中的邮箱列表。 例:A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM

    # 我们可以通过一个简单的正则表达式,这里不考虑其他复杂条件
    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']
    
    # 然而我们发现,下列正则表达式也有同样的结果
    lst2 = re.findall('\\S+@\\S+', s)
    print(lst2)

    这就比较奇怪了,因为在其他语言的正则表达式中,\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

    我们发现 \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 中的字符串转义和正则表达式级别的字符串转义。由于 s 在 Python 不是可转义字符,解释器将 \s 这样的字符串理解为两个字符 \ 和 s。将 s 替换为 n,它将其理解为换行符。

    虽然没有提及到更权威的说法,但是也反应出了,如果是 \s 会被当做是两个字符,如果是 \\s 因为 \\ 是可转义字符,被当做了 \ 一个字符,\\s 也就被当做了 \s 两个字符。所以才会出现这种情况。

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

    3. 正则表达式匹配方法 match

    在学习正则表达式匹配规则时候发现,Python 正则匹配的方式和其他的稍有不同,比如上一条提到的 \S\\S 的问题,然后还有下面的:

    Python 的正则匹配是从头匹配,举个例子,如果我们要匹配一个字符串中的电话号码

    在 js 中你可以用下列的正则匹配

    // 使用 JS 的方式,我们可以有下列的写法
    '我的手机号码是15900000000不要告诉别人,否则我就把你号码是13900000000告诉别人'.match(/1[0-9]{10}/g)
    // (2) ['15900000000', '13900000000']

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

    import re
    str = '我的手机号码是15900000000不要告诉别人,否则我就把你号码是13900000000告诉别人'
    
    # 错误的写法
    mah = re.match('1[0-9]{10}', str)
    print(mah)
    # None

    因为 Python 的匹配 match 默认是从开头开始匹配的,而 1 并不一定是给定的字符串的首字母。

    # 应该使用另一个方法 findall 代替
    mah = re.findall('1[0-9]{10}', str)
    print(mah)
    # ['15900000000', '13900000000']

    从这一点可以看出,Python 的很多库都提供用不同于其他语言的方法,作为其他语言转学 Python 的小伙伴要实际测试过方法或者熟知的情况下使用,而不应该不加思考的定式思维,一厢情愿的觉得 Python 就和其他的语言一样。

    4. 帮助文档 pydoc

    Python 中对库或者方法的帮助查看可以用下列的方式进行:

    • 【可选】在命令行环境下输入 python 即可进入 Python 编译环境
    • 使用 dir(库、对象) 的方式查看库或者对象可以提供的方法
    dir('字符串') # 查看字符串有哪些操作方法
    
    import re
    dir(re) # 查看正则表达式库有哪些操作方法
    • 使用 help(库、对象) 的方式查看库或者对象的帮助信息
    import re
    help(re) # 查看正则表达式库的帮助文档
    dir(re.match) # 查看正则表达式的 `match` 的帮助信息

    如果我们是想把帮助文档写入文本文件中,可以在 命令行中 使用命令:

    # 将 re 库的帮助信息到 html 文档
    python -m pydoc -w re
    
    # windows 下可以用下列方法输出到文本文件
    python -m pydoc re > d:\re.txt

    更多关于 pydoc 的信息可以参考官方文档 pydoc

    5. 字符串 encode base64 编码

    一些教程上对字符串的 base64 编码的方式是这样的:

    str = "this is string example....wow!!!";
    print "Encoded String: " + str.encode('base64','strict')
    
    # 预计输出结果
    Encoded String: dGhpcyBpcyBzdHJpbmcgZXhhbXBsZS4uLi53b3chISE=

    但是这个代码却会报错:

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

    据了解,这种错误的写法其实是来源于 Python 2.x 的写法,但是在 Python 3.x 中写法发生了变化,字符串的 base64 正确编码方式应该是:

    import base64
    str = "this is string example....wow!!!"
    
    # 返回原字符串编码为字节串对象的版本
    strb = str.encode()
    base64b = base64.b64encode(strb)
    base64 = base64b.decode()
    print(base64)

    6. Python 调用 C# 动态链接库

    在百度搜索了很多关于 Python 调用 C# 动态链接库的方式,大多是如下代码:

    import clr
    # clr.FindAssembly('DotNetWithPython.dll') # dll在当前目录
    clr.AddReferenceToFile('DotNetWithPython.dll') # dll在当前目录
    
    from DotNetWithPython import * # 导入动态链接库中的所有类
    
    if __name__ == '__main__':
        mainapp = MainForm() # 初始化 MainForm 类对象

    可惜啊,没有能正常使用的,我也不清楚到底是哪里出了问题,为什么都没有效果呢,难不成这些都是 Python 2.x 的用法吗?(我学的是 Python 3.x)

    作了如下思考:

    python 的 clr 即 PythonNet,那么是否直接到 PythonNet 官方或者 github 上查找相关代码呢?

    编程客栈

    于是搜索到了下列地址:pythonnet.github.io编程客栈/按照里面给出的代码逐个尝试,首先是这个:

    from System import String
    from System.Collections import *

    我们发现会报错:

    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 相关类的调用必须要 import clr 我们继续尝试,当尝试到下列代码时:

    imwww.cppcns.comport 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

    从给出的错误信息中,我们可以看出,我们需要对空间进行引用:

    import clr
    clr.AddReference('System.Drawing')
    from System.Drawing import Point
    
    p = Point(5, 5)
    print(p)
    # {X=5,Y=5}

    到了这一步,我们基本确定 Python 调用 C# 是没有问题的,那么如果才能调用自己定义的 dll 动态链接库呢?我们尝试按照前文系统类的引用方式:

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

    结果报错:

    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 可以正常调用 .NET 本身提供的类对象,调用不到我的 自己写的动态链接库和 .NET 本身提供的差异在于不在系统环境中,自己的 dll 在当前目录或者其他目录

    于是我们使用 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']

    我们发现了方法 FindAssembly 感觉很像,于是我们按照前文系统类的引用方式及这一句进行测试:

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

    还是一样的错误,我都要哭了,于是我只能到 PythonNet Github 的 issues 中寻找答案,发现提出这个问题的人很多,并且问题被锁定在了 .net core、.net 5,而 .Net Framework 中没有出现这种问题,我于是新建了一个基于 .Net Framework 4.x 的项目进行简单测试,http://www.cppcns.com发现确实不会报错。

    现在问题很明确了,但是并没有得到解决,于是我只能一条条看那难懂的 issues 列表,功夫不负有心人,我找到了这个帖子 issues 1536,明确的给出了说法,Pythonnet 2.5 does not support .NET 5It is supported in v3 previews.

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

    C:\Users\Adm编程客栈inistrator>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,我对项目进行降级测试,发现 .net core 仅在版本为 net core 1.x 时候支持,2.x-3.x、.NET 5 均不支持。

    所以你如果使用的是 pythonnet 2.x 版本,就不要尝试使用更高版本的 .net core 实现你的功能了,否则需要更新 pythonnet 到更高版本

    继续看 issues 1536,发现即使更新了版本还是会存在问题,并跟踪到了 issues 1473 我尝试将 pythonnet 升级到 3.x previews 版本但是出现的错误,没有升级成功,所以并没有继续测试后续的功能。

    总结

    到此这篇关于Python 3.x踩坑实战汇总的文章就介绍到这了,更多相关Python3.x踩坑内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜