首页 > 解决方案 > 在 Python 中使用递归时出现 UnboundLocalError?

问题描述

一个简单的递归搜索如下所示。关于嵌套函数是否是好的做法似乎存在矛盾的意见。一个 MOOC 正在使用下面的模板教授递归,但社区中的许多人反对这种方法。

无论如何,如果下面的递归片段是从这个工作版本更改的,

# subgroups exist, recurse into each one
for group in groups:
    get_all_users(group)

到捕获返回参数的这个版本,

# subgroups exist, recurse into each one
for group in groups:
    users = get_all_users(group)

然后出现以下错误:

Traceback (most recent call last):
  File "problem_4.py", line 84, in <module>
    print(is_user_in_group("sub_child_1_user", sub_child_1_group))
  File "problem_4.py", line 53, in is_user_in_group
    users = get_all_users(group)
  File "problem_4.py", line 37, in get_all_users
    users.extend(group.get_users())
UnboundLocalError: local variable 'users' referenced before assignment

这特别令人困惑,因为这不是进行更改的行,这意味着如果逻辑在这里失败,它应该在工作版本中失败。在调用递归函数之前,还清楚地定义了名为“用户”的列表。我对变量范围的理解是用户列表现在可以完全访问嵌套递归函数,那么为什么会在一个意想不到的地方抛出一个关于它“在赋值之前被引用”的错误呢?

class Group:

    # group has a name, users, and children groups
    def __init__(self, name):
        self.name = name
        self.groups = []
        self.users = []

    # simple getters and setters
    def get_name(self):
        return self.name
    def get_groups(self):
        return self.groups
    def get_users(self):
        return self.users
    def add_group(self, group):
        self.groups.append(group)
    def add_user(self, user):
        self.users.append(user)

def is_user_in_group(user, group):
    """
    Return True if user is in the group, False otherwise.

    Args:
      user(str): user name/id
      group(class:Group): group to check user membership against
    """

    # initialize a blank list for output
    users = []

    # recursive function to collect all users in passed group
    def get_all_users(group):

        # add the users on this group level
        users.extend(group.get_users())

        # get the subgroup children of this group
        groups = group.get_groups()

        # base case, there are no subgroups and we are at the lowest level
        if len(groups) == 0:
            return users

        # subgroups exist, recurse into each one
        for group in groups:
            get_all_users(group)

        return users

    # start recursion
    users = get_all_users(group)

    # search through the all collected users
    for _user in users:

        # current user matches passed user
        if user == _user:
            return True

    return False

########## TESTING ##########

# create a few groups
parent_group = Group("parent_group")
child_1_group = Group("child_1_group")
child_2_group = Group("child_2_group")
sub_child_1_group = Group("sub_child_1_group")

# define the group heirarchy
parent_group.add_group(child_1_group)
parent_group.add_group(child_2_group)
child_1_group.add_group(sub_child_1_group)

# add a user to each group level
parent_group.add_user("parent_user")
child_1_group.add_user("child_1_user")
child_2_group.add_user("child_2_user")
sub_child_1_group.add_user("sub_child_1_user")

# check for the presence of the sub_child_1_user in its native sub_child_1_group
print(is_user_in_group("sub_child_1_user", sub_child_1_group))
# True

# the sub_child_1_user is also a member of the parent group
print(is_user_in_group("sub_child_1_user", parent_group))
# True

# check for the presence of a nonexistant user
print(is_user_in_group("sub_child_not_added", child_1_group))
# False

# verify the presence of the parent_user in the top level parent_group
print(is_user_in_group("parent_user", parent_group))
# True

标签: pythonrecursion

解决方案


每当递归成为问题时,我的建议是保持简单并相信递归过程。以下是我可能会如何处理您的is_user_in_group()功能:

def is_user_in_group(user, group):
    """
    Return True if user is in the group, False otherwise.

    Args:
        user(str): user name/id
        group(class:Group): group to check user membership against
    """

    # recursive function to collect all users in passed group
    def get_all_users(group):
        # initialize a blank list for output
        users = []

        # add the users on this group level
        users.extend(group.get_users())

        # get the subgroup children of this group
        groups = group.get_groups()

        # if subgroups exist, recurse into each one
        for group in groups:
            users.extend(get_all_users(group))

        return users

    # start recursion
    users = get_all_users(group)

    # search through the all collected users
    return user in users

解决此问题的更好方法是在递归时进行检查,user以便在获得肯定结果时停止递归,并且仅在否定结果的情况下完全扩展树。


推荐阅读