主要参考廖雪峰
安装参考:Windows 系统安装 Python 3.8 详解
一、介绍
面向对象、解释型语言,跨平台的脚本语言。
可以做 日常任务(eg 自动备份MP3)、做网站(eg youtube、豆瓣)、做网络游戏的后台。
python为我们提供了非常完美的代码库,包括网络、文件、gui、数据库、文本等大量内容。
定位:优雅、明确、简单。代码越少,开发效率越高。
python的好处:
1、学习容易
Python是一门比较容易学习的语言,因为它是非常高级的语言,比C和C++这样的语言,还要高级几个层次。它不需要管理内存分配,不需要定义变量的类型即可使用,内置了很多数据类型直接使用,而不需要考虑怎么样创建这些类型,比如列表、字典、字符串这样高级的功能。
另外,用它写出的代码,可以直接运行,不需要进行编译的操作。
还有一点,用它写出的代码非常短。
2、开发效率高
Python是一门开发效率最高的语言,它比C有6倍的开发效率,简单来说,如果一个C开发人员工作6天,使用python的开发人员只需要工作一天即可,意味着做Python开发人员可一周只上一天班。
它比C++有2倍的开发效率,它比Java和C#也有1.5倍的开发效率。有这么高的开发效率,当然是用性能换来的代价,不过从目前硬件技术进步来看,目前的CPU计算能力普遍是过剩的,并且越来越多硬件成本降低,但人工的成本越来越贵。其实从社会进步来看,也是工具越来越先进,但人的大脑管理复杂程度并没有跟着提高,显然要提高起来就比较困难了。
目前在嵌入式系统都慢慢走向多核的CPU,在手机方面,都已经进入64位的8核时代了。在嵌入式系统方面,也有Pyboard这样的开源项目来进行了,这样的嵌入式Python主要适用于机器人控制方面。
3、调试运行方便
无论是在Windows平台,还是Linux平台,都一样开发和调试。跨平台运行更加方便,如果没有使用平台差别的API接口,只要写一遍代码,就可以在Windows平台或Linux平台上运行。
4、开源免费
Python无论在商业上,还是教育上,都是免费使用,意味可以零成本进入学习它,使用它。Python拥有众多功能完善的开发库可以使用。
5、测试领域需求
测试是软件开发里有相当大的工作量,比如模块测试,当开发人员把一个模块功能完成之后,需要测试这个模块是否正确,就需要搭建一堆测试代码,才可以验证的。这时,如果使用C++或Java来写这些功能,显然没有使用Python来得快,从前面效率就可以看到。
因此,通常就会变成这样的开发模式:发布的软件是使用C++或Java开发,但测试的代码使用Python来开发。
比如嵌入式系统涉及网络通讯方面,需要不断地向嵌入式系统发送网络数据和接收网络数据,就可以使用Python搭建一个测试环境出来,这样花费很少的时间,就可以对嵌入式系统进行验证,提高代码的质量,减少嵌入式系统与其它系统的调试时间,以及以后维护时间。
另外,通过使用Python语言编写众多的脚本,就可以提高自动化测试水平,每发布一个版本,就可以把以前的测试用例,全自动化测试一遍,这样会大大提高对软件快速发布的要求。像我所见过的测试用例,就有几万个,如果靠手工方式测试,验证起来是非常慢的,并且并不全面。目前采用全自动化测试之后,每天24小时运行,一台电脑相当于10个测试员工的工作量,可见带来多大效率的提升。在可以预见的将来,在测试领域里,Python的工作需求会持续增加,要求所有测试人员都会掌握这个好用的工具。
二、.py文件
- windows中不能像.exe文件那样直接运行.py文件。
- linux中可以,只需在文件最上方加一行 #!/usr/bin/env python3,执行时直接 ./01.py 否则就需要python 01.py
参考:#!/usr/bin/env python 有什么用?
第一部分是 #! (叫做
Sha-bang
,有的翻译组将它译作 释伴
,即“解释伴随行”的简称。后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。)
第二部分是 /usr/bin/env python
执行python程序的三种方式
1、解释器:cpython 解释器,将程序代码转换成cpu能够执行的机器码,装完python后就有
2、交互式:ipython软件,执行类似1,都是在终端中 python 01.py
3、IDE:pycharm中执行 【我的Pycharm,我做主】
三、基础
1、注释
单行注释 # 多行注释 """(3个引号)
2、大小写敏感,语句结尾分号可以省略,主要通过换行来识别语句的结束。
3、4个空格(tab)的缩进
4、数据类型:
- 整数(大小没有限制,超出一定范围为inf 无限大)
- 浮点数(除法的结果是浮点数,9/3=3.0 。地板除则是返回整数9//3=3,整数的地板除结果永远是整数 即使除不尽)
- 字符串
- 布尔值(True、False)
- 空值(None)
5、变量:大小写英文、数字和_ ,但不能数字开头。【py文件也不要以数字开头】
查看一个变量的类型用type()
6、字符串(str、bytes)
在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,print('包含中文的str')
对于单个字符的编码,Python提供了ord()
函数获取字符的整数表示,chr()
函数把编码转换为对应的字符
eg:ord('A')=65
chr(25991)='文'
Python的字符串类型是str
,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str
变为以字节为单位的bytes
。
Python对bytes
类型的数据用带b
前缀的单引号或双引号表示:x = b'ABC'
要计算str
包含多少个字符,可以用len()
函数。如果换成bytes
,len()
函数就计算字节数
Python 3 字符串中的 STR 和 Bytes 究竟有什么区别
- Python2的字符串有两种:str和Unicode,Python3的字符串也有两种:str和Bytes。
- Python2里面的str和Unicode是可以混用的,在都是英文字母的时候str和unicode没有区别。
- 而Python3严格区分文本(str)和二进制数据(Bytes),文本总是Unicode,用str类型,二进制数据则用Bytes类型表示。
由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
格式化
在Python中,采用的格式化方式和C语言是一致的,用%
实现。
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
%
运算符就是用来格式化字符串的。
小数点:保留两位有效小数
>>> a = 12.345
>>> print("%.2f" % a)
7、高级变量类型
- list 列表,有序集合 []
classmates = ['Michael', 'Bob', 'Tracy']
list是个可变列表 [ ] ,元素类型可以不同。索引从0开始,最后一个元素的索引是len(classmates) - 1 。如果要取最后一个元素,除了计算索引位置外,还可以用
-1
做索引,直接获取最后一个元素。
添加元素:追加到末尾 append() 添加到指定位置 insert(index,str)
删除元素:删除末尾元素 pop() 删除指定位置的元素,用pop(i)
方法,其中i
是索引位置
list元素也可以是另一个list,eg:s = ['python', 'java', ['asp', 'php'], 'scheme']
- tuple元组,有序列表 ()
一经定义 元素不能改变,代码更安全,元素类型可以不同。如果可能,能用tuple代替list就尽量用tuple。
classmates = ('Michael', 'Bob', 'Tracy')
定义只有1个元素的tuple定义时必须加一个逗号
,
t = (1,)
元组添加元素,其实是新建一个元组:
new_classmates =classmates +(5,)
8、循环
Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来
Python提供一个range()
函数,可以生成一个整数序列,再通过list()
函数可以转换为list.
range(101)
就可以生成0-100的整数序列.
sum = 0 for x in range(101): sum = sum + x print(sum)
for
循环其实可以同时使用两个甚至多个变量,比如dict
的items()
可以同时迭代key和value:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
第二种循环是while循环
9、dict 字典 {}
Python内置了字典:dict的支持,dict全称dictionary
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
判断一个key是否存在:一是通过in
判断key是否存在;二是通过dict提供的get()
方法,如果key不存在,可以返回None
,或者自己指定的value:
'Thomas' in d 不存在则返回 False
d.get('Thomas') 或者
d.get('Thomas',-1)
dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。
在Python中,字符串、整数、None空对象 等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key
在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
10、set 集合
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key,无序的不重复元素序列。
要创建一个set,需要提供一个list作为输入集合:
s = set([1, 2, 3])
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
四、函数
1、定义函数
使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。eg:
def my_abs(x): if x >= 0: return x else: return -x
返回多个值,直接在return中定义 return nx, ny
x, y = move(100, 100, 60, math.pi / 6)
但其实这只是一种假象,Python函数返回的仍然是单一值:原来返回值是一个tuple!
r = move(100, 100, 60, math.pi / 6)
print(r)=(151.96152422706632, 70.0)
2、函数的参数
- 位置参数:
def power(x, n):
函数有两个参数:x
和n
,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x
和n
- 默认参数:
def power(x, y, n=2):
设置默认参数时,有几点要注意:
一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);
因为会产生歧义。例如 def foo(p1, p2=6, p3) 若调用函数foo(1, 2),系统会调用foo(1, 2)其中p1 = 1 p2重新复制成了2 p2 = 2,第三个位置参数就会不见了,这时候系统就会报出错了。
二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小(不怎么变化)的参数放后面。变化小的参数就可以作为默认参数。
使用默认参数有什么好处?最大的好处是能降低调用函数的难度。
注意:定义默认参数要牢记一点:默认参数必须指向不变对象!
- 可变参数
可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。eg:
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
调用 calc(1,2,3)
而允许把list或tuple的元素变成可变参数传进去:
nums = [1, 2, 3]
calc(*nums)
*nums
表示把nums
这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
五、高级特性
在Python中,代码不是越多越好,而是越少越好。代码不是越复杂越好,而是越简单越好。
基于这一思想,我们来介绍Python中非常有用的高级特性,1行代码能实现的功能,决不写5行代码。
切片
取一个list或tuple的部分元素是非常常见的操作。取前3个元素,应该怎么做?
可以 [L[0], L[1], L[2]] 或者用循环,但都太麻烦
Python提供了切片(Slice)操作符,能大大简化这种操作:L[0:3] 从索引
0
开始取,直到索引3
为止,但不包括索引3
。索引0可以省略,L[:3]
每两个取一个:L[:10:2]
字符串也有切片操作
总结:
切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的结束位置,为列表末位时可省略;m 可以不提供,默认值是1,不允许为0 ,当m为负数时,列表翻转。注意:这些值都可以大于列表长度,不会报越界。
切片的基本含义是:从序列的第i位索引起,向右取到后n位元素为止,按m间隔过滤 。
迭代
如果给定一个list或tuple,我们可以通过for
循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
还可以作用在其他可迭代对象上。eg dict字典、str字符串
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
还有,同时引用两个变量,在Python里是很常见的,比如下面的代码:
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
列表生成式
运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list
例如,list(rang(1,11))
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
写列表生成式时,把要生成的元素表达式(x * x)
放到前面,后面跟for
循环,就可以把list创建出来,十分有用.
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
还可以使用两层循环,可以生成全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads']
把一个list中所有的字符串变成小写:
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
注意:如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()
方法,所以列表生成式会报错,
正确做法,使用内建的isinstance
函数可以判断一个变量是不是字符串:
>>> x = 'abc'
>>> y = 123
>>> isinstance(x, str)
True
>>> isinstance(y, str)
False
L1 = ['Hello', 'World', 18, 'Apple', None] L2 = [x.lower() for x in L1 if isinstance(x,str)] # 测试: print(L2) if L2 == ['hello', 'world', 'apple']: print('测试通过!') else: print('测试失败!')
生成器与yield
1、通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
2、要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
如果要一个一个打印出generator的元素来,可以通过next()
函数获得generator的下一个返回值,即 next(g)
3、generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
不过,基本上永远不会调用next()
,而是通过for
循环来迭代(因为generator也是可迭代对象)它,并且不需要关心StopIteration
的错误。
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
4、generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),可用普通函数实现也可以用generator实现。
#!/usr/bin/env python # encoding: utf-8 # file: Fibonacci.py # 斐波拉契数列 #著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到: #1, 1, 2, 3, 5, 8, 13, 21, 34, ... # 普通函数 def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b # 先计算右边,再赋值 n = n + 1 return 'done' print(fib(1)) print(fib(3)) print(fib(6)) # 生成器,,只需要把print(b)改为yield b def fibg(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b # 先计算右边,再赋值 n = n + 1 return 'done' f = fibg(6) # 我们在循环过程中不断调用yield,就会不断中断。 # 当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。 for n in fibg(6): print(n)
最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
请注意区分普通函数和generator函数,普通函数调用直接返回结果;
generator函数的“调用”实际返回一个generator对象。
yield与return 的区别
- return:就是在程序中返回某个值,返回之后程序就不再往下运行了。
- yield:把它看做一个是生成器(generator)的一部分(带yield的函数才是真正的迭代器)
带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。
迭代器
迭代对象(ITerable):可以直接作用于for循环的对象,注意有两类
- 一类是集合数据类型,如
list
、tuple
、dict
、set
、str
等; - 一类是
generator
,包括生成器和带yield
的generator function。
迭代器 (ITerator):可以被next()
函数调用并不断返回下一个值的对象【它表示的是一个惰性计算的序列】,只有一类
- 生成器generator是迭代器
可以使用isinstance()
判断一个对象是否是Iterator
对象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
你可能会问,为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
六、模块
1、介绍
在Python中,一个.py文件就称之为一个模块(Module)。
好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。
使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,但是也要注意,尽量不要与内置函数名字冲突。点这里查看Python的所有内置函数。
你也许还想到,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。也就是模块的父目录
请注意,每一个包目录下面都会有一个__init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py
可以是空文件,也可以有Python代码,因为__init__.py
本身就是一个模块,而它的模块名就是mycompany
。
创建自己的模块时,要注意:
- 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
- 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行
import abc
,若成功则说明系统存在此模块。
2、使用模块
以内建的sys
模块为例,编写一个hello
的模块:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' __author__ = 'Michael Liao' import sys def test(): args = sys.argv if len(args)==1: print('Hello, world!') elif len(args)==2: print('Hello, %s!' % args[1]) else: print('Too many arguments!') if __name__=='__main__': test()
【注意:代码中的下划线都是两根】
第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第6行使用__author__
变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
以上就是Python模块的标准文件模板,当然也可以全部删掉不写,但是,按标准办事肯定没错。
import sys 导入sys
模块后,我们就有了变量sys
指向该模块,利用sys
这个变量,就可以访问sys
模块的所有功能。
也可以: from 模块名 import 变量或者函数 ,eg:from math import pi 代码中就可以直接使用pi变量了【而不用写math.pi 】。也可以from math import *
最后,注意到这两行代码:
if __name__=='__main__':
test()
当我们在命令行运行hello
模块文件时,Python解释器把一个特殊变量__name__
置为__main__;
而如果在其他地方导入该hello
模块时,这个if
判断将失败,不会执行test()方法。
【__name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。】
因此,这种if
测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。参考下文
3、模块作用域
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc
,x123
,PI
等;
类似【字母前后是双下划线】__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊变量,hello
模块定义的文档注释也可以用特殊变量__doc__
访问,我们自己的变量一般不要用这种变量名;
类似【字母前是单/双下划线】_xxx
和__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc
,__abc
等;
4、安装第三方模块
以前用pip,现在推荐直接使用Anaconda【开源的Python包管理器】,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。
可以从Anaconda官网下载GUI安装包,安装包有500~600M,所以需要耐心等待下载。网速慢的同学请移步国内镜像。下载后直接安装,Anaconda会把系统Path中的python指向自己自带的Python,并且,Anaconda安装的第三方模块会安装在Anaconda自己的路径下,不影响系统已安装的Python目录。
安装好Anaconda后,重新打开命令行窗口,输入python,可以看到Anaconda的信息。
5、import与from...import的区别
彻底搞懂Python 中的 import 与 from import
Python 导入其他模块的方式:
- 什么时候用
import xxx
? - 什么时候用
from xxx import yyy
? - 什么时候用
from xxx.yyy import zzz
? - 什么时候用
from xxx import *
?
1、系统自带的模块
以正则表达式模块re为例:
import re target = 'abc1234xyz' re.search('(d+)', target)
from re import search target = 'abc1234xyz' search('(d+)', target)
查看他们的类型
>>> import re
>>> type(re)
<class 'module'>
>>> from re import search
>>> type(search)
<class 'function'>
可以看到,直接使用import re
导入的re
它是一个module
类,也就是模块。我们把它成为正则表达式模块
。而当我们from re import search
时,这个search
是一个function
类,我们称呼它为search 函数
。
一个模块里面可以包含多个函数。
如果在你的代码里面,你已经确定只使用search
函数,不会再使用正则表达式里面的其他函数了,那么你使用两种方法都可以,没什么区别。但是,如果你要使用正则表达式下面的多个函数,或者是一些常量,那么用第一种方案会更加简洁清晰。
2、第三方模块
例如lxml
它既能处理xml
的数据,又能处理html
的数据,于是这种库会划分子模块,lxml.html
模块专门负责html
相关的数据。
你可以:from lxml import html
但是不能 import lxml
注:你只能导入一个模块或者导入一个函数或者类,你不能导入一个文件夹
无论你使用的是import xxx
还是from xxx.yyy.zzz.www import qqq
,你导入进来的东西,要不就是一个模块(对应到.py 文件的文件名),或者是某个.py 文件中的函数名、类名、变量名。
无论是import xxx
还是from xxx import yyy
,你导入进来的都不能是一个文件夹的名字。
总结:
- 无论是使用
import
还是from import
,第一个要求是代码能够正常运行,其次,根据代码维护性,团队编码风格来确定选择哪一种方案。 - 如果我们只会使用到某个模块下面的一个函数(或者常量、类)并且名字不会产生混淆,可识别性高,那么
from 模块名 import 函数名
这没有什么问题。 - 如果我们会用到一个模块下面的多个函数,或者是我们将要使用的函数名、常量名、类名可能会让人产生混淆(例如 re.S、re.I),那么这种情况下,
import 模块名
然后再模块名.xxx
来调用会让代码更加清晰,更好维护。 - 但无论什么情况下,都禁止使用
from xxx import *
这种写法,它会给你带来无穷无尽的噩梦。【因为不知道你导入进了啥,写程序这个事情,几乎在任何情况下,白名单都优于黑名单。白名单是可控的,黑名单是总会有漏网之鱼的。】
七、程序入口与__name__
1、程序入口
对于很多编程语言来说,程序都必须要有一个入口,比如 C,C++,以及完全面向对象的编程语言 Java,C# 等。C 和 C++ 都需要有一个 main 函数来作为程序的入口,同样,Java 和 C# 必须要有一个包含 Main 方法的主类来作为程序入口。
而 Python 则有不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。
一个 Python 源码文件除了可以被直接运行外,还可以作为模块(也就是库)被导入。不管是导入还是直接运行,最顶层的代码都会被运行(Python 用缩进来区分代码层次)。而实际上在导入的时候,有一部分代码我们是不希望被运行的。
举一个例子来说明一下,假设我们有一个 const.py 文件,内容如下:
PI = 3.14 def main(): print "PI:", PI main()
我们在这个文件里边定义了一些常量,然后又写了一个 main 函数来输出定义的常量,最后运行 main 函数就相当于对定义做一遍人工检查,看看值设置的都对不对。然后我们直接执行该文件(python const.py),输出:
PI: 3.14
现在,我们有一个 area.py 文件,用于计算圆的面积,该文件里边需要用到 const.py 文件中的 PI 变量,那么我们从 const.py 中把 PI 变量导入到 area.py 中:
from const import PI def calc_round_area(radius): return PI * (radius ** 2) def main(): print "round area: ", calc_round_area(2) main()
运行 area.py,输出结果:
PI: 3.14
round area: 12.56
可以看到,const 中的 main 函数也被运行了,实际上我们是不希望它被运行,提供 main 也只是为了对常量定义进行下测试。这时,if __name__ == '__main__'
就派上了用场。把 const.py 改一下:
PI = 3.14 def main(): print "PI:", PI if __name__ == "__main__": main()
然后再运行 area.py,输出如下:
round area: 12.56
再运行下 const.py,输出如下:
PI: 3.14
这才是我们想要的效果。
if __name__ == '__main__'
就相当于是 Python 模拟的程序入口。Python 本身并没有规定这么写,这只是一种编码习惯。由于模块之间相互引用,不同模块可能都有这样的定义,而入口程序只能有一个。
到底哪个入口程序被选中,这取决于 __name__
的值。
2、__name__
__name__
是内置变量,用于表示当前模块的名字,同时还能反映一个包的结构。来举个例子,假设有如下一个包:
a
├── b
│ ├── c.py
│ └── __init__.py
└── __init__.py
目录中所有 py 文件的内容都为:
print __name__
我们执行 python -c "import a.b.c"
, 【-c 指定要执行的命令】输出结果:
a
a.b
a.b.c
由此可见,__name__
可以清晰的反映一个模块在包中的层次。其实,所谓模块名就是 import 时需要用到的名字,例如:
import tornado
import tornado.web
这里的 tornado 和 tornado.web 就被称为模块的模块名。
如果一个模块被直接运行,则其没有包结构,其 __name__
值为 __main__
。例如在上例中,我们直接运行 c.py 文件(python a/b/c.py),输出结果如下:
__main__
而被其它模块导入执行的值为该模块(a.py)的名字a
所以,if __name__ == '__main__'
我们简单的理解就是: 如果模块是被直接运行的,则代码块被运行,如果模块是被导入的,则代码块不被运行。