这种类型的属性 value/function 检查是 Python 中的代码味道吗?

Is this type of attribute value/function checking, a code smell in Python?

注意:根据建议

,这是 CodeReview 上的crossposted

前提:我有一个class层次结构(Python),其中tidy是方法之一。它删除类型为 ASTIgnore 的节点,并将该节点的子节点重新绑定到其父节点。

目标节点无法删除自身,也不会查看其父节点(用于重新绑定)。因此,目标(ASTIgnore 类型)的删除将发生在其父级,其中父级 检查其子级的类型

问题:这需要如何实现以减少代码异味?

这些方法中哪一个最不坏,或者还有其他方法(见底部)?

# A)
if child.nodetype == "ASTIgnore":

# B)
if child.isIgnored():

# C)
if child.isIgnoreType:

# D)
if isinstance(child, ASTIgnore):

其中,classes 和 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

关于使用 Tell-Not-Ask 策略的 Duck Typing 问题:

我是 Python 的新手,想成为一名 Pythonic 编码员(并且总体上是一名更好的编码员)。因此,

我如何Duck Type上面的?如果 attribute/function 在对象构造之外从未被触及,是否会检查属性值(igIgnoreType)/函数(isIgnored)被视为 Duck Typing

我有另一个实现,其中 tidyIgnore 类型节点中过载。 不再进行类型检查,但父对象仍然需要删除目标子对象,并重新绑定孙子对象。在这里,Ignore 类型 return 它们的子节点,对于叶节点来说是 []。但是,仍然会检查 return 是否为 None。我确定这肯定是 Duck Typing,但正在检查 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

根据 Eric's 投票,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

我认为使用 tidy 方法中的 return 值是在节点之间传递信息的好方法。无论如何,您将在每个 children 上调用 tidy,因此获得一个 return 值告诉您如何处理 child 会使整个代码更简单.

您可以通过使用 super 从派生的 class 调用基础 class 的实现并仅更改 return 值来避免重复自己:

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