全局变量与参数

全局变量与局部变量

  • 定义在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'\bvarname\s*\(\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)
# 结论,在使用可变参数的函数中,如果要将传入参数原封不动的传递
# 给另外一个函数,需要加上*和**。否则传递过去的是一个元组或字典
# 这里加上的*和**,实际上是起的拆包的作用,将元组和字典分别拆分
# 成多个位置参数和命名参数

猜你喜欢

转载自www.cnblogs.com/good18/p/11883094.html