【问题标题】:Why is calling function causing a NameError?为什么调用函数会导致 NameError?
【发布时间】:2019-12-16 16:48:12
【问题描述】:

我正在下面编写此代码,但是当我尝试调用函数 size() 时,它会抛出错误

class Node:
    def __init__(self,data):
        self.data=data
        self.left=None
        self.right=None

    def insert(self,data):
            if self.data:
                if data<self.data:
                    if self.left is None:
                        self.left=Node(data)
                    else:
                        self.left.insert(data)
                else:
                    if self.right is None:
                        self.right=Node(data)
                    else:
                        self.right.insert(data)
            else:
                self.data=data

    def size(node):
        if node is None:
            return 0 
        else:
            return (size(node.left)+ 1 + size(node.right)) 

root=Node(4)

root.insert(5)
root.insert(3)
root.insert(8)

print(size(root))

下面的错误被抛出:

NameError                                 Traceback (most recent call last)
<ipython-input-7-8c72ba7719dc> in <module>
     41 root.insert(8)
     42 
---> 43 print(size(root))
     44 
     45 #root.print()

NameError: name 'size' is not defined

【问题讨论】:

  • 没错,size 未在此上下文中定义。它是在 Node 类中定义的,不过
  • 快速警告:如果你的树可以任意增长(接近或超过一千个节点深度),你将会遇到RecursionErrors。如果这永远不会发生,请忽略我,但如果可能,您需要在不使用递归的情况下重写它。
  • @Roope:不要认为这真的涵盖了 OP 的问题。如所写,通常称为self 的方法的参数改为命名node,但这是无害的。问题是尝试做size(SOMENODE),当他们以只有SOMENODE.size() 有效的方式定义方法时; size 不应该在类体内缩进,或者它需要作为方法调用,而不是顶级函数。

标签: python python-3.x


【解决方案1】:

class 语句之后定义size

class Node:
    def __init__(self,data):
        self.data=data
        self.left=None
        self.right=None

    def insert(self,data):
        ...


def size(node):
    if node is None:
        return 0
    else:
        return (size(node.left)+ 1 + size(node.right)) 

或者让它成为一个合适的方法:

class Node:
    def __init__(self,data):
        self.data=data
        self.left=None
        self.right=None

    def insert(self,data):
        ...

    def size(self):
        rv = 1
        if self.left is not None:
            rv += self.left.size()
        if self.right is not None:
            rv += self.right.size()
        return rv

【讨论】:

  • 注意:让它成为一个顶级函数是……不寻常的,至少可以这么说。尤其是在像size 这样的情况下,它似乎应该由len 处理(通过定义特殊方法__len__)。除非你有一大堆类,其中size 都有意义,并且在逻辑上与len 不同(在这种情况下,OP 应该查看functools.singledispatch 以使其易于管理),size 应该是方法(可能是__len__),而不是顶级函数。
  • @ShadowRanger 是否应该使用len 是一个有趣的案例,嗯。你知道是否有任何资源可以标准化/定义len 应该做什么?
  • @AlexanderCécile: The docs themselves 只需说:“返回对象的长度(项目数)。”对于这种情况,考虑到不涉及顶级集合,这有点奇怪,并且考虑到成本是 O(n),而不是像典型的 __len__ 实现那样的 O(1),这可能是可以避免的,但事实并非如此意味着你想使用一个不同的、高度通用的顶级函数,只是你应该使用一个适当命名的方法。
  • 另一个论点:需要__len__ 的ABC 实际上被命名为Sized,因此如果您保留名称size 但让它使用不同的行为,显然会有一些含义冲突,因为“长度”和“大小”在很大程度上是同义词(在 Python 和许多其他语言中)。
  • @AlexanderCécile:嗯。就个人而言,我会使用一个顶级类来存储根 Node 并在内部保留预先计算的长度(为了提高效率),并提供其余的 Collection (或者更好,SequenceSet) ABC。让Node 报告一个“大小”,而它与Node 本身的大小并不真正相关,这总是很奇怪。如果这不可能,我更喜欢像num_child_nodes 这样更精确的方法名称(它会报告比size 小一的值,因为它不会自己计算)。
【解决方案2】:

在您的代码中,size 是绑定到Node 对象的方法,因此您需要调用root.size()(因为rootNode 实例)

【讨论】:

  • size 也必须适当定义。现在,这看起来更像是一个缩进问题; size 应该在class 语句之后而不是中定义。
【解决方案3】:

在与@ShadowRanger 讨论后,这是我想出的。

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass
class Node:
    data: Any
    left: Node = None
    right: Node = None

    def insert(self, data: Any) -> None:
        if self.data:
            if data < self.data:
                if self.left is None:
                    self.left = Node(data)
                else:
                    self.left.insert(data)
            else:
                if self.right is None:
                    self.right = Node(data)
                else:
                    self.right.insert(data)
        else:
            self.data = data

    def num_child_nodes(self) -> int:
        num_nodes = 0
        if self.left:
            num_nodes += 1 + self.left.num_child_nodes()
        if self.right:
            num_nodes += 1 + self.right.num_child_nodes()
        return num_nodes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    相关资源
    最近更新 更多