[英]How does sympy work? How does it interact with the interactive Python shell, and how does the interactive Python shell work?
按Enter键时内部会发生什么?
除了好奇之外,我的动机是要弄清楚当你发生什么时会发生什么
from sympy import *
并输入一个表达式。 它如何从Enter到呼叫
__sympifyit_wrapper(a,b)
在sympy.core.decorators? (当我尝试检查评估时,这是winpdb带给我的第一个地方。)我猜想有一些内置的eval函数可以正常调用,并在导入sympy时被覆盖?
好好玩了之后我想我已经得到了它...当我第一次提出问题我不知道有关运算符重载的问题 。
那么,这个python会话中发生了什么?
>>> from sympy import *
>>> x = Symbol(x)
>>> x + x
2*x
事实证明,解释器如何评估表达式并没有什么特别之处; 重要的是python翻译
x + x
成
x.__add__(x)
和Symbol继承自Basic类,它定义__add__(self, other)
以返回Add(self, other)
。 (如果你想看看,可以在sympy.core.symbol,sympy.core.basic和sympy.core.add中找到这些类。)
因此,正如Jerub所说, Symbol.__add__()
有一个名为_sympifyit
的装饰器 ,它在评估函数之前基本上将函数的第二个参数转换为一个sympy表达式,在此过程中返回一个名为__sympifyit_wrapper
的函数,这是我之前看到的。
使用对象来定义操作是一个非常光滑的概念; 通过定义自己的运算符和字符串表示,您可以非常轻松地实现一个简单的符号代数系统:
symbolic.py -
class Symbol(object):
def __init__(self, name):
self.name = name
def __add__(self, other):
return Add(self, other)
def __repr__(self):
return self.name
class Add(object):
def __init__(self, left, right):
self.left = left
self.right = right
def __repr__(self):
return self.left + '+' + self.right
现在我们可以做到:
>>> from symbolic import *
>>> x = Symbol('x')
>>> x+x
x+x
通过一些重构,它可以很容易地扩展到处理所有基本算术 :
class Basic(object):
def __add__(self, other):
return Add(self, other)
def __radd__(self, other): # if other hasn't implemented __add__() for Symbols
return Add(other, self)
def __mul__(self, other):
return Mul(self, other)
def __rmul__(self, other):
return Mul(other, self)
# ...
class Symbol(Basic):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Operator(Basic):
def __init__(self, symbol, left, right):
self.symbol = symbol
self.left = left
self.right = right
def __repr__(self):
return '{0}{1}{2}'.format(self.left, self.symbol, self.right)
class Add(Operator):
def __init__(self, left, right):
self.left = left
self.right = right
Operator.__init__(self, '+', left, right)
class Mul(Operator):
def __init__(self, left, right):
self.left = left
self.right = right
Operator.__init__(self, '*', left, right)
# ...
只需稍微调整一下,我们就可以从一开始就获得与sympy会话相同的行为。我们将修改Add
这样如果它的参数相等,它将返回一个Mul
实例。 这有点棘手,因为我们在创建实例之前就已经开始了; 我们必须使用__new__()
而不是__init__()
:
class Add(Operator):
def __new__(cls, left, right):
if left == right:
return Mul(2, left)
return Operator.__new__(cls)
...
不要忘记为符号实现相等运算符:
class Symbol(Basic):
...
def __eq__(self, other):
if type(self) == type(other):
return repr(self) == repr(other)
else:
return False
...
瞧。 无论如何,你可以想到要实现的各种其他事情,比如运算符优先级,替换评估,高级简化,区分等等,但我认为基础知识非常简单很酷。
这与secondbanana的真正问题没什么关系 - 这只是对Omnifarious'赏金的一击;)
解释器本身很简单。 事实上,你可以自己编写一个简单的(不太接近完美,不处理异常等):
print "Wayne's Python Prompt"
def getline(prompt):
return raw_input(prompt).rstrip()
myinput = ''
while myinput.lower() not in ('exit()', 'q', 'quit'):
myinput = getline('>>> ')
if myinput:
while myinput[-1] in (':', '\\', ','):
myinput += '\n' + getline('... ')
exec(myinput)
您可以在正常提示中完成大部分习惯:
Waynes Python Prompt
>>> print 'hi'
hi
>>> def foo():
... print 3
>>> foo()
3
>>> from dis import dis
>>> dis(foo)
2 0 LOAD_CONST 1 (3)
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
>>> quit
Hit any key to close this window...
真正的魔法发生在词法分析器/解析器中。
词法分析,或lexing正在打破单个令牌的输入。 标记是关键字或“不可分割的”元素。 例如, =
, if
, try
, :
, for
, pass
和import
都是Python令牌。 要查看Python如何标记程序,您可以使用tokenize
模块。
将一些代码放在名为“test.py”的文件中,并在该目录中运行以下命令:
来自tokenize import tokenize f = open('test.py')tokenize(f.readline)
用于print "Hello World!"
你得到以下:
1,0-1,5:姓名'print'
1,6-1,19:STRING'“hello world”'
1,19-1,20:NEWLINE'\\ n'
2,0-2,0:ENDMARKER''
代码被标记化后,它将被解析为抽象语法树 。 最终结果是程序的python字节码表示。 用于print "Hello World!"
你可以看到这个过程的结果:
from dis import dis
def heyworld():
print "Hello World!"
dis(heyworld)
当然所有语言都是lex,解析,编译然后执行他们的程序。 Python lexes,parses和compiles to bytecode。 然后字节码被“编译”(翻译可能更准确)到机器代码然后执行。 这是解释语言和编译语言之间的主要区别 - 编译语言直接从原始源编译为机器代码,这意味着您只需在编译之前进行lex / parse,然后就可以直接执行该程序。 这意味着更快的执行时间(没有lex / parse阶段),但这也意味着要达到初始执行时间,您必须花费更多时间,因为必须编译整个程序。
我只是检查了sympy的代码( http://github.com/sympy/sympy ),看起来__sympifyit_wrapper
是装饰者。 它会调用的原因是因为某些代码看起来像这样:
class Foo(object):
@_sympifyit
def func(self):
pass
而__sympifyit_wrapper
是@_sympifyit
返回的包装器。 如果你继续调试,你可能已经找到了这个函数(在我的例子中名为func
)。
我收集了在sympy/__init__.py
导入的众多模块和包中的一个,其中一些内置代码被替换为sympy版本。 这些同情版本可能使用该装饰器。
>>>
使用的exec
将不会被替换,操作的对象将是。
Python交互式解释器并没有做太多与Python代码运行时有任何不同之处。 它确实有一些魔法来捕获异常并在执行它们之前检测不完整的多行语句,这样你就可以完成它们的输入,但就是这样。
如果您真的很好奇,标准代码模块是Python交互式提示的相当完整的实现。 我认为这并不是Python实际使用的东西(也就是说,我相信,用C实现),但你可以深入研究Python的系统库目录,并实际看看它是如何完成的。 我的位于/usr/lib/python2.5/code.py
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.