【Python】关于python里的global、__globals__、全局变量与多进程的一点理解

目录

  • 1 单个模块内global
    • 1. 1 先不global的话的情况是:可读但不可写
      • 1.1.1 python语句左右读的问题(未解决)
      • 1.1.2 python语句上下读的问题(未解决)
    • 1.2 global之后:全局可读,局部可写
      • 1.2.1 全局可读
      • 1.2.2 局部可写
  • 2 多个模块内的global
  • 3 `__globals__`
  • 4 多进程
    • 4.1 进程间全局变量不互通的原因
    • 4.2 不同进程里的a指向同一地址的原因

1 单个模块内global

总结: global a表示a为本py模块内均可读、局部(仅本函数内,本函数内部调用的其他函数不包括在内)可写。(这里读与写是个人理解的方式,并不代表python的真正机制,只是好记)

1. 1 先不global的话的情况是:可读但不可写

# foo.py
# 代码1
a = 3

def f():
	b = a + 1
	print(b)
	
if __name__ == '__main__':
	f()

结果是4,表明可以读到a

# foo.py
# 代码2
a = 3

def f():
	a = a + 1
	print(b)
	
if __name__ == '__main__':
	f()

结果报错,这里就表示不可写。但是仍有个问题:

1.1.1 python语句左右读的问题(未解决)

# foo.py
# 代码3
import dis

a = 3

def f():
	b = a + 1
	print(a)
	
if __name__ == '__main__':
	print(dis.dis(f))

dis函数式可以看到cpu的运行过程,结果是:

  8           0 LOAD_GLOBAL              0 (a)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (b)

  9           8 LOAD_GLOBAL              1 (print)
             10 LOAD_GLOBAL              0 (a)
             12 CALL_FUNCTION            1
             14 POP_TOP
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
None

这里直接看每一行表示运行的步骤,首先导入的是全局变量a。可以看出这里把a看成的是全局变量,是可以读到的。接下来看一下a = a + 1会怎么样:

# foo.py
# 代码4
import dis

a = 3

def f():
	a = a + 1
	print(a)
	
if __name__ == '__main__':
	print(dis.dis(f))
  8           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (a)

  9           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                0 (a)
             12 CALL_FUNCTION            1
             14 POP_TOP
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
None

这里是LOAD_FAST,表示是载入局部变量,所以没有读到,这里不太懂python为什么同样是从右往左但是会有不同的变量区域读法。这里并不是写出现问题,而是读不到了。(但是可以记为不可写,就不会出现a+=1的问题)。应该是有机制先读了左边,我猜的。还有问题:

1.1.2 python语句上下读的问题(未解决)

按说,python是一行一行运行的吧?将代码3的b = a + 1下一行添一句a = b,按说应该出错在a = b这一行:

# foo.py
# 代码5
import dis

a = 3

def f():
	b = a + 1
	a = b
	print(a)
	
if __name__ == '__main__':
	f()

结果:

Traceback (most recent call last):
  File "/Users/tanghui/Documents/python就业班/一些草稿纸/foo.py", line 15, in <module>
    f()
  File "/Users/tanghui/Documents/python就业班/一些草稿纸/foo.py", line 8, in f
    b = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

表示出错在b = a + 1,此时a又读不到了。如果用dis函数看一下,又发现python又开始load_fast,当然load不到啊。。不知道为啥子。

1.2 global之后:全局可读,局部可写

1.2.1 全局可读

表明你在函数f中改了之后,函数g中可以读到这个修改(外部也可以,全局都可以读):

# foo.py
# 代码6
a = 3

def f():
	global a
	a += 1
	print(a)  # 4
	
def g():
	print(a)  # 4
	
if __name__ == '__main__':
	f()
	g()

1.2.2 局部可写

如果先将a在全局定义好,然后在函数内部global它:

# foo.py
# 代码7
a = 3  # 外部定义

def f():
	global a
	a += 1
	print(a)  # 4
	
if __name__ == '__main__':
	f()

这下可以修改了,但是别的函数内还是不能修改:

# foo.py
# 代码8
a = 3  # 外部定义

def f():
	global a
	a += 1
	print(a)  # 4
	
def g():
	a += 1  # 报错 UnboundLocalError: local variable 'a' referenced before assignment
	print(a)
	
if __name__ == '__main__':
	f()
	g()

如果在f内调用g来修改,也还是不行:

# foo.py
# 代码8
a = 3  # 外部定义

def f():
	global a
	g()
	
def g():
	a += 1  # 还是报错 UnboundLocalError: local variable 'a' referenced before assignment
	print(a)
	
if __name__ == '__main__':
	f()

所以这里的局部可改是仅本函数内,本函数内部调用的其他函数仍不可改。

2 多个模块内的global

【Python】关于python里的global、__globals__、全局变量与多进程的一点理解_第1张图片
得到的结果是 3
接下来把左边程序的a = 3 拿走,运行
【Python】关于python里的global、__globals__、全局变量与多进程的一点理解_第2张图片
结果是报错:a未定义。
所以我们能看到两个模块的global是不互通的,读的层面就不互通,也不会这个模块找不到就到别的模块找,更别说写。所以我说’全局可读‘的’全局‘表自个儿的模块内可读。
可以通过内置函数__globals__来看一下这个为什么不互通

3 __globals__

这个函数看到全局(本模块)可读的量!不一定可写,可写是要看区域的。

# foo.py
# 代码9
a = 3

def f():
	global a
	a += 1

if __name__ == '__main__':
	# f() # 没运行
	print(f.__globals__.keys())
	print(f.__globals__['a'])

输出结果是:

dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'dis', 'a', 'f'])
3

输出结果里有a,因为f()没运行,所以看到的是外部的a = 3

# foo.py
# 代码10
a = 3

def f():
	global a
	a += 1

if __name__ == '__main__':
	f()
	print(f.__globals__.keys())
	print(f.__globals__['a'])

输出结果是:

dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'dis', 'a', 'f'])
4

输出的结果有a,是运行了f函数之后,a是4了。至于这个为什么是f.__globals__这个无所谓,如果还有个g函数,写g.__globals__得到的结果是一样的,因为打印的都是全局可读的量。
两个模块里的global的(读写)不通的,可以用查看两个模块的__globals__(这个函数的结果只表示可读的量)来看:
【Python】关于python里的global、__globals__、全局变量与多进程的一点理解_第3张图片
输出的结果表明f只能打印f里的__globals__的a,而右边函数也有自己的__globals__的a,他俩是不互通的。所以也能解释2里面的问题。

4 多进程

其实这一连串的问题的开始就是我看了好几家的视频教学,这里有个问题感觉没有解释清楚,就是多进程下不可变变量的id没有变的问题。这里两个问题,一,进程间为啥全局变量不互通;二,既然不互通为啥指向同一个内存。
问题的一个描述:
【Python】关于python里的global、__globals__、全局变量与多进程的一点理解_第4张图片

4.1 进程间全局变量不互通的原因

多进程,会多一份资源,并且不互通,这里我老想不通为啥会这样,其实现在我知道了就和上面的两个模块的情况一样!两边拿的__globals__是不同的。所以互不影响,你改了我看不到我改了你看不到。所以多进程就相当于多模块,本来一个py一个模块,现在开了多进程表示多个模块在一个py里,运行py,进程管理器会看到两个.py。所以这里表面的纠结解开了。

4.2 不同进程里的a指向同一地址的原因

一个测试:
【Python】关于python里的global、__globals__、全局变量与多进程的一点理解_第5张图片
没动之前指向同一个id,是因为python对-5~256内的int数据以及所有str数据,都泡在一个池子里,都会指向同一个

In [1]: a = 4

In [2]: b = 4

In [3]: id(a)
Out[3]: 4389426416

In [4]: id(4)
Out[4]: 4389426416

In [5]: a = "asdfghjklzxcvbnm"

In [6]: b = "asdfghjklzxcvbnm"

In [7]: id(a)
Out[7]: 4427009360

In [8]: id(b)
Out[8]: 4427009360

这和那个global是两码事,别放一起想就好了,这是数据的存储机制,节约内存的,与使用上无关。

over

你可能感兴趣的:(【Python】关于python里的global、__globals__、全局变量与多进程的一点理解)