Как работает симпй? Как он взаимодействует с интерактивной оболочкой Python и как работает интерактивная оболочка Python? - PullRequest
16 голосов
/ 07 июля 2010

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

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

from sympy import *

ивведите выражение.Как это происходит от Введите до вызова

__sympifyit_wrapper(a,b)

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

Ответы [ 4 ]

11 голосов
/ 07 августа 2010

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

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

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

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

x + x

в

x.__add__(x)

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

Так, как говорил Иерув, 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
    ...

И вуаля. В любом случае, вы можете подумать о других вещах, которые нужно реализовать, таких как приоритет операторов, оценка с заменой, расширенное упрощение, дифференцирование и т. Д., Но я думаю, что это очень круто, что основы настолько просты.

6 голосов
/ 11 августа 2010

Это не имеет ничего общего с вопросом real 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...

Настоящее волшебство происходит в лексере / парсере.

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

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

from tokenizetokenize f = open ('test.py') tokenize (f.readline)

Для print "Hello World!" вы получаете следующее:

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

5 голосов
/ 07 июля 2010

Я только что проверил код 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 в том виде, в котором он используется >>>, не будет заменен, объекты, над которыми будут работать, будут.

1 голос
/ 12 августа 2010

Интерактивный интерпретатор Python не делает ничего, что отличается от любого другого запуска кода Python.В нем есть какая-то магия, позволяющая ловить исключения и обнаруживать неполные многострочные операторы перед их выполнением, чтобы вы могли закончить их вводить, но это все.

Если вам действительно интересно, стандартный Программный модуль является довольно полной реализацией интерактивного приглашения Python.Я думаю, что это не совсем то, что на самом деле использует Python (то есть, я полагаю, реализованный в C), но вы можете покопаться в каталоге системной библиотеки вашего Python и посмотреть, как это делается.Шахта на /usr/lib/python2.5/code.py

...