python提供了_python基础

主要参考廖雪峰

一、介绍

面向对象、解释型语言,跨平台的脚本语言。

可以做 日常任务(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程序的三种方式

1、解释器:cpython 解释器,将程序代码转换成cpu能够执行的机器码。装完python后既有

2、交互式:ipython软件,执行类似1,都是在终端中 python 01.py

3、IDE: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')=65chr(25991)='文'

Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

Python对bytes类型的数据用带b前缀的单引号或双引号表示:x = b'ABC'

要计算str包含多少个字符,可以用len()函数。如果换成bytes,len()函数就计算字节数

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编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

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 =0for x in range(101):

sum= sum +xprint(sum)

for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }

>>> for k, vin 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 不存在则返回 Falsed.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:

defmy_abs(x):if x >=0:returnxelse: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的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);

因为会产生歧义。例如deffoo(p1, p2=6, p3)  若调用函数foo(1, 2),系统会调用foo(1, 2)其中p1 = 1 p2重新复制成了2 p2 = 2,第三个位置参数就会不见了,这时候系统就会报出错了。

二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小(不怎么变化)的参数放后面。变化小的参数就可以作为默认参数。

使用默认参数有什么好处?最大的好处是能降低调用函数的难度。

注意:定义默认参数要牢记一点:默认参数必须指向不变对象!

可变参数

可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。eg:

def calc(*numbers):

sum=0for n innumbers:

sum= sum + n *nreturn 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]

字符串也有切片操作

迭代

如果给定一个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 xin range(1,11)]

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

写列表生成式时,把要生成的元素表达式(x * x)放到前面,后面跟for循环,就可以把list创建出来,十分有用.

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

>>> [x * x for xin 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 ifisinstance(x,str)]#测试:

print(L2)if L2 == ['hello', 'world', 'apple']:print('测试通过!')else:print('测试失败!')

View Code

生成器与yield

1、通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

2、要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> g = (x * x for x in range(10))

>>> g

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, ...

#普通函数

deffib(max):

n, a, b= 0, 0, 1

while n

a, b= b, a + b #先计算右边,再赋值

n = n + 1

return 'done'

print(fib(1))print(fib(3))print(fib(6))#生成器,,只需要把print(b)改为yield b

deffibg(max):

n, a, b= 0, 0, 1

while n

a, b= b, a + b #先计算右边,再赋值

n = n + 1

return 'done'f= fibg(6)#我们在循环过程中不断调用yield,就会不断中断。#当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

for n in fibg(6):print(n)

View Code

最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

请注意区分普通函数和generator函数,普通函数调用直接返回结果;

generator函数的“调用”实际返回一个generator对象。

迭代器

迭代对象(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'

importsysdeftest():

args=sys.argvif len(args)==1:print('Hello, world!')elif len(args)==2:print('Hello, %s!' % args[1])else:print('Too many arguments!')if __name__=='__main__':

test()

View Code

【注意:代码中的下划线都是两根】

第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,它们有什么区别呢,请看实例:

1.当模块test.py中没有类,只有方法add,此方法实现传入的数字+1功能

(1)如果使用import导入:import  test

调用方法:test.add(1)

(2)使果用form...import导入:from  test  import  *

调用方法:add(1)

2.当模块test.py中有类,类名为cal,类中有方法add,此方法实现传入数字+1功能

(1)如果使用import导入:import  test

调用方法:test.cal().add(1)

(2)如果使用from...import导入:from  test  import  *

调用方法:cal().add(1)

分析

from A import a1 与 import A

1、语法分析:首先from A import a1 是从A模块导入a1工具(可以是某个 函数,全局变量,类),import A是导入整个A模块的全部内容(包括全部的函数,全局变量,类)。

2、内存分析:from...import...会在内存中创建并加载该模块工具的副本,当有另外一个程序导入时,会在内存中创建另一个副本进行加载,不会共用一个副本。所以程序进行的修改不会影响到被导入的原模块,且不同程序之间不会互相影响。

import...方法导入模块会在内存中直接加载该模块的全部属性。当出现多个程序导入该模块时,会共用一个模块,程序之间会互相影响,包括原模块。

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

3、from...import *  属于from...import...只不过是加载了模块的全部属性。

总结

当多个程序需要同时导入一个模块并且都是访问时,使用import 可以节省内存空间

当多个程序需要同时导入一个模块当其中一个需要修改模块,那么修改的模块的程序应该使用from来导入模块。

七、程序入口与__name__

1、程序入口

对于很多编程语言来说,程序都必须要有一个入口,比如 C,C++,以及完全面向对象的编程语言 Java,C# 等。C 和 C++ 都需要有一个 main 函数来作为程序的入口,同样,Java 和 C# 必须要有一个包含 Main 方法的主类来作为程序入口。

而 Python 则有不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。

一个 Python 源码文件除了可以被直接运行外,还可以作为模块(也就是库)被导入。不管是导入还是直接运行,最顶层的代码都会被运行(Python 用缩进来区分代码层次)。而实际上在导入的时候,有一部分代码我们是不希望被运行的。

举一个例子来说明一下,假设我们有一个 const.py 文件,内容如下:

PI = 3.14

defmain():print "PI:", PI

main()

View Code

我们在这个文件里边定义了一些常量,然后又写了一个 main 函数来输出定义的常量,最后运行 main 函数就相当于对定义做一遍人工检查,看看值设置的都对不对。然后我们直接执行该文件(python const.py),输出:

PI: 3.14

现在,我们有一个 area.py 文件,用于计算圆的面积,该文件里边需要用到 const.py 文件中的 PI 变量,那么我们从 const.py 中把 PI 变量导入到 area.py 中:

from const importPIdefcalc_round_area(radius):return PI * (radius ** 2)defmain():print "round area:", calc_round_area(2)

main()

View Code

运行 area.py,输出结果:

PI: 3.14

round area: 12.56

可以看到,const 中的 main 函数也被运行了,实际上我们是不希望它被运行,提供 main 也只是为了对常量定义进行下测试。这时,if __name__ == '__main__' 就派上了用场。把 const.py 改一下:

PI = 3.14

defmain():print "PI:", PIif __name__ == "__main__":

main()

View Code

然后再运行 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__' 我们简单的理解就是: 如果模块是被直接运行的,则代码块被运行,如果模块是被导入的,则代码块不被运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值