全局变量与参数
Posted good18
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了全局变量与参数相关的知识,希望对你有一定的参考价值。
全局变量与局部变量
- 定义在py文件中的变量为全局变量,全局变量可以在任意地方访问
- 定义在函数中的变量为局部变量,局部变量只能在定义的函数中使用,外界无法访问
- 当在函数中要访问一个变量,比如name时,python解释器会首先在函数内寻找局部变量,如果函数内有name=”Tom”这样的定义,则使用函数内的name,如果函数内没有这样的定义,就会去函数外找全局变量,如果函数外没有定义这样的全局变量,python解释器就再去内置函数中查找,找不到则报错。
全局变量与局部变量重名产生的问题(假设都叫name)
- 当函数中没有global关键字时,例如name=”Tom”,此时函数中使用的name就是自己的局部变量,修改局部name不影响全局的name。函数外访问的name是全局的name。
- 当函数中有global关键字时,例如global name,这条语句的含义是表明在本函数内使用的name是全局变量,此时函数中的name实际上就是全局变量,修改name就是修改全局的name。
- 如果全局变量是列表,字典等可变对象,如list1,dict1,则在函数内部可以不用加global关键字,就可以调用append等方法为列表,字典等添加、删除数据,但是如果要修改全局变量,例如list1=100,则必须加global。这一点类似函数调用时传递参数。
- 结论:python中的变量名实际上是指向一块内存区域,如果要将全局变量指向新的内存区域,即将全局变量名放在=的左边,必须加global,否则不需要加,例如读取变量的值或者修改列表元素等。
- 解决:其实全局变量与局部变量重名完全可以避免,在python编码规范中,全局变量被看做是“常量”,通常全部大写,局部变量通常定义为小写或大小写组合。
语句内部变量外部可用
# 在for,while,if语句中声明的变量,在这些语句外部依然可以访问
# 这一点与C系的不同,调用locals()就可以发现。
def func():
# for语句
for i in range(10):
sum=i
# if语句
if 10>2:
res=99
# while语句
while True:
a=100
break
# 输出变量
print(sum)
print(res)
print(a)
print(locals())
# {'i': 9, 'sum': 9, 'res': 99, 'a': 100}
#
func()
嵌套函数的重名变量访问
- global专门指全局变量,无论在嵌套函数的哪个层次,都将global修饰的变量指向py文件中定义的 全局变量
- 如果内层嵌套函数的变量用nonlocal来修饰,则该变量指向外层嵌套函数中的同名变量,使用规则与global相同
name="ABC"
def f1():
name="Jack"
def f2():
nonlocal name
# 这里的name指向f1中的name
name="Tom"
print(name)
f2()
print(name)
f1()
#输出为Tom Tom
- 注意:如果一个函数没有嵌套在另一个函数内,不能使用nonlocal关键字
函数即变量
- 我们可以将函数和变量视为同一种东西,也包括后面要讲的类。
- 变量名,类名,函数名,都是一个标识,指向一块内存地址,只是保存的东西不一样罢了
def foo():
print("foo")
bar()
def bar():
print("bar")
foo()
# 为什么bar定义在foo后面,还是可以正常调用?
# 解释:
# def foo……表明在内存中定义一个变量foo,指向一块内存,内容是函数体
# def bar……表明在内存中定义一个变量bar,指向一块内存,内容是函数体:
# 调用foo()的时候,foo和bar两个变量都定义完毕,因此foo中能够找到bar
# 所以函数可以正常调用
def foo():
print("foo")
bar()
foo()
# 在这里调用为什么报错?
# 解释:
# python解释器运行到foo()时,变量foo已经定义完毕,但bar还没有定义,
# 解释器找不到名为bar的变量,当然找不到里面的函数代码,因此报错
def bar():
print("bar")
简单递归
def digui(n):
if n==1:
return 1
return n*digui(n-1)
print(digui(10))
递归的效率不高,可以用循环代替,但优势在于算法容易理解
理解递归调用:可以将代码中递归调用的函数名看做函数体的代码,因为函数就是代码块。
格式化时间
import time
today=time.strftime("%Y{}%m{}%d{},%H{}%M{}%S{}",time.localtime()).format("年","月","日","时","分","秒")
# 2019年06月14日,13时39分48秒
多重列表解析
data=[]
for i in range(10):
data.append([0 for _ in range(10)])
data[1][5]=1
data[2][3]=5
data[3][9]=20
data[6][2]=99
data[9][0]=30
#本例中演示的是将二维数组变为一维数组
#注意两个for的顺序,与i相关的要放在最后
print([i for item in data for i in item])
函数注解
我们可以为函数的参数和返回值添加注解,这种注解不具有强制性,是一种类似注释的说明性文字。
def mystrings(n:int,s:str)->str:
return s*n
print(mystrings(10,"a"))
# python并不进行类型检查,下行代码正常运行
print(mystrings(10,10))
字符串与变量的转换
字符串→变量名(对象)
# 有三种方法可以获取字符串对应的变量(对象)
class MyClass(object):
a = 100
def show(self):
la = 999
s = "la"
print(locals()[s])
ga = 1024
# 通过getattr函数获取对象成员
s = "a"
print(getattr(MyClass, s))
# 通过globals函数获取全局变量
s = "ga"
print(globals()[s])
# 通过locals函数获取局部变量
mc = MyClass()
mc.show()
变量名→同名字符串
import inspect
import re
def varname(p):
for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
m = re.search(r'varnames*(s*([A-Za-z_][A-Za-z0-9_]*)s*)', line)
if m:
return m.group(1)
abc = 100
print(varname(abc))#abc
print(type(varname(abc)))#<class 'str'>
*args与**kwargs
*的打包作用
a,*b,c=[1,2,3,4,5]
print(a)
print(b)
print(c)
# 1
# [2, 3, 4]
# 5
# 在这里*起的是打包的作用
作为函数参数的拆包
#-----------------例1------------------------
def testArgs(*args):
print(args)
testArgs(1,2,3)
testArgs([1,2,3])
testArgs(*[1,2,3])#这里*的作用是拆包,列表将不再作为一个整体
# (1, 2, 3) 将1,2,3放到一个元组中,赋值给args
# ([1, 2, 3],) 将[1,2,3]作为一个整体,args是只有一个元素的元组
# (1, 2, 3)效果等同于testArgs(1,2,3)
def testKwargs(**kwargs):
print(kwargs)
#-----------------例2------------------------
my_dict=dict(name="Tom",age=19,country="China")
# 解释,如果采用下面的方式,把字典直接作为参数传递给**kwargs
# 则程序报错,因为这里需要的是一个命名参数,而实际传递的
# 是一个位置参数,内容为一个字典,如果需要将字典作为参数传递
# 给**kwargs,必须加上**进行拆包
# testKwargs(my_dict)
testKwargs(**my_dict)
#-------------------例3----------------------
def test(name,age):
print(name,age)
# 此处**将字典拆包为命名参数
my_dict={"name":"Tom","age":18}
test(**my_dict)
# 程序输出
# Tom 18
#-----------------结论------------------------
# 在作为函数实参传递时,列表和字典对象都只作为一个单独的参数传递
# 在列表(元组)前面使用*,在字典前面使用**,都会进行拆分
# *会将列表(元组)拆分为多个位置参数
# **会将字典拆分为多个命名参数
# 函数形参声明中的*和**,与外界调用时实参前使用的*和**,作用不同
# 不要混淆
传递给其他函数
def test02(*args,**kwargs):
print(args)
print(kwargs)
def test01(*args,**kwargs):
# 解释1:*args,**kwargs为可变参数,python解释器
# 会将外界所有传入的参数放到args和kwargs两个变量中
# 从下面的print语句可以看出,args是一个元组
# kwargs是一个字典
print(args) #(1, 2, 3)
print(kwargs) #{'m': 10, 'n': 20}
# 解释2:当我们将args和kwargs直接传递给test02时
# test02会将这一个元组,一个字典作为两个参数
# 赋值给test02的args,而kwargs为空.
test02(args,kwargs)
# ((1, 2, 3), {'m': 10, 'n': 20})
# {}
# 解释3:当我们将*args,**kwargs传递给test02时
# 实际上是将外界调用的参数原封不动的传递给test02
# 因此输出的结果当然为一个元组,一个字典
test02(*args,**kwargs)
# (1, 2, 3)
# {'m': 10, 'n': 20}
test01(1,2,3,m=10,n=20)
# 结论,在使用可变参数的函数中,如果要将传入参数原封不动的传递
# 给另外一个函数,需要加上*和**。否则传递过去的是一个元组或字典
# 这里加上的*和**,实际上是起的拆包的作用,将元组和字典分别拆分
# 成多个位置参数和命名参数
以上是关于全局变量与参数的主要内容,如果未能解决你的问题,请参考以下文章
C语言基础:作用域规则(局部变量,全局变量,形式参数)全局变量与局部变量在内存中的区别初始化局部变量和全局变量
学习笔记1(三元运算深浅拷贝动态参数全局变量与局部变量set数据类型 )