Python 4 道笔试题

20-06-04 编程 #code #python

4 道常见的 python 面试题和解答,以及一些 python 陷阱的链接。

问题

  1. 题目 1
    def change(v):
        v[1] = 4
        return v
        
        
    a = [1, 2, 3]
    print(change(a))
    print(a)    
  1. 题目 2
def append1(x=[]):
    x.append(1)
    return x
    
    
def now(n=time.time()):
    time.sleep(1)
    return n
    
print(append1(), append1()) #?
print(now(), now()) #?
    
  1. 题目 3
def arr_multi():
    x = [[0] * 3] * 3
    x[0][0] = 42
    return x
    
print(arr_multi())
  1. 题目 4
def fn_for():
    f = [lambda x: x * i for i in range(3)]
    print(f[0](1), f[1](1), f[2](1))
    
print(fn_for())
  1. 题目 5
>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
run, what happen next?

A. `t` becomes `(1, 2, [30, 40, 50, 60])`.
B. `TypeError` is raised with the message `'tuple' object does not support item assignment`.
C. Neither.
D. Both *A* and *B*.

解答

[1, 4, 3]
[1, 4, 3]

# 就是简单的引用传递,但是很多人不自信,在选择题里面频频出错。
# python 中所有的都是对象,id(obj) 会返回地址。
# 但是如果新建对象是 short string,int [-5,256],不可变的空集合 (empty tuples) 等情况不会真的创建新对象。

from copy import copy, deepcopy

arr1 = [1,2,3,[4,5,6]]
arr2 = copy(arr1) # shallow copy, new id, but elements in array is same id

id(arr1[0]) == id(arr2[0]) 

#deepcopy
arr3 = deepcopy(arr1) # elements id is new 

2.

# 结果:
[1, 1] [1, 1]
1590544209.9695618 1590544209.9695618

# 不少人认为是:[1] [1, 1].其实还是没有深入理解引用的原理,
# 翻译一下就很好理解了:
y = append1()  # id(y) == id(x), y=[1]
y = append1()  # id(y) == id(x), y=[1,1]
print(y,y)


# 最好不要使用 [] 作为默认参数,使用下面的形式:
def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

# 或者
def fun(count=[]):
    count.append(2) #这里 count 两次调用如果都使用默认参数的话,则是同一个数组,非常危险!
    return count
fun()   #[2]
fun()   #[2,2]
   

3.

[[42, 0, 0], [42, 0, 0], [42, 0, 0]]
# list 是 mutable, []*3 表示是引用复制三次。

# 赋值后为什么只改变列的值?
2 2 2
None

本意其实是想得到一个函数列表[0x,1x,2*x],
但是 Python’s closures are late binding. This means that the values of variables used in closures are looked up at the time the inner function is called.
解决方案是偏函数 partial

from functools import partial
def fix_fn_for():
    f = [partial(lambda y, x: y * x, x=i) for i in range(3)]
    print(f[0](1), f[1](1), f[2](1))

或:

fl=[lambda x, i=i: x*i for i in range(3)]

5.

答案是D
可以使用dis.dis('s[a] += b')查看执行码
大概得流程是将s[a]加载到stack(tos: top of stack) 执行tos + b最后是赋值 s[a] = tos 但是因为s是tuple属于不可变类型抛错

1. 永远不在默认参数tuple内等使用可变参数同题目2 可变类型包括list set dict bytesarray四种
2. += 中赋值不是原子操作
3. dis模块其实没有那么复杂尝试使用发现问题

常见 python 陷阱
The 10 Most Common Mistakes in Python
Some Common Gotchas in Python