Как работает sympy? Как он взаимодействует с интерактивной оболочкой Python и как работает интерактивная оболочка Python?

что происходит внутри, когда я нажимаю Enter?

моя мотивация спрашивать, помимо простого любопытства, чтобы выяснить, что происходит, когда вы

from sympy import *

и введите выражение. Как же Enter вызов

__sympifyit_wrapper(a,b)

в sympy.ядро.декораторы? (Это первое место winpdb взял меня, когда я попытался проверить оценку.) Я бы предположил, что есть какая-то встроенная функция eval, которая обычно вызывается и переопределяется, когда вы импортируете sympy?

4 ответов


хорошо, поиграв с ним еще немного, я думаю, что он у меня есть.. когда я впервые задал вопрос, я не знал о перегрузка операторов.

Итак, что происходит в этой сессии python?

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

оказывается, нет ничего особенного в том, как интерпретатор оценивает выражение; важно то, что python переводится как

x + x

на

x.__add__(x)

и символ наследуется от базового класса, который определяет __add__(self, other) вернуться Add(self, other). (Эти классы находятся в sympy.ядро.символ, sympy.ядро.базовые, и sympy.ядро.добавьте, если хотите взглянуть.)

так как 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 ' bounty;)

сам переводчик довольно прост. На самом деле вы можете написать простой (нигде не идеальный, не обрабатывает исключения и т. д.) себя:

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

настоящая магия происходит в лексер/парсер.

лексический анализ, или лексический разбивает входные данные на отдельные токены. Токены-это ключевые слова или" неделимые " элементы. Например, =, if, try, :, for, pass и import все токены Python. Чтобы узнать, как питон маркирует программу можно использовать tokenize модуль.

поместите некоторый код в файл под названием 'test.py-и запустите в этом каталоге следующее:

из tokenize импорт tokenize Ф= открыть('test.py') маркировки(Ф.с readline)

на print "Hello World!" вы получите:

1,0-1,5: имя 'печать'
1,6-1,19: строка '"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, анализирует и компилирует байт-код. Затем байт-код "компилируется" (перевод может быть более точным) в машинный код, который затем выполняется. Это основное различие между интерпретируемыми и скомпилированными языками-скомпилированные языки компилируются непосредственно в машинный код из исходного источника, что означает, что вы нужно только lex / parse перед компиляцией, а затем вы можете напрямую выполнить программу. Это означает более быстрое время выполнения (без этапа lex/parse), но также означает, что для получения начального времени выполнения вам нужно потратить гораздо больше времени, потому что вся программа должна быть скомпилирована.


Я только что проверил код sympy (at 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