这种类型的属性值/函数检查,Python中的代码味道?

这种类型的属性值/函数检查,Python中的代码味道?

问题描述:

:这是crossposted在代码审查,按照建议这种类型的属性值/函数检查,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 

鸭打字的事项,推荐给不-ASK政策

我是新来的Python,并希望成为一个Python的编码器(和一般的更好的编码器)。因此,

我如何鸭型以上?检查属性值(igIgnoreType)/函数(isIgnored)是否被认为是Duck Typing如果属性/函数从未被触及超出对象构造?

我确实有另一个实现,其中tidy过载忽略类型节点。 没有更多的类型检查,但父母仍然必须删除目标孩子,并rebind孙子。在这里,忽略类型返回它们的子节点,对于叶子节点将会是[]。但是,仍然会检查回报是否为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

基于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 
+0

IMO'isIgnored()'让你的代码变得最容易理解。拥有这个额外的路标比读取重复代码的第二密集函数更容易。 –

+0

发布到Code Review:我将有机会获得答案。 –

+0

[Crossposted](http://codereview.stackexchange.com/questions/142983/is-this-type-of-attribute-value-function-checking-a-code-smell-in-python)这在CodeReview上。增加了Eric的投票实施。 –

我认为使用从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()

+0

这对'tidy'方法非常有效。我会记住这个技巧。 **除此之外,我还有其他一些函数('stringify','print_tree','emitPredicate','transform_ControlFlow'等等) - 这大大受益于整个多态性方面(基类获得通用的版本和一些子类可能会重载它,即方法操作实际上取决于调用它的类型)。但是,在阅读了单一责任原则之后,我很担心。这是严重违反SRP的吗? –

+0

我不认为单一责任原则在这个规模上很重要。它主要是关于大型设计(例如模块级别),并不需要应用于每个班级。我猜你所有的'ASTNode'子类将永远相互紧密耦合。这对于SRP来说并不是什么大问题,因为如果有任何设计变更的话,这些类通常都会一下子被修改。 – Blckknght

+0

没错!大多数'ASTNode'子类只是“通过”,而一些扩展了新的数据属性。试图从类中分离出核心方法肯定会需要某种类型的_ checking_(比如'isIgnored()','isControl()'等等)。 ** ___ **我需要阅读更多关于整个固件和python成语的信息。谢谢!我用'super'将显式调用替换为基类('__init__','tidy')。 –