zoukankan      html  css  js  c++  java
  • Python常见陷阱

    大多数情况下,Python的目标是成为一门简洁和一致的语言,同时避免意外情况。 然而,有些情况可能会使新人困惑。

    其中一些情况是有意为之的,但可能有潜在的风险。而另一些可以说是语言的缺陷。 总的来说,下面是一些乍看起来很取巧的行为,不过只要您注意了强调的事项, 这些行为通常是可取的。

    可变默认参数:

    例如:

    def append_to(element,to=[]):
        to.append(element)
        return to

    操作

    my_list = append_to(12)
    print(my_list)
    
    my_other_list = append_to(42)
    print(my_other_list)

    期望:

    [12]
    [42]

    结果:

    [12]
    [12,42]

    原因:

    当函数被定义时,一个新的列表就会被创建一次,而且同一个列表在每次成功的调用中都被使用。

    当函数被定义时,Python的默认参数就被创建一次,而不是每次调用函数的时候被创建。这意味着,如果使用一个可变默认参数并改变了它,将会在未来所有对此函数的调用中改变这个对象。

    解决办法:

    在每次函数调用中,通过使用指示没有提供参数的默认参数(例如None)来创建一个新的对象。

    def append_to(element, to = None):
        if to is None:
            to = []
        to.append(element)
        return to

    注意:把列表对象作为第二个参数传入。

    什么情况下陷阱不是陷阱?

    有时可以专门“利用”(或者说特地使用)这种行为来维护函数调用间的状态。这通常用于编写缓存函数。

    迟绑定闭包

    另一个常见的困惑就是Python在闭包(或者在周围全局作用域)中绑定变量的方式。

    例如:

    def create_multipliers():
        return [lambda x : i * x for i in range(5)]

    期望:

    for multiplier in create_multipliers():
        print(multiplier(2))

    一个包含五个函数的列表,每个函数都有他们自己的封闭变量i诚意他们的参数,得到:

    0
    2
    4
    6
    8

    结果:

    8
    8
    8
    8
    8

    五个函数被创建了,他们全都是4乘以x

    原因:

    Python中的闭包是迟绑定。这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的。

    这里,不论任何返回的函数是如何被调用的,i 的值是调用时在周围作用域中查询到的。接着,循环完成,i 的值最终变成了4。

    关于这个陷阱有一个普遍严重的误解,被认为是和Python中的lambda有关。由lambda表达式创建的函数并没有什么特别,事实上,同样的问题也出现在使用普通的定义上:

    def create_multipliers():
        multipliers = []
    
        for i in range(5):
            def multiplier(x):
                return i * x
            multipliers.append(multiplier)
    
        return multipliers

    解决办法:

    最一般的解决方案可以说是有点取巧。由于Python拥有在前文提到的为函数默认参数赋值的行为,故可以创建一个立即绑定参数的闭包。

    def create_multipliers():
        return [lambda x, i=i : i * x for i in range(5)]

    或者,可以使用functools.partial函数:

    from functools import partial
    from operator import mul
    
    def create_multipliers():
        return [partial(mul, i) for i in range(5)]

    什么情况下陷阱不是陷阱

    有时您就想要闭包有如此表现,迟绑定在很多情况下是不错的。不幸的是,循环创建 独特的函数是一种会使它们出差错的情况。

    字节码(.pyc)文件无处不在!

    默认情况下,当使用文件执行Python代码时,Python解释器会自动将该文件的字节码版本写入磁盘。 比如, module.pyc

    这些“.pyc”文件不应该加入到您的源代码仓库。

    理论上,出于性能原因,此行为默认为开启。 没有这些字节码文件,Python会在每次加载文件时 重新生成字节码。

    禁用字节码(.pyc)文件

    幸运的是,生成字节码的过程非常快,在开发代码时不需要担心。

    那些文件很讨厌,所以让我们摆脱他们吧!

    $ export PYTHONDONTWRITEBYTECODE=1

    使用 $PYTHONDONTWRITEBYTECODE 环境变量,Python则不会把这些文件写入磁盘, 您的开发环境将会保持良好和干净。

    我建议在您的 ~/.profile 里设置这个环境变量。

    删除字节码(.pyc)文件

    以下是删除所有已存在的字节码文件的好方法:

    $ find . -type f -name "*.py[co]" -delete -or -type d -name "__pycache__" -delete

    从项目根目录运行,所有 .pyc 文件会嗖地一下消失, 好多了~

    版本控制忽略

    如果由于性能原因仍然需要 .pyc 文件,您可以随时将它们添加到版本控制存储库的忽略文件中。 流行的版本控制系统能够使用文件中定义的通配符来应用特殊规则。

    一份忽略文件将确保匹配的文件未被检入存储库。 Git 使用 .gitignore,而 Mercurial使用``.hgignore```。

    至少您的忽略文件应该是这样的。

    syntax:glob   # .gitignore 文件不需要这行
    *.py[cod]     # 将匹配 .pyc、.pyo 和 .pyd文件
    __pycache__/  # 排除整个文件夹

    您可能希望根据需要添加更多文件和目录。下次提交到存储库时,这些文件将不被包括。

    转载自:https://pythonguidecn.readthedocs.io/zh/latest/writing/gotchas.html

  • 相关阅读:
    数据结构与算法分析-Code Blocks中出现的找不到头文件的问题
    数据结构与算法分析-用C语言实现栈(数组方式)
    数据结构与算法分析-用C语言实现栈(链表方式)
    数据结构与算法分析-用C语言实现单链表
    C语言经典算法100例-结束语
    C++ Primer 7.33 练习编写成员函数
    C语言经典算法100例-073-链表逆序插入节点
    C语言经典算法100例-072-创建一个链表
    LintCode-编辑距离
    LintCode-乘积最大子序列
  • 原文地址:https://www.cnblogs.com/LYliangying/p/9552283.html
Copyright © 2011-2022 走看看