- 您需要多少位架构师才能实现一种编程语言?
- 一百 一个将编写实现,而99个将说出他们可以做得更好。
在本文中,我不仅要涉及语言本身,而且要涉及CPython及其标准库的实现细节,这将确保您没有任何简单的方法来使Python应用程序成为多线程,快速或易于支持的对象,以及创建如此之多的原因替代实现(PyPy,Cython,Jython,IronPython,适用于.NET的Python,Parakeet,Nuitka,Stackless,Unladen Swallow),其中一半已经过时; 很少有人理解为什么其他选择没有机会赢得与其他语言的生存斗争。 是的,有GDScript用于解决性能问题;还有Nim,用于解决一般性的所有问题,而无需用户过多地显式声明类型。 但是,鉴于行业的巨大惯性,我意识到在未来十年中,新语言肯定不会占据很大的市场。 但是,我相信可以通过更改编写代码的样式来提高python的效率,其中大部分保留了原始语法,并完全保留了新旧代码之间交互的可能性。 我将专注于CPython的问题,而不是其最接近的竞争对手PyPy,因为PyPy实际上绕过了CPython的所有相同问题。
我是一名拥有七年经验的程序员,主要从事桌面应用程序的开发,并特别注重Web和多线程数据库。 您问,“等等,但是python与用户界面,Web Front和多线程有什么共同点?”。 我会回答“就是这样-什么都没有。” 我将C,Delphi,JavaScript和SQL用于任务。 我对这种情况不太满意,并且不久前,我尝试参加Eric Snow的项目,以在CPython中实现对多个解释器的支持:
https://www.python.org/dev/peps/pep-0554/
https://github.com/ericsnowcurrently/multi-core-python
不幸的是,很快就了解到:
- 对于如此受欢迎的项目,CPython的支持很差,并且在尝试重绘实现时会遇到很多老问题。 结果,多年来,埃里克(Eric)一直在选择口译人员,但进展缓慢。
- 即使成功实现了多个解释器,也不清楚如何进一步组织并行执行。 PEP建议使用简单的渠道,但是随着任务变得更加复杂,这种工具变得危险,面临着冻结和不可预测的行为的威胁;
- 语言本身存在很大的问题,无法阻止口译员直接交换数据,也无法保证行为的可预测性。
现在更详细地说明问题。
可修改的类定义
是的,我知道python中的类是在运行时声明的。 但是,该死,为什么要把变量放进去呢? 为什么要向旧对象添加新方法? 您不能在任何Java中的类外部声明函数和变量,但是python中没有这样的限制(并且python是在Java之前创建的)。 此外,我请您注意如何为与对象本身而不是向类添加类似的方法而陷入困境,这需要类型.MethodType,函数.__ get __,functools.partial等。
首先,我想问一个奇怪的问题:为什么python根本需要方法? 不是函数(如紧密的JavaScript中的函数),而是类方法。 因素之一:Guido没有提出更好的方法来使函数简称(因此没有gtk_button_set_focus_on_click),因为目前尚不清楚如何从一堆具有短名称的相似函数中为该特定对象选择必要的函数。 不过,len,iter,next,isinstance,slice,dict,dir,str,repr,hash,类型在python中出现了-现在这些是对应类方法的包装,名称中带有下划线,一旦内置简单类型就不再存在类,并且只能通过这些功能来工作。 就我个人而言,方法(对象)和object.method的记录之间并没有太大区别-尤其是方法是静态函数时,通常它并不关心要接受哪个第一个参数(自我)。
一般情况下的动态类定义:
- 不要进行模块化测试 。 当整个系统正常工作时,在测试中正确编写的一段代码可能会出错,并且您在CPython中不会受到保护。
- 造成优化的巨大困难。 类声明并不保证您可以实际操作该类。 因此,PyPy唯一成功的优化程序项目使用跟踪来检测探查方法执行的实际操作顺序;
- 不要与并行代码执行对接。 例如,相同的多处理程序可用于类定义的副本,如果上帝禁止,请在其中一个副本中更改类的描述,则您的应用程序可能会崩溃。
动态类的一个更微妙的版本是通过__getattribute__,__getattr__和其他方法重写属性访问。 通常,它们用作常规的getter-setter方法,以将功能委托给字段对象,并偶尔组织DSL 。 所有这些功能都可以以更加文明的方式实现,而无需将类变成描述的转储,有时行为难以保证。 顺便说一句,对于getters / setters,这种机制已经存在-这些是属性描述符: https : //www.python.org/dev/peps/pep-0252/#id5
类的热交换是调试和通用编码所必需的,但是对于开发人员而言,它仍应是一种专用工具,而不是无法消除的运行时工件。
编译类只是Cython和Nuitka已经采取的一小步,但是仅此一步而没有其他更改就不足以在执行速度方面获得任何显着效果,因为例如python大量使用了动态变量绑定,这是无处不在的不会在已编译的代码中消失。
多重继承
我认为这是帽子游行的领袖。 在python本身及其扩展的实现中,它甚至还没有达到C函数的水平。 “但是接口呢?”您反对。 需要C ++和Java接口来声明协议,以调用对象的方法,以便随后在编译时对这些协议进行静态验证,并生成在运行时将由其他对源对象一无所知的代码使用的方法表。 这些角色在python中几乎完全消失了,因此没有理由说明它们的存在。 我喜欢Go中制作界面的方式-它与python ABC非常相似: https : //www.python.org/dev/peps/pep-3119
多重继承不是并行化和优化的直接问题,但是会使代码的可读性和可维护性复杂化-这就是所谓的千层面代码(类似于意大利面条式代码)。
发电机
这是GoTo的一个真正被忽略的情况,当执行不只是无法控制地跳过代码-还会跳过堆栈。 当生成器与上下文管理器相交时,会发生特别激烈的游戏(您好,PEP 567)。 如果python中普遍存在将应用程序纠缠成紧密连接的可变状态的趋势,而这种可变状态却无法为测试,并行化和程序优化操作留出空间,那么生成器就是其中的佼佼者。
您认为该计划的结果是什么:
import contextlib
@contextlib.contextmanager
def context_manager():
try:
print('')
yield
finally:
print('')
def gen_in_manager():
m = context_manager()
with m:
for i in range(5):
yield i
g1 = gen_in_manager()
next(g1)
print('')
, :
import contextlib
@contextlib.contextmanager
def context_manager():
try:
print('')
yield
finally:
print('')
def gen_in_manager():
m = context_manager()
with m:
for i in range(5):
yield i
def test():
g1 = gen_in_manager()
next(g1)
test()
print('')
.
, , : async/await, .
: RPython -. , . , , .
https://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines
>>> a = ([42],)
>>> a[0] += [43, 44]
TypeError: 'tuple' object does not support item assignment
>>> a
([42, 43, 44],)
>>> a = ([42],)
>>> b = a[0]
>>> b += [43, 44]
>>> a
([42, 43, 44],)
>>> x = y = [1,2,3]
>>> x = x + [4]
>>> x == y
False
>>> x = y = [1,2,3]
>>> x += [4]
>>> x == y
True
>>> x = [[]]*5
>>> x
[[], [], [], [], []]
>>> x[0].append(0)
>>> x
[[0], [0], [0], [0], [0]]
, «'tuple' object does not support item assignment» : . , , , , . , , x[0].append(0) , CPython , . , .
— [] ? , , . , Clojure , - partial-copy-on-write . , .
? , , . , copy-on-write , : b.append.., b[].., b +=… : , , — , , . , , , , // , .
copy-on-write? , , (), ( ), , .
- . ? , copy-on-write, «({'first': 1, 'second': 2},)», , , , , , «{'first': 1, 'second': 2}».
https://ru.wikipedia.org/wiki/_()
« — »
: https://en.wikipedia.org/wiki/Multiple_dispatch#Use_in_practice
, 13–32% (a.k.a. ), 2.7–6.5% — . : , , , . , , .
, — , . , , . , double char, , double .
float a = 2.0;
float *src = &a;
char *dest = malloc(sizeof(float));
memcpy(dest, src, sizeof(float));
printf("%f", *(double*)dest);
, (, , ) — - , , , .
, ( ) . : «a = b + c», , . ?
- : «if (Py_TYPE(obj) == &PyLong_Type) {long val = PyLong_AsLong...}» type();
- : «PyArg_ParseTuple()» , «this.number = int(param1); this.text = str(param2)» — , . , , ( , );
- // . — .
, . , ? CPython, , , , , . , : . , ? : , / , / . C «a = b + c» ( , ):
def PyNumber_Add(b, c):
slotb = b.__add__
if not type(b) is type(c) and c.__add__:
slotc = c.__add__
if slotc is slotb:
slotc = None
if slotb:
if slotc and isinstance(c, b.__class__):
return slotc(b, c)
else:
return slotb(b, c)
else:
return slotb(b, c)
if isinstance(b, str) and isinstance(c, str):
a = unicode_concatenate(b, c)
else:
a = PyNumber_Add(b, c)
, , . « » — , , , , . , , , « », «», «», ; — .
: , . , . : , . , , — .
, . , , . , - , .
, , ( ), ( ), . , , ( ). , , — , ? .
— : , . , , . , , - , , : . , , — MRO:
https://www.python.org/download/releases/2.3/mro/
https://ru.wikipedia.org/wiki/C3-
, 3.4 « » ( Argument — Monty Python ), . « »… , : 3.4 2014, 1991.
. , (trait) Rust ( , , , , ):
https://doc.rust-lang.org/1.8.0/book/traits.html
, , , , , . «». , , . , - , , Rust-. , __iter__ — «». , , , , . , « - , ». , , C++ ranges, , , .
:
from collections.abc import Iterable, Container
from itertools import filterfalse
class MyList(Trait, Iterable, Container):
pass
def __sub__(a: MyList, b: object):
return list(filterfalse(lambda x: x == b, a))
def __sub__(a: MyList, b: Container):
return list(filterfalse(lambda x: x in b, a))
a = MyList([1, 2, 3, 4, 5])
print(a - [2, 5]) # , print(__sub__(a, b))
# : [1, 3, 4]
print(a - 3)
# : [1, 2, 4, 5]
MyList, Iterable ( __iter__) Container ( __contains__), list, list MyList, MyList list, list MyList. :
from collections.abc import Container
from itertools import filterfalse
class MyList(list):
def __sub__(self, b):
if isinstance(b, Container):
return list(filterfalse(lambda x: x in b, a))
else:
return list(filterfalse(lambda x: x == b, a))
a = MyList([1, 2, 3, 4, 5])
print(a - [2, 5])
# : [1, 3, 4]
print(a - 3)
# : [1, 2, 4, 5]
: , «a», . « », -, .
, — , , , . , , .
, , . , , , , , , . - , — - , . :
>>> a = [1, 2, 3]
...
>>> a = '15'
...
>>> for i in map(lambda x: x*2, a):
>>> print(i)
11
55
2
4
6
.
, , . None — , NoneType. , — , - , . :
>>> class A():
>>> def __init__(self, value):
>>> self.val = value
>>>
>>> a = A('2')
>>> a.val = []
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign str (not "list") to str
>>> a.myattr = []
>>> a.myattr = 2
A val, . , - (myattr) — , , .
, . — , , . , :
>>> class A():
>>> def __init__(self, value):
>>> self.val = value
>>>
>>> def func():
>>> a = A(None)
>>> a.val = 2
>>> print(a.__dict__)
>>>
>>> func()
{'val': 2}
A<None or Int>, . , , .
: , : , , , «-», «». — , ; - , , , , . , , «list», , , list comprehension, , , BUILD_LIST LIST_APPEND ( CPython) — ? , « », « - ».
, - () , «a.val = int(newval)». , , , . , __setattr__ __setattribute__, c 2.2 __set__ ( https://www.python.org/dev/peps/pep-0252/ ). : , , — C++/Java/C#. , : __set__, __get__, __delete__, , :
>>> a = StrictDict({'first': 1 })
>>> a = { 'dummy': 666 }
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StrictDictError: "first" key is missing in the assignment source
, ( « »): copy-on-write , , -, copy-on-write :
>>> a = COWList([1, 2, 3])
>>> b = a
>>> a.append(4)
>>> b.append(5)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 5]
, CPython , «»:
https://github.com/python/cpython/blob/master/Objects/typeobject.c#L5074
, __slots__ . , , , , , , , , . ( ) . , : __slots__ , PyHeapTypeObject->ht_slots, __dict__, PyTypeObject->tp_dictoffset. , .
, , . , , , , , « , ; — "", ""», . , , . «<> = new <>()», «var = new <>()». , ReasonML , , — JS , , .
PyPy, V8 JavaScript LuaJIT, , . - , , . AOT , asm.js, WebAssembly, PNaCl.
:
- Bauer, A.M. and Saal, H.J. (1974). Does APL really need run-time checking? Software — Practice and Experience 4: 129–138.
- Kaplan, M.A. and Ullman, J.D. (1980). A scheme for the automatic inference of variable types. J. A CM 27(1): 128–145.
- Borning, A.H. and Ingalls, D.H.H. (1982). A type declaration and inference system for Smalltalk. In Conference Record of the Ninth Annual ACM Symposium on Principles of Programming Languages (pp. 133–141)
- https://ru.wikipedia.org/wiki/Standard_ML — 1984 .
, Standard ML , , .
, - , . , , — , , ( ), :
- Frank Pfenning. (1988). Partial polymorphic type inference and higher-order unification. In Proceedings of the 1988 ACM Conference on Lisp and Functional Programming, pp. 153–163
- Cardelli, Luca; Martini, Simone; Mitchell, John C.; Scedrov, Andre (1994). An extension of system F with subtyping. Information and Computation, vol. 9. North Holland, Amsterdam. pp. 4–56
- Benjamin C. Pierce, and David N. Turner. (1997). Local type inference. Indiana University CSCI Technical Report #493, pp. 1-25
(1998) Local type inference. POPL '98 Proceedings of the 25th ACM SIGPLAN-SIGACT symposium on Principles of programming languages, pp. 252-265
(2000). Local type inference. ACM Transactions on Programming Languages and Systems (TOPLAS). Vol. 22(1), pp. 1-44
— , Scala, 2001 .
1991 , 1994 — , 1995 — «Matz», . , , . , , — , , , , , ZeroMQ, RabbitMQ, Kafka. , , , , , . , ? . - , Crystal, , .
— . , , .