getter setter как функция в классе Python, выдающая ошибку «атрибут не найден» - PullRequest
0 голосов
/ 05 января 2019
import operator
import re
from ply import lex, yacc

class Lexer(object):

    tokens = [
        'COMMA',
        'TILDE',
        'PARAM',
        'LP',
        'RP',
        'FUNC'
    ]

    # Regular expression rules for simple tokens
    t_COMMA = r'\,'
    t_TILDE = r'\~'
    t_PARAM = r'[^\s\(\),&:\"\'~]+'

    def __init__(self, dict_obj):
        self.dict_obj = dict_obj

    def t_LP(self, t):
        r'\('
        return t

    def t_RP(self, t):
        r'\)'
        return t

    def t_FUNC(self, t):
        # I want to generate token for this FUNC from the keys of model map
        # For eg: r'key1|key2'
        r'(?i)FUNC'
        return t

    # Define a rule so we can track line numbers
    def t_newline(self, t):
        r'\n+'
        t.lexer.lineno += len(t.value)

    # A string containing ignored characters (spaces and tabs)
    t_ignore = ' \t'

    # Error handling rule
    def t_error(self, t):
        print("Illegal character '%s' on line %d, column %d" % (t.value[0], t.lexer.lineno, t.lexer.lexpos))
        t.lexer.skip(1)

    # Build the lexer
    def build_lexer(self, **kwargs):
        self.lexer = lex.lex(module=self, **kwargs)
        return self.lexer

class Parser(object):

    tokens = Lexer.tokens

    def __init__(self, **kwargs):
        self.parser = yacc.yacc(module=self, **kwargs)
        self.lexer = None
        self._dict_obj = None
        self.error = ""
        self.result = ""

    @property
    def dict_obj(self):
        return self._dict_obj

    @dict_obj.setter
    def dict_obj(self, dict_obj):
        self._dict_obj = dict_obj
        self.lexer = Lexer(self._dict_obj).build_lexer()

    # Handles LP expression RP
    def p_expression(self, p):
        """
        expression : LP expression RP
        """

    # Handles TILDE PARAM - call search
    def p_tilde_param(self, p):
        """
        expression : TILDE PARAM
        """     
        p[0] = p[2]
        return p[0]

    # Handles ANY LP PARAM RP - call search
    def p_expression_any(self, p):
        """
        expression : FUNC LP PARAM RP
        """
        p[0] = p[3]
        return p[0]

    # Error handling rule
    def p_error(self, p):
        if p:
            stack_state_str = " ".join([symbol.type for symbol in self.parser.symstack[1:]])
            self.error = "Syntax error at %s, type %s, on line %d, Parser state: %s %s . %s" % (
                p.value, p.type, p.lineno, self.parser.state, stack_state_str, p
            )
        else:
            self.error = "SYNTAX ERROR IN INPUT"

    def get_result(self, input_):
        input_ = input_.strip()
        if input_:
            self.result = self.parser.parse(input_, lexer=self.lexer)      
            return self.result
        else:
            raise ValueError("EMPTY EXPRESSION ERROR")

def parser(input_):
    par_obj = Parser()
    par_obj.dict_obj = {
      'key1' : 'value1',
      'key2'   : 'value2'
    }
    return par_obj.get_result(input_)

result = parser("~hello")

Выше приведен код лексера и анализатора, использующий библиотеку ply. Я только что инкапсулировал весь мой код в форме класса. Проблемы, с которыми я сталкиваюсь:

1.) Я пытаюсь передать dict_obj классу анализатора. Я не знаю, что я делаю неправильно и получаю сообщение об ошибке:

AttributeError: 'Parser' object has no attribute 'dict_obj'

2.) Что я пытаюсь сделать?

Я хочу передать этот dict_obj классу синтаксического анализатора, а затем передать его и классу lexer, а затем использовать его в методе lexer - одном из методов токенов (t_FUNC). В этом методе мое регулярное выражение вернет ключи объекта dict obj.

Я думаю, что делаю что-то не так и, следовательно, не могу это реализовать. Пожалуйста, помогите.

1 Ответ

0 голосов
/ 05 января 2019

В вашем конструкторе (__init__) для объекта Parser вы просите Ply сгенерировать синтаксический анализатор до того, как объект Parser будет полностью построен:

def __init__(self, **kwargs):
    self.parser = yacc.yacc(module=self, **kwargs)
    # This is the critical line:
    self._dict_obj = None

Чтобы построить анализатор из объекта (yacc.yacc(module=self)), Ply необходимо перебрать все атрибуты объекта. Например, ему нужно найти все функции синтаксического анализатора, чтобы извлечь их строки документации, чтобы определить грамматику.

Ply использует встроенную функцию dir для создания словаря всех атрибутов объекта. Поскольку ваш Parser объект имеет собственный атрибут dict_obj, этот ключ возвращается из dir, и поэтому Ply пытается кэшировать этот атрибут с его значением. Но когда он вызывает gettattr(module, 'dict_obj'), вызывается геттер, и он пытается вернуть self._dict_obj. Тем не менее, self._dict_obj еще не был определен, так что в итоге выдается ошибка:

AttributeError: 'Parser' object has no attribute '_dict_obj'

Обратите внимание, что это , а не сообщение об ошибке, о котором вы сообщили в своем вопросе; эта ошибка говорит об отсутствии атрибута dict_obj. Возможно, это была ошибка копирования и вставки.

Если переместить вызов на yacc.yacc до конца инициализатора, эта конкретная проблема исчезнет:

def __init__(self, **kwargs):
    self.lexer = None
    self._dict_obj = None
    self.error = ""
    self.result = ""
    self.parser = yacc.yacc(module=self, **kwargs)

Однако в отрывке кода есть ряд других проблем, которые затрудняют проверку этого решения. К ним относятся:

  1. Нет LexerNmsysSearch. Я предположил, что вы имели в виду Lexer.

  2. Нет node_expression. Я понятия не имею, что это должно быть, поэтому я просто удалил тест.

  3. Ваша грамматика не соответствует тестируемому вводу, поэтому синтаксический анализатор немедленно выдает синтаксическую ошибку. Я изменил ввод на "(~hello)", пытаясь создать что-то, что можно разобрать.

  4. Действия парсера не устанавливают семантические значения, поэтому self.parse.parse() не возвращает никакого значения. Это заставляет get_result выдавать ошибку.

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

...