2024年6月16日 Python - 基础

基础语法

编码

默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码:

# -*- coding: cp-1252 -*-

标识符

  • 第一个字符必须是字母表中字母或下划线 _
  • 标识符的其他的部分由字母、数字和下划线组成
  • 标识符对大小写敏感

在 Python 3 中,可以用中文作为变量名,非 ASCII 标识符也是允许的

Python 保留字

保留字即关键字,我们不能把它们用作任何标识符名称。Python 的标准库提供了一个 keyword 模块,可以输出当前版本的所有关键字:

>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

注释

Python 中单行注释以 # 开头

多行注释可以用多个 # 号,还有 '''"""

#!/usr/bin/python3

# 第一个注释
# 第二个注释

'''
第三个注释
第四个注释
'''

"""
第五个注释
第六个注释
"""
print("Hello, Python!")

行与缩进

Python 最具特色的就是使用缩进来表示代码块,不使用大括号 {}

缩进的空格数是可变的,但是同一个代码块的语句必须包含相同的缩进空格数。

以下代码最后一行语句缩进数的空格数不一致,会导致运行错误:

if True:
    print("Answer")
    print("True")
else:
    print("Answer")
  print("False")  # 缩进不一致,会导致运行错误

报错信息:

IndentationError: unindent does not match any outer indentation level

多行语句

Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠 \ 来实现多行语句

[] , {} , 或 () 中的多行语句,不需要使用反斜杠 \

total = item_one + \
        item_two + \
        item_three

total = ['item_one', 'item_two', 'item_three',
         'item_four', 'item_five']

空行

函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,以突出函数入口的开始。

空行与代码缩进不同,空行并不是 Python 语法的一部分。书写时不插入空行,Python 解释器运行也不会出错。但是空行的作用在于分隔两段不同功能或含义的代码,便于日后代码的维护或重构。

记住:空行也是程序代码的一部分。

同一行使用多条语句

Python 可以在同一行中使用多条语句,语句之间使用分号 ; 分割

#!/usr/bin/python3

import sys; x = 'runoob'; sys.stdout.write(x + '\n')

使用交互式命令行执行,输出结果为:

>>> import sys; x = 'runoob'; sys.stdout.write(x + '\n')
runoob
7

此处的 7 表示字符数,runoob 有 6 个字符,\n 表示一个字符,加起来 7 个字符。

多个语句构成代码组

缩进相同的一组语句构成一个代码块,我们称之 代码组

ifwhiledefclass 这样的复合语句,首行以关键字开始,以冒号 : 结束,该行之后的一行或多行代码构成代码组

我们将首行及后面的代码组称为一个 子句(clause)

多个变量赋值

a = b = c = 1
a, b, c = 1, 2, "runoob"

Python3 解释器

Linux/Unix的系统上,一般默认的 python 版本为 2.x,我们可以将 python3.x 安装在 /usr/local/python3 目录中。

安装完成后,我们可以将路径 /usr/local/python3/bin 添加到您的 Linux/Unix 操作系统的环境变量中,这样您就可以通过 shell 终端输入下面的命令来启动 Python3

$ PATH=$PATH:/usr/local/python3/bin/python3    # 设置环境变量
$ python3 --version
Python 3.4.0

在 Window 系统下你可以通过以下命令来设置 Python 的环境变量,假设你的 Python 安装在 C:\Python34 下:

set path=%path%;C:\python34

交互式编程

Linux 下:

$ python3
Python 3.6.8 (default, Apr 19 2021, 17:20:37) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux

Windows 下:

> python
Python 3.10.1 (tags/v3.10.1:2cd268a, Dec  6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32

脚本式编程

通过以下命令执行该脚本:

$ python3 hello.py

在 Linux/Unix 系统中,你可以在脚本顶部添加以下命令让 Python 脚本可以像 SHELL 脚本一样可直接执行:

#! /usr/bin/env python3

然后修改脚本权限,使其有执行权限,命令如下:

$ chmod +x hello.py

执行以下命令:

./hello.py

运算符

Python 语言支持以下类型的运算符:

  • 算术运算符
  • 比较(关系)运算符
  • 赋值运算符
  • 逻辑运算符
  • 位运算符
  • 成员运算符
  • 身份运算符
  • 运算符优先级

算术运算符

以下假设变量 a=10 ,变量 b=21

运算符 描述 实例
+ 加 - 两个对象相加 a + b 输出结果 31
- 减 - 得到负数或是一个数减去另一个数 a - b 输出结果 -11
* 乘 - 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 210
/ 除 - x 除以 y b / a 输出结果 2.1
% 取模 - 返回除法的余数 b % a 输出结果 1
** 幂 - 返回x的y次幂 a**b 为 10 的21次方
// 取整除 - 向下取接近商的整数 >>> 9//2 4
>>> -9//2 -5
#!/usr/bin/python3

a = 21
b = 10
c = 0

print("a 的值位:", a)  # 21
print("b 的值位:", b)  # 10

c = a + b
print("a + b 的值为:", c)  # 31

c = a - b
print("a - b 的值为:", c)  # 11

c = a * b
print("a * b 的值为:", c)  # 210

c = a / b
print("a / b 的值为:", c)  # 2.1

c = a % b
print("a % b 的值为:", c)  # 1

# 修改变量 a 、b 、c
a = 2
b = 3
c = a ** b
print("a ** b 的值为:", c)  # 8

a = 10
b = 5
c = a // b
print("a // b 的值为:", c)  # 2

比较运算符

以下假设变量 a 为 10 ,变量 b 为 20 :

运算符 描述 实例
== 等于 - 比较对象是否相等 (a == b) 返回 False
!= 不等于 - 比较两个对象是否不相等 (a != b) 返回 True
> 大于 - 返回 x 是否大于 y (a > b) 返回 False
< 小于 - 返回 x 是否小于 y 。所有比较运算符返回 1 表示真,返回0表示假。这分别与特殊的变量 True 和 False 等价。注意,这些变量名的大写。 (a < b) 返回 True
>= 大于等于 - 返回 x 是否大于等于 y (a >= b) 返回 False
<= 小于等于 - 返回 x 是否小于等于 y (a <= b) 返回 True

赋值运算符

以下假设变量 a 为 10 ,变量 b 为 20 :

运算符 描述 实例
= 简单的赋值运算符 c = a + b 将 a + b 的运算结果赋值为 c
+= 加法赋值运算符 c += a 等效于 c = c + a
-= 减法赋值运算符 c -= a 等效于 c = c - a
*= 乘法赋值运算符 c *= a 等效于 c = c * a
/= 除法赋值运算符 c /= a 等效于 c = c / a
%= 取模赋值运算符 c %= a 等效于 c = c % a
**= 幂赋值运算符 c **= a 等效于 c = c ** a
//= 取整除赋值运算符 c //= a 等效于 c = c // a
:= 海象运算符,可在表达式内部为变量赋值。Python3.8 版本新增运算符 在这个示例中,赋值表达式可以避免调用 len() 两次:
if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)")

位运算符

按位运算符是把数字看作二进制来进行计算的。Python中的按位运算法则如下:

下表中变量 a 为 60,b 为 13二进制格式如下:

a = 0011 1100

b = 0000 1101

-----------------

a&b = 0000 1100

a|b = 0011 1101

a^b = 0011 0001

~a  = 1100 0011
运算符 描述 实例
& 按位与运算符:参与运算的两个值,如果两个相应位都为 1 ,则该位的结果为 1 ,否则为 0 (a & b) 输出结果 12 ,二进制解释: 0000 1100
| 按位或运算符:只要对应的二个二进位有一个为 1 时,结果位就为 1 (a | b) 输出结果 61 ,二进制解释: 0011 1101
^ 按位异或运算符:当两对应的二进位相异时,结果为 1 (a ^ b) 输出结果 49 ,二进制解释: 0011 0001
~ 按位取反运算符:对数据的每个二进制位取反,即把 1 变为 0 ,把 0 变为 1。~x 类似于 -x-1 (~a) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。
<< 左移动运算符:运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0 a << 2 输出结果 240 ,二进制解释: 1111 0000
>> 右移动运算符:把 >> 左边的运算数的各二进位全部右移若干位, >> 右边的数指定移动的位数 a >> 2 输出结果 15 ,二进制解释: 0000 1111

逻辑运算符

Python 语言支持逻辑运算符,以下假设变量 a 为 10 ,b为 20 :

运算符 逻辑表达式 描述 实例
and x and y 布尔"与" - 如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。 (a and b) 返回 20
or x or y 布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 (a or b) 返回 10
not not x 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True not(a and b) 返回 False

成员运算符

除了以上的一些运算符之外,Python 还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。

运算符 描述 实例
in 如果在指定的序列中找到值返回 True ,否则返回 False x 在 y 序列中 , 如果 x 在 y 序列中返回 True
not in 如果在指定的序列中没有找到值返回 True ,否则返回 False x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True
#!/usr/bin/python3

a = 10
b = 20
list = [1, 2, 3, 4, 5]

if (a in list):
    print("1 - 变量 a 在给定的列表中 list 中")
else:
    print("1 - 变量 a 不在给定的列表中 list 中")   # true

if (b not in list):
    print("2 - 变量 b 不在给定的列表中 list 中")   # true
else:
    print("2 - 变量 b 在给定的列表中 list 中")

# 修改变量 a 的值
a = 2
if (a in list):
    print("3 - 变量 a 在给定的列表中 list 中")   # true
else:
    print("3 - 变量 a 不在给定的列表中 list 中")

身份运算符

身份运算符用于比较两个对象的存储单元

运算符 描述 实例
is is 是判断两个标识符是不是引用自一个对象 x is y , 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False
is not is not 是判断两个标识符是不是引用自不同对象 x is not y , 类似 id(x) != id(y)。如果引用的不是同一个对象则返回结果 True,否则返回 False

id() 函数用于获取对象内存地址

#!/usr/bin/python3

a = 20
b = 20

if (a is b):
    print("1 - a 和 b 有相同的标识")   # true
else:
    print("1 - a 和 b 没有相同的标识")

if (id(a) == id(b)):
    print("2 - a 和 b 有相同的标识")   # true
else:
    print("2 - a 和 b 没有相同的标识")

# 修改变量 b 的值
b = 30
if (a is b):
    print("3 - a 和 b 有相同的标识")
else:
    print("3 - a 和 b 没有相同的标识")   # true

if (a is not b):
    print("4 - a 和 b 没有相同的标识")   # true
else:
    print("4 - a 和 b 有相同的标识")

is== 区别:is 用于判断两个变量引用对象是否为同一个, == 用于判断引用变量的值是否相等

运算符优先级

以下表格列出了从最高到最低优先级的所有运算符, 相同单元格内的运算符具有相同优先级。

运算符 描述
(expressions...) , [expressions...] , {key: value...} , {expressions...} 圆括号的表达式
x[index] , x[index:index] , x(arguments...) , x.attribute 读取,切片,调用,属性引用
await x await 表达式
** 乘方(指数)
+x , -x , ~x 正,负,按位非 NOT
* , @ , / , // , % 乘,矩阵乘,除,整除,取余
+ , - 加和减
<< , >> 移位
& 按位与 AND
^ 按位异或 XOR
| 按位或 OR
in , not in , is , is not , < , <= , > , >= , != , == 比较运算,包括成员检测和标识号检测
not x 逻辑非 NOT
and 逻辑与 AND
or 逻辑或 OR
if -- else 条件表达式
lambda lambda 表达式
:= 赋值表达式

Pyhton3 已不支持 <> 运算符,可以使用 != 代替

条件控制

if 语句

Python 中用 elif 代替了 else if ,所以 if 语句的关键字为:if – elif – else

注意:

  1. 每个条件后面要使用冒号 : ,表示接下来是满足条件后要执行的语句块
  2. 使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块

简单的 if 实例:

#!/usr/bin/python3

var1 = 100
if var1:    # true
    print("1 - if 表达式条件为 true")
    print(var1)

var2 = 0
if var2:    # false
    print("2 - if 表达式条件为 true")
    print(var2)
print("Good bye!")

狗的年龄计算判断:

#!/usr/bin/python3

age = int(input("请输入你家狗狗的年龄: "))
print("")
if age <= 0:
    print("你是在逗我吧!")
elif age == 1:
    print("相当于 14 岁的人。")
elif age == 2:
    print("相当于 22 岁的人。")
elif age > 2:
    human = 22 + (age - 2) * 5
    print("对应人类年龄: ", human)

### 退出提示
input("点击 enter 键退出")

数字的比较运算:

#!/usr/bin/python3

# 该实例演示了数字猜谜游戏
number = 7
guess = -1
print("数字猜谜游戏!")
while guess != number:
    guess = int(input("请输入你猜的数字:"))

    if guess == number:
        print("恭喜,你猜对了!")
    elif guess < number:
        print("猜的数字小了...")
    elif guess > number:
        print("猜的数字大了...")

if 嵌套

# !/usr/bin/python3

num = int(input("输入一个数字:"))
if num % 2 == 0:
    if num % 3 == 0:
        print("你输入的数字可以整除 2 和 3")
    else:
        print("你输入的数字可以整除 2,但不能整除 3")
else:
    if num % 3 == 0:
        print("你输入的数字可以整除 3,但不能整除 2")
    else:
        print("你输入的数字不能整除 2 和 3")

match

Python中的match语句是Python 3.10及以后版本中引入的新特性,用于模式匹配

它允许你根据对象的模式来检查对象,并执行相应的代码块。

x = 2
match x:
    case 1:
        print(1)
    case 2:
        print(2)
    case _:
        print('other')

循环语句

Python 中的循环语句有 forwhile

while 循环

同样需要注意冒号和缩进。另外,在 Python 中没有 do..while 循环。

无限循环

#!/usr/bin/python3

var = 1
while var == 1:  # 表达式永远为 true
    num = int(input("输入一个数字  :"))
    print("你输入的数字是: ", num)

print("Good bye!")

无限循环在服务器上客户端的实时请求非常有用。

简单语句组

类似 if 语句的语法,如果你的 while 循环体中只有一条语句,你可以将该语句与 while 写在同一行中

#!/usr/bin/python

flag = 1

while (flag): print('欢迎访问菜鸟教程!')

print("Good bye!")

while-else

else的下级代码:
没有通过 break 退出循环,循环结束后,会执行的代码

num = 1
sum = 5
while num < 5:
    sum += num
    if sum == 10:
        break
    num += 1
else:
    print('没有执行break语句')

for 语句

for 循环可以遍历任何可迭代对象,如一个列表或者一个字符串

break 语句用于跳出当前循环体

#!/usr/bin/python3

sites = ["Baidu", "Google", "Runoob", "Taobao"]
for site in sites:
    if site == "Runoob":
        print("菜鸟教程!")
        break
    print("循环数据 " + site)
else:
    print("没有循环数据!")
print("完成循环!")

for-else

同 while-else

for i in range(5):
    if i == 3:
        print("Found 3, breaking the loop")
        # break
    print(i)
else:
    print("Loop completed without breaking")

range() 函数

如果你需要遍历数字序列,可以使用内置 range() 函数。它会生成数列

# 指定上限,下限默认 0
for i in range(5):  # [0,4]
    print(i)

# 指定上下限
for i in range(5, 9):  # [5,8]
    print(i)
    
# 指定步长    
for i in range(0, 10, 3):  # {0,3,6,9}
    print(i)
  
# 负数
for i in range(-10, -100, -30):  # {-10,-40,-70}
    print(i)

结合 range()len() 函数以遍历一个序列的索引

a = ['Google', 'Baidu', 'Runoob', 'Taobao', 'QQ']
for i in range(len(a)):
    print(i, a[i])

输出结果:

0 Google
1 Baidu
2 Runoob
3 Taobao
4 QQ

使用 range() 函数来创建一个列表

print(list(range(5)))   # [0, 1, 2, 3, 4]

break 和 continue 语句及循环中的 else 子句

break 语句可以跳出 for 和 while 的循环体。如果你从 for 或 while 循环中终止,任何对应的循环 else 块将不执行。

continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。

用于查询质数的循环例子:

#!/usr/bin/python3

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, '等于', x, '*', n // x)
            break
    else:
        # 循环中没有找到元素
        print(n, ' 是质数')

pass 语句

pass 是空语句,是为了保持程序结构的完整性。

pass 不做任何事情,一般用做占位语句

以下实例在字母为 o 时,执行 pass 语句块:

#!/usr/bin/python3

for letter in 'Runoob':
    if letter == 'o':
        pass
        print('执行 pass 块')
    print('当前字母 :', letter)

print("Good bye!")

输出结果为:

当前字母 : R
当前字母 : u
当前字母 : n
执行 pass 块
当前字母 : o
执行 pass 块
当前字母 : o
当前字母 : b
Good bye!

输入和输出

print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end=""

#!/usr/bin/python3

x = "a"
y = "b"
# 换行输出
print(x)
print(y)

print('---------')
# 不换行输出
print(x, end=" ")
print(y, end=" ")
print()

执行结果为:

a
b
---------
a b

输出格式美化

Python 两种输出值的方式: 表达式语句和 print() 函数。

第三种方式是使用文件对象的 write() 方法,标准输出文件可以用 sys.stdout 引用。

如果你希望输出的形式更加多样,可以使用 str.format() 函数来格式化输出值。

如果你希望将输出的值转成字符串,可以使用 repr()str() 函数来实现。

  • str() : 函数返回一个用户易读的表达形式
  • repr() : 产生一个解释器易读的表达形式
s = 'Hello, Runoob'
print(str(s))   # Hello, Runoob

print(repr(s))  # 'Hello, Runoob'

print(str(1 / 7))   # 0.14285714285714285

x = 10 * 3.25
y = 200 * 200
s = 'x 的值为: ' + repr(x) + ',  y 的值为:' + repr(y) + '...'
print(s)    # x 的值为: 32.5,  y 的值为:40000...

#  repr() 函数可以转义字符串中的特殊字符
hello = 'hello, runoob\n'
hellos = repr(hello)
print(hellos)   # 'hello, runoob\n'

# repr() 的参数可以是 Python 的任何对象
print(repr((x, y, ('Google', 'Runoob'))))   # (32.5, 40000, ('Google', 'Runoob'))

两种方式输出一个平方与立方的表

for x in range(1, 11):
    print(repr(x).rjust(2), repr(x * x).rjust(3), end=' ')
    # 注意前一行 'end' 的使用
    print(repr(x * x * x).rjust(4))

print("==============================")

for x in range(1, 11):
    print('{0:2d} {1:3d} {2:4d}'.format(x, x * x, x * x * x))

字符串对象的 rjust() 方法, 它可以将字符串靠右, 并在左边填充空格。

还有类似的方法, 如 ljust()center() 。 这些方法并不会写任何东西, 它们仅仅返回新的字符串。

另一个方法 zfill() , 它会在数字的左边填充 0 ,如下所示:

z = '12'.zfill(5)
print(z)    # 00012

z = '-3.14'.zfill(7)
print(z)    # -003.14

z = '3.14159265359'.zfill(5)
print(z)    # 3.14159265359

str.format() 的基本使用如下:

print('{}网址: "{}!"'.format('菜鸟教程', 'www.runoob.com'))   # 菜鸟教程网址: "www.runoob.com!"

在括号中的数字用于指向传入对象在 format() 中的位置,如下所示:

print('{0} 和 {1}'.format('Google', 'Runoob'))   # Google 和 Runoob

print('{1} 和 {0}'.format('Google', 'Runoob'))   # Runoob 和 Google

如果在 format() 中使用了关键字参数, 那么它们的值会指向使用该名字的参数。

print('{name}网址: {site}'.format(name='菜鸟教程', site='www.runoob.com'))
菜鸟教程网址: www.runoob.com

位置及关键字参数可以任意的结合:

>>> print('站点列表 {0}, {1}, 和 {other}。'.format('Google', 'Runoob', other='Taobao'))
站点列表 Google, Runoob, 和 Taobao。

!a (使用 ascii() ), !s (使用 str() ) 和 !r (使用 repr() ) 可以用于在格式化某个值之前对其进行转化

可选项 : 和格式标识符可以跟着字段名。 这就允许对值进行更好的格式化

: 后传入一个整数,可以保证该域至少有这么多的宽度。 用于美化表格时很有用。

如果你有一个很长的格式化字符串,而你不想将它们分开, 那么在格式化时通过变量名而非位置会是很好的事情。

最简单的就是传入一个字典, 然后使用方括号 [] 来访问键值

也可以通过在 table 变量前使用 ** 来实现相同的功能:

import math

print('常量 PI 的值近似为: {}。'.format(math.pi))   # 常量 PI 的值近似为: 3.141592653589793。

# !r 使用 repr()
print('常量 PI 的值近似为: {!r}。'.format(math.pi))     # 常量 PI 的值近似为: 3.141592653589793。

# 可选项 : 和格式标识符可以跟着字段名
print('常量 PI 的值近似为 {0:.3f}。'.format(math.pi))   # 常量 PI 的值近似为 3.142。

# 在 : 后传入一个整数, 可以保证该域至少有这么多的宽度
table = {'Google': 1, 'Runoob': 2, 'Taobao': 3}
for name, number in table.items():
    print('{0:10} ==> {1:10d}'.format(name, number))
"""
Google     ==>          1
Runoob     ==>          2
Taobao     ==>          3
"""

# 传入一个字典, 然后使用方括号 [] 来访问键值
table = {'Google': 1, 'Runoob': 2, 'Taobao': 3}
print('Runoob: {0[Runoob]:d}; Google: {0[Google]:d}; Taobao: {0[Taobao]:d}'.format(table))      # Runoob: 2; Google: 1; Taobao: 3

# 也可以通过在 table 变量前使用 ** 来实现相同的功能
table = {'Google': 1, 'Runoob': 2, 'Taobao': 3}
print('Runoob: {Runoob:d}; Google: {Google:d}; Taobao: {Taobao:d}'.format(**table))     # Runoob: 2; Google: 1; Taobao: 3

老版本的字符串格式化

% 操作符也可以实现字符串格式化。 它将左边的参数作为类似 sprintf() 式的格式化字符串,而将右边的代入,然后返回格式化后的字符串

import math

print('常量 PI 的值近似为:%5.3f。' % math.pi)   # 常量 PI 的值近似为:3.142。

因为 str.format() 是比较新的函数, 大多数的 Python 代码仍然使用 % 操作符。但是因为这种旧式的格式化最终会从该语言中移除, 应该更多的使用 str.format()

读取键盘输入

Python 提供了 input() 内置函数 从标准输入读入一行文本,默认的标准输入是键盘。

#!/usr/bin/python3

str = input("请输入:")
print("你输入的内容是: ", str)

命名空间和作用域

命名空间

命名空间(Namespace) 是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

一般有三种命名空间:

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

img

命名空间查找顺序:

假设我们要使用变量 runoob ,则 Python 的查找顺序为:局部的命名空间 -> 全局命名空间 -> 内置命名空间

如果找不到变量 runoob ,它将放弃查找并引发一个 NameError 异常

命名空间的生命周期:

命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

因此,我们无法从外部命名空间访问内部命名空间的对象。

# var1 是全局名称
var1 = 5


def some_func():
    # var2 是局部名称
    var2 = 6

    def some_inner_func():
        # var3 是内嵌的局部名称
        var3 = 7

作用域

作用域就是一个 Python 程序可以直接访问命名空间的正文区域。

在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种,分别是:

有四种作用域:

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
  • E(Enclosing):包含了非局部(non-local) 也非全局(non-global) 的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal
  • G(Global):当前脚本的最外层,比如当前模块的全局变量。
  • B(Built-in): 包含了内建的变量/关键字等,最后被搜索。

规则顺序: L –> E –> G –> B

在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

img

g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    def inner():
        i_count = 2  # 局部作用域

内置作用域是通过一个名为 builtin 的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。在 Python3.0 中,可以使用以下的代码来查看到底预定义了哪些变量:

>>> import builtins
>>> dir(builtins)

Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问

>>> if True:
...  msg = 'I am from Runoob'
... 
>>> msg
'I am from Runoob'

实例中 msg 变量定义在 if 语句块中,但外部还是可以访问的。

如果将 msg 定义在函数中,则它就是局部变量,外部不能访问

全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中

#!/usr/bin/python3

total = 0  # 这是一个全局变量


# 可写函数说明
def sum(arg1, arg2):
    # 返回2个参数的和."
    total = arg1 + arg2  # total在这里是局部变量.
    print("函数内是局部变量 : ", total)  # 30
    return total


# 调用sum函数
sum(10, 20)
print("函数外是全局变量 : ", total)  # 0

global 和 nonlocal 关键字

当内部作用域想修改外部作用域的变量时,就要用到 globalnonlocal 关键字了。

#!/usr/bin/python3
 
num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) 
    num = 123
    print(num)
fun1()
print(num)

输出结果:

1
123
123

如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了

#!/usr/bin/python3
 
def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()

输出结果:

100
100

有一种特殊情况:

#!/usr/bin/python3

a = 10


def test():
    a = a + 1
    print(a)


test()

以上程序执行,报错信息如下:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 5, in test
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。

posted @ 2024-06-17 07:25  流星<。)#)))≦  阅读(12)  评论(0编辑  收藏  举报