简体   繁体   English

同情心如何运作? 它如何与交互式Python shell交互,以及交互式Python shell如何工作?

[英]How does sympy work? How does it interact with the interactive Python shell, and how does the interactive Python shell work?

What happens internally when I press Enter ? Enter键时内部会发生什么?

My motivation for asking, besides plain curiosity, is to figure out what happens when you 除了好奇之外,我的动机是要弄清楚当你发生什么时会发生什么

from sympy import *

and enter an expression. 并输入一个表达式。 How does it go from Enter to calling 它如何从Enter到呼叫

__sympifyit_wrapper(a,b)

in sympy.core.decorators? 在sympy.core.decorators? (That's the first place winpdb took me when I tried inspecting an evaluation.) I would guess that there is some built-in eval function that gets called normally, and is overridden when you import sympy? (当我尝试检查评估时,这是winpdb带给我的第一个地方。)我猜想有一些内置的eval函数可以正常调用,并在导入sympy时被覆盖?

All right after playing around with it some more I think I've got it.. when I first asked the question I didn't know about operator overloading . 好好玩了之后我想我已经得到了它...当我第一次提出问题我不知道有关运算符重载的问题

So, what's going on in this python session? 那么,这个python会话中发生了什么?

>>> from sympy import *
>>> x = Symbol(x)
>>> x + x
2*x

It turns out there's nothing special about how the interpreter evaluates the expression; 事实证明,解释器如何评估表达式并没有什么特别之处; the important thing is that python translates 重要的是python翻译

x + x

into

x.__add__(x)

and Symbol inherits from the Basic class, which defines __add__(self, other) to return Add(self, other) . 和Symbol继承自Basic类,它定义__add__(self, other)以返回Add(self, other) (These classes are found in sympy.core.symbol, sympy.core.basic, and sympy.core.add if you want to take a look.) (如果你想看看,可以在sympy.core.symbol,sympy.core.basic和sympy.core.add中找到这些类。)

So as Jerub was saying, Symbol.__add__() has a decorator called _sympifyit which basically converts the second argument of a function into a sympy expression before evaluating the function, in the process returning a function called __sympifyit_wrapper which is what I saw before. 因此,正如Jerub所说, Symbol.__add__()有一个名为_sympifyit装饰器 ,它在评估函数之前基本上将函数的第二个参数转换为一个sympy表达式,在此过程中返回一个名为__sympifyit_wrapper的函数,这是我之前看到的。

Using objects to define operations is a pretty slick concept; 使用对象来定义操作是一个非常光滑的概念; by defining your own operators and string representations you can implement a trivial symbolic algebra system quite easily: 通过定义自己的运算符和字符串表示,您可以非常轻松地实现一个简单的符号代数系统:

symbolic.py -- 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

Now we can do: 现在我们可以做到:

>>> from symbolic import *
>>> x = Symbol('x')
>>> x+x
x+x

With a bit of refactoring it can easily be extended to handle all basic arithmetic : 通过一些重构,它可以很容易地扩展到处理所有基本算术

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)

# ...

With just a bit more tweaking we can get the same behavior as the sympy session from the beginning.. we'll modify Add so it returns a Mul instance if its arguments are equal. 只需稍微调整一下,我们就可以从一开始就获得与sympy会话相同的行为。我们将修改Add这样如果它的参数相等,它将返回一个Mul实例。 This is a bit trickier since we have get to it before instance creation; 这有点棘手,因为我们创建实例之前就已经开始了; we have to use __new__() instead of __init__() : 我们必须使用__new__()而不是__init__()

class Add(Operator):
    def __new__(cls, left, right):
        if left == right:
            return Mul(2, left)
        return Operator.__new__(cls)
    ...

Don't forget to implement the equality operator for Symbols: 不要忘记为符号实现相等运算符:

class Symbol(Basic):
    ...
    def __eq__(self, other):
        if type(self) == type(other):
            return repr(self) == repr(other)
        else:
            return False
    ...

And voila. 瞧。 Anyway, you can think of all kinds of other things to implement, like operator precedence, evaluation with substitution, advanced simplification, differentiation, etc., but I think it's pretty cool that the basics are so simple. 无论如何,你可以想到要实现的各种其他事情,比如运算符优先级,替换评估,高级简化,区分等等,但我认为基础知识非常简单很酷。

This doesn't have much to do with secondbanana's real question - it's just a shot at Omnifarious' bounty ;) 这与secondbanana的真正问题没什么关系 - 这只是对Omnifarious'赏金的一击;)

The interpreter itself is pretty simple. 解释器本身很简单。 As a matter of fact you could write a simple one (nowhere near perfect, doesn't handle exceptions, etc.) yourself: 事实上,你可以自己编写一个简单的(不太接近完美,不处理异常等):

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)

You can do most of the stuff you're used to in the normal prompt: 您可以在正常提示中完成大部分习惯:

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...

The real magic happens in the lexer/parser. 真正的魔法发生在词法分析器/解析器中。

Lexical Analysis, or lexing is breaking the input into individual tokens. 词法分析,或lexing正在打破单个令牌的输入。 The tokens are keywords or "indivisible" elements. 标记是关键字或“不可分割的”元素。 For instance, = , if , try , : , for , pass , and import are all Python tokens. 例如, =iftry:forpassimport都是Python令牌。 To see how Python tokenizes a program you can use the tokenize module. 要查看Python如何标记程序,您可以使用tokenize模块。

Put some code in a file called 'test.py' and run the following in that directory: 将一些代码放在名为“test.py”的文件中,并在该目录中运行以下命令:

from tokenize import tokenize f = open('test.py') tokenize(f.readline) 来自tokenize import tokenize f = open('test.py')tokenize(f.readline)

For print "Hello World!" 用于print "Hello World!" you get the following: 你得到以下:

1,0-1,5: NAME 'print' 1,0-1,5:姓名'print'
1,6-1,19: STRING '"hello world"' 1,6-1,19:STRING'“hello world”'
1,19-1,20: NEWLINE '\\n' 1,19-1,20:NEWLINE'\\ n'
2,0-2,0: ENDMARKER '' 2,0-2,0:ENDMARKER''

Once the code is tokenized, it's parsed into an abstract syntax tree . 代码被标记化后,它将被解析抽象语法树 The end result is a python bytecode representation of your program. 最终结果是程序的python字节码表示。 For print "Hello World!" 用于print "Hello World!" you can see the result of this process: 你可以看到这个过程的结果:

from dis import dis
def heyworld():
    print "Hello World!"
dis(heyworld)

Of course all languages lex, parse, compile and then execute their programs. 当然所有语言都是lex,解析,编译然后执行他们的程序。 Python lexes, parses, and compiles to bytecode. Python lexes,parses和compiles to bytecode。 Then the bytecode is "compiled" (translated might be more accurate) to machine code which is then executed. 然后字节码被“编译”(翻译可能更准确)到机器代码然后执行。 This is the main difference between interpreted and compiled languages - compiled languages are compiled directly to machine code from the original source, which means you only have to lex/parse before compilation and then you can directly execute the program. 这是解释语言和编译语言之间的主要区别 - 编译语言直接从原始源编译为机器代码,这意味着您只需在编译之前进行lex / parse,然后就可以直接执行该程序。 This means faster execution times (no lex/parse stage), but it also means that to get to that initial execution time you have to spend a lot more time because the entire program must be compiled. 这意味着更快的执行时间(没有lex / parse阶段),但这也意味着要达到初始执行时间,您必须花费更多时间,因为必须编译整个程序。

I just inspected the code of sympy (at http://github.com/sympy/sympy ) and it looks like __sympifyit_wrapper is a decorator. 我只是检查了sympy的代码( http://github.com/sympy/sympy ),看起来__sympifyit_wrapper是装饰者。 The reason it will called is because there is some code somewhere that looks like this: 它会调用的原因是因为某些代码看起来像这样:

class Foo(object):
    @_sympifyit
    def func(self):
        pass

And __sympifyit_wrapper is a wrapper that's returned by @_sympifyit . __sympifyit_wrapper@_sympifyit返回的包装器。 If you continued your debugging you may've found the function (in my example named func ). 如果你继续调试,你可能已经找到了这个函数(在我的例子中名为func )。

I gather in one of the many modules and packages imported in sympy/__init__.py some built in code is replaced with sympy versions. 我收集了在sympy/__init__.py导入的众多模块和包中的一个,其中一些内置代码被替换为sympy版本。 These sympy versions probably use that decorator. 这些同情版本可能使用该装饰器。

exec as used by >>> won't have been replaced, the objects that are operated on will have been. >>>使用的exec将不会被替换,操作的对象将是。

The Python interactive interpreter doesn't do a lot that's any different from any other time Python code is getting run. Python交互式解释器并没有做太多与Python代码运行时有任何不同之处。 It does have some magic to catch exceptions and to detect incomplete multi-line statements before executing them so that you can finish typing them, but that's about it. 它确实有一些魔法来捕获异常并在执行它们之前检测不完整的多行语句,这样你就可以完成它们的输入,但就是这样。

If you're really curious, the standard code module is a fairly complete implementation of the Python interactive prompt. 如果您真的很好奇,标准代码模块是Python交互式提示的相当完整的实现。 I think it's not precisely what Python actually uses (that is, I believe, implemented in C), but you can dig into your Python's system library directory and actually look at how it's done. 我认为这并不是Python实际使用的东西(也就是说,我相信,用C实现),但你可以深入研究Python的系统库目录,并实际看看它是如何完成的。 Mine's at /usr/lib/python2.5/code.py 我的位于/usr/lib/python2.5/code.py

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM