Python---NameError:free variable 'attr_str' referenced before assignment in enclosing scope
先看一段代码,这段代码来自动物书 《流畅的Python》的126页,将其中的示例5-10的代码写进一个文件tag.py,这个文件用来生成HTML标签,
def tag(name,*content,cls = None,**attrs):
"""生成一个html标签,in tag.py"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ' '.join(' %s="%s"'%(attr,value) for attr,value in sorted(attrs.items()))
else:
attrs_str = ' '
if content:
return '\n'.join('<%s%s>%s</%s>'%(name,attr_str,c,name) for c in content)
else:
return '<%s%s/>'%(name,attr_str)
但是笔者在实现这个文件时,出现了一个问题:
NameError: free variable 'attr_str' referenced before assignment in enclosing scope
错误信息的大概意思是:*变量 ‘attr_str’ 在封闭范围内在赋值之前被引用。
在我第一次遇到这个问题时,我想python不是不用声明变量吗?为什么还会出现这样的问题呢?在解决这个bug之前我们先来看看什么叫*变量!
*变量:
参考自简书,《python学习笔记 - local, global and free variable》
When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the block’s environment.
If a name is bound in a block, it is a local variable of that block. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.
以上的引用我们可以了解到*变量的含义,如果一个变量在一个代码块中使用了,但是未在这个代码块中定义,那么这个变量就是*变量。
从上面我们的代码我们可以看到, attr_str虽然使用了,但是未定义,所以python shell抛出了NameError错误,既然找到了错误原因,那怎么解决呢?
- 在文件开头定义全局变量:
global attr_str #定义全局变量
def tag(name,*content,cls = None,**attrs):
"""生成一个html标签,in tag.py"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ' '.join(' %s="%s"'%(attr,value) for attr,value in sorted(attrs.items()))
else:
attrs_str = ' '
if content:
return '\n'.join('<%s%s>%s</%s>'%(name,attr_str,c,name) for c in content)
else:
return '<%s%s/>'%(name,attr_str)
- 在函数内部提前进行引用:
def tag(name,*content,cls = None,**attrs):
"""生成一个html标签,in tag.py"""
attr_str = ''
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ' '.join(' %s="%s"'%(attr,value) for attr,value in sorted(attrs.items()))
else:
attrs_str = ' '
if content:
return '\n'.join('<%s%s>%s</%s>'%(name,attr_str,c,name) for c in content)
else:
return '<%s%s/>'%(name,attr_str)
再次运行程序就okay啦!