分享

2022年Python十级试题(全国卷B)

 古明地觉O_o 2022-12-08 发布于北京

试题来源:Python之美

答案作者:古明地觉


第一题

问题:

答案解析:

这道题答案选择 A,可以根据排除法,B C D 都是错误的。那么下面分析一下,为什么它们是错误的。

首先 Python 有一个模块叫 builtins,像内置的类、内置函数等等都在里面。

import builtins

# list 等价于 builtins.list
print(
    builtins.list("123")
)  # ['1', '2', '3']

# Python 内置了一个变量 __builtins__
# 它等价于 import builtins
print(
    __builtins__ is builtins
)  # True

所以 __builtins__ 已经指向了一个模块,因此 B 选项错误,而 import builtins 是正确的。同理 C 选项也是错误的,它是个干扰项。

最后是 D 选项,import 的后面应该是 __future__,不是 __futures__。那么这个模块是做什么的呢?我们知道 Python3 和 Python2 是不兼容的,比如 print 在 Python2 里面是一个关键字,但在 Python3 里面就是一个普通的内置函数。

而 __future__ 可以将 Python3 的特性导入到 Python2 中,方便代码兼容,举个例子:

# print 在 Python2 里面是一个关键字
>>> print = 123
  File "<stdin>", line 1
    print = 123
          ^
SyntaxError: invalid syntax
>>> 
# 此时 print 就不是关键字了,而是普通的函数
>>> from __future__ import print_function
>>> print = 123
>>> 

所以通过排除法,可以排除 B C D,那么正确选项 A 是咋回事呢。其实这是官方玩的一个小彩蛋:

import __hello__
"""
Hello world!
"""

导入之后会自动打印 Hello world,我们通过 CPython 源码可以找到它,位于 Python/frozen.c 中。

可以看到里面不仅定义了 __hello__,还定义了 __phello__。

import __hello__
"""
Hello world!
"""


import __phello__
"""
Hello world!
"""

打印的结果是一样的。


第二题

问题:

答案解析:

这个问题在上一篇文章中我们说过:

"""
(0, 1) if True else None, None
等价于
((0, 1) if True else None), None
"""

所以 x 为 (0, 1),y 为 None,因此答案选择 C


第三题

问题:

答案解析:

这道题的答案可能让人有些费解,我直接说结论:

try:
    ...  # 代码块
except Exception as e:
    ...  # 代码块

# 上面这段代码的真实情况
# 其实是这样
try:
    ...  # 代码块
except Exception as e:
    try:
        ...  # 代码块
    finally:
        del e

所以答案为 D,会抛出 NameError。因为在异常处理的时候,如果把异常赋予了一个变量,那么这个变量在异常处理结束时会被删掉,因此变量 e 只能在 except 里面使用。

当使用 as 将异常赋值给一个变量时,该变量将在 except 子句结束时被清除,这意味着异常必须赋值给一个不同的名称(不同于外部指定的变量),才能在 except 子句之后引用它(外部指定的变量)。

而变量被清除是因为在附加了回溯信息的情况下,它们会形成堆栈帧的循环引用,在下一次垃圾回收执行之前,会使所有局部变量都保持存活。

说人话就是,如果不将 as 后面的变量删除,那么会多一次 GC。

既然此处提到了异常处理,这里就再补充一个很多人应该不知道的知识点。

def f():
    try:
        raise IndexError()
    except ValueError:
        pass
    finally:
        return

f()
print("无事发生")
"""
无事发生
"""

按理说,except 无法捕获 try 里面引发的异常,那么应该报错才对啊,为啥这里没报错呢。

首先 finally 子句始终会被执行,它应该负责一些资源清理等善后工作,如果 except 子句没有成功捕获异常,那么 finally 子句执行结束后会再将异常抛出来。但如果 finally 里面出现了 return、break、continue 等关键字,那么异常就不会抛出来了,而是会被丢弃掉。


第四题

问题:

答案解析:

这道题还是考察了海象运算符。

(x := [12]).extend(x)

# 等价于如下
x = [12]
x.extend(x)

所以答案为 B,比较简单。


第五题

问题:

答案解析:

这里先补充一个知识点,Python 的变量从 C 的角度来看只是一个泛型指针。比如 a = 1,实际上是先创建整数 1,然后再让变量 a 保存整数 1 的地址。所以 Python 里面一定是先有对象,然后才有变量。

对于 for 循环来说亦是如此,先将可迭代对象里面的值迭代出来,然后再将值赋给循环变量。所以上面的循环相当于做了如下事情:

  • 第一次循环:i, d[i] = (0, 'X'),其中 i = 0;

  • 第二次循环:i, d[i] = (0, 'i'),其中 i = 1;

  • 第三次循环:i, d[i] = (0, 'a'),其中 i = 2;

  • 第四次循环:i, d[i] = (0, 'o'),其中 i = 3;

所以答案是 A,循环体里面的 i = i + 1 没有意义,因为每次循环都会对变量 i 重新赋值。


第六题

问题:

答案解析:

这道题乍一看可能有点让人纳闷,其实这道题主要考察的是内置函数 reversed。它和 sorted 不同,reversed 返回的不是一个列表,而是一个迭代器。

而迭代器只能顺序遍历一次,所以 == 左边的 sorted(y) 是 [7, 8, 9],右边的 sorted(y) 是空列表,因此结果是 False,答案是 B


第七题

问题:

答案解析:

这道题实际上还是考察了对 Python 变量的理解,对于 Go 这样的静态语言来说,变量就是对象所在内存的别名,比如 a = 123,a 对应的内存存储的就是 123 本身。但 Python 不同,对于 Python 而言,a = 123,a 对应的内存存储的是整数 123 的内存地址。

# a 保存了 123 的地址
a = 123
# 将 a 赋值给 b
# 相当于将 a 本身拷贝一份给 b
b = a

# 所以此时 a 和 b 都保存了 123 的地址
# 相当于两个变量都指向了同一个对象
# 但 a 和 b 本身没有什么关系

# 如果再让 a = 456
# 那么结果就相当于让 a 保存 456 的地址
# 但 b 不受影响,它保存的还是 123 的地址
a = 456
print(a)  # 456
print(b)  # 123

所以说 Python 是值传递或引用传递都是不准确的,应该说 Python 是变量的赋值传递,对象的引用传递。因为 Python 的变量代表的不是对象本身,而是对象的指针(准确的说是引用)。

  • 站在变量的角度上,b = a 确实是将 a 存储的值拷贝一份给 b,所以是变量的赋值传递;

  • 站在对象的角度上,b = a 相当于是将 a 指向对象的地址拷贝一份给 b,所以也是对象的引用传递;

def f1(lst):
    lst = [112233]

def
f2(lst):
    lst.append(44)

lst = [123]
f1(lst)
print(lst)  # [1, 2, 3]
"""
调用 f1(lst) 的时候,相当于将全局变量 lst 
拷贝一份给局部变量 lst,但我们说变量只是一个指针
所以两个 lst 都指向同一个列表

而在 f1 内部,局部变量 lst 指向了新的列表
它不影响外界的全局变量 lst 
所以 print(lst) 的结果仍是 [1, 2, 3]
"""


f2(lst)
print(lst)  # [1, 2, 3, 44]
"""
变量只是一个指针,变量传递时会拷贝指针
但操作变量时,会自动操作变量指向的内存

两个 lst 都指向同一个列表,而列表支持本地修改
所以此时外界的 lst 会受到影响
"""

所以题目就很简单了,b = a 之后,两个变量都指向了 [1, 2, 3]。而 a = a + [4, 5, 6] 相当于对 a 重新赋值了,让 a 指向了新的列表,但 b 不受影响,所以答案是 A

如果将问题再改一下,将第三行代码改成 a += [4, 5, 6],那么结果就会发生改变,答案就变成了 B。

a = [123]
print(id(a))
# a 指向了新的列表
# 所以前后地址不一样
a = a + [456]
print(id(a))
"""
2400457668480
2398937549824
"""


a = [123]
print(id(a))
# 对于列表而言,b += [4, 5, 6]
# 等价于 b.extend([4, 5, 6])
# 此时相当于本地修改,所以前后地址不变
a += [456]
print(id(a))
"""
2400457668480
2400457668480
"""

所以列表的 += 比较特殊,要注意。


第八题

问题:

答案解析:

这个没什么可说的,答案是 C


第九题

问题:

答案解析:

a = [123]
b = [456]

c = [*a, *b]
print(c)
"""
[1, 2, 3, 4, 5, 6]
"""


d1 = {"name""古明地觉"}
d2 = {"address""地灵殿"}
print({**d1, **d2})
"""
{'name': '古明地觉', 'address': '地灵殿'}
"""

没什么可说的,答案是 C


第十题

问题:

答案解析:

对于一个生成器而言,迭代出来的是 yield 后面的值。

def gen():
    yield 1
    yield 2
    yield 3
    return "result"

print(
    list(gen())
)  # [1, 2, 3]

而生成器的返回值需要手动捕获 StopIteration 异常,然后调用它的 value 属性才能拿到。

def gen():
    return "result"
    yield 123

# return 下面的代码无法执行
# 但里面出现了 yield
# 那么它就是一个生成器函数
# 这是 Python 在编译的时候就决定的
print(
    list(gen())
)  # []
# 而 return 上面没有 yield
# 所以返回的就是空列表


def
func():
    return "result"

# 此时结果又变了,因此它不是生成器
# 而是普通的函数调用
print(
    list(func())
)  # ['r', 'e', 's', 'u', 'l', 't']

所以答案很清晰了,答案选 C


小结

本试题来源于 《Python 之美》,总的来说问题很不错,后续还有更新的话,我再继续解答。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多