CodeGym /课程 /Python SELF ZH /标准错误

标准错误

Python SELF ZH
第 20 级 , 课程 1
可用

7.1 错误使用表达式作为函数参数的默认值

你已经学到了很多东西,让我们看看新手们最常见的错误吧——最好从别人的错误中学习,而不是自己的对吧?

Python 允许通过为函数参数设置默认值来声明可选参数。这当然是语言的一个很便利的特性,但如果默认值的类型是可变的,可能会导致不愉快的后果。例如,考虑以下的函数定义:


def foo(bar=[]):  # bar 是一个可选参数 
# 默认值是一个空列表
    bar.append("baz")  # 这一行可能会成为个问题...
    return bar

当下常见的错误是认为每次调用函数而不提供该参数的值时,默认参数的值都会被重置为默认值。

在上面的代码中,比如说,你可能会认为多次调用 foo()(即不为参数 bar 提供值)时,它总是会返回 ["baz"],因为你可能认为每次调用 foo()(不提供参数 bar)时,bar 都被设置为 [](即一个新的空列表)。

但让我们看看实际会发生什么:


result = foo()
print(result)  # ["baz"]
            
result = foo()
print(result)  # ["baz", "baz"]
            
result = foo()
print(result)  # ["baz", "baz", "baz"]

为什么函数每次调用 foo() 时都继续将值 baz 添加到已有列表中,而不是每次创建一个新列表呢?

对这个问题的答案是对 Python "底层" 机制的更深了解。具体说来,函数的默认值只在函数定义的时候被初始化一次

因此,参数 bar 仅在第一次定义 foo() 时被初始化为默认值(即空列表),而后续的 foo() 调用(即不提供参数 bar)会继续使用首次定义函数时为参数 bar 创建的同一个列表。

顺便说一下,常见的“解决方法”如下:


def foo(bar=None):             
    if bar is None:
        bar = []                                          
    bar.append("baz")   
    return bar
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]

7.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 赋个值:


B.x = 2
print(A.x, B.x, C.x)
# 1 2 1

一切如预期。

现在我们改变类 A 的变量:


A.x = 3
print(A.x, B.x, C.x)
# 3 2 3

奇怪的是,我们只是改变了 A.x。为什么 C.x 也改变了?

在 Python 中,类变量被处理为字典,并遵循常称为方法解析顺序 (MRO) 的规则。因此,在上面的代码中,由于属性 x 没有在C 中找到,它会在其基类中找到(在上面的例子中只有 A,尽管 Python 支持多重继承)。

换句话说,C 没有自己独立于 A 的属性 x。因此,对 C.x 的引用实际上是对 A.x 的引用。如果没有正确考虑这些情况,这可能会引起问题。在学习 Python 时,请特别注意类属性及其操作。

7.3 错误指定异常块的参数

假设你有以下代码段:


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 表达式不接受这样指定的异常列表。因此在上面的代码中,IndexError 异常没有被 except 表达式捕获;相反,异常会被绑定到名为 IndexError 的参数。

重要! 你可能会在网上的例子中看到这样的代码,因为它在 Python 2.x 中曾被使用。

使用 except 表达式捕获多个异常的正确方法是将第一个参数指定为一个元组,包含所有需要捕获的异常。此外,为了更好的可读性,你可以使用 as 关键字,比如这样:


try:
    l = ["a", "b"]
    int(l[2])
except (ValueError, IndexError) as e:  
    pass
2
任务
Python SELF ZH, 第 20 级, 课程 1
已锁定
默认参数。
默认参数。
2
任务
Python SELF ZH, 第 20 级, 课程 1
已锁定
错误的参数指示
错误的参数指示
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION