Skip to content

WTFPython

摘录一些wtfpython有趣的例子.

Be careful with chained operations

comparisons

在 python 中, 针对链式比较操作符, 可以等价认为将每个操作符两侧的表达式两两比较, 再通过 and 进行合并. 下述例子中表达式等价.

False == False in [False] # False
(False == False) and (False in [False])

Keep trying... *

def one_more_func(): # A gotcha!
    try:
        for i in range(3):
            try:
                1 / i
            except ZeroDivisionError:
                # Let's throw it here and handle it outside for loop
                raise ZeroDivisionError("A trivial divide by zero error")
            finally:
                print("Iteration", i)
                break
    except ZeroDivisionError as e:
        print("Zero division error occurred", e)

one_more_func()
# Iteration 0

finally中使用 breakcontinuereturn, 会打断并丢弃临时保存的异常信息.

Evaluation time discrepancy

array = [1, 8, 15]
# A typical generator expression
gen = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]

list(gen) # [8]

生成器表达式对 in 子句在声明时计算并保存对象的引用, 对 if 子句在运行时计算并寻找对象.

在复制 gen 对象后, gen 对象本身保存了对原始 array 对象的引用.

在实际遍历gen对象时, if子句中使用到的array对象会在当前上下文寻找, 并且访问到是修改过后的array对象.

array_3 = [1, 2, 3]
array_4 = [10, 20, 30]
gen = (i + j for i in array_3 for j in array_4)

array_3 = [4, 5, 6]
array_4 = [400, 500, 600]
list(gen) # [401, 501, 601, 402, 502, 602, 403, 503, 603]

但是在上述例子中, 生成器gen 只保存了对 array_3的引用, 对array_4是在运行时通过寻找上下文. 对应的解释在pep-289 中可见.

Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run.

Let's see if you can guess this?

a, b = a[b] = {}, 5
a
# {5: ({...}, 5)}

根据python-assignment-statements里的规则, 赋值语句在计算出最右边的expression_list后, 按从左到右的顺序赋值给左侧的target_list

所以上述例子可以简单的转换为如下

expression_list = {}, 5
a, b = expression_list
a[b] = expression_list

Lossy zip of iterators *

>>> numbers = list(range(7))
>>> numbers
[0, 1, 2, 3, 4, 5, 6]
>>> first_three, remaining = numbers[:3], numbers[3:]
>>> first_three, remaining
([0, 1, 2], [3, 4, 5, 6])
>>> numbers_iter = iter(numbers)
>>> list(zip(numbers_iter, first_three))
[(0, 0), (1, 1), (2, 2)]
# so far so good, let's zip the remaining
>>> list(zip(numbers_iter, remaining))
[(4, 3), (5, 4), (6, 5)]

zip 在 python 的近似实现如下

def zip(*iterables):
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel: return
            result.append(elem)
        yield tuple(result)

zip 在消费numbers_iter中的值 3 后, 在对first_three进行消费时被终止. 此时numbers_iter3 相当于被丢弃

Midnight time doesn't exist?

from datetime import datetime

midnight = datetime(2018, 1, 1, 0, 0)
midnight_time = midnight.time()

noon = datetime(2018, 1, 1, 12, 0)
noon_time = noon.time()

if midnight_time:
    print("Time at midnight is", midnight_time)

if noon_time:
    print("Time at noon is", noon_time)

3.5 以前的版本, 如果datetime.time对象表示的是 UTC 下的午夜时间, 对象会被认为是 Flase