欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python:命名空间学习

程序员文章站 2022-07-13 17:57:35
...

一、一个bug引发的思考

最近在学习python小项目,其中一个模块是有关时间的,就设计了一个辅助类叫做MyTimer

代码如下:(简略版)

import os
import time

class MyTimer():
    # 私有方法:把时间戳转化为时间: 
    def __timestamp_to_time(self,timestamp):
        timeStruct = time.localtime(timestamp)
        return time.strftime("%Y-%m-%d %H_%M_%S",timeStruct)
    # 获得文件创建时间
    def get_file_createtime(self,filePath):
        t = os.path.getctime(filePath)
        create_time = self.__timestamp_to_time(t)
        return create_time
# 测试函数  
time = MyTimer()
filepath = 'G:\\Users\\IMUHERO\\old\\a.txt'
print(time1.get_file_createtime(filepath))

使用测试函数测试功能时一直报错,说没有找到 localtime

Traceback (most recent call last):
  File ".\timer.py", line 67, in <module>
    print(time.get_file_createtime(filepath))
  File ".\timer.py", line 60, in get_file_createtime
    create_time = self.__timestamp_to_time(t)
  File ".\timer.py", line 54, in __timestamp_to_time
    timeStruct = time.localtime(timestamp)
AttributeError: 'MyTimer' object has no attribute 'localtime'

可是在time这个模块里面确实有localtime这个方法呀,到底咋回事呢?


二、修正BUG

认真检查一下才发现,自己犯了一个低级错误,把实例化对象的命名和导入的包重复了。

python:命名空间学习

这样就使得导入的time,被我自己命名的time对象覆盖了,我自己实例化的对象当然没有localtime啦,因为内部我还要调用外部time模块来实现功能呢。

 

把这个bug解决后也引发了我自己的思考,python的命名空间规则是怎么样的呢?全局变量,局部变量的作用范围和java有什么不同呢?读和写在python的编译期会有什么不同?

三、示例总结

下面通过几个小例子总结一下:

1.全局变量、局部变量和global关键字

范例一:

name = "test"

def Test():
    print (name)

def Test1():
    name = "test1"
    print (name)

def Test2():
    name += "1"
    print (name)
    

Test()
Test1()
Test2()

 运行这段代码的结果是什么? 

test
test1
Traceback (most recent call last):
  File ".\test.py", line 23, in <module>
    Test2()
  File ".\test.py", line 19, in Test2
    name += "1"
UnboundLocalError: local variable 'name' referenced before assignment

可以看到Test()和Test1()都能正常执行

-》其中Test()函数内是没有name这个变量的,那他为什么能够执行呢?

这里的原因是:Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间

函数中定义的是局部空间,局部空间没有就去全局空间找,然后找到并打印。

-》 Test1()也是一样的道理,在局部空间我们定义了name,所以直接打印出来就结束了。

基础知识补充:

一般有三种命名空间:

  • 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
  • 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  • 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

 python:命名空间学习

-》那么问题来了,为什么在Test2()中会报错:本地变量"name"未定义呢?

这里就涉及到python在编译期对读和写的不同操作,在Test2()中新增加了name += "1"

按道理说,局部变量没找到,应该去全局变量读取并且做修改,但是这里却没有。

我个人的理解是,一般情况下写操作是不安全的,对全局变量进行修改可能导致不可预料的结果,所以Python不允许。

-》那么我们怎么样才能对全局变量进行修改呢?

这里一个比较常用的方法是 global 关键字

python:命名空间学习
这里我们顺便打印一下全局变量看一下结果:

test
test1
test2
test2

这次没有报错了,name += "2"得出了正确结果

我们可以注意到,全局变量name也发生了改变,变成了修改后的test2,说明global关键字可以对全局变量进行修改。

 


继续拓展:如果我们想要在函数内部获取全局变量来做操作,并且不希望改变全局变量的值怎么办?

2、闭包和nonlocal关键字

再举一个例子

假设一个人初始走0步,第一次走2步,第二次走3步,第三次走5步,那么计算他每一次走完的总步数。

看到这个问题大多数同学会想到使用 global 关键字 

(1)用global解决

示例代码:

origin = 0
def factory(step):
    global origin
    origin += step
    print(origin)

factory(2)
factory(3)
factory(5)

可以得出正确答案:

2
5
10

但是我们的origin也发生了改变,我们命名叫做origin就是起始值的意思,起始值怎么能够一直变呢。

还有没有更好的办法,不去修改起始值?


(2)用闭包解决

 要理解闭包首先要理解python的独特之处

俗话说:Python一切皆对象

比较好的证明就是,python可以return一个函数,比如:

 

python:命名空间学习

而闭包,就是函数+环境变量

闭包 = 函数+环境变量

下面的这个例子就是闭包,在函数里面嵌套函数,return的是内部函数。

python:命名空间学习

最终打印的结果是50.

而全局变量a的结果仍然是10.

 

-》滴——————————————

今天学习时间到了,明天继续补充。

加油:)

相关标签: python 命名空间