在Python 3.5.0中这段代码:
a = (1,2)
class Foo(object):
b = (3,4)
c = tuple((i,j) for j in b for i in a)
d = tuple((i,j) for i in a for j in b)
产生
Traceback (most recent call last):
File "genexprtest.py", line 2, in <module>
class Foo(object):
File "genexprtest.py", line 5, in Foo
d = tuple((i,j) for i in a for j in b)
File "genexprtest.py", line 5, in <genexpr>
d = tuple((i,j) for i in a for j in b)
NameError: name 'b' is not defined
为什么会出现此错误?为什么我在前一行没有得到这个错误?
答案 0 :(得分:1)
这是因为表达式for i in a
具有局部变量范围,而表达式for j in b
位于范围内,因此找不到b
。<登记/>
实际上,如果你写c = tuple((i, j) for i in a for j in b)
,它会抛出相同的异常。
解决方案将b
放入类定义的范围内(正如您已经做过的那样)并通过self.b
引用它。
答案 1 :(得分:0)
我花了很多年的时间进行实验,并且我有一个关于你为什么会遇到这个错误的理论。我不确定,但这确实解释了为什么它适用于c
而不适用于d
。我希望这会对你有所帮助,如果你不同意则发表评论:)
def Tuple(this):
print(a) # this always works
try:
print(b) # this always gives an error
except NameError:
print("...b is not defined")
try:
return tuple(this) # this only gives an error for d and e
except NameError:
print("...couldn't make it a tuple")
a = (1,2)
class Foo(object):
b = (3,4)
c = Tuple((i,j) for j in b for i in a)
d = Tuple((i,j) for i in a for j in b)
e = Tuple((i,j,k) for i in a for j in b for k in (5, 6))
f = Tuple((i,j,k) for j in b for i in (5, 6) for k in a)
print("\nc:", c,"\nd:", d,"\ne:", e,"\nf:", f)
发生了什么:每次调用Tuple()
函数时,都未定义b
,但始终定义a
。这解释了为什么您收到d
和e
的错误,但它没有解释为什么c
和f
工作,即使b
未定义'
我的理论:在将整个事物转换为元组之前计算第一个for
循环。例如,如果您尝试执行此操作:Tuple((a, b, c) for a in loop1, for b in loop2 for c in loop3)
,则在Foo类中首先计算for a in loop1
,然后它将移动到foo并计算循环2和3.
总结:
答案 2 :(得分:0)
在我看来,错误的产生是因为b
被定义为类变量。要正确使用它,您需要将其视为(self.b
)。
此外,您应该使用构造函数:
a = (1, 2)
class Foo(object):
def __init__(self):
self.b = (3, 4)
self.c = tuple((i, j) for j in self.b for i in a)
self.d = tuple((i, j) for i in a for j in self.b)
这是一个更清晰的代码。它表现得很好。希望它有所帮助。
编辑:如果您不想使用__init__
,还可以使用方法获取c
和d
:
a = (1, 2)
class Foo(object):
b = (3, 4)
def get_c(self):
return tuple((i, j) for j in self.b for i in a)
def get_d(self):
return tuple((i, j) for i in a for j in self.b)
这也完美无缺。 您可以尝试这两种实现:
inst = Foo()
# 1st one
print(inst.c)
print(inst.d)
# 2nd one
print(inst.get_c())
print(inst.get_d())
答案 3 :(得分:0)
针对您的具体案例的解决方案是使用itertools:
namespace Tests\Unit\Services;
use App\User;
use App\Api\LocalStorageService;
public function testCreateFolder()
{
$user = factory(User::class)->make();
$localStorageService = $this->createMock(LocalStorageService::class);
$localStorageService->method('createFolder')
->willReturn(array('folder_id' => 'random_id', 'folder_path' => ("/Users/" . $user->email)));
}
看似意外的行为的解释是双重的:
只有{strong>根类范围才能访问d = tuple(itertools.product(a, b))
等裸类属性。见pep 227:
无法访问类范围中的名称。名称在最里面的封闭函数范围内解析。如果类定义出现在嵌套作用域链中,则解析过程将跳过类定义。
生成器中的嵌套循环不像你期望的那样起作用。第一个循环实际上是最外层,第二个循环最里面。来自python docs:
后续条款无法立即评估,因为它们可能取决于之前的for循环。例如:(x * y表示范围(10),y表示条形(x))。
第二点可以用添加的换行符说明。
b
这意味着d = tuple((i,j)
for i in a
for j in b)
实际上是从内部循环(嵌套范围)引用的,因此抛出了b
。然而,在第一个发生器中,引用位于外部,它可以正常工作。