了解来自新年挑战的 Python 代码

Understanding this Python code from 2014's new year challenge

我刚刚浏览了这个页面 here 并找到了这个条目 :

print sum(ord(c) for c in 'Happy new year to you!')

这是 python 代码并在执行时打印年份。有人可以帮助 Java 开发人员准确了解这里发生了什么吗?

需要理解的几点:

  • 默认情况下,字符串是可迭代的,因此可以简单地迭代字符串中的每个元素:

    for c in 'Hello there':
        print c
    
  • ord 是一个内置函数,returns 字符的实际数字代码点。

  • 表达式ord(c) for c in 'Happy new year to you!'是一个generator expression。此 returns 返回一个生成器函数的结果,它会在后续调用 __next__() 时检索整个生成器表达式的结果。这既发生在我们的幕后,又是以懒惰的方式完成的;如果未调用 __next__() 部分,则不会生成下一个值。如果您要生成的表达式包含 lot 个值,这将很有用。

    这实际上是代码片段的症结所在;它表达了一些必须以更简洁的方式在 Java 中更笨拙地写的东西。

  • sum 将列表作为参数,returns 其内容的总数值。

ord() 将字符转换为其 ASCII 值。 sum() 将定义了加法运算的对象集合相加,在本例中为数学标量加法。

sum() 中的表达式是生成器表达式,一种可迭代语句,在 Java 中没有明确的等效项,但类似于 .NET 中的 LINQ。本质上,它是一个内联的 for-each 循环,遍历字符串 "Happy new year to you!" 中的每个字符,用 ord 计算字符的 ASCII 值,并将这些数值相加

int s = 0;

for (char c: "Happy new year to you!".toCharArray())
  s += (int) c;

System.out.println(s);

print 是一条语句(在 Python 2.x 中),它将打印其后的表达式。

(请注意,在 Python 3.x 中,print() 是一个打印其参数的函数。)

表达式是对内置函数的调用 sum()。无论求和,结果都是 2014,所以 print 打印 2014.

sum() 正在传递一个名为 "generator expression" 的特殊结构。这类似于 "list comprehension" 但效率更高一些。 [1]生成器表达式的基本格式是:

expression for variable in iterable

这里,变量citerable是一个字符串,'Happy new year to you!' expression是对内置函数ord()的调用,returns 一个整数,表示传递给它的字符;例如,ord('A') returns 65.

所以,这对字符串中所有字符的序数值求和;总和是 2014 年,打印出来了。

[1] 列表理解构建值列表。生成器表达式不构建任何东西,但可以重复调用以一次产生一个值。 Python 中接受迭代的函数能够接受生成器表达式并从中获取值。

您可以使用生成器表达式编写此代码以构建列表,然后对列表求和。但如果你这样做,列表将被构造,一次查看,然后被垃圾收集。当您只想对值求和时,为什么还要浪费精力分配和销毁列表对象呢?因此,生成器表达式。

1) 内置函数ord returns char 的整数值

>>> help(ord)
Help on built-in function ord in module __builtin__:

ord(...)
    ord(c) -> integer

    Return the integer ordinal of a one-character string.

2) for 循环对字符串的每个字符进行迭代 'Happy new year to you!'

>>> for c in 'Happy new year to you':
...     print ord(c)
...
72
97
112
112
...

3) (ord(c) for c in 'Happy new year to you!')generator 表达式 python.

>>> result = (ord(c) for c in 'Happy new year to you!')
>>> result.next()
72
>>> result.next()
97

4) sum内置函数returns每个字符的整数值总和:

>>> help(sum)
Help on built-in function sum in module __builtin__:

sum(...)
    sum(sequence[, start]) -> value

    Returns the sum of a sequence of numbers (NOT strings) plus the value
    of parameter 'start' (which defaults to 0).  When the sequence is
    empty, returns start.

所以组合所有这些表达式的结果是:

>>> sum(ord(c) for c in 'Happy new year to you!')
2014

另一个可能的解决方案是:

>>> sum(map(lambda c:ord(c), 'Happy new year to you!'))
2014

在此代码片段中找到并被 "naked" ( ) 包围的形式的表达式称为生成器理解。它产生一种特定类型的可迭代对象,在 Python 中称为生成器。

还有其他的理解。由裸括号包围的表达式将是一个列表推导式。示例:

[char for char in "string"] 

这将产生一个列表:

['s','t','r','i','n','g']

和 "naked" 大括号(又名集合推导)产生一个集合:

{char for char in "string"} 

这使得集合:

{'s','t','r','i','n','g'}

(还有字典推导)

正如我在一开始所说的那样,仅在这种 something for something in something_else 形式的语句周围使用括号会产生一种特殊的迭代器,称为 Python 中的生成器(而不是列表或一组,如上面的例子)。

然而,在 Python 中,很多其他东西都是可迭代的,包括字符串。在生成器内部,每个字符在字符串被迭代时被检索,一次一个,因为它被依次调用,st,...等等。检索到的字符就是char 为该次迭代引用的对象。

ord(char) 部分在迭代字符串时将 ord 函数依次应用于每个 charord 函数只是查找从字符串中检索到的特定字符的 unicode 编号。该 unicode 值然后是当前迭代的整体生成器的结果。

要从生成器中获取值,您必须以某种方式对其进行迭代 - 例如使用 next()for...in 语句。但通常你也可以将生成器作为参数应用于任何接收可迭代参数的函数。在这种情况下,sum()(这显然意味着将一系列连续的参数加在一起)被应用于生成器的所有结果。生成器产生的每个结果都是该系列的一个成员。

所以代码的整体效果是将字符串字符的所有unicode值加在一起。 2014年的整体结果似乎只是一个巧合。那里没有什么神秘或神奇的事情发生。