代码如下:
Copy def _Create ( self ):
...
INSTANCETYPE_STRING = self . machine_type
cpu_base_set = { "SMALL" : 1 , "MEDIUM" : 2 , "LARGE" : 4 }
for typeKey in cpu_base_set . keys ():
if typeKey in INSTANCETYPE_STRING :
cpu_base = cpu_base_set [ typeKey ]
type_base = typeKey
instance_type_all = INSTANCETYPE_STRING . split ( "." ) [ 1 ]
instance_type_cpu , instance_type_mem = instance_type_all . split (type_base) # 异常报错在这一行
if "X" in instance_type_cpu :
cpu_multiple = instance_type_cpu . split ( "X" ) [ 0 ]
instance_cpu = cpu_base * cpu_multiple
else :
instance_cpu = cpu_base
instance_mem = instance_type_mem
代码简单说明:
INSTANCETYPE_STRING
字符串是虚拟机实例的规则,字符串类似S2.2XLARGE16
cpu_base_set
使用字典结构,表示规格关键字和VCPU的计算基数映射关系,例如LARGE
关键字表示计算技术是4个VCPU
typeKey
遍历cpu_base_set
字典的每个key,取出key对比是否包含在INSTANCETYPE_STRING
中,如果包含,则将对应VCPU数量赋值给cpu_base
;同时记录下此时的typeKey
给type_base
这样就知道这个实例的类型,例如LARGE
instance_type_all.split(type_base)
是为了将字符串instance_type_all
(2XLARGE16
)中LARGE
前后的字符2X
(2倍cpu_base
)和数字16
提取出来(内存大小)
Python程序执行过程抛出异常,其中显示本地变量在分配前被引用错误:
Copy UnboundLocalError: local variable 'type_base' referenced before assignment
在What are the rules for local and global variables in Python? 介绍了类似的在一个函数中仅仅加了一个设置状态就会导致原先可以工作的代码出现UnboundLocalError
异常,和这里的报错非常相似:
原先的可工作代码
Copy >>> x = 10
>>> def bar ():
... print (x)
>>> bar ()
10
修改后导致异常代码
Copy >>> x = 10
>>> def foo ():
... print (x)
... x += 1
>>> foo ()
Traceback (most recent call last):
...
UnboundLocalError : local variable 'x' referenced before assignment
这是因为当你在一个作用域范围(scope)内设置变量,这个变量就会成为这个scope的local变量,并且影响(shadow)其他scope的任何相似命名的变量。由于在foo
函数中最后的声明设置一个新的值给x
,这样编译器就识别它作为一个本地变量。而在这之前print(x)
试图答应这个还没有初始化的本地变量就会导致错误。
可以这样理解:如果在一个作用域(函数)内如果设置变量,这个变量就会被编译器识别local变量。此时就需要确保这个local变量在设置前没有被引用,否则就会出现UnboundLoaclError
。
以上案例我们可以通过声明x
变量为global
,这样就可以访问scope之外的变量x = 10
Copy >>> x = 10
>>> def foobar ():
... global x
... print (x)
... x += 1
... print (x)
...
>>> foobar ()
10
11
>>> print (x)
11
可以看到在scope之变量也可以修改。
另外,在Python 3中,还有一个nonlocal
关键字可以实现相似功能:
Copy >>> def foo ():
... x = 10
... def bar ():
... nonlocal x
... print (x)
... x += 1
... bar ()
... print (x)
>>> foo ()
10
11
Python中的本地和全局变量规则
在Python中,变量只在一个函数中引用(只使用不赋值)的是隐含全局。如果变量在函数体内部任何一个位置被赋值,就会被假设为本地变量,除非其明确声明为全局变量。
解决
参考了What are the rules for local and global variables in Python? ,尝试修改:
Copy def _Create ( self ):
"""Create a VM instance."""
global cpu_base
global type_base
cpu_base = ""
type_base = ""
...
INSTANCETYPE_STRING = self . machine_type
cpu_base_set = { "SMALL" : 1 , "MEDIUM" : 2 , "LARGE" : 4 }
for typeKey in cpu_base_set . keys ():
if typeKey in INSTANCETYPE_STRING :
cpu_base = cpu_base_set [ typeKey ]
type_base = typeKey
instance_type_all = INSTANCETYPE_STRING . split ( "." ) [ 1 ]
instance_type_cpu , instance_type_mem = instance_type_all . split (type_base)
if "X" in instance_type_cpu :
cpu_multiple = instance_type_cpu . split ( "X" ) [ 0 ]
instance_cpu = cpu_base * cpu_multiple
else :
instance_cpu = cpu_base
instance_mem = instance_type_mem
这里在函数_Create(self)
的前面首先申请变量cpu_base
和type_base
是全局变量,似乎可以绕过这个问题。但是实际上还是会出现其他异常
为何这里会出现异常的UnboundLoaclError
呢?原因在
Copy if typeKey in INSTANCETYPE_STRING :
cpu_base = cpu_base_set [ typeKey ]
type_base = typeKey
可以看到,只有满足了条件之后,才会对数据进行赋值。也就是说,如果typeKey
没有包含在INSTANCETYPE_STRING
(取决于用户输入的参数),就会导致没有对local变量赋值,就直接行instance_type_all.split(type_base)
。这就是程序的逻辑错误:
任何时候,一定要进行用户输入数据的校验。如果采用if判断,一定要考虑是/否
两种情况。对于没有成功的判断,一定要有异常处理。
Python是一种弱类型语言,所以变量类型要小心处理。
实际解决方法,需要采用在用户输入入口进行字符串判断,以及在if
判断中增加异常处理:
Copy for typeKey in cpu_base_set . keys ():
if typeKey in INSTANCETYPE_STRING :
cpu_base = cpu_base_set [ typeKey ]
type_base = typeKey
else :
...
参考