一、Python 中的赋值
1.1 Python 中万物皆对象
1 | print(type(1)) # <class 'int'> |
1.2 Python 中赋值的意义
2.1 如果 = 号右边是对象, 则让 = 号左边的变量指向该对象, 成为对该对象的一个 引用
2.2 如果 = 号右边是变量, 则让 = 号左边的变量指向右边变量所指的对象, 成为那个对象的 引用
2.3 通过其它表达式而不是等号进行赋值的操作, 意义与上面相同
1 | # 定义一个变量 test_number, 该变量为对常量 1 的引用 |
二. Python 中变量的作用域
任何变量都有自己的作用域, 作用域有优先级。在 Python 中,作用域的优先级是这样的:
- 当前函数的作用域
- 外围的作用域
- 模块级别的作用域
- 内置函数的作用域
对处于高优先级作用域中的变量进行更改, 并不会更改到低优先级作用域中的变量。
1 | test_number = 1 |
猜猜这段代码的输出?
其实这段代码会报错:
1 | --------------------------------------------------------------------------- |
为什么呢?
从直觉上来看, print(test_number)
时, test_function
作用域内还没有对 test_number
的定义, 所以应该输出模块级别作用域中的 test_number
, 也就是 1. 后来 test_number = 2
只是在函数作用域内部修改了 test_number
, 并不影响外部。 所以结果应该是:
1 | 1 |
但其实上面的逻辑有错误。
test_number = 2
到底是对函数内部 test_number
的赋值还是定义? 如果是定义, 之前的
print(test_number)
中的 test_number
是什么意思?要知道当时它都还没有被定义。
而如果是赋值, 那它是什么时候定义的?
为了避免这种 delimma, Python 有这么一个规范, 在当前作用域内,每一个变量在第一次被赋值时
才被定义。
那这样的话, 在上面的例子中, test_number
在第 5 行时被定义, 导致作用域内有了该变量,第 4 行就不能再往外引用了。
而第 4 行时是无法得到 test_number
的引用的, 因为它到第 5 行才被定义。
删掉第 4 行或者第 5 行就会消除错误。
三、一些简单的应用
3.1 首先是这个经典的函数参数问题
1 | def test_function_locals(arg=[], arg2={}): |
其结果是什么想必大家都知道了。但我以前只知道不应该使用 mutable 作为参数, 却不明白原因。
原因是什么呢?
1 | print(dir(test_function_locals)) # [..., __call__, ...] |
函数是个 callable
对象,这两个参数都是 test_function_locals
这个函数对象的属性, 属于
这个函数对象内部的作用域。
所以可以把函数调用想象成调用某个 object 的 __call__
函数, 该 __call__
函数引用并修改了
该 object 的 instance attribute。
3.2 还是一个函数相关的问题
1 | lambdas = [] |
结果是什么大家也知道。原因呢?
在三次迭代中, lambdas 分别加入了指一个不同的函数对象, 而不是重复加入了同一个函数对象三次。
这三个函数对象虽然不同,但所使用的 i 都是对外部作用域的 i 的引用。
当这三个函数被分别调用时, i 的值已经是 2 了。
并且, 如果在调用这些函数之前 del i
, 则会报错:
1 | lambdas = [] |