嵌套函数声明的范围

时间:2015-01-16 02:45:14

标签: python function python-2.7 scope closures

我有以下代码:

def function_reader(path):
    line_no = 0 
    with open(path, "r") as myfile:
        def readline():
            line_no +=1
            return myfile.readline()

Python不断返回:

  

UnboundLocalError:在赋值之前引用的局部变量'line_no'

执行line_no +=1时。

我理解问题是嵌套函数声明在python中有奇怪的作用域(虽然我不明白它为什么用这种方式编程)。我主要想知道是否有一种简单的方法来帮助python解析引用,因为我真的很喜欢它提供的功能。

3 个答案:

答案 0 :(得分:3)

不幸的是,在Python 2.x中没有办法做到这一点。嵌套函数只能读取封闭函数中的名称,而不能重新分配它们。

一种解决方法是使line_no成为一个列表,然后更改其单个项目:

def function_reader(path):
    line_no = [0]
    with open(path, "r") as myfile:
        def readline():
            line_no[0] += 1
            return myfile.readline()

然后,您将通过line_no[0]访问行号。以下是演示:

>>> def outer():
...     data = [0]
...     def inner():
...        data[0] += 1
...     inner()
...     return data[0]
...
>>> outer()
1
>>>

此解决方案有效,因为我们不会重新分配名称line_no,只会改变它引用的对象。


请注意,在Python 3.x中,使用nonlocal statement

可以轻松解决此问题
def function_reader(path):
    line_no = 0
    with open(path, "r") as myfile:
        def readline():
            nonlocal line_no
            line_no += 1
            return myfile.readline()

答案 1 :(得分:1)

通过使用闭包很难说出你想要实现的目标。但问题是,使用这种方法,当你从外部函数返回ValueError: I/O operation on closed file时,你将以readline结束,如果从外部函数返回readline(),则只返回第一行。

如果您只想重复调用readline()或循环浏览文件并记住当前行号,那么最好使用一个类:

class FileReader(object):
    def __init__(self, path):
        self.line_no = 0
        self.file = open(path)

    def __enter__(self):
        return self

    def __iter__(self):
       return self

    def next(self):
        line = next(self.file)
        self.line_no += 1
        return line

    def readline(self):
        return next(self)

    def __exit__(self, *args):
        self.file.close()

用法:

with FileReader('file.txt') as f:
    print next(f)
    print next(f)
    print f.readline()
    print f.line_no # prints 3
    for _ in xrange(3):
        print f.readline() 
    print f.line_no # prints 6
    for line in f:
        print line
        break
    print f.line_no # prints 7

答案 2 :(得分:0)

获取下一行并跟踪行号的更多Pythonic方法是枚举内置:

with open(path, "r") as my file:
    for no, line in enumerate(myfile, start=1):
        # process line

这适用于所有当前的Python版本。