PEP这玩意有点类似C++各个编译器厂商给标准委员会的提案,但比起C++,显然兼容性包袱低的Python的提案更加大胆,很多居然成为了标准。

PEP 202 – List Comprehensions,列表生成式

形如:

>>> print [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

的形式,减少了map的使用,可以很方便地构造新的list。

就是这几个嵌套for的语法有点迷惑:

>>> print [(i, f) for i in nums for f in fruit]
[(1, 'Apples'), (1, 'Peaches'), (1, 'Pears'), (1, 'Bananas'),
 (2, 'Apples'), (2, 'Peaches'), (2, 'Pears'), (2, 'Bananas'),
 (3, 'Apples'), (3, 'Peaches'), (3, 'Pears'), (3, 'Bananas'),
 (4, 'Apples'), (4, 'Peaches'), (4, 'Pears'), (4, 'Bananas')]
>>> print [(i, f) for i in nums for f in fruit if f[0] == "P"]
[(1, 'Peaches'), (1, 'Pears'),
 (2, 'Peaches'), (2, 'Pears'),
 (3, 'Peaches'), (3, 'Pears'),
 (4, 'Peaches'), (4, 'Pears')]
>>> print [(i, f) for i in nums for f in fruit if f[0] == "P" if i%2 == 1]
[(1, 'Peaches'), (1, 'Pears'), (3, 'Peaches'), (3, 'Pears')]

都写得这么复杂了,还是单独取出来吧。。。。。。

PEP 274 – Dict Comprehensions,字典生成式

和上面的语法类似,只不过外壳换成了花括号,循环体换成了用冒号连接的键值对:

>>> print {i : chr(65+i) for i in range(4)}
{0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}

通过这种办法可以快速构造一个字典。

PEP 234 – Iterators,迭代器

编程语言的官方中文真是任重道远,这东西用英文表达比用中文容易多了。

iter

对于某个对象来说,只要该对象拥有__iter__方法(或者说重写了__iter__方法/Java,拥有__iter__特征/Rust),就是一个Iterable对象(一般翻译为[可迭代对象])。某种意义上来说,一个对象是可迭代的,意味着可以当成一个“数组”来处理。

遗憾的是,我没有找到一个只是可迭代的对象,没有实现迭代器(__next__),或者生成器(yield)的示例。似乎这个语法一定要配合这两个才能使用。

next

对于某个可迭代对象来说,只要该对象拥有__next__方法,该对象就是一个Iterator(一般翻译为[迭代器对象])。用如其名,这意味着当你拿到对象的某个迭代器,你可以马上获取下一个(或者下一个不可达)

以斐波那契数列为例:

class Fib:
    def __init__(self, max):
        self.a = 0
        self.b = 1
        self.max = max

    def __iter__(self):
        return self
    
    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib
    
fib = Fib(5)
for i in fib:
    print(i)

注意__next__需要内部判断迭代上限,并抛出StopIteration异常。
另外,对于我此处的代码,迭代行为会修改对象的成员,这意味着如果我想再迭代一次,我就得创建一个新对象。

PEP 279 – The enumerate () built-in function,enumerate 枚举

翻译为“枚举”不是很恰当,因为Python里真的有枚举这玩意。而这个函数和Python的枚举没有半毛钱关系。。。

官方文档提供了内部的接口:

def enumerate(collection):
   'Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...'
   i = 0
   it = iter(collection)
   while 1:
      yield (i, it.next())
      i += 1

可以看出来enumerate接收一个迭代器对象,并在调用时生成一个tuple,第一个元素是索引,第二个是迭代器本身返回的元素。

使用的话,也很简答:

for index, ele in enumerate(fib):
    print(index, ele)

PEP 285 – Adding a bool type,布尔值

Strong and bitter words indicate a weak cause. 看PEP的文档还挺有意思的。

说起来 iso646.h 还引入了(你不需要 include 这个文件也可以让这个特性生效)andnotor,作为&&!||的可选替代。并非C语言标准委员会想让语言更易于理解,单纯是为了兼容有些键盘上没有这些符号的场景。。。。。。

布尔值的引入本身没有太大问题,现代编程语言终究要走到这一步的,C++也是如此。

另外,当我在网上查资料编写这篇文章时,我读到了一篇很好的文章https://mp.weixin.qq.com/s/YQbk0smMTCexsi3Ytd2AzA,不过作者的说法有点偏激了。至少我觉得他不了解C/C++。下面相关的内容是关于这篇文章的,如果你不想读,可以跳过。

内存复用

如果你学过Java,你对内存复用的设计应该不陌生。简单来说就是 满足一定要求的字符串或者数字,新创建时并不会新分配内存,而是复用已有的内存。 以Python为例子:

Python 对 -5256 之间的整数对象进行缓存(在命令行Python下不一定生效):

a = 100
b = 100
print(f"id(a) = {id(a)}")
print(f"id(b) = {id(b)}")

c = 3000
d = 3000
print(f"id(c) = {id(c)}")
print(f"id(d) = {id(d)}")

然而在我的环境上,打印出来的id似乎是一模一样的:

id(a) = 139857353119184
id(b) = 139857353119184
id(c) = 139857351916560
id(d) = 139857351916560

百思不得其解。

Python 对符合标识符规则的字符串(仅包含字母、数字、下划线)进行驻留:

s1 = "hello"
s2 = "hello"
print(id(s1))
print(id(s2))

s3 = "hello!@SSSSSSSVVVVV))2222"  # 包含非标识符字符,不驻留
s4 = "hello!@SSSSSSSVVVVV))2222"
print(id(s3))
print(id(s4))

图中打印出来的id也不符合上面的规则:

139637337804912
139637337804912
139637337867568
139637337867568

想来Python的解释器还进行了其他优化。

C++倒是没有内存驻留这种设计,事实上对于常量来说,可以使用#define进行宏定义,或者使用constexpr进行修饰,这两种做法都会使得在编译期,编译器会逐个对代码中出现常量的地方进行文本替换。

返回对象的字符串表示

优秀的设计。本质上是提供了语言级别的接口来打印类的信息,类本身只需要实现接口就好。示例代码如下:

class Worker:
    def __str__(self) -> str:
        return "Worker"
    
    def __repr__(self) -> str:
        return "[DEBUG] Worker"
    
worker = Worker()
print(worker) # 或者 print(str(worker))
print(repr(worker))

打印如下:

Worker
[DEBUG] Worker

C++也可以重载operator<<来实现类似的功能,但据我所知的日志库都没用这玩意(官方新版的f-string就更没用了)

布尔泛化

将布尔值作为整数的子集并非Python首创,这一设计实际上来自C,而Python这样设计布尔值很大程度上也是考虑和C的兼容性。但是Python的另一个布尔泛化的设计就十分优秀了。

C/C++绝大多数变量也可以作为布尔值进行if判断,但这种判断效果很有限:

  • 0转换为false,非零转换为true
  • nullptr/NULL转换为false,非空指针转换为true
  • 对于其他的变量大部分都是true

Python的判断就比较多了,明确为 False 的对象:

  • 布尔类型本身:False
  • 数值类型:0(整数、浮点数、复数)
  • 空容器:[](列表)、{}(字典)、()(元组)、''(字符串)、set()(集合)
  • 特殊对象:None
Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐