设计和历史常见问题¶

为什么Python使用缩进来分组语句？¶

Guido van Rossum 认为使用缩进进行分组非常优雅，并且大大提高了普通Python程序的清晰度。大多数人在一段时间后就学会并喜欢上这个功能。

```if (x <= y)
x++;
y--;
z++;
```

Why are floating point calculations so inaccurate?¶

People are often very surprised by results like this:

```>>> 1.2 - 1.0
0.19999999999999996
```

and think it is a bug in Python. It’s not. This has nothing to do with Python, but with how the underlying C platform handles floating point numbers, and ultimately with the inaccuracies introduced when writing down numbers as a string of a fixed number of digits.

The internal representation of floating point numbers uses a fixed number of binary digits to represent a decimal number. Some decimal numbers can’t be represented exactly in binary, resulting in small roundoff errors.

In decimal math, there are many numbers that can’t be represented with a fixed number of decimal digits, e.g. 1/3 = 0.3333333333…….

In base 2, 1/2 = 0.1, 1/4 = 0.01, 1/8 = 0.001, etc. .2 equals 2/10 equals 1/5, resulting in the binary fractional number 0.001100110011001…

Floating point numbers only have 32 or 64 bits of precision, so the digits are cut off at some point, and the resulting number is 0.199999999999999996 in decimal, not 0.2.

A floating point number’s `repr()` function prints as many digits are necessary to make `eval(repr(f)) == f` true for any float f. The `str()` function prints fewer digits and this often results in the more sensible number that was probably intended:

```>>> 1.1 - 0.9
0.20000000000000007
>>> print 1.1 - 0.9
0.2
```

One of the consequences of this is that it is error-prone to compare the result of some computation to a float with `==`. Tiny inaccuracies may mean that `==` fails. Instead, you have to check that the difference between the two numbers is less than a certain threshold:

```epsilon = 0.0000000000001  # Tiny allowed error
expected_result = 0.4

if expected_result-epsilon <= computation() <= expected_result+epsilon:
...
```

为什么不能在表达式中赋值？¶

```while (line = readline(f)) {
// do something with line
}
```

```while True:
if not line:
break
...  # do something with line
```

```if (x = 0) {
// error handling
}
else {
// code that only works for nonzero x
}
```

```line = f.readline()
while line:
...  # do something with line...
```

The best approach is to use iterators, making it possible to loop through objects using the `for` statement. For example, in the current version of Python file objects support the iterator protocol, so you can now write simply:

```for line in f:
...  # do something with line...
```

为什么Python对某些功能（例如list.index()）使用方法来实现，而其他功能（例如len(List)）使用函数实现？¶

(a) 对于某些操作，前缀表示法比后缀更容易阅读 – 前缀（和中缀！）运算在数学中有着悠久的传统，就像在视觉上帮助数学家思考问题的记法。比较一下我们将 x*(a+b) 这样的公式改写为 x*a+x*b 的容易程度，以及使用原始OO符号做相同事情的笨拙程度。

(b) 当读到写有len(X)的代码时，就知道它要求的是某件东西的长度。这告诉我们两件事：结果是一个整数，参数是某种容器。相反，当阅读x.len()时，必须已经知道x是某种实现接口的容器，或者是从具有标准len()的类继承的容器。当没有实现映射的类有get()或key()方法，或者不是文件的类有write()方法时，我们偶尔会感到困惑。

为什么 join()是一个字符串方法而不是列表或元组方法？¶

```", ".join(['1', '2', '4', '8', '16'])
```

```"1, 2, 4, 8, 16"
```

```"1, 2, 4, 8, 16".split(", ")
```

is an instruction to a string literal to return the substrings delimited by the given separator (or, by default, arbitrary runs of white space). In this case a Unicode string returns a list of Unicode strings, an ASCII string returns a list of ASCII strings, and everyone is happy.

`join()` is a string method because in using it you are telling the separator string to iterate over a sequence of strings and insert itself between adjacent elements. This method can be used with any argument which obeys the rules for sequence objects, including any new classes you might define yourself.

Because this is a string method it can work for Unicode strings as well as plain ASCII strings. If `join()` were a method of the sequence types then the sequence types would have to decide which type of string to return depending on the type of the separator.

If none of these arguments persuade you, then for the moment you can continue to use the `join()` function from the string module, which allows you to write

```string.join(['1', '2', '4', '8', '16'], ", ")
```

异常有多快？¶

```try:
value = mydict[key]
except KeyError:
mydict[key] = getvalue(key)
value = mydict[key]
```

```if key in mydict:
value = mydict[key]
else:
value = mydict[key] = getvalue(key)
```

In Python 2.0 and higher, you can code this as ```value = mydict.setdefault(key, getvalue(key))```.

为什么Python中没有switch或case语句？¶

```def function_1(...):
...

functions = {'a': function_1,
'b': function_2,
'c': self.method_1, ...}

func = functions[value]
func()
```

```def visit_a(self, ...):
...
...

def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
```

难道不能在解释器中模拟线程，而非得依赖特定于操作系统的线程实现吗？¶

Answer 2: Fortunately, there is Stackless Python, which has a completely redesigned interpreter loop that avoids the C stack.

为什么lambda表达式不能包含语句？¶

Python的 lambda表达式不能包含语句，因为Python的语法框架不能处理嵌套在表达式内部的语句。然而，在Python中，这并不是一个严重的问题。与其他语言中添加功能的lambda表单不同，Python的 lambdas只是一种速记符号，如果您懒得定义函数的话。

可以将Python编译为机器代码，C或其他语言吗？¶

Cython 将带有可选注释的Python修改版本编译到C扩展中。 Nuitka 是一个将Python编译成 C++ 代码的新兴编译器，旨在支持完整的Python语言。要编译成Java，可以考虑 VOC

Python如何管理内存？¶

The details of Python memory management depend on the implementation. The standard C implementation of Python uses reference counting to detect inaccessible objects, and another mechanism to collect reference cycles, periodically executing a cycle detection algorithm which looks for inaccessible cycles and deletes the objects involved. The `gc` module provides functions to perform a garbage collection, obtain debugging statistics, and tune the collector’s parameters.

Jython relies on the Java runtime so the JVM’s garbage collector is used. This difference can cause some subtle porting problems if your Python code depends on the behavior of the reference counting implementation.

Sometimes objects get stuck in tracebacks temporarily and hence are not deallocated when you might expect. Clear the tracebacks with:

```import sys
sys.exc_clear()
sys.exc_traceback = sys.last_traceback = None
```

Tracebacks are used for reporting errors, implementing debuggers and related things. They contain a portion of the program state extracted during the handling of an exception (usually the most recent exception).

In the absence of circularities and tracebacks, Python programs do not need to manage memory explicitly.

Why doesn’t Python use a more traditional garbage collection scheme? For one thing, this is not a C standard feature and hence it’s not portable. (Yes, we know about the Boehm GC library. It has bits of assembler code for most common platforms, not for all of them, and although it is mostly transparent, it isn’t completely transparent; patches are required to get Python to work with it.)

Traditional GC also becomes a problem when Python is embedded into other applications. While in a standalone Python it’s fine to replace the standard malloc() and free() with versions provided by the GC library, an application embedding Python may want to have its own substitute for malloc() and free(), and may not want Python’s. Right now, Python works with anything that implements malloc() and free() properly.

In Jython, the following code (which is fine in CPython) will probably run out of file descriptors long before it runs out of memory:

```for file in very_long_list_of_files:
f = open(file)
```

Using the current reference counting and destructor scheme, each new assignment to f closes the previous file. Using GC, this is not guaranteed. If you want to write code that will work with any Python implementation, you should explicitly close the file or use the `with` statement; this will work regardless of GC:

```for file in very_long_list_of_files:
with open(file) as f:
```

列表是如何在CPython中实现的？¶

CPython的列表实际上是可变长度的数组，而不是lisp风格的链表。该实现使用对其他对象的引用的连续数组，并在列表头结构中保留指向该数组和数组长度的指针。

字典是如何在CPython中实现的？¶

CPython的字典实现为可调整大小的哈希表。与B-树相比，这在大多数情况下为查找（目前最常见的操作）提供了更好的性能，并且实现更简单。

Dictionaries work by computing a hash code for each key stored in the dictionary using the `hash()` built-in function. The hash code varies widely depending on the key; for example, “Python” hashes to -539294296 while “python”, a string that differs by a single bit, hashes to 1142331976. The hash code is then used to calculate a location in an internal array where the value will be stored. Assuming that you’re storing keys that all have different hash values, this means that dictionaries take constant time – O(1), in computer science notation – to retrieve a key. It also means that no sorted order of the keys is maintained, and traversing the array as the `.keys()` and `.items()` do will output the dictionary’s content in some arbitrary jumbled order.

为什么字典key必须是不可变的？¶

• 哈希按其地址（对象ID）列出。这不起作用，因为如果你构造一个具有相同值的新列表，它将无法找到；例如:

```mydict = {[1, 2]: '12'}
print mydict[[1, 2]]
```

would raise a KeyError exception because the id of the `[1, 2]` used in the second line differs from that in the first line. In other words, dictionary keys should be compared using `==`, not using `is`.

• 使用列表作为键时进行复制。这没有用的，因为作为可变对象的列表可以包含对自身的引用，然后复制代码将进入无限循环。

• 允许列表作为键，但告诉用户不要修改它们。当你意外忘记或修改列表时，这将产生程序中的一类难以跟踪的错误。它还使一个重要的字典不变量无效： `d.keys()` 中的每个值都可用作字典的键。

• 将列表用作字典键后，应标记为其只读。问题是，它不仅仅是可以改变其值的顶级对象；你可以使用包含列表作为键的元组。将任何内容作为键关联到字典中都需要将从那里可到达的所有对象标记为只读 —— 并且自引用对象可能会导致无限循环。

```class ListWrapper:
def __init__(self, the_list):
self.the_list = the_list

def __eq__(self, other):
return self.the_list == other.the_list

def __hash__(self):
l = self.the_list
result = 98767 - len(l)*555
for i, el in enumerate(l):
try:
result = result + (hash(el) % 9999999) * 1001 + i
except Exception:
result = (result % 7777777) + i * 333
return result
```

为什么 list.sort() 没有返回排序列表？¶

In Python 2.4 a new built-in function – `sorted()` – has been added. This function creates a new list from a provided iterable, sorts it and returns it. For example, here’s how to iterate over the keys of a dictionary in sorted order:

```for key in sorted(mydict):
...  # do whatever with mydict[key]...
```

如何在Python中指定和实施接口规范？¶

Python 2.6 adds an `abc` module that lets you define Abstract Base Classes (ABCs). You can then use `isinstance()` and `issubclass()` to check whether an instance or a class implements a particular ABC. The `collections` module defines a set of useful ABCs such as `Iterable`, `Container`, and `MutableMapping`.

为什么没有goto？¶

```class label: pass  # declare a label

try:
...
if condition: raise label()  # goto label
...
except label:  # where to goto
pass
...
```

为什么原始字符串（r-strings）不能以反斜杠结尾？¶

```f = open("/mydir/file.txt")  # works fine!
```

```dir = r"\this\is\my\dos\dir" "\\"
dir = r"\this\is\my\dos\dir\ "[:-1]
dir = "\\this\\is\\my\\dos\\dir\\"
```

为什么Python没有属性赋值的“with”语句？¶

Python有一个 ‘with’ 语句，它封装了块的执行，在块的入口和出口调用代码。有些语言的结构是这样的:

```with obj:
a = 1               # equivalent to obj.a = 1
total = total + 1   # obj.total = obj.total + 1
```

Python使用动态类型。事先不可能知道在运行时引用哪个属性。可以动态地在对象中添加或删除成员属性。这使得无法通过简单的阅读就知道引用的是什么属性：局部属性、全局属性还是成员属性？

```def foo(a):
with a:
print x
```

```function(args).mydict[index][index].a = 21
function(args).mydict[index][index].b = 42
function(args).mydict[index][index].c = 63
```

```ref = function(args).mydict[index][index]
ref.a = 21
ref.b = 42
ref.c = 63
```

为什么 if/while/def/class语句需要冒号？¶

```if a == b
print a
```

```if a == b:
print a
```

为什么Python在列表和元组的末尾允许使用逗号？¶

Python 允许您在列表，元组和字典的末尾添加一个尾随逗号:

```[1, 2, 3,]
('a', 'b', 'c',)
d = {
"A": [1, 5],
"B": [6, 7],  # last trailing comma is optional but good style
}
```

```x = [
"fee",
"fie"
"foo",
"fum"
]
```