设计和历史常见问题
目录
- 设计和历史常见问题
- 为什么Python使用缩进来分组语句?
- 为什么简单的算术运算得到奇怪的结果?
- 为什么浮点计算不准确?
- 为什么Python字符串是不可变的?
- 为什么必须在方法定义和调用中显式使用“self”?
- 为什么不能在表达式中赋值?
- 为什么Python对某些功能(例如list.index())使用方法来实现,而其他功能(例如len(List))使用函数实现?
- 为什么 join()是一个字符串方法而不是列表或元组方法?
- 异常有多快?
- 为什么Python中没有switch或case语句?
- 难道不能在解释器中模拟线程,而非得依赖特定于操作系统的线程实现吗?
- 为什么lambda表达式不能包含语句?
- 可以将Python编译为机器代码,C或其他语言吗?
- Python如何管理内存?
- 为什么CPython不使用更传统的垃圾回收方案?
- CPython退出时为什么不释放所有内存?
- 为什么有单独的元组和列表数据类型?
- 列表是如何在CPython中实现的?
- 字典是如何在CPython中实现的?
- 为什么字典key必须是不可变的?
- 为什么 list.sort() 没有返回排序列表?
- 如何在Python中指定和实施接口规范?
- 为什么没有goto?
- 为什么原始字符串(r-strings)不能以反斜杠结尾?
- 为什么Python没有属性赋值的“with”语句?
- 为什么 if/while/def/class语句需要冒号?
- 为什么Python在列表和元组的末尾允许使用逗号?
为什么Python使用缩进来分组语句?
为什么简单的算术运算得到奇怪的结果?
请看下一个问题。
为什么浮点计算不准确?
用户经常对这样的结果感到惊讶:
>>> 1.2 - 1.0
0.19999999999999996
>>> x = 1.2
为
x
存储的值是与十进制的值
1.2
(非常接近) 的近似值,但不完全等于它。在典型的机器上,实际存储的值是:
1.0011001100110011001100110011001100110011001100110011 (binary)
它对应于十进制数值:
1.1999999999999999555910790149937383830547332763671875 (decimal)
典型的 53 位精度为 Python 浮点数提供了 15-16 位小数的精度。要获得更完整的解释,请参阅 Python 教程中的
浮点算术 一章。
为什么Python字符串是不可变的?
有几个优点。一个是性能:知道字符串是不可变的,意味着我们可以在创建时为它分配空间,并且存储需求是固定不变的。这也是元组和列表之间区别的原因之一。另一个优点是,Python 中的字符串被视为与数字一样“基本”。任何动作都不会将值 8 更改为其他值,在 Python 中,任何动作都不会将字符串 "8" 更改为其他值。
为什么必须在方法定义和调用中显式使用“self”?
为什么不能在表达式中赋值?
自 Python 3.8 开始,你能做到的!赋值表达式使用海象运算符
:= 在表达式中为变量赋值:
while chunk := fp.read(200):print(chunk)
请参阅
PEP 572 了解详情。
为什么Python对某些功能(例如list.index())使用方法来实现,而其他功能(例如len(List))使用函数实现?
https://mail.python.org/pipermail/python-3000/2006-November/004643.html
为什么 join()是一个字符串方法而不是列表或元组方法?
从Python 1.6开始,字符串变得更像其他标准类型,当添加方法时,这些方法提供的功能与始终使用String模块的函数时提供的功能相同。这些新方法中的大多数已被广泛接受,但似乎让一些程序员感到不舒服的一种方法是:
", ".join(['1', '2', '4', '8', '16'])
结果如下:
"1, 2, 4, 8, 16"
反对这种用法有两个常见的论点。第一条是这样的:“使用字符串文本(String Constant)的方法看起来真的很难看”,答案是也许吧,但是字符串文本只是一个固定值。如果在绑定到字符串的名称上允许使用这些方法,则没有逻辑上的理由使其在文字上不可用。第二个异议通常是这样的:“我实际上是在告诉序列使用字符串常量将其成员连接在一起”。遗憾的是并非如此。出于某种原因,把
split()
作为一个字符串方法似乎要容易得多,因为在这种情况下,很容易看到:
"1, 2, 4, 8, 16".split(", ")
是对字符串文本的指令,用于返回由给定分隔符分隔的子字符串(或在默认情况下,返回任意空格)。
join()
是字符串方法,因为在使用该方法时,您告诉分隔符字符串去迭代一个字符串序列,并在相邻元素之间插入自身。此方法的参数可以是任何遵循序列规则的对象,包括您自己定义的任何新的类。对于字节和字节数组对象也有类似的方法。
异常有多快?
为什么Python中没有switch或case语句?
你可以通过一系列
if... elif... elif... else
.轻松完成这项工作。对于switch语句语法已经有了一些建议,但尚未就是否以及如何进行范围测试达成共识。有关完整的详细信息和当前状态,请参阅
PEP 275 。对于需要从大量可能性中进行选择的情况,可以创建一个字典,将case 值映射到要调用的函数。例如:
def function_1(...):...
functions = {'a': function_1,'b': function_2,'c': self.method_1, ...}
func = functions[value]
func()
对于对象调用方法,可以通过使用
getattr()
内置检索具有特定名称的方法来进一步简化:
def visit_a(self, ...):......def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
建议对方法名使用前缀,例如本例中的
visit_
。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。
难道不能在解释器中模拟线程,而非得依赖特定于操作系统的线程实现吗?
为什么lambda表达式不能包含语句?
Python的 lambda表达式不能包含语句,因为Python的语法框架不能处理嵌套在表达式内部的语句。然而,在Python中,这并不是一个严重的问题。与其他语言中添加功能的lambda表单不同,Python的 lambdas只是一种速记符号,如果您懒得定义函数的话。函数已经是Python中的第一类对象,可以在本地范围内声明。因此,使用lambda而不是本地定义的函数的唯一优点是你不需要为函数创建一个名称 -- 这只是一个分配了函数对象(与lambda表达式生成的对象类型完全相同)的局部变量!
可以将Python编译为机器代码,C或其他语言吗?
Cython 将带有可选注释的Python修改版本编译到C扩展中。Nuitka 是一个将Python编译成 C++ 代码的新兴编译器,旨在支持完整的Python语言。要编译成Java,可以考虑 VOC 。
Python如何管理内存?
为什么CPython不使用更传统的垃圾回收方案?
CPython退出时为什么不释放所有内存?
为什么有单独的元组和列表数据类型?
列表是如何在CPython中实现的?
CPython的列表实际上是可变长度的数组,而不是lisp风格的链表。该实现使用对其他对象的引用的连续数组,并在列表头结构中保留指向该数组和数组长度的指针。这使得索引列表
a[i]
的操作成本与列表的大小或索引的值无关。当添加或插入项时,将调整引用数组的大小。并采用了一些巧妙的方法来提高重复添加项的性能; 当数组必须增长时,会分配一些额外的空间,以便在接下来的几次中不需要实际调整大小。
字典是如何在CPython中实现的?
为什么字典key必须是不可变的?
class ListWrapper:def __init__(self, the_list):self.the_list = the_listdef __eq__(self, other):return self.the_list == other.the_listdef __hash__(self):
l = self.the_list
result = 98767 - len(l)*555for i, el in enumerate(l):try:
result = result + (hash(el) % 9999999) * 1001 + iexcept Exception:
result = (result % 7777777) + i * 333return result
为什么 list.sort() 没有返回排序列表?
如何在Python中指定和实施接口规范?
为什么没有goto?
为什么原始字符串(r-strings)不能以反斜杠结尾?
为什么Python没有属性赋值的“with”语句?
Python有一个 'with' 语句,它封装了块的执行,在块的入口和出口调用代码。有些语言的结构是这样的:
with obj:
a = 1 # equivalent to obj.a = 1
total = total + 1 # obj.total = obj.total + 1
在Python中,这样的结构是不明确的。其他语言,如ObjectPascal、Delphi和C++ 使用静态类型,因此可以毫不含糊地知道分配给什么成员。这是静态类型的要点 -- 编译器
总是 在编译时知道每个变量的作用域。Python使用动态类型。事先不可能知道在运行时引用哪个属性。可以动态地在对象中添加或删除成员属性。这使得无法通过简单的阅读就知道引用的是什么属性:局部属性、全局属性还是成员属性?例如,采用以下不完整的代码段:
def foo(a):with a:print(x)
该代码段假设 "a" 必须有一个名为 "x" 的成员属性。然而,Python中并没有告诉解释器这一点。假设 "a" 是整数,会发生什么?如果有一个名为 "x" 的全局变量,它是否会在with块中使用?如您所见,Python的动态特性使得这样的选择更加困难。然而,Python 可以通过赋值轻松实现 "with" 和类似语言特性(减少代码量)的主要好处。代替:
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
这也具有提高执行速度的副作用,因为Python在运行时解析名称绑定,而第二个版本只需要执行一次解析。
为什么 if/while/def/class语句需要冒号?
为什么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"
]
这个列表看起来有四个元素,但实际上包含三个 : "fee", "fiefoo" 和 "fum" 。总是加上逗号可以避免这个错误的来源。允许尾随逗号也可以使编程代码更容易生成。