社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

2022 年 Python “十级”试题,30 道答案全解析,涨见识了!

Python猫 • 2 年前 • 439 次点击  
△点击上方“Python猫”关注 ,回复“1”领取电子书

你好,我是猫哥。上周分享了一篇《2022 年 Python “十级”试题(全国卷A)》,文章很受大家欢迎。原作者还出了 B 卷和 C 卷,题目越来越有趣。今天分享的是作者对全部 30 道题目的完整解析,大家可以看到题目的灵感、出题意图和原理解析,相信会有更多收获~~PS.@古明地觉 大佬也写了系列详细的答案解析,可对照阅读(跳转)。

作者:dongwm

来源:https://www.dongwm.com/post/python-ten-level-exam

好久没登录豆瓣,前天上线看到【追剧小露珠】发的 《绝命毒师十级学者统一考试》 ,没事答题发现还挺有趣的,然后,就想到了《Python 十级考试》这个主题,找一些和 Python 有关的、常见但是很可能会被答错的题目写成试卷,结果一准备发现还凑够了三份试题,所以在微信公众号连续写了三篇:

  1. 2022 年 Python 十级试题 (全国卷 A)
  2. 2022 年 Python 十级试题 (全国卷 B)
  3. 2022 年 Python 十级试题 (全国卷 C)

这些题目的灵感主要来自 wtfpython 和 《Mouse Vs Python》的作者 Mike Driscoll 的 Tweet ,当然我也加了一些自己的私货。

其中第三篇的题目会更难一点,如果你想要试试,可以先通过公众号文章链接进去写写答案。完了再来看我之后写的这篇试题答案和解析。

另外如果我标注了【送分题】的题目你没答对,我建议你找个时间再专注的学一次 Python。

题目 1

先看全国卷 A 的 10 道题目。


这个题目来自 Raymond Hettinger 的 Tweet 。

答案是 B. 因为- 1(中间有空格) 其实就是-1,也就是说可以这么表示score -= (-1)

题目 2

送分题,答案是 A,也就是抛 SyntaxError 错误,因为海象操作符需要使用括号不能直接用,因为需要和普通的赋值区分开来。

题目 3


题目来源:https://github.com/satwikkansal/wtfpython#-deleting-a-list-item-while-iterating

这个题目主要是考验对迭代的理解。在循环时先迭代了第一个元素 1 (索引 0) 然后 remove 删除这个元素,剩下了三个元素 2,3,4,但是注意,这里 2 的索引是 0,3 的索引是 1。下一次迭代应该是索引 1,就是迭代并删掉 3,把 2 给略过了,接着会把 4 略过。略过的就会留下,所以结果是[2, 4]

题目 4


送分题,答案是 D,因为min是自带的函数,如果把它替换成其他的对象就不能正常运行了,那么就会抛错 TypeError。

题目 5


题目来源:https://github.com/satwikkansal/wtfpython#-be-careful-with-chained-operations

答案是 A,这个特别反直觉对吧。但要注意比较方式是按顺序把相邻的 2 个分别比较, 官网这么说 :

if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.

所以False == False in [False]的意思是(False == False) and (False in [False]),所以结果是 True。

题目 6


送分题,答案是 A,因为 bool 值也是一种数字 (True 为 1,False 为 0):

In : isinstance(True, int)
Out: True

In : 'haha' * True
Out: 'haha'

In : 'haha' * False
Out: ''

题目 7


答案是 B。这道题我就是想让大家知道判断可以直接在 print 里面写,而不需要这样:

In : a = 100

In : result = a if a > 100 else 1

In : print(result)
1

题目 8


送分题,答案是 D,知识点是列表解包 (Unpacking)。

题目 9


题目来源:https://github.com/satwikkansal/wtfpython#-hash-brownies

答案是 C. 在 Python 的字典中,它不关心键的类型,只要它们的值一样那么就是同一个键值对,后面的赋值会替换前面的值:

In : 1 == 1.0
Out: True

题目 10


答案是 A,来源找不到了,我之前还专门写过一篇文章讲这个 一段迷惑的使用海象操作符的代码

题目 11

再看全国卷 B 的 10 道题目。


这个不了解的比较难,答案是 A。这是 Python freeze 自动创建的模块,除此之外还有__phello__:

In : import __hello__
Hello world!

In : import __phello__
Hello world!

In : import __phello__  # 只有第一次import才会执行,之后就【缓存】了。

而其他选项中,__builtin__是 Python 2 时代的模块,还有个 Python2/3 都可以用的 builtins 模块,但是没有__builtins__。另外有__future__futures但没有__futures__,都是用来混淆的。

题目 12


题目来源:https://github.com/satwikkansal/wtfpython#-needles-in-a-haystack-

答案是 C。这个和题目 10 其实很像。你可以把它理解成这是一个赋值语句,逗号前面的赋值给 x,后面的赋值给 y。如果加上括号就是另外一个意思了:




    
In : x, y = (0, 1) if True else (None, None)

In : x
Out: 0

In : y
Out: 1

这样就表示根据判断条件赋值不同的元组了。

题目 13


题目来源:https://github.com/satwikkansal/wtfpython#-the-disappearing-variable-from-outer-scope

答案是 D。而在 Python2 中,e 的结果是Exception(),注意这个和 wtfpython 项目里的说明不符。

在 Python3 为什么直接抛 NameError 呢?因为:

except E as N:
    foo

在 except 作用域里面实际上相当于:

except E as N:
    try:
        foo
    finally:
        del N

也就是说,最终在离开作用域时会把 as 的别名 N 删掉,这样 e 就不存在了,所以是 NameError。

题目 14


送分题,答案是 B。这是海象操作符的常见应用场景,首先先在(x := [1, 2])里给 x 赋值为[1, 2],然后再对这个 x 执行x.extend(x)

题目 15


答案是 A。这个题目是展示在循环时还能做其他很多事情,例如顺便对迭代对象赋值。很久前我还发过类似的 豆瓣广播 :


题目 16


题目来源:https://github.com/satwikkansal/wtfpython#-all-sorted-

答案是 B。这个第一次我也想错了。它的问题在于对一个迭代对象做两次迭代时,后面那次的迭代开始时迭代对象已经为空了:




    
In : x = 7, 8, 9

In : y = reversed(x)

In : sorted(y), sorted(y)
Out: ([7, 8, 9], [])

题目 17


题目来源:https://github.com/satwikkansal/wtfpython#-same-operands-different-story

送分题,答案是 A。a += [4, 5, 6]会生成新的列表 a,但是 b 引用的是旧的 a,所以不会受到影响。

题目 18


题目来源:https://github.com/satwikkansal/wtfpython#-loop-variables-leaking-out

送分题,答案是 C。for 循环会影响作用域之外的变量值,但是有一点需要注意,从 Python 3 开始,列表解析不会影响作用域之外的变量值,举个例子:

In : x = 1

In : print([x for x in range(5)])
[0, 1, 2, 3, 4]

In : x
Out: 1  # 未受到列表解析的影响

题目 19


绝对的送分题,答案是 C。不过这个题目我没写好,原题目对于 Python 熟悉的开发者自动会去掉 A/B2 个答案,应该选项是:

A. {range(0, 3)}
B. (range(0, 3))
C. {0, 1, 2}
D. {1, 2, 3}

这样就更具备迷惑性了。其实就是把一个 range 类型的可迭代对象在集合里面解包。

题目 20


题目来源:https://github.com/satwikkansal/wtfpython#-yielding-from-return-

这个其实是语言设计的问题,答案 C。

首先说yield from其实就是:

for i in range(x):
    yield i

的意思。主要考验大家对于生成器和 Python 3.3 新加入的yield from的熟悉程度。如果一个函数内有yield或者yield from,那么这就是一个生成器:




    
In : def a():
...:     yield 1
...:

In : a()
Out: 

In : def b():
...:     yield from [1, 2]
...:

In : b()
Out: 

当时设计时 生成器内可以使用 return ,事实上是停止生成器的用途,如官方所说:

"... return expr in a generator causes StopIteration(expr) to be raised upon exit from the generator."

所以return ['wtf']等于raise StopIteration(['wtf']),在执行list()的时候,就会捕捉错误直接结束。

但是我们这个例子中,直接符合的 return 的条件,就造成它直接返回了空列表 (因为一上来就 raise 了 StopIteration)。

题目 21

最后看全国卷 C 的 10 道题目。这也是我认为最难的一份卷子了。


考验对__future__模块的了解程度,答案是 C。大家应该了解每个选项的意义,我这里就不挨个提了。

题目 22


题目来源:https://github.com/satwikkansal/wtfpython#-name-resolution-ignoring-class-scope

答案是 A。首先,嵌套在类定义中的范围会忽略在类级别绑定的变量。

另外如题目 18 里面提到的,Python 3 的列表解析 / 生成器有自己的作用域,而不会影响外面。

题目 23


题目来源:https://github.com/satwikkansal/wtfpython#-evaluation-time-discrepancy

答案是 C。在生成器表达式中,in是声明时计算,条件判断时在运行时执行计算。所以这个题目中gen = (x for x in array if array.count(x) > 0)也就是gen = (x for x in [1, 8, 15] if [2, 8, 22].count(x) > 0)

这个地方需要大家仔细理解。

题目 24


题目灵感:https://github.com/satwikkansal/wtfpython#-deep-down-were-all-the-same

我们先思考==is 的关系:==表示值相等,is表示指向的内容地址一样,所以对比的 2 个选项可能==但是可能不is,因为is需要更高的要求,同样的,如果is了,那么肯定==

我们挨个去确定。WTF() == WTF()表示 2 个实例,肯定不会是一样的;WTF() is WTF()既然都不相等,所以is就更不可能了。

如果你理解==is的关系,现在就可以知道答案是 C。但是为什么id(WTF()) == id(WTF())id(WTF()) is id(WTF())不对呢?

在一个表达式中,如果执行 2 遍 id 函数,后面那次会被分配到相同内存位置,所以相等==。但是id(WTF()) is id(WTF())其实和本题目关系不大,是我发挥的选项,举个更直接的例子:

In : a = id(0)

In : b = id(0)

In : a
Out: 4373539088

In : b
Out: 4373539088

In : a is b
Out: False

In : id(0) is id(0)
Out: False

这个是池化的问题,如果数字不在 - 5 到 256 之间,Python 不会缓存数字对象,而 id 函数执行的结果远大于这个值所以就是 False 了。

我们在最新的 Python3.10 试一下,默认的输出不符合预期,我再换个思路:

In : 256 is 256  # 错误输出
<>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
  256 is 256
Out: True

In : 257 is 257  # 错误输出
<>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
  257 is 257
Out: True

In : def add(n):  # 我们改个思路
...:     return n + 256
...:

In : add(0) is add(0)  # 256 is 256
Out: True

In : add(1) is add(1)  # 257 is 257
Out: False  # 超过256 所以是False了

题目 25


题目来源:https://github.com/satwikkansal/wtfpython#-all-true-ation-

这个其实仔细仔细分析是可以找到答案的,答案为 B。

all的意思是把参数做循环,每个元素都符合要求才是 True,只要有一个不符合就是 False。所以all([])是空列表结果为空, all([[]])表示列表只有一个元素[],而空列表是 False,所以结果是 False。

最后 2 个选项稍微让人迷惑,列表只有一项元素,分别是[[]][[[]]],它们都是非空的,所以布尔值是 True:

In : bool([[]])
Out: True

In : bool([[[]]])
Out: True

题目 26


这个相对比较简单,资深的 Python 开发应该会写过这样的代码,答案是 C。

在 Python 中,如果对变量赋值,那么这个变量就会编程当前范围的本地变量,所以在函数some_func里面a += 1a就成了函数里的本地变量,但是在函数范围里面,前面并没有定义 a 或者对 a 赋值,只有a += 1,所以就抛 UnboundLocalError 了。

如果想让程序不报错,解决办法是在函数内加 global 关键字:

In : a = 1

In : def some_func():
...:     global a
...:     a += 1
...:     return a
...:

In : some_func()
Out: 2

但是注意,global 要谨慎使用,如它的名字所提示的,使用它会影响全局变量的结果。

题目 27


答案是 B。这个题目考查对于字符串的striprstrip等方法的含义。默认情况下它们是用作去掉字符串行位空格的:

In : ' s '.strip()
Out: 's'

In : ' s '.rstrip()
Out: ' s'

In : ' s '.lstrip()
Out: 's '

但是也可以传入其他字符串,实现replace函数所做把对应匹配项替换成空:

In : ' s '.rstrip('s')
Out: ' s '

In : ' s '.rstrip('s ')
Out: ''

In : 'abc'.rstrip('bc')
Out: 'a'

所以原字符串中包含的.US.TXT都会被替换,也就是. U S T X这几个字符会被替换成空。

注意,如果只是想要移除后缀,可以使用 Python 3.9 新加入的removesuffix/removeprefix:

In : 'WKHSS.US.TXT'.removesuffix('.US.TXT')
Out: 'WKHSS'

题目 28


题目来源:https://github.com/satwikkansal/wtfpython#-how-not-to-use-is-operator

答案是 A。这其实是 Python3.7 的一个 BUG,已经在https://bugs.python.org/issue34100里面修复。

题目 29


本题目考验对nonlocal的理解,答案是 D。在前的题目中我们也有所涉及。先看一个例子:

In : i = 0
...:
...: def a():
...:     i = 1
...:     print('local:', i)
...:
...: a()
local: 1

In : i
Out: 0

这是符合预期的,函数作用域内对 i 的赋值是本地的,不会影响外面的全局变量。之前在题目 26 中提到的 global 方案可以影响外面的全局变量,但是如果是嵌套的作用域呢:

In : x = 0
...: def outer():
...:     x = 1
...:     def inner():
...:         x = 2
...:         print("inner:", x)
...:
...:     inner()
...:     print("outer:", x)
...:
...: outer()
...: print("global:", x)
...:
inner: 2
outer: 1
global: 0

使用global改造:

In : x = 0
...: def outer():
...:     x = 1
...:     def inner():
...:         global x
...:         x = 2
...:         print("inner:", x)
...:
...:     inner()
...:     print("outer:", x)
...:
...: outer()
...: print("global:", x)
...:
inner: 2
outer: 1
global: 2

可以看到在最里面使用global,无论嵌套多少层,都会直接改最外面的那层x = 0。那么怎么影响到outer里面的x呢?这就是nonlocal的作用:

In [32]: x = 0
    ...: def outer():
    ...:     x = 1
    ...:     def inner():
    ...:         nonlocal x
    ...:         x = 2
    ...:         print("inner:", x)
    ...:
    ...:     inner()
    ...:     print("outer:", x)
    ...:
    ...: outer()
    ...: print("global:", x)
    ...:
inner: 2
outer: 2
global: 0

所谓nonlocal,其实就是说x不是函数 inner 的本地变量,那么就会向上影响到 outer。也就是影响 Enclosing (嵌套的父级函数的局部) 作用域。关于 Python 的 LEGB 作用域,可以深入搜索了解。

现在思考下,如果nonlocal的父级没有这个局部变量会继续向父级传播,但是注意,noncal声明的是函数内的局部变量,父级函数内没有此变量会报错:

In : x = 0
...: def outer():
...:     # x = 1  #注释了
...:     def inner():
...:         nonlocal x
...:         x = 2
...:         print("inner:", x)
...:
...:     inner()
...:     print("outer:", x)
...:
...: outer()
...: print("global:", x)
...:
Input In [36]
  nonlocal x
  ^
SyntaxError: no binding for nonlocal 'x' found

题目 30


这个题目考查对dataclasses模块的了解,它的问题在于类 A 的 c 已经定义的默认值,但是继承的 B 却没有。

我们看一下字典的同类问题就能了解:

In : d = dict(a, b=1, c)
  Input In [37]
    d = dict(a, b=1, c)
                      ^
SyntaxError: positional argument follows keyword argument

位置参数都要放在关键字参数之前,这个题目稍微有点防水。本来我是想出 Python 3.8 新增的参数类型的约束问题,既然想了就列出来大家感受下:

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

# 上述函数的合法调用项是:

A. f(10, 20, 30, d=40, e=50, f=60)
B. f(10, b=20, c=30, d=40, e=50, f=60)
C. f(a=10, b=20, c=30, d=40, e=50, f=60)
D. f(10, 20, 30, 40, 50, f=60)

答案是 A。Python 3.8 新增的语法/约束在/之前的参数必须在位置上指定并且不能用作关键字参数,*约束它之后的参数必须使用关键字参数而不能用位置参数。

为了优化 SEO,贴一下报错:

In : f(a=10, b=20, c=30, d=40, e=50, f=60)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [], in ()
----> 1 f(a=10, b=20, c=30, d=40, e=50, f=60)

TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'

In : f(10, 20, 30, 40, 50, f=60)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [], in ()
----> 1 f(10, 20, 30, 40, 50, f=60)

TypeError: f() takes 4 positional arguments but 5 positional arguments (and 1 keyword-only argument) were given

延伸阅读

  1. https://peps.python.org/pep-0570/


又到了每年的开学季,最近图灵出版社有一大波图书折扣活动,电子书全场 6 折,非常实惠,机不可失,感兴趣的同学赶紧出手吧!


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/146698
 
439 次点击