IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python程序员经常犯的10个错误,这些坑你踩过吗? -> 正文阅读

[Python知识库]Python程序员经常犯的10个错误,这些坑你踩过吗?

关于Python

Python是一种解释性、面向对象并具有动态语义的高级程序语言。它内建了高级的数据结构,结合了动态类型和动态绑定的优点,这使得它在快速应用开发中非常有吸引力,并且可作为脚本或胶水语言来连接现有的组件或服务。Python 支持模块和包,从而鼓励了程序的模块化和代码重用。

关于这篇文章

Python简单易学的语法可能会使Python开发者——尤其是那些编程的初学者忽视了它的一些微妙的地方并低估了这门语言的能力。

有鉴于此,本文列出了一个“10强”名单,枚举了甚至是高级Python开发人员有时也难以捕捉的错误。

1: 滥用表达式作为函数参数的默认值


Python允许为函数的参数提供默认的可选值。尽管这是语言的一大特色,但是它可能会导致一些易变默认值的混乱。例如,看一下这个Python函数的定义:

\>>> def foo(bar=\[\]):    
       bar.append("baz")      
       return bar  

一个常见的错误是认为在函数每次不提供可选参数调用时可选参数将设置为默认指定值。在上面的代码中,例如,人们可能会希望反复(即不明确指定bar参数)地调用foo()时总返回’baz’,由于每次foo()调用时都假定(不设定bar参数)bar被设置为[](即一个空列表)。

\>>> foo()  
\["baz"\]  
\>>> foo()  
\["baz", "baz"\]  
\>>> foo()  
\["baz", "baz", "baz"\]

耶?为什么每次foo()调用时都要把默认值"baz"追加到现有列表中而不是创建一个新的列表呢?

答案默认参数在定义时求值(比如说当你首次导入模块时)。因此,bar参数在初始化时为其默认值(即一个空列表),即foo()首次定义的时候,但当调用foo()时(即,不指定bar参数时)将继续使用bar原本已经初始化的参数。

下面是一个常见的解决方法:

\>>> def foo(bar=None):  
       if bar is None:   
           bar = \[\]  
       bar.append("baz")  
       return bar  
         
\>>> foo()  
\["baz"\]  
\>>> foo()  
\["baz"\]  
\>>> foo()  
\["baz"\]

小编补充:

另外一个常见的例子就是默认参数是一个表达式,如:

import datetime  
  
def log(message, time=datetime.datetime.now()):  
    print("{0}: {1}".format(time, message))

期望的是每次记录不同的时间,然而未能如愿,记录的是同一个时间。运行如下代码:

import time  
log('message 1')  
time.sleep(1)  
log('message 2')  
time.sleep(1)  
log('message 3')  
time.sleep(1)

结果如下:

2017-07-25 20:53:20.225000: message 1

2017-07-25 20:53:20.225000: message 2

2017-07-25 20:53:20.225000: message 3


2: 错误地使用类变量


考虑一下下面的例子:

\>>> class A(object):  
        x = 1  
  
\>>> class B(A):  
        pass  
  
\>>> class C(A):  
        pass  
  
\>>> print A.x, B.x, C.x  
1 1 1

没有任何异常。

\>>> B.x = 2  
\>>> print A.x, B.x, C.x  
1 2 1

也和你想要的结果一样。

\>>> A.x = 3  
\>>> print A.x, B.x, C.x  
3 2 3

结果似乎有点出乎意料了。

我们只改了A.x,为什么C.x也改了?

在Python中,类变量在内部当做字典来处理,其遵循常被引用的方法解析顺序(MRO)。所以在上面的代码中,由于class C中的x属性没有找到,它会向上找它的基类(尽管Python支持多重继承,但上面的例子中只有A)。换句话说,class C中没有它自己的x属性,其独立于A。因此,C.x事实上是A.x的引用。

3: 为 except 指定错误的参数

假设你有如下一段代码:

\>>> try:  
        l = \["a", "b"\]  
        int(l\[2\])  
    except ValueError, IndexError:    
        pass  
  
Traceback (most recent call last):  
  File "<stdin>", line 3, in <module>  
IndexError: list index out of range

这里的问题在于 except 语句并不接受以这种方式指定的异常列表。相反,在Python 2.x中,使用语法 except Exception, e 是将一个异常对象绑定到第二个_可选_参数(在这个例子中是 e)上,以便在后面使用。所以,在上面这个例子中,IndexError 这个异常并不是被except语句捕捉到的,而是被绑定到一个名叫 IndexError的参数上时引发的。

在一个except语句中捕获多个异常的正确做法是将第一个参数指定为一个含有所有要捕获异常的元组。并且,为了代码的可移植性,要使用as关键词,因为Python 2 和Python 3都支持这种语法:

\>>> try:  
        l = \["a", "b"\]  
        int(l\[2\])  
    except (ValueError, IndexError) as e:    
        pass  
\>>>

4: 不理解Python的作用域

Python是基于 LEGB 来进行作用于解析的, LEGB是 Local, Enclosing, Global, Built-in 的缩写。看起来“见文知意”,对吗?实际上,在Python中还有一些需要注意的地方,先看下面一段代码:

\>>> x = 10  
\>>> def foo():  
        x += 1  
        print x  
  
\>>> foo()  
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
  File "<stdin>", line 2, in foo  
UnboundLocalError: local variable 'x' referenced before assignment

这里出什么问题了?

上面的问题之所以会发生是因为当你给作用域中的一个变量赋值时,Python 会自动的把它当做是当前作用域的局部变量****,从而会隐藏外部作用域中的同名变量

很多人会感到很吃惊,当他们给之前可以正常运行的代码的函数体的某个地方添加了一句赋值语句之后就得到了一个 UnboundLocalError 的错误。

尤其是当开发者使用列表 list 时,这个问题就更加常见. 请看下面这个例子:

\>>> lst = \[1, 2, 3\]  
\>>> def foo1():  
        lst.append(5)   \# 没有问题...  
     
\>>> foo1()  
\>>> lst  
\[1, 2, 3, 5\]  
  
\>>> lst = \[1, 2, 3\]  
\>>> def foo2():  
        lst += \[5\]      \# ... 但是这里有问题!  
     
\>>> foo2()  
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>  
  File "<stdin>", line 2, in foo  
UnboundLocalError: local variable 'lst' referenced before assignment

嗯?为什么 foo2 报错,而 foo1 没有问题呢?

原因和之前那个例子的一样,不过更加令人难以捉摸。foo1 没有对 lst 进行赋值操作,而 foo2 做了。要知道, lst += [5] 是 lst = lst + [5] 的缩写,我们试图对 lst 进行赋值操作(Python把它当成了局部变量)。此外,我们对 lst 进行的赋值操作是基于 lst 自身(这再一次被Python当成了局部变量),但此时还未定义。因此出错!

5:在迭代时修改列表 (List)

下面代码中的问题应该是相当明显的:

\>>> odd = lambda x : bool(x % 2)  
\>>> numbers = \[n for n in range(10)\]  
\>>> for i in range(len(numbers)):  
        if odd(numbers\[i\]):  
            del numbers\[i\]    
  
Traceback (most recent call last):  
        File "<stdin>", line 2, in <module>  
IndexError: list index out of range

在迭代的时候,从一个列表或者数组中删除元素,对于任何有经验的开发者来说,这是一个众所周知的错误。尽管上面的例子非常明显,但是许多高级开发者在更复杂的代码中也并非是故意而为之的。

幸运的是,Python包含大量简洁优雅的编程范例,若使用得当,能大大简化和精炼代码。这样的好处是能得到更简化和更精简的代码,能更好的避免程序中出现当迭代时修改一个列表这样的bug。一个这样的范例是列表生成式(list comprehensions)。而且,列表生成式针对这个问题是特别有用的,通过更改上文中的实现,得到一段极佳的代码:

\>>> odd = lambda x : bool(x % 2)  
\>>> numbers = \[n for n in range(10)\]  
\>>> numbers\[:\] = \[n for n in numbers if not odd(n)\]   
\>>> numbers  
\[0, 2, 4, 6, 8\]

看完上半部分,这些坑你踩过吗?

最后,非常感谢您阅读我的文章!有任何问题可以后台私信我,我都很乐意解答。(下方是今日福利)

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:00:43  更:2022-01-16 13:01:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:46:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码