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赋给xx是函数的局部变量。所以,当我们在函数内改变x的值的时候,在主块中定义的x不受影响。最后一个print语句中,我们证明了主块中的x的值确实没有受到影响。

如果你想要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是全局的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。

你可以使用定义在函数外的变量的值(假设在函数内没有同名的变量)。然而,我并不鼓励你这样做,并且你应该尽量避免这样做,因为这使得程序的读者会不清楚这个变量是在哪里定义的。使用global语句可以清楚地表明变量是在外面的块定义的。源程序如下:

#!/usr/bin/python

# Filename: func_global.py

def func():

    global x

    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

不同测试之间共享变量,这种方法还是有点不合适,最好是充分利用框架里面的setupteardown,将每个测试用例之间的耦合性降到最低。

 

参考:

http://woodpecker.org.cn/abyteofpython_cn/chinese/index.html