Python基础教程——条件、循环及其他语句
条件、循环及其他语句
再谈 print 和 import
打印多个参数
print可用于打印一个表达式,这个表达式要么是字符串,要么将自动转换字符串。
可同时打印多个表达式,条件是用逗号分隔它们:
>>> print('Age:',42)
Age: 42
如上,在参数之间插入了一个空格字符。
在需要合并文本和变量值,而又不想使用字符串格式设置功能时,这种行为很有帮助。
>>> name = 'Gumby'
>>> salutation = 'Mr.'
>>> greeting = 'Hello,'
>>> print(greeting, salutation, name)
Hello, Mr. Gumby
若字符串变量 greeting 不包含逗号,要在结果中添加可如下:
>>> print(greeting,',' , salutation, name)
Hello, , Mr. Gumby
上述执行,会在逗号前添加一个空格。下面的方案可解决:
>>> print(greeting +',' , salutation, name)
Hello,, Mr. Gumby
它将逗号和变量 greeting 相加。
可自定义分隔符:
>>> print("I","wish","to","register","a","complaint",sep="_")
I_wish_to_register_a_complaint
还可自定义结束字符串,以替换默认的换行符。
导入时重命名
从模块导入时,通常使用
import somemodule
或使用
from somemodule import somefunction
或
from somemodule import somefunction, anotherfunction, yetanotherfunction
或
from somemodule import *
仅当确定要导入模块中的一切时,采用使用最后一种方式。
如果有两个模块,都包含函数 open;
可使用第一种方式导入这两个模块,像下面这样调用函数:
module1.open(...)
module2.open(...)
还有一种办法:在语句末尾添加 as 子句并指定别名。
下面是一个导入整个模块并给它指定别名的例子:
>>> import math as foobar
>>> foobar.sqrt(4)
2.0
下面是一个带人特定函数并给它指定别名的例子:
>>> from math import sqrt as foobar
>>> foobar(4)
2.0
对于前面的函数 open,可像下面这样导入它们:
from module1 import open as open1
from module2 import open as open2
赋值魔法
序列解包
可同时(并行)给多个变量赋值:
>>> x,y,z = 1,2,3
>>> print(x,y,z)
1 2 3
使用这种方式还可以交换多个变量的值。
>>> x,y = y,x
>>> print(x,y,z)
2 1 3
这里执行的操作称为序列解包(或可迭代对象解包):将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中。
下面例子进行解释:
>>> values = 1,2,3
>>> values
(1, 2, 3)
>>> x,y,z = values
>>> x
1
这在使用返回元组(或其他序列或可迭代对象)的函数或方法时很有用。
要从字典中随便获取(或删除)一个键-值对,可使用方法 popitem,它随便获取一个键-值对并以元组的方式返回。
接下来,可直接将返回的元组解包到两个变量中。
>>> scoundrel = {'name':'Robin','girlfriend':'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'
这让函数能够返回被打包成元组的多个值,然后通过一条赋值语句轻松地访问这些值。
要解包的序列包含的元素个数必须与你在等号左边列出的目标个数相同,否则Python将引发异常。
>>> x,y,z = 1,2
Traceback (most recent call last):
File "<pyshell#555>", line 1, in <module>
x,y,z = 1,2
ValueError: not enough values to unpack (expected 3, got 2)
>>> x,y,z = 1,2,3,4
Traceback (most recent call last):
File "<pyshell#556>", line 1, in <module>
x,y,z = 1,2,3,4
ValueError: too many values to unpack (expected 3)
可使用星号运算符( * )来收集多余的值,这样无需确保值和变量的个数相同,如下:
>>> a,b, *rest = [1,2,3,4]
>>> rest
[3, 4]
还可将带星号的变量放在其他位置。
>>> name = "Albus Percival Wulfric Brian Dunbledore"
>>> first, *middle, last = name.split()
>>> middle
['Percival', 'Wulfric', 'Brian']
赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。
在变量和值的个数相同时亦如此。
>>> a, *b, c = "abc"
>>> a,b,c
('a', ['b'], 'c')
链式赋值
链式赋值是一种快捷方式,用于将多个变量关联到同一个值。
类似于并行赋值,但只涉及一个值:
x = y = somefunction()
等价于
y = somefunction()
x = y
不等价于
x = somefunction()
y = somefunction()
增强赋值
可以不编写代码 x = x + 1,而将右边表达式中的运算符(这里是 + )移到赋值运算符(=)的前面,从而写成 x += 1。
这称为增强赋值,适用于所有标准运算符,如 *、/、% 等。
>>> x = 2
>>> x += 1
>>> x *= 2
>>> x
6
增强赋值也可用于其他数据类型(只要使用的双目运算符可用于这些数据类型)。
>>> fnord = 'foo'
>>> fnord += 'bar'
>>> fnord *= 2
>>> fnord
'foobarfoobar'
通过使用增强赋值,可让代码更紧凑、更简洁,同时在很多情况下的可读性更强。
代码块:缩进的乐趣
代码块是一组语句,可在满足条件时执行(if 语句),可执行多次(循环),等等。
代码块是通过缩进代码(即在前面加空格)来创建的。
注意:
也可使用制表符俩缩进代码块。
Python将制表符解释为移到下一个制表位(相邻制表位相距8个空格),
但标准(也是更佳的)做法是只使用空格(而不使用制表符)来缩进,且每级缩进4个空格。
在同一个代码块中,各行代码的缩进量必须相同。
下面演示了如何缩进:
this is a line
this is another line:
this is another block
continuing the same block
the last line of this block
phew, there we escaped the inner block
很多语言中,都使用一个特殊的单词或字符(如 begin 或 { )来标识代码块的起始位置,并使用另一个特殊的单词或字符(如 end 或 } )来标识结束位置。
Python中国,使用冒号( : )指出接下来是一个代码块,并将该代码块中的每行代码都随机相同的程度。
发现缩进量与之前相同时,就知道当前代码块到此结束了。
条件和条件语句
目前为止,在编写的程序中,语句都是逐条执行的。
更进一步,让程序选择是否执行特定的语句块。
这正是布尔值的用武之地
真值也称为布尔值,是以在真值方面做出了巨大贡献的George Boole 命名的。
用作布尔表达式(如用作 if 语句中的条件)时,下面的值都将被解释器视为假:
False None 0 "" () [] {}
标准值False和None、各种类型(包括浮点数、复数等)的数值0、空序列(如空字符串、空元组和空列表)以及空映射(如空字典)都被视为假,而其他各种值都被视为真,包括特殊值True。
>>> True
True
>>> False
False
>>> True == 1
True
>>> False == 0
True
>>> True + False + 42
43
因此,如果看到一个返回1或0的表达式,就知道这实际上意味着True或False。
布尔值True和False属于类型bool,而 bool 与 list、str 和 tuple 一样,可用来转换其他的值。
>>> bool('I think, therefore I am')
True
>>> bool(42)
True
>>> bool('')
False
>>> bool(0)
False
任何值都可用作布尔值,因此不需要显式地进行转换。
有条件地执行和 if 语句
运行如下脚本:
name = input('What is your name?')
if name.endswith('Gumby'):
print('Hello, Mr.Gumby')
这是if语句,能够有条件地执行代码。
意味着,如果条件(if 和 冒号之间的表达式)为前面定义的真,就执行后续代码块(这里是一条print语句);如果条件为假,就不执行。
else 子句
可使用else子句增加一种选择(之所以叫子句是因为else不是独立的语句,而是if语句的一部分)。
name = input('What is your name?')
if name.endswith('Gumby'):
print('Hello, Mr.Gumby')
else:
print('Hello, stranger')
这里,如果没有执行第一个代码块(因为条件为假),将进入第二个代码块。
有一个与if语句很像的“亲戚”,它就是条件表达式——C语言中三目运算符的Python版本。
下面的表达式使用if和else确实其值:
status = "friend" if name.endswith("Gumby") else "stranger"
如果条件(紧跟在 if 后面)为真,表达式的结果为提供的第一个值(这里为“friend”),否则为第二个值(这里为“stranger”)。
elif 子句
要检查多个条件,可使用elif。
elif 是 else if 的缩写,由一个 if 子句和一个 else子句组合而成,也就是包含条件的else子句。
num = int(input('Enter a number:'))
if num > 0:
print('The number is positive')
elif num < 0:
else:
print('The number is zero')
代码块嵌套
可将if语句放在其他if语句块中,如下:
name = input('What is your name?')
if name.endswith('Gumby')
if name.startswith('Mr.')
print('Hello,Mrs.Gumby')
else:
print('Hello, Gumby')
else:
print('Hello, stranger')
在这里,如果名字以Gumby结尾,就同时检查名字开头,这是在第一个代码块中使用一条独立的if语句完成的。
还使用了 elif。最后一个分组(else子句)没有指定条件——如果没有选择其他分支,就选择最后一个分支。
若需要,这里的两个 else 子句都可省略。如果省略里面的else子句,将忽略并非以Mr. 或 Mrs. 打头的名字(假设名字为 Gumby)。
如果省略外面的 else 子句,将忽略陌生人。
更复杂的条件
1. 比较运算符
在条件表达式中,最基本的运算符可能是比较运算符,它们用于执行比较。
Python也支持链式比较:可同时使用多个比较运算符,如 0 < age < 100。
- 相等运算符
要确定两个对象是否相等,可使用比较运算符,用两个等号(==)表示。
>>> "foo"== "foo"
True
>>> "foo"=="bar"
False
一个等号时:
>>> "foo"="foo"
SyntaxError: can't assign to literal
一个等号是赋值运算符,用于修改值。
- is::相同运算符
这个运算符很有趣,其作用看似与 == 一样,但实际是哪个并非如此。
>>> x = y = [1,2,3]
>>> z = [1,2,3]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
is 检查两个对象是否相同(而不是相等)。
变量x和y指向同一个列表,而 z 指向另一个列表(其中包含的值以及这些值的排列顺序都与前一个列表相同)。
这两个列表虽然相等,但并非同一个对象。
示例:
>>> x = [1,2,3]
>>> y = [2,4]
>>> x is not y
True
>>> del x[2]
>>> y[1]=1
>>> y.reverse()
这个示例中,首先创建了两个不同的列表x和y。
稍微修改了两个列表,它们虽然相等,但依然是两个不同的列表。
>>> x == y
True
>>> x is y
False
显然,这两个列表相等但不相同。
== 用来检查两个对象是否相等,而 is 用来检查两个对象是否相同(是同一个对象)。
- in:成员资格运算符
运算符 in ,与其他比较运算符一样,它也可用于条件表达式中。
name = input('What is your name?')
if 's' in name:
print('Your name contains the letter "s".')
else:
print('Your name does not contain the letter "s".')
- 字符串和序列的比较
字符串是根据字符的字母排列顺序进行比较的。
>>> "alpha" < "beta"
True
虽然基于的是字母排列顺序,但字母都是 Unicode 字符,它们是按码点排列的。
字符是根据顺序排列的。要获悉字母的顺序值,可使用函数 ord 。函数的作用与函数 chr 相反
>>> ord ("→")
8594
>>> ord ("←")
8592
>>> chr(8593)
'↑'
这种方法,可能与排序的方式相反。
涉及大写字母时,排列顺序就可能与想要的不同。
>>> "a" < "B"
False
一个诀窍是忽略大小写。
可使用字符串方法 lower,如下:
>>> "a".lower() < "B".lower()
True
>>> 'FnOrD'.lower() == 'Fnord'.lower()
True
其他序列的比较方式与此相同,但这些序列包含的元素可能不是字符,而是其他类型的值。
>>> [1,2] < [2,1]
True
如果序列的元素为其他序列,将根据通用的规则对这些元素进行比较。
>>> [2,[1,4]] < [2,[1,5]]
True
2. 布尔运算符
很多返回真值的表达式,可能需要检查多个条件。
例如,假设要编写一个程序,让他读取一个数,并检查这个数是否位于1~10。 可如下:
number = int(input('Enter a number between 1 and 10: '))
if number <= 10:
if number >=1:
print('Great!')
else:
print('Wrong!')
else:
print('Wrong!')
上述内容输入了 print(‘Wrong!’)两次。
重复劳动可不是好事。
如下方式解决:
number = int(input('Enter a number between 1 and 10: '))
if number <= 10 and number >=1:
print('Great!')
else:
print('Wrong!')
运算符 and 是一个布尔运算符。
它接受两个真值,并在这两个值都为真时返回真,否则返回假。
还有布尔运算符: or 和 not。
通过使用这三个运算符,能以任何方式组合真值。
if ((cash > price) or customer_has_good_credit) and not_of_stock:
give_goods()
布尔运算符有个有趣的特征:只做必要的计算。
短路逻辑(或者延迟求值):布尔运算符常被称为逻辑运算符,在有些情况下降“绕过”第二个值。
断言
if 语句有一个和有用的“亲戚”,其工作原理类似如下:
if not condition:
crash program
让程序在错误条件出现时立即崩溃胜过以后再崩溃。
可要求某些条件得到满足,为此可在语句中使用关键字assert。
>>> age = 10
>>> assert 0 < age < 100
>>> age = -1
>>> assert 0 <age < 100
Traceback (most recent call last):
File "<pyshell#614>", line 1, in <module>
assert 0 <age < 100
AssertionError
当必须满足特定条件,程序才能正确地原先,可在程序中添加assert语句充当检查点。
还可在条件后面添加一个字符串,对断言做出说明。
>>> age = -1
>>> assert 0 < age <100, 'The age must be realistic'
Traceback (most recent call last):
File "<pyshell#616>", line 1, in <module>
assert 0 < age <100, 'The age must be realistic'
AssertionError: The age must be realistic
循环
例,如下伪代码
send mail
with one month send mail
with one month send mail
with one month
(... and so on)
如果希望程序这样不断执行下去,直到人为停止,可编写类似如下的代码(伪代码):
while we aren't stopped:
send mail
wait one month
假设要打印1~100的所有数。可采用笨办法:
print(1)
print(2)
print(3)
...
print(99)
print(100)
while 循环
为避免繁琐的代码,可如下这样做:
x = 1
while x<=100:
print(x)
x += 1
还可使用循环来确保用户输入名字,如下:
name = ''
while not name:
name = input('Please enter your name: ')
print('Hello, {}!'.format(name))
while 语句非常灵活,可用于在条件为真时反复执行代码块。
for 循环
为序列(或其他可迭代对象)中每个元素指向代码块。
可迭代对象是可使用for循环进行遍历的对象。
为此,可使用for语句:
words = ['this','is','an','ex','parrot']
for word in words:
print(word)
或
numbers = [0,1,2,3,4,5,6,7,8,9]
for number in numbers:
print(number)
鉴于迭代(也就是遍历)特定范围内的数是一种常见的任务,Python提供了一个创建范围的内置函数。
>>> range(0,10)
range(0, 10)
>>> list(range(0,10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
范围类似于切片。
它们包含起始位置(这里为0),但不包含结束位置(这里为10)。
如果值提供了一个位置时,将把这个位置视为结束位置,并假定起始位置为0。
>>> range(10)
range(0, 10)
下面打印数1~100:
for number in range(1,101):
print(number)
提示: 只有能够使用for循环,就不要使用while循环。
迭代字典
要遍历字典的所有关键字,可使用普通的for语句。
d = {'x':1, 'y':2, 'z':3}
for key in d:
print(key, 'corresponds to', d[key])
也可使用keys等字典方法来获取所有的键。
如果只对值感兴趣,可使用 d.values。
d.items 以元组的方式返回键-值对。
for循环的优点之一是,可在其中使用序列解包。
for key, value in d.items():
print(key, 'corresponds to', value)
注意,字典元素的排列顺序是不确定的。
一些迭代工具
1. 并行迭代
同时迭代两个序列。
假设如下两个列表:
name = ['anne','beth','george','damon']
ages = [12, 45, 32, 102]
打印名字和对应的年龄,如下:
for i in range(len(names)):
print(name[i],'is',ages[i],'years old')
i 是用作循环索引的变量的标准名称。
并行迭代工具是内置函数zip,它将两个序列“缝合”起来,并返回一个由元组组成的序列。
返回值是一个适合迭代的对象,要查看其内容,可使用list将其转换为列表。
>>> list(zip(names,ages))
[('Alice', 12), ('Beth', 45), ('Cecil', 32), ('Dee-Dee', 102)]
“缝合”后,可在循环中将元组解包。
for name,age in zip(names,ages):
print(name,'is',age,'years old')
函数zip可用于“缝合”任意数量的序列。
当序列的长度不同时,函数zip将在最短的序列用完后停止“缝合”。
>>> list(zip(range(5), range(100000000)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
2. 迭代时获取索引
在迭代对象序列的同时获取当前对象的索引。
例如,替换一个字符串列表中所有包含子串 ‘xxx’的字符串。
可如下:
for string in strings:
if 'xxx' in string:
index = strings.index(string) # 在字符串列表中查找字符串
strings[index] = '[censored]'
index += 1
替换前搜索是不必要的。
如果没有替换,搜索返回的索引可能不对(即返回的是该字符串首次出现处的索引)。
另一个更佳的解决方案:
index = 0
for string in strings:
if 'xxx' in string:
strings[index] = '[censored]'
index +=1
另一种解决方案是使用内置函数 enumerate。
for index, string in enumerate(srings):
if 'xxx' in string:
strings[index] = '[censored]'
这个函数让你能够迭代索引-值对,其中的索引是自动提供的。
3. 反向迭代和排序后再迭代
函数:reversed 和 sorted。
类似于列表方法 reverse 和 sort(sorted 接受的参数也与sort 类似),但可用于任何序列或可迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。
>>> sorted([4,3,6,8,3])
[3, 3, 4, 6, 8]
>>> sorted('Hello,world!')
['!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello,world!'))
['!', 'd', 'l', 'r', 'o', 'w', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello,world!'))
'!dlrow,olleH'
注意,sorted返回一个列表,而reversed像zip那样返回一个可迭代对象。
不能对它执行索引或切片操作,也不能直接对它调用列表的方法。
要执行这些操作,可先使用list对返回的对象进行转换。
跳出循环
循环会不断地执行代码块,直到条件为假或使用完序列中的所有元素。
想要中断循环、开始新迭代或直接结束循环。
1. break
要结束(跳出)循环,可使用 break。
如下例:要找出小于100的最大平方值,可从100开始向下迭代,找到一个平方值后,无需再迭代,因此直接跳出循环。
from math import sqrt
for n in range(99, 0, -1):
root = sqrt(n)
if root == int(root):
print(n)
break
range 中的第让它三个参数是——步长,即序列中相邻数的差。
通过将步长设置为负数,可让range向下迭代。
2. continue
语句continue没有break用得多。
它结束当前迭代,并调到下一次迭代开头。
意味着跳过循环体中余下的语句,但不结束循环。
这在循环体庞大而赋值,且存在多个要跳过它的原因时很有用。
这种情况,可使用 continue,如下:
for x in seq:
if condition1: continue
if condition2: continue
if condition3: continue
do_something()
do_something_else()
do_another_thing()
etc()
在很多情况下,使用一条 if 语句就可以了。
for x in seq:
if not (condition1 or condition2 or condition3)
do_something()
do_something_else()
do_another_thing()
etc()
必须熟悉break语句,因为在 while True 循环中经常用到它。
3. while True/break成例
例如:
假设要在用户根据提示输入单词时执行某种操作,并在用户没有提供单词时结束循环。
一种办法如下:
word = 'dummy'
while word:
word = input('Please enter a word: ')
# 使用这个单词做些事情:
print('The word was', word)
这些代码的运行情况如下:
Please enter a word: first
The word was first
Please enter a word: second
The word was second
Please enter a word:
为进入循环,需要将一个哑值(未用的值)赋给 word。
这样的哑值通常意味着你的做法不太对。
尝试消除这个哑值:
word = input('Please enter a word: ')
while word:
# 使用这个单词做些事情:
print('The word was', word)
word = input('Please enter a word: ')
哑值消除了,但包含重复的代码。
避免重复,可使用成例 while True / break。
while True:
word = input('Please enter a word: ')
if not word: break
# 使用这个单词做些事情:
print('The word was ', word)
while True导致循环永不结束,但将提偶家放在了循环体内的一条 if 语句中,而这条 if 语句将在条件满足时调用 break 。
if / break 行将整个循环分成两部分:
第一部分负责设置(如果使用常规 while 循环,将重复这部分),第二部分在循环条件为真时使用第一部分初始化的数据。
循环中的 else 子句
可在循环开始前定义一个布尔变量并将其设置为False,再在跳出循环时将其设置为 True。
这样就可在循环后面使用一条 if 语句来判断循环是否是提前结束的。
break_out = False
for x in seq:
do_something(x)
if condition(x):
broke_out = True
break
do_something_else(x)
if not broke_out:
print("I didn't break out!")
一种简单的办法是在循环中添加一条 else 子句,它仅在没有调用 break 时才执行。
示例:
from math import sqrt
for n in range(99, 81, -1):
root = sqrt(n)
if root == int(root):
print(n)
break
else:
print("Didn't find it!")
简单推导
列表推导是一种从其他列表创建列表的方式,类似于数学中的 集合推导 。
列表推导的工作原理非常简单,有点类似于 for 循环。
>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
在列表推导中添加一条 if 语句。
>>> [x*x for x in range(10) if x % 3 == 0]
[0, 9, 36, 81]
可添加更多的for部分。
>>> [(x,y) for x in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
作为对比,下面的两个for循环创建同样的列表:
result = []
for x in range(3):
for y in range(3)
result.append((x, y))
使用多个 for 部分时,也可添加 if 子句。
>>> girls = ['alice','bernice','clarice']
>>> boys = ['chris','arnold','bob']
>>> [b+'+'+g for b in boys for g in girls if b[0] == g[0]]
['chris+clarice', 'arnold+alice', 'bob+bernice']
使用圆括号代替方括号并不能实现元组推导,而是将创建生成器。
可使用花括号来执行字典推导。
>>> squares = {i:"{} squared is {}".format(i, i**2) for i in range(10)}
>>> squares[8]
'8 squared is 64'
在列表推导中,for前面只有一个表达式,而在字典推导中, for 前面有两个用冒号分隔的表达式。
这两个表达式分别为键及其对应的值。
三人行
语句: pass 、 del 和 exec 。
什么都不做
有时候什么都不用做。
可使用pass语句。
>>> pass
>>>
这里什么都没有发生。
一条什么都不做的语句,可将其用作占位符。
如下:
if name == 'Ralph Auldus Melish'
print('Welcome!')
elif name == 'Enid':
# 还未完成......
elif name == 'Bill Gates':
print('Access Denied')
这些代码不能运行,因为Python中代码块不能为空。
修复这个问题,需要在中间的代码块中添加一条pass语句。
if name == 'Ralph Auldus Melish'
print('Welcome!')
elif name == 'Enid':
# 还未完成......
pass
elif name == 'Bill Gates':
print('Access Denied')
使用 del 删除
对于不再使用的对象,Python通常会将其删除(因为没有任何变量或数据结构成员指向它)。
>>> scoundrel = {'age':42, 'first name':'Robin','last name':'of Locksley'}
>>> robin = scoundrel
>>> scoundrel
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> scoundrel = None
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = None
robin 和 scoundrel指向同一个字典,因此将None赋给scoundrel后,依然可以通过robin来访问这个字典。
将robin也设置为None之后,这个字典就漂浮在计算机内存中,没有任何名称与之相关联,再也无法获取或使用它了。
因此,Python解释器直接将其删除。这被称为垃圾收集。
也可将其他任何值(而不是None)赋给两个变量,这样字典也将消失。
另一种方法是使用 del 语句。
这不仅会删除到对象的引用,还会删除名称本身。
>>> x = 1
>>> del x
>>> x
Traceback (most recent call last):
File "<pyshell#654>", line 1, in <module>
x
NameError: name 'x' is not defined
有时不太好理解。
例如,在下面的示例中,x和y指向同一个列表:
>>> x = ["Hello","world"]
>>> y = x
>>> y[1] = "Python"
>>> x
['Hello', 'Python']
通过删除x并不能删除y。
>>> del x
>>> y
['Hello', 'Python']
x和y指向同一个列表,但删除x对y没有任何影响,因为只删除名称x,而没有删除列表本身(值)。
使用 exec 和 eval 执行字符串即计算器结果
exec 和 eval 现在都是函数,但exec以前是一种语句,而eval 与它紧密相关。
1. exec
函数exec将字符串作为代码执行。
>>> exec("print('Hello, world!')")
Hello, world!
调用函数 exec 时,大多数情况下,还应向它传递一个命名空间——用于放置变量的地方;否则代码将污染你的命名空间,即修改你的变量。
例如,假设代码使用了名称sqrt:
>>> from math import sqrt
>>> exec("sqrt = 1")
>>> sqrt(4)
Traceback (most recent call last):
File "<pyshell#664>", line 1, in <module>
sqrt(4)
TypeError: 'int' object is not callable
函数 exec 主要用于动态地创建代码字符串。
为安全起见,要提供一个字典以充当命名空间。
为此,添加第二个参数——字典,用作代码字符串的命名空间。
>>> from math import sqrt
>>> scope = {}
>>> exec('sqrt = 1', scope)
>>> sqrt(4)
2.0
>>> scope['sqrt']
1
注意,如果尝试将scope打印出来,将发现它包含很多内容,这是因为自动在其中添加了包含所有内置函数和值的字典_builtins_ 。
>>> len(scope)
2
>>> scope.keys()
dict_keys(['__builtins__', 'sqrt'])
2. eval
eval 是一个类似于 exec 的内置函数。
exec 执行一系列Python语句,而eval计算用字符串表示的Python表达式的值,并返回结果(exec 什么都不返回,因为它本身是条语句)。
可使用如下代码创建一个Python计算器:
>>> eval(input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 6+18*2
42
与 exec 一样,也可向 eval 提供一个命名空间。
浅谈作用域
向exec或eval提供命名空间时,可在使用这个命名空间前在其中添加一些值。
>>> scope = {}
>>> scope['x'] = 2
>>> scope['y'] = 3
>>> eval('x*y', scope)
6
同样,同一个命名空间可用于多次调用exec或eval。
>>> scope = {}
>>> exec('x=2', scope)
>>> eval('x*x', scope)
4
小结
本章介绍的新函数
学习参考资料:
《Python基础教程》 第3版