11.md 6.2 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8
# Python 命名空间和范围

> 原文: [https://www.programiz.com/python-programming/namespace](https://www.programiz.com/python-programming/namespace)

#### 在本教程中,您将了解名称空间,从名称到对象的映射以及变量的范围。

## Python 中的名称是什么?

W
wizardforcel 已提交
9
如果您曾经读过“Python 之禅”(Python 解释器中的类型`import this`),则最后一行指出,**命名空间是一个很棒的主意-让我们做更多的事情!** 那么这些神秘的命名空间是什么? 让我们首先看看名字是什么。
W
wizardforcel 已提交
10 11 12

名称(也称为标识符)只是赋予对象的名称。 Python 中的所有内容都是[对象](https://www.programiz.com/python-programming/class)。 名称是访问基础对象的一种方式。

W
wizardforcel 已提交
13
例如,当我们执行分配`a = 2`时,`2`是存储在内存中的对象,而`a`是与之关联的名称。 我们可以通过[内置函数](https://www.programiz.com/python-programming/built-in-function) `id()`获得某个对象的地址(在 RAM 中)。 让我们看看如何使用它。
W
wizardforcel 已提交
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

```py
# Note: You may get different values for the id

a = 2
print('id(2) =', id(2))

print('id(a) =', id(a))
```

**输出**

```py
id(2) = 9302208
id(a) = 9302208
```

在这里,两者都引用相同的对象`2`,因此它们具有相同的`id()`。 让我们做些有趣的事情。

```py
# Note: You may get different values for the id

a = 2
print('id(a) =', id(a))

a = a+1
print('id(a) =', id(a))

print('id(3) =', id(3))

b = 2
print('id(b) =', id(b))
print('id(2) =', id(2))
```

W
wizardforcel 已提交
49
**输出**
W
wizardforcel 已提交
50 51 52 53 54 55 56 57 58 59 60

```py
id(a) = 9302208
id(a) = 9302240
id(3) = 9302240
id(b) = 9302208
id(2) = 9302208
```

上述步骤序列中发生了什么? 让我们用一个图来解释一下:

W
wizardforcel 已提交
61 62
![Memory diagram of a variable](img/8e3c21e63f8a64674e3edc5cbf22272f.png "Memory Diagram")

W
wizardforcel 已提交
63
Python 中变量的内存图
W
wizardforcel 已提交
64 65 66



W
wizardforcel 已提交
67
最初,创建一个对象`2`并将其与名称`b`关联,当我们执行`a = a+1`时,将创建一个新的对象`3`,而现在将`a`与这个对象关联起来。
W
wizardforcel 已提交
68 69 70

注意,`id(a)``id(3)`具有相同的值。

W
wizardforcel 已提交
71
此外,当执行`b = 2`时,新名称`b`与先前的对象`2`相关联。
W
wizardforcel 已提交
72 73 74 75 76 77 78 79 80

这是有效的,因为 Python 不必创建新的重复对象。 名称绑定的这种动态特性使 Python 功能强大。 名称可以引用任何类型的对象。

```py
>>> a = 5
>>> a = 'Hello World!'
>>> a = [1,2,3]
```

W
wizardforcel 已提交
81
所有这些都是有效的,`a`将引用不同实例中的三种不同类型的对象。 [函数](https://www.programiz.com/python-programming/function)也是对象,因此名称也可以引用它们。
W
wizardforcel 已提交
82 83 84 85 86 87 88 89 90 91

```py
def printHello():
    print("Hello")

a = printHello

a()
```

W
wizardforcel 已提交
92
**输出**
W
wizardforcel 已提交
93 94 95 96 97

```py
Hello
```

W
wizardforcel 已提交
98
`a`可以使用相同的名称,我们可以使用该名称来调用该功能。
W
wizardforcel 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

* * *

## Python 中的命名空间是什么?

现在我们了解了名称是什么,我们可以继续进行命名空间的概念。

简而言之,名称空间是名称的集合。

在 Python 中,您可以将名称空间想象为已定义的每个名称到对应对象的映射。

不同的名称空间可以在给定时间共存,但是完全隔离。

当我们启动 Python 解释器时,将创建一个包含所有内置名称的名称空间,并且只要该解释器运行,该名称空间就会存在。

这就是为什么内置`id()``print()`等功能始终可以从程序的任何部分使用的原因。 每个[模块](https://www.programiz.com/python-programming/modules)创建其自己的全局名称空间。

这些不同的名称空间是隔离的。 因此,不同模块中可能存在的相同名称不会冲突。

模块可以具有各种功能和类。 调用函数时会创建一个本地名称空间,该名称空间中定义了所有名称。 与类相似。 下图可能有助于阐明这一概念。

W
wizardforcel 已提交
120 121
![Nested Namespaces in Python Programming](img/ff842860b03567ecdbc3206f0d184539.png "Different Nested Namespaces")

W
wizardforcel 已提交
122
Python 中不同名称空间的图
W
wizardforcel 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135



* * *

## Python 变量范围

尽管定义了各种唯一的名称空间,但我们可能无法从程序的每个部分访问它们。 范围的概念开始起作用。

范围是程序的一部分,从那里可以直接访问名称空间而无需任何前缀。

在任何给定时刻,至少有三个嵌套作用域。

W
wizardforcel 已提交
136
1.  具有局部名称的当前函数的范围
W
wizardforcel 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
2.  具有全局名称的模块的范围
3.  具有内置名称的最外部作用域

在函数内部进行引用时,将在本地名称空间中搜索名称,然后在全局名称空间中搜索,最后在内置名称空间中搜索。

如果另一个函数内有一个函数,则新作用域嵌套在本地作用域内。

* * *

## Python 范围和命名空间的示例

```py
def outer_function():
    b = 20
    def inner_func():
        c = 30

a = 10
```

W
wizardforcel 已提交
157
在这里,变量`a`在全局命名空间中。 变量`b``outer_function()`的本地名称空间中,而`c``inner_function()`的嵌套本地名称空间中。
W
wizardforcel 已提交
158

W
wizardforcel 已提交
159
当我们在`inner_function()`中时,`c`对我们来说是本地的,`b`是非本地的,`a`是全局的。 我们可以读取`c`并为其分配新值,但只能从`inner_function()`读取`b``a`
W
wizardforcel 已提交
160

W
wizardforcel 已提交
161
如果我们尝试为`b`赋值,则会在本地名称空间中创建一个与非本地`b`不同的新变量`b`。 当我们为`a`分配一个值时,也会发生同样的事情。
W
wizardforcel 已提交
162

W
wizardforcel 已提交
163
但是,如果我们将`b`为全局变量,则所有引用和赋值都将转到全局变量。 同样,如果我们想重新绑定变量`b`,则必须将其声明为非本地变量。 以下示例将进一步阐明这一点。
W
wizardforcel 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

```py
def outer_function():
    a = 20

    def inner_function():
        a = 30
        print('a =', a)

    inner_function()
    print('a =', a)

a = 10
outer_function()
print('a =', a)
```

如您所见,该程序的输出为

```py
a = 30
a = 20
a = 10
```

W
wizardforcel 已提交
189
在此程序中,在单独的命名空间中定义了三个不同的变量`a`并进行了相应的访问。 在以下程序中,
W
wizardforcel 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

```py
def outer_function():
    global a
    a = 20

    def inner_function():
        global a
        a = 30
        print('a =', a)

    inner_function()
    print('a =', a)

a = 10
outer_function()
print('a =', a)
```

程序的输出是。

```py
a = 30
a = 30
a = 30 
```

W
wizardforcel 已提交
217
在这里,由于使用了关键字`global`,所有引用和分配都指向全局`a`