簡體   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?

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正在打破單個令牌的輸入。 標記是關鍵字或“不可分割的”元素。 例如, =iftry:forpassimport都是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM