有一个数组a = [1,1,1,1]
,现在想创建另一个数组[2,1,1,1]
,很容易想到将a复制一份,然后把第一个值改成2就可以了:
a = [1,1,1,1]
b = a
b[0] = 2
print('a=',a)
print('b=',b)
结果出乎预料,我们没想改a,只想复制一份a给到b,但是a却被改变了。
在Python中要格外注意这种情况,Python的赋值是传址。这和其他的主流语言都不一样。比如说b=a
,是将b指向a的内存,而不是复制一份a的值给到b.
来看一种对传址的错误理解:
a = [1,1,1,1]
b = a
b = 2
print('a=',a)
print('b=',b)
既然是传址,b = a
后,a,b应该指向同一块内存,那么a,b应该都被修改为2才对啊?
Python的赋值不能用“修改”来理解,Python不会去修改原来的内存,所有的赋值都是改变指向的内存,比如将变量a改为2,并不是将a原来的内存修改掉,而是将2的地址赋给a:
a = 1
print(id(a))
a = 2
print(id(a))
我们看到两次赋值a的内存地址是不相同的,所以Python的赋值并不会去修改原来的内存,而是将变量指向另外的内存。
在函数传值的时候,Python也没有将内存复制一份,而是函数里的变量和函数外的变量指向同一块内存:
def test(arr):
arr[0] = 888
print(arr)
a = [1,1,1]
test(a)
print(a)
函数外的变量a与函数内的变量arr指向同一个内存,对arr的某一个节点进行赋值时,相当与仅更改了这个节点的内存指向,所以arr的内存指向没有变更,也就发生了函数内外变量同时变化的情况。
如果对arr整体进行赋值相当于更改了arr的内存指向,所以仅会函数内部的变量值更改。
def test(arr):
arr = [6,6,6]
print(arr)
a = [1,1,1]
test(a)
print(a)
可以使用format
函数来填充字符串中的{}
"There are an estimated {} people worldwide in {}".format("7.53 billion", 2019)
如果想在循环的时候返回计数,可以使用enumerate
函数
mylist = ['a','b','c']
for key, value in enumerate(mylist):
print("{} : {}".format(key, value))
循环字典类型的变量如果想返回键,可以使用items
函数
mydict = {'frist':'a','second':'b','third':'c'}
for key, value in mydict.items():
print("{} : {}".format(key, value))
其他语言的三元运算符都是a?b:c
的形式,但Python的三元运算符不是这样的,它是if else
的形式:
condition = False
1 if condition else 2
condition = True
1 if condition else 2
推导式是Python的一大特点,它可以快速生成列表:
[x**2 for x in range(10)]
推导式还可以和判断连用:
[x**2 for x in range(10) if x % 3 is 0]
python的上下文管理器可以创建一个临时变量,这个变量仅在with下起作用.
如果你使用过其他语言的文件读写,应该知道文件读写分为三个步骤:
而在Python中,完全不用这么麻烦,只需要:
with open('some_file', 'w') as file:
file.write('hello!')
不需要释放资源么?不需要,因为with
语法已经帮你做了.
在进入with
的时候会自动触发open
类中的__enter__
方法,做好初始化操作,然后返回一个变量赋值给as
后面的变量;当退出with
的时候,会自动触发open
类中的__exit__
方法,做垃圾清理工作,比如说关闭文件的步骤在这里已经做好了.
所以,能用with
的时候尽量使用with
,只有好处没有坏处.
for
循环还有一个else
从句,我们大多数人并不熟悉。这个else
从句会在循环正常结束时执行。这意味着,循环没有遇到任何break
. 一旦你掌握了何时何地使用它,它真的会非常有用。
考虑一个寻找某些东西的程序,当找到的时候会触发break
,如果没找到会进入else
:
for item in container:
if search_something(item):
# 找到了
process(item)
break
else:
# 没找到
not_found_in_container()
Python贴心的给我们提供了调试器,当遇到pdb.set_trace()
的时候函数会暂停并等待我们的输入,这时我们可以打印此刻的变量值,也可以执行下面的指令:
命令 | 作用 |
---|---|
b | 设置断点 |
c | 继续执行程序 |
l | 查看当前行的代码段 |
s | 进入函数 |
r | 执行代码直到从当前函数返回 |
q | 中止并退出 |
n | 执行下一行 |
enter | 重复执行上一条命令 |
import pdb
def make_bread():
for i in range(3):
pdb.set_trace()
make_bread()
这是两个魔法变量,只需要一个例子就能弄明白他们的用法:
def test_var_args(f_arg, *args, **kwargs):
print("normal arg:", f_arg)
for arg in args:
print("*args:", arg)
for key, value in kwargs.items():
print("**kwargs:{0} == {1}".format(key, value))
test_var_args('driving', 'python', 'eggs', 'test', name='a', address='b', usertype ='c',)
上述函数中只定义了一个常规参数,但是传参的时候传了一大堆.多出来的参数会被*args
和**kwargs
捕获,其中*args
会捕获多出的没有指定变量名的参数,而**kwargs
捕获到指定变量名的参数.注意,他们使用的时候是有顺序的,同时使用的时候要按照fargs, *args, **kwargs
的顺序.