吐槽:Python正在从简明转向臃肿,从实用转向媚俗
△点击上方“Python猫”关注 ,回复“1”领取电子书
忍了很久,今日得闲,吐槽一下Python的新增语言特性。以下总结了导致Python危险转向的十大槽点,排名不分先后。
1. 媚俗的静态类型检查
2. 画蛇添足的变量类型声明
当一个拥有5年工作经验的Python程序员读到下面的代码时,会有什么样的感受呢?
>>> name: str
>>> age: int = 18
>>> gilrfriends: list = ['奥黛丽·赫本', '卡米拉·贝勒', '安吉丽娜·朱莉']
>>> gilrfriends: list = '奥黛丽·赫本, 卡米拉·贝勒, 安吉丽娜·朱莉' # 打着右转灯左转
>>> gilrfriends
'奥黛丽·赫本, 卡米拉·贝勒, 安吉丽娜·朱莉'
3. 鸡肋般的函数及参数的类型注解
def sphere(center, radius, color, slices=90):
"""绘制球体
center - 球心坐标,长度为3的元组或列表
radius - 半径,浮点型
color - 顶点颜色,字符串
slices - 球面分片数(数值越大越精细),整型
"""
assert isinstance(center, (tuple, list)) and len(center)==3, '期望参数center是长度为3的元组或列表'
print('Hello')
return True
from typing import Union
def sphere(center:Union[tuple,list], radius:float, color:str, slices:int=90) -> bool:
"""绘制球体
center - 球心坐标,长度为3的元组或列表
radius - 半径,浮点型
color - 顶点颜色,字符串
slices - 球面分片数(数值越大越精细),整型
"""
print('Hello')
return True
4. 不加限制的扩展运算符
>>> s = 'Hello Wold'
>>> s + '#'*(16-len(s)) if len(s) < 16 else s
'Hello Wold######'
>>> s = 'Hello Wold'
>>> s + '#'*(16-c) if (c:=len(s)) < 16 else s
'Hello Wold######'
>>> a = {'x':1, 'y':2}
>>> b = {'y':3, 'z':4}
>>> a | b # 返回一个新的字典
{'x': 1, 'y': 3, 'z': 4}
>>> a |= b # 将b合并到a中
>>> a
{'x': 1, 'y': 3, 'z': 4}
>>> a = {'x':1, 'y':2}
>>> b = {'y':3, 'z':4}
>>> a.update(b)
>>> a
{'x': 1, 'y': 3, 'z': 4}
>>> {**a, **b}
{'x': 1, 'y': 3, 'z': 4}
5. 越俎代庖的字符串方法
大量的内置函数或方法,固然可以为程序员带来更多的便利,但也会使系统臃肿,同时增加了入门难度,使学习曲线变得陡峭。如何平衡这个尺度,是一个见仁见智的问题。在我看来,不断增加函数和方法,不但违背了Python的初衷,破坏了API的稳定性,也让程序员失去了选择和乐趣。比如,Py3.9为str对象新增了removeprefix()和removesuffix()两个方法,分别用于删除字符串前缀和后缀。
>>> s = '[主播]上港获得前场任意球(1:1)'
>>> s.removeprefix('[主播]')
'上港获得前场任意球(1:1)'
>>> s.removesuffix('(1:1)')
'[主播]上港获得前场任意球'
>>> s = '[主播]上港获得前场任意球(1:1)'
>>> s if s.find('[主播]') < 0 else s[len('[主播]'):]
'上港获得前场任意球(1:1)'
>>> s if (n:=s.rfind('(1:1)')) < 0 else s[:n]
'[主播]上港获得前场任意球'
6. 啼笑皆非的字符串前缀
Py2时代,u是最常见的字符串前缀,Py3时代,u前缀销声匿迹,b成为了最常见的字符串前缀。无论是Py2还是Py3,原生字符串前缀r都是Python程序员眼中的熟客。从Py3.6开始,突然又冒出来一个新的字符串前缀。
>>> score = '1:1'
>>> f'当前比分{score}'
'当前比分1:1'
>>> 语文=95
>>> 数学=100
>>> f'{语文=},{数学=}'
'语文=95,数学=100'
MyGod,这哪儿是代码,简直是神符!说实话,格式化字符串时,除了C语言风格的%外,我连format()函数都没用过,现在却有可能要面对同事写出来的包含加强版f前缀的代码。伤心的我,只能一笑了之。
7. 不讲逻辑的字典顺序
Py3.6发布的时候,就有人到处宣传说,字典有序了。起初我以为那不过自媒体出于猎奇的目的,随便说说,吸引眼球的。在逻辑上,字典是数据的无序集合,仅依赖于键检索,如果字典有序,那就不是字典了。没想到,Py3.7居然真的将“字典保持插入顺序”作为新增特性正式对外公布了。幸好,官方说的是“字典保持插入顺序”,而不是“字典有序”,总算为逻辑保留了最后的底裤。
我说字典无序,不是指字典在物理实体上实现的时候真的无序,而是指它的顺序对用户而言没有明确的界定,不能作为数据的特性使用。也许Py3.7使用了新的机制可以更高效地存储和检索字典数据,但这并不是程序员所关注的。新的机制带来的字典顺序稳定,并不能成为我们可以信赖和使用的字典属性。
8. 惟恐天下不乱的函数参数限定符
说到函数的定义和传参,恐怕没有比Python更灵活的语言了。方便的同时,位置参数、默认参数、可变参数、关键字参数等几种参数类型和使用顺序,也给程序员带来了很多使用上的困惑。然而,Py3.8坚定地认为,函数参数的使用还不够混乱,还需要再刺激一下,以期达到从大乱到大治的目的。于是,函数参数限定符登场了——为了增强效果,Python官方选择斜杠(/)作为这个限定符的表现形式。
>>> def func(a, b, /, c, d, *, e, f):
print('OK')
>>> func(1,2,3,4,e=5,f=6)
OK
>>> func(1,2,3,d=4,e=5,f=6)
OK
这里,a, b是位置参数,e, f是关键字参数,c, d既可以是位置参数,也可以是关键字参数。好了,关于函数参数限定符就说这么多吧,反正也用不着——除非你是在用Python函数完全模拟C代码编写的函数。
9. 毫无优雅感的泛型函数
说到泛型,Python程序员通常会有两种表情:一种是惊讶:“Python有泛型吗?”;另一种是哂笑:“Python有型吗?”且不讨论Python是否“有型”,作为动态语言,泛型难道不是与生俱来的吗?也许Python团队不这么认为,或者他们认为Python程序员不这么认为。为了配得上编程语言排行榜上大佬的位次,从Py3.4开始,Python团队就为Python定制了一个泛型函数装饰器。无奈这个机制实在太过牵强,用它写出来得代码丑得一批,估计没几个人会用它。
>>> from functools import singledispatch
>>> @singledispatch
def say_hello(x):
print('抱歉,我不认识你')
>>> @say_hello.register(int)
def _(x):
print('你好,整数')
>>> @say_hello.register(str)
def _(x):
print('你好,字符串')
>>> say_hello(5)
你好,整数
>>> say_hello('abc')
你好,字符串
>>> say_hello([1,2,3])
抱歉,我不认识你
>>> def say_hello(x):
if isinstance(x, int):
print('你好,整数')
elif isinstance(x, str):
print('你好,字符串')
else:
print('抱歉,我不认识你')
>>> say_hello(5)
你好,整数
>>> say_hello('abc')
你好,字符串
>>> say_hello([1,2,3])
抱歉,我不认识你
10. 前后打脸的亡羊补牢
当我第一次读到typing.List和typing.Dict的时候,有那么一小会儿,感觉像是在读java或者是C++的代码。为什么不是熟悉的list和dict呢?仅仅是为了类型提示(type hints) ,为了让集成开发工具读懂代码,生生搞出了比list和dict还复杂的概念。最终的结果是,IDE读懂了代码,程序员却看不明白了。
罗里吧嗦写了这么多,希望不会让Python程序员心生沮丧。事实上,不管我或者我们如何吐槽,Python依然是一门伟大的编程语言。本文的观点,仅是我作为一个原教旨主义者的个人观点,稍微掺杂了一点调侃。欢迎各位发表高见,不要顾及我的颜面。我会装作看不见,不解释不辩论。杠爷请绕行。
近期热门文章推荐: