Является ли этот тип проверки значения атрибута/функции запахом кода в Python?


Примечание : это crossposted на CodeReview, в соответствии с рекомендацией

Предпосылка : у меня есть иерархия классов (Python), где tidy - один из методов. Он удаляет узлы типа ASTIgnore и повторно связывает дочерние узлы этого узла с его родительским.

Целевой узел не может удалить себя и не видит своего родителя (для повторной привязки). Таким образом, удаление цели (типа ASTIgnore) произойдет у ее родителя, где родитель проверяет тип своих потомков.

Вопрос : Как это должно быть реализовано, чтобы уменьшить запах кода?

Какой из этих подходов наименее плох, или есть другие (см. Внизу)?
# A)
if child.nodetype == "ASTIgnore":

# B)
if child.isIgnored():

# C)
if child.isIgnoreType:

# D)
if isinstance(child, ASTIgnore):

Где классы и tidy выглядят следующим образом. Я удалю избыточность, основываясь на самой чистой реализации.

class ASTNode(object):
    def __init__(self):
        self.nodetype = self.__class__.__name__
        self.isIgnoreType = False

    def isIgnored(self):
        return False

    def tidy(self):
        # Removes "Ignore" type/attribute nodes while maintaining hierarchy
        if self.children:
            for child in self.children:
                child.tidy()

            for i, child in reversed(list(enumerate(self.children))):
                #--------- Is this Bad? ----------
                if child.nodetype == "ASTIgnore":
                #------ --------------------------
                    if not child.children:
                        # leaf node deletion
                        self.children.pop(i)
                    else:
                        # target node deletion + hierarchy correction
                        grandkids = child.children
                        self.children[i:i+1] = grandkids


class ASTIgnore(ASTNode):
    def __init__(self):
        ASTNode.__init__()
        self.isIgnoreType = True

    def isIgnored(self):
        return True

По вопросу о типировании утки с политикой "не спрашивай" :

Я есть новичок в Python, и хотел бы быть pythonic coder (и лучшим кодером в целом). Следовательно,

Как я утка типа выше? Будет ли проверка значения атрибута (igIgnoreType) / функции (isIgnored) считаться утиной типизацией , если атрибут / функция никогда не затрагивается за пределами построения объекта?

У меня есть другая реализация, где tidy перегружается в узлах типа Ignore. больше нет проверки типов , но родитель все равно должен удалить целевое дитя, и перезимовать внуков. Здесь типы Ignore возвращают свои дочерние элементы, которые будут [] для конечных узлов. Но, есть еще проверка на то, был ли возврат None. Я уверен, что это, конечно, утка набрав , но проверка на None и код-репликация, плохой код?

class ASTNode(object):
    def tidy(self):
        for i, child in reversed(list(enumerate(self.children))):
            grandkids = child.tidy()
            if grandkids is not None:
                self.children[i:i+1] = grandkids

        return None

class ASTIgnore(ASTNode):
    def tidy(self):
        for i, child in reversed(list(enumerate(self.children))):
            grandkids = child.tidy()
            if grandkids is not None:
                self.children[i:i+1] = grandkids

        return self.children

_edit0

Основываясь наголосовании Эрика , реализация проверки функций isIgnored() будет выглядеть как

def tidy(self):
    """
    Clean up useless nodes (ASTIgnore), and rebalance the tree
    Cleanup is done bottom-top
      in reverse order, so that the deletion/insertion doesn't become a pain
    """
    if self.children:
        # Only work on parents (non-leaf nodes)
        for i, child in reversed(list(enumerate(self.children))):
            # recurse, so as to ensure the grandkids are clean
            child.tidy()

            if child.isIgnored():
                grandkids = child.children
                self.children[i: i + 1] = grandkids
1 2

1 ответ:

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

Вы можете избежать повторения, используя super для вызова реализации базового класса из производного класса и просто изменив возвращаемое значение:

class ASTIgnore(ASTNode):
    def tidy(self):
        super().tidy() # called for the side-effects, return value is overridden
        return self.children

Если вы используете Python 2, где super немного немного менее волшебно, чем в Python 3, вам нужно будет использовать super(ASTIgnore, self) вместо super().