Подтвердить что ты не робот

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

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

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

from sympy import *

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

__sympifyit_wrapper(a,b)

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

4b9b3361

Ответ 1

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

Итак, что происходит в этом сеансе 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
    ...

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

Ответ 2

Это не имеет особого отношения к второму вопросу - это просто выстрел в Omnifarious 'bounty;)

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

print "Wayne 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...

Реальная магия происходит в lexer/parser.

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

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

from tokenize import tokenize f = open ('test.py') токенизировать (f.readline)

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

1,0-1,5: NAME 'print'
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), но это также означает, что для достижения этого начального времени выполнения вам нужно потратить намного больше времени, потому что вся программа должна быть скомпилирована.

Ответ 3

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

Ответ 4

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

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