Python变量作用域与Pyunit
今天遇到一个问题:如何在pyunit不同用例中共享变量?在实现过程中遇到一些问题,有一些涉及基础概念的,现总结出来,以记录。
一、模块中的全局变量
当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是局部的。这称为变量的作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。下面是个例子:
#!/usr/bin/python
# Filename: func_local.py
def func(x):
print 'x is', x
x = 2
print 'Changed local x to', x
x = 50
func(x)
print 'x is still', x
运行程序,输出的结果如下:
$ python func_local.py
x is 50
Changed local x to 2
x is still 50
在函数中,我们第一次使用x的值的时候,Python使用函数声明的形参的值。接下来,我们把值2赋给x。x是函数的局部变量。所以,当我们在函数内改变x的值的时候,在主块中定义的x不受影响。最后一个print语句中,我们证明了主块中的x的值确实没有受到影响。
如果你想要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是全局的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。
你可以使用定义在函数外的变量的值(假设在函数内没有同名的变量)。然而,我并不鼓励你这样做,并且你应该尽量避免这样做,因为这使得程序的读者会不清楚这个变量是在哪里定义的。使用global语句可以清楚地表明变量是在外面的块定义的。源程序如下:
#!/usr/bin/python
# Filename: func_global.py
def func():
globalx
print 'x is', x
x = 2
print 'Changed local x to', x
x = 50
func()
print 'Value of x is', x
运行程序输出如下:
$ python func_global.py
x is 50
Changed global x to 2
Value of x is 2
global语句被用来声明x是全局的——因此,当我们在函数内把值赋给x的时候,这个变化也反映在我们在主块中使用x的值的时候。
你可以使用同一个global语句指定多个全局变量。例如global x, y, z。
二、类中的变量
类中有两种类型的域——类的变量和对象的变量,它们根据是类还是对象拥有这个变量而区分。
类的变量由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,所以当某个对象对类的变量做了改动的时候,这个改动会反映到所有其他的实例上。
对象的变量 由类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是是互不相关的。下面一个例子会使这个易于理解。
#!/usr/bin/python
# Filename: objvar.py
class Person:
'''Represents a person.'''
population = 0
def __init__(self, name):
'''Initializes the person's data.'''
self.name = name
print '(Initializing %s)' % self.name
# When this person is created, he/she
# adds to the population
Person.population += 1
def __del__(self):
'''I am dying.'''
print '%s says bye.' % self.name
Person.population -= 1
if Person.population == 0:
print 'I am the last one.'
else:
print 'There are still %d people left.' % Person.population
def sayHi(self):
'''Greeting by the person.
Really, that's all it does.'''
print 'Hi, my name is %s.' % self.name
def howMany(self):
'''Prints the current population.'''
if Person.population == 1:
print 'I am the only person here.'
else:
print 'We have %d persons here.' % Person.population
swaroop = Person('Swaroop')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam')
kalam.sayHi()
kalam.howMany()
swaroop.sayHi()
swaroop.howMany()
运行程序,输出如下:
$ python objvar.py
(Initializing Swaroop)
Hi, my name is Swaroop.
I am the only person here.
(Initializing Abdul Kalam)
Hi, my name is Abdul Kalam.
We have 2 persons here.
Hi, my name is Swaroop.
We have 2 persons here.
Abdul Kalam says bye.
There are still 1 people left.
Swaroop says bye.
I am the last one.
这个例子,有助于说明类与对象的变量的本质。这里,population属于Person类,因此是一个类的变量。name变量属于对象(它使用self赋值)因此是对象的变量。
观察可以发现__init__方法用一个名字来初始化Person实例。在这个方法中,我们让population增加1,这是因为我们增加了一个人。同样可以发现,self.name的值根据每个对象指定,这表明了它作为对象的变量的本质。
记住,你只能使用self变量来参考同一个对象的变量和方法。这被称为 属性参考 。
三、Pyunit的初始化
之前也研究了类的变量,现在可以将测试类里面写一个对象的变量去作为贡献变量,具体实现就是在__init__方法里面去声明变量,然后使用。
但是在实际的运行过程中,结果并不是想象的那样,每个用例的值都会被重新赋值一次,不能做到共享变量的目的。后来发现pyunit在调用执行时候,每个用例都会重新生成一个测试类,就是一个用例对应一个测试实例,而不是类里面所有的用例对应一个测试实例。
现在的解决办法就是使用类的变量去构造共享变量。
测试验证OK!
PS:
不同测试之间共享变量,这种方法还是有点不合适,最好是充分利用框架里面的setup和teardown,将每个测试用例之间的耦合性降到最低。