一般来说,作为常量放在文件最开头,或者最外面的变量,是属于全局变量(静态变量),一般最好不要轻易改动他的取值,不管是在函数里,还是在主程序中。
其实python里面,什么东西都是涉及到命名域/作用域的,当然很多时候是隐式的给出的。实际上你可以通过把握三点来解决,不可变(传值)/可变对象(传引用),重新绑定(针对于可变对象的特殊情况)。或者可以说,python传递的是对象的引用。
其实如果不使用关键字global,函数也是可以改变全局变量的。请看下面的例子。
a=[1,2,3]
def add(x):
a.append(100)
a[0]=1000
return x
print(add(a))
print(a)
输出:
a=[1,2,3]
def add(x):
x+=[100]
#注意不能使用x=x+[100],这在python里面是叫做重新绑定
return x
print(add(a))
print(a)
输出:
a=[1,2,3]
def add(x):
a.append(100)
return x
print(add(a))
print(a)
但是上面的例子是可变对象list,但如果是不可变对象,那么它根本就没有那些原地改变的方法,也就不可能在函数里面改变,从而改变外部的全局变量,或者说它永远都对应着重新绑定(不改变原值)。详见下面例子。
a=100
#字符串,元组也一样
def add(x):
x=1000
return x
print(add(a))
print(a)
a=100
#字符串,元组也一样
def add(x):
a=1000
return x
print(add(a))
print(a)
这里的话,其实又可以延伸出另外一个知识点,python变量引用,以及重新赋值(或者叫绑定,该变量引用另外一个对象)的问题。关于这点网上其实有很介绍,python到底是传值还是传引用。但这里我想强调的重点是,对于重新绑定,就算是在函数里面对一个可变的全局变量(之前已绑定一个可变对象)进行重新绑定,它也不会对最终的全局变量造成影响。详见下面的例子。
a=[1,2,3]
def add(x):
a=[100,200,300]
return x
print(add(a))
print(a)
a=[1,2,3]
def add(x):
x=[100,200,300]
return x
print(add(a))
print(a)
其实这里不管是函数(函数也是一种特殊的类对象),python一切皆对象嘛。。上面的这些,从广义上面来讲,都可以用命名空间来解释,python中其实都是传递引用的,只是在不同命名空间会造成联系,或者屏蔽。。
那么如果我实在是想在函数里面修改全局变量呢,该如何做,这个时候global关键字就发挥作用了,就算是重新绑定(不论是可变或者不可变对象),它也会及时生效。。
num = 1
def fun():
num = 123
print(num)
fun()
print(num)
此时没有使用global关键字,无法对全局变量num进行修改,运行结果如下:
想要对全局变量进行修改,要用到关键字global!
#global关键字(内部作用域想要对外部作用域的变量进行修改)
num = 1
def fun():
global num
num = 123
print(num)
fun()
print(num)
运行结果如下:
注意:如果一个函数申明了global变量,如果一次执行中被多次调用,那么都是指这个全局变量,非常具有混乱性,是有历史的,类似于空列表作为函数默认参数的效果。
上面的关键字global,要先申明是全局的某个变量,再使用重新绑定。程序文件最开头的全局变量默认都是全局类型的,可加也可以不加global关键字。
其实python对这个引用以及作用域的设计很不严格,跟其他语言中也很不一样,具体情况具体分析,其次编译过程(一切皆对象,函数编译也会有记录)与调用执行过程可以是相互作用的,不同的,参考这里的一个例子以及之前默认值可变对象调用自动加1的例子。
a=[1,2,3]
def add(a):
a=a+[100]
b=a
print(id(a))
return b
print(add(a))
print(id(a))
print(a)
a=[1,2,3]
def add(a):
a.append(100)
b=a
print(id(a))
return b
print(add(a))
print(id(a))
print(a)
a=[1,2,3]
def add(a):
a+=[100]
b=a
print(id(a))
return b
print(add(a))
print(id(a))
print(a)
a=[1,2,3]
def add(a):
a+=[100]
b=a
return b
print(add(a))
print(a)
a=[1,2,3]
def add(a):
a=a+[100]
b=a
return b
print(add(a))
print(a)
a=[1,2,3]
def add(a):
a=a.append(100)
b=a
return b
print(add(a))
print(a)
a=[1,2,3]
def add(a):
b=a
a=a.append(100)
return b
print(add(a))
print(a)
a=[1,2,3]
def add():
b=a
a=a.append(100)
return b
print(add())
print(a)
a=[1,2,3]
def add():
b=a
a=a+[100]
return b
print(add())
print(a)
a=[1,2,3]
def add():
b=a
a+=[100]
return b
print(add())
print(a)
a=[1,2,3]
def add():
a+=[100]
b=a
return b
print(add())
print(a)
全局变量局部变量在编译期间就已经决定,在同一个作用域不能有模棱两可的定义,否则编译就会报错,其次同一内存,同一变量,还会有在不同作用域内以及不同的变量指向它,如果是可变对象,还是可以直接修改的(毕竟都是引用),或者强行用global申明,要具体情况具体分析,灵活应用,一般来说还是使用一般操作就好,不要花里胡哨。
其实最后几个例子还是很神奇的,这就是编译器的作用,直接阻止运行,在编译期间会直接设定全局,局部变量,设定好作用域,在调用时有违反直接报错(未定义变量),尽管可能某些情况下,局部变量与全局变量地址一样,但是内部已经解绑,属于不同作用域(虽然有时可以通过可变对象的属性进行原地修改,+=也是原地修改,但是已经解绑,区分了局部变量与全局变量,也就是作用域,不定义的局部变量直接使用必定报错,所以仍然要满足定义与调用一致的条件),这是最基本的,其次才是可变与不可变对象的引用关系(不同作用域可能共享一个变量地址)。如果想不报错,直接使用全局作用域,就需要告诉编译器信息了,也就是global关键字的作用,强行改变,不过这是不推荐的,写代码还是要遵守一下规范,保持良好的习惯以及清晰易懂的风格