一. 安装
1. windows下的安装
(自行去官网或镜像网下载)
注意: 安装的时候 勾上Add Python 3.5 to PATH ,然后点“Install Now”即可完成安装
Windows下开发建议使用 pyCharm 集成 pylint 开发
或者 vscode , IDLE
1.1 基本操作:
Windows 下建议使用 IDLE 编辑器
# 查看版本
python -V
python --version
# 进入交互模式
python # 或 py
2. CentOS7 下安装
# 安装 python3.5 可能使用的依赖
yum install -y openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel gcc
# 下载
tarVersion="3.6.2"
tarName="Python-${tarVersion}.tgz"
dirName=`tar -tf "${tarName}" | head -1`
dirName=${dirName%%\/*}
wget "https://www.python.org/ftp/python/${tarVersion}/${tarBallName}"
tar xf ${tarBallName}
cd ${dirName}
mkdir /usr/local/python3
# 编译
./configure --prefix=/usr/local/python3
make && make install
# 拷贝到PATH中
cp /usr/local/python3/bin/python3.6 /usr/bin/
以下是可选步骤
# 备份旧版本python,链接新版本python
mv /usr/bin/python /usr/bin/python.bak
ln -s /usr/local/python3/bin/python3 /usr/bin/python
# 修改yum配置文件
vi /usr/bin/yum
将第一行指定的python版本改为python2.7(#!/usr/bin/python 改为 #!/usr/bin/python2.7)
二. 简单的示例
1. 脚本
脚本规范
"""
module 描述
"""
` # -*- coding: utf-8 -*-`
# import 不允许使用 from xx import *
# 只允许使用下面的格式
import xx
from yy import zz
# 每级缩进用 4 个空格
# 绝不要混合使用 tab 和空格
# 限制所有行的最大行宽为 79 字符
# 用两行空行分割顶层函数和类的定义
# 包和模块名 必须遵循小写蛇形格式
# 函数名 必须遵循小写蛇形格式
# 类名使用大驼峰格式
# 内部使用的类使用一个额外的前导下划线
def example_func(a_str,b_str):
"""
func 描述
:param a_str:
:param b_str:
:return:
"""
print(a_str,b_str)
三. 语法1 - 通用语法
1. 注释
注释
使用 #
开头 ,或使用 '''
或 """
进行 多行注释
2. 结尾的 ;
是可选的
Python
通常是一行写完一条语句,语句结尾的 ;
是可选的
但是有时确实语句很长, 需要多行写完, 怎么办? 使用 \
转义回车符,
例如
total=item_one + \
item_two
同时,有的结构中本身就允许多行输入, 例如 []
{}
或 ()
中
total=[1,
2]
当然, 同一行也可以写多个语句,以 ;
隔开即可
print("Hello");print("World");
3. 代码块
以 :
结尾时, "缩进" 的语句视为代码块
空行
也是程序代码的一部分!!
函数之间或类的方法之间用 空行
分隔,表示一段新的代码的开始
4. 大小写敏感
5. python中的保留字
查看python中的保留字
交互模式下
import keyword
print(keyword.kwlist)
# 输出
"""
['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec',
'finally', 'for', 'from','global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass',
'print', 'raise', 'return', 'try', 'while', 'with', 'yield']
"""
四. 语法2 - 变量
1. 变量声明和变量类型
每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建
也可以使用 del
语句删除一些对象引用: del var_a, var_b
# 声明方式
a=1
a=b=c=1
a,b,c=1,2,"runoob"
d=None
# 删除变量
del a,b,c
try:
print(a)
except Exception as e:
print('error occurred:',e)
# error occurred: name 'a' is not defined
标准数据类型
-
Number: 包括 int,float,complex,bool
-
String: 包括
-
List(可变数组)
-
Tuple(不可变数组)
-
Sets(只有key的集合)
-
Dictionary(键值对集合)
判断变量类型 -- type 和 isinstance
type
和 isinstance
的区别是:
type(some_var)
返回的是变量所属的类
isinstance(some_var,int)
是判断变量是否是指定的类或其子类的对象
issubclass(cls1,cls2)
是判断 cls1 是否是 cls2或其子类
a=20
b=5.5
c=True
d=4+3j
# 打印变量类型
print(type(a), type(b), type(c), type(d))
# 输出 <type 'int'> <type 'float'> <type 'bool'> <type 'complex'>
a=111
# 判断变量类型
print(isinstance(a,int))
# 输出 True
print(issubclass(type(a),int))
# 输出 True
2. 数字类型
# 整数(int)
a=20
# 浮点数(float 1.23,3E-2)
b=5.5
# 布尔(bool)
c=True
# 复数(complex a=1+2j)
d=4+3j
print(20+10)
print(20-10)
print(20*10)
print(20/10) # 输出 2.0, 除法输出 float
print(5/4) # 输出1.25
print(5//4) # 整除 输出 1
print(5%3) # 取余 输出 2
print(5**2) # 幂运算 输出 25
# bool 运算使用 and or not
print(True and False)
print(True or False)
print(not False)
# 数字判断运算符
print(a >= 18)
3. 字符串
a='astr'
a="aStr"
a='''multiline
str'''
a="""multiline
str"""
a="' in str"
a='" in str'
a="\" in str" # 使用 \ 转义
a="\\n of this line is not lf"
a=r"\n of this line is not lf" # 在字符串前加 `r` 或 `R` 表示是自然字符串
a=u"this is an unicode string" # 在字符串前加 `u` 或 `U` 表示是unicode字符串
# 在最新的Python 3版本中,字符串是以Unicode编码的
# 按字面意义级联字符串
a="this " "is " "string" # 等同于 a="this is string"
3.1 字符编码和解码 encode和decode
# 对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符
print(ord('A')) # 输出 65
print(ord('中')) # 输出 20013
print(chr(66)) # 输出 'B'
print(chr(25991)) # 输出 '文'
print('\u4e2d\u6587') # 输出 '中文'
print('中文'.encode()) # 等价于 print('中文'.encode('utf-8'))
# 输出
# b'\xe4\xb8\xad\xe6\x96\x87'
print(b'\xe4\xb8\xad\xe6\x96\x87'.decode())
# 输出 '中文'
# 如果bytes中包含无法解码的字节,decode()方法会报错:
try:
print(b'\xe4\xb8\xad\xff'.decode())
except Exception as e:
print('error occurred:',e)
# 如果要忽略错误,可以传入errors='ignore'忽略错误的字节:
print(b'\xe4\xb8\xad\xff'.decode( errors='ignore'))
# 输出 '中'
计算字符串字符数 or 字节数
1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节
print(len('中文'))
# 输出 2
print(len('中文'.encode()))
# 输出 6
3.2 格式化
- %nd 整数
- %.nf 浮点数
- %s 字符串
- %x 十六进制整数
print('Hi, %s, you have $%d.' % ('Michael', 1000000))
# 只有一个%?时,括号可以省略
print("我叫 %s " % '小明')
print("今年 %3d 岁!" % (10)) # 数字10 将占3个字符的位置
# 还可以使用 format 格式化
print('Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125))
4. List
元组的元素可以修改, 使用 []
定义
a_list = [ 'a', 'b','c','d','e','f','g']
tinylist = [1, 2,] # 等价于 tinylist = [1, 2]
# 获取list长度
print(len(a_list))
# 索引元素
print(a_list[0])
# 索引下标为 1和2,不包含3的元素
print(a_list[1:3])
print(a_list[2:])
a_list[0] = 9
# 设置下标为 2,3,4 不包含5的元素
a_list[2:5] = [13, 14, 15]
a_list[2:5] = []
del(a_list[2:5])
# 输出两次列表
print(tinylist * 2)
# 连接列表
print(a_list + tinylist)
# 获取列表元素最大值
print(max(a_list))
# 统计 'a' 在列表中出现的次数
print(a_list.count('a'))
# 将元组转换为列表
a_seq = (20,)
print(list(a_seq))
# 成员测试
if('a' in a_list) :
print('a 在集合中')
else :
print('a 不在集合中')
# 遍历
for value in ['tic', 'tac', 'toe']:
print(value)
for index, value in enumerate(['tic', 'tac', 'toe']):
print(index, value)
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
print('What is your {0}? It is {1}.'.format(q, a))
4.1更多列表函数
http://www.runoob.com/python3/python3-list.html
http://www.runoob.com/python3/python3-data-structure.html
4.2 列表推导式
vec = [2, 4, 6]
# 推导出一个新的列表
print([ 3*x for x in vec ])
# 输出 [6, 12, 18]
print([[x, x**2] for x in vec])
# 输出 [[2, 4], [4, 16], [6, 36]]
print([3*x for x in vec if x > 3])
# 输出 [12,18]
vec1 = [2, 4, 6]
vec2 = [4, 3, -9]
print([x*y for x in vec1 for y in vec2])
# 类似于矩阵相乘
# 输出 [8, 6, -18, 16, 12, -36, 24, 18, -54]
print([vec1[i]*vec2[i] for i in range(len(vec1))])
# 输出 [8, 12, -54]
freshfruit = [' banana', ' loganberry ', 'passion fruit ']
print([weapon.strip() for weapon in freshfruit])
# 输出 ['banana', 'loganberry', 'passion fruit']
4.3 map()函数
map()函数接收两个参数,一个是函数,一个是 Iterable对象
(如 列表)
并返回一个 Iterator对象
f=lambda x:x*x
# map函数表示对列表中的每个对象都执行f函数,并返回一个新的列表
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
# 转换为 list 对象
print(list(r))
# 连起来写就是这样
print(list(map(f,[1, 2, 3, 4, 5, 6, 7, 8, 9])))
4.4 reduce() 函数
reduce() 函数 表示依次对列表中的2个元素执行 某个函数,其效果如下
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
add=lambda x,y:x+y;
sum=reduce(add,[1,3,5,7,9]);
print(sum) # 输出 25
4.5 filter() 函数:
is_odd=lambda n:n%2==1
print(list(filter(is_odd,[1, 2, 4, 5, 6, 9, 10, 15])))
4.6 sorted() 函数
f=lambda x:1/abs(x)
print(sorted([36, 5, -12, 9, -21]))
print(sorted([36, 5, -12, 9, -21],key=abs,reverse=True))
print(sorted([36, 5, -12, 9, -21],key=f,reverse=True))
5. Tuple
元组的元素不能修改, 使用 ()
定义
tup4=1,2,3 # 等价于 tup4=(1,2,3)
# 空元组
tup1 = ()
# 包含一个元素的元组 需要在元素后添加逗号
tup2 = (20,)
tup3=(20) # 等价于 tup3=20 !! 等价于声明了一个 int 变量!
tuple的陷阱
当你定义包含一个元素的 tuple 时, 使用
tup2 = (20,)
而不是 tup2 = (20)
后者相当于声明了一个 int 变量!
6. Set(集合)
Set 是一个无序不重复元素的序列, 使用 {}
定义
set和dict的唯一区别仅在于没有存储对应的value,
但是,set的原理和dict一样,所以,同样只能放入不可变对象(hashable对象),
因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。
把list放入set 会报错。
# 声明
students = {'Tom', 'Jim', 'Mary', 'Tom', 'Jack', 'Rose'}
a = set('abracadabra')
print(a)
# 输出 {'d', 'b', 'r', 'a', 'c'}
print(set([1, 2, 2]))
# 输出 {1, 2}
# 成员测试
if('Rose' in student) :
print('Rose 在集合中')
else :
print('Rose 不在集合中')
# 遍历
for stu in students:
print(stu)
# 集合运算
a = set('dbrac')
b = set('zalmc')
# 差集
print(a - b)
# 并集
print(a | b)
# 交集
print(a & b)
# 异或
print(a ^ b)
7. Dictionary(字典)
Dictionary
无序的对象集合,键(key)必须使用不可变类型,且必须是唯一的.
Dictionary
和 Set
一样,也使用 {}
定义, 但是其内容是 key:value
格式,而不是只有 key
.
# 声明
a_dict = {}
tinydict = {'name': 'runoob','code':1 }
tinydict = dict(name='runoob',code=1)
tinydict=dict([('name', 'runoob'), ('code', 1)])
tinydict={x: x**2 for x in (2, 4, 6)}
# 赋值
a_dict['one'] = "1 - 菜鸟教程"
a_dict[2] = "2 - 菜鸟工具"
a_dict.pop('three')
# 索引
print(a_dict['one'])
# 如果字典中不存在该key,则索引该key的内容会报异常
# 可以使用 tinydict.get('name') 获取key的内容,
# key 不存在时返回None,而不是异常
print(a_dict.get('three','defalutvalue'))
# 输出所有键
print(tinydict.keys())
# 输出所有值
print(tinydict.values())
# 成员测试
if('name' in tinydict) :
print('name 在字典中')
else :
print('name 不在字典中')
# 遍历
knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
print(k,v);
8. 数据类型转换
# 取整
int(1.234)==1
math.floor(1.234)==1
# 将别的进制的字符串转成整形
int("0o77",8)==63
int("0xff",16)==255
oct(63)=='0o77'
hex(255)=='0xff'
# eval
eval("1+1")==2
五. 语法3 - 运算符&函数
1. 运算符:
-
数值运算
+
-
*
/
//
(整除)%
(取余)**
幂运算 -
赋值运算符
+=
-=
*=
... -
比较运算符:
==
!=
>
>=
<=
-
位运算符
&
|
^
(异或)~
(取反)<<
>>
-
逻辑运算符
and
or
not
-
成员运算符
a in list
a not in list -
身份运算符 is / is not
a is b
a is not b
id(a) == id(b)
六. 语法4 - 流程语句
1. if 语句
import random
a=random.choice(range(10))
if (a>8):
print('a>8')
elif a>5:
print('a>5')
else:
print('other')
2. while 循环
在Python中没有do..while循环
a=1
while a<3:
print(a)
a+=1
else:
print('end')
# 输出
1
2
end
a=1
while a<4:
if a>2:
break
print(a)
a+=1
else:
print('end')
# 输出
1
2
3. for 循环
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)
else:
print('end')
# 输出
Michael
Bob
Tracy
end
4. 迭代器
可以直接作用于for循环的对象统称为可迭代对象:Iterable
可以被 next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
可以使用 isinstance()
判断一个对象是否是 Iterable对象
list、tuple、dict、set、str 等都是 Iterable对象
,
但不是 Iterator
把 list、dict、str等 Iterable
变成 Iterator
可以使用 iter()
函数
你可能会问,为什么list、dict、str等数据类型不是Iterator?
这是因为Python的 Iterator对象
表示的是一个数据流,Iterator对象
可以被 next()
函数调用并不断返回下一个数据,直到没有数据时抛出 StopIteration错误
可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过 next()
函数实现按需计算下一个数据,
所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。
而使用list是永远不可能存储全体自然数的。
isinstance([], Iterable)
# True
isinstance([], Iterator)
# False
a_list=[1,2,3,4]
it = iter(a_list)
# 转换回列表可以
a_list2=list(it)
# 但是转换回列表后 it 也就废了,不能再对其进行遍历了
it = iter(a_list)
# 遍历 Iterator
for x in it:
print(x, end=" ")
while True:
try:
print(next(it))
except StopIteration:
break
# 使用列表推导式生成 Iterator 的示例
g = (x * x for x in range(10))
print(type(g))
# 输出 <class 'generator'>
# 遍历 generator
for n in g:
print(n)
5. 生成器:
使用了 yield
的函数被称为生成器(generator
)
-
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
-
在调用生成器运行的过程中,每次遇到
yield
时函数会暂停并保存当前所有的运行信息,返回yield
的值。
并在下一次执行next()
方法时从当前位置继续运行。
以下实例使用 yield
实现斐波那契数列
http://www.runoob.com/python3/python3-iterator-generator.html
六. 语法4 - 其他语句
1. pass 语句:
空语句,不做任何事情,一般用做占位语句,
因为有的时候你确实什么都不想做, 但不写语句语法有过不去,所以...
def nop():
pass
2. 模块
举个例子,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块。
现在,假设我们的abc和xyz这两个模块名字与其他模块冲突了,于是我们可以通过包来组织模块,避免冲突。
方法是选择一个顶层包名,比如mycompany,按照如下目录存放:
mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py
现在,abc.py模块的名字就变成了mycompany.abc,类似的,xyz.py的模块名变成了mycompany.xyz
注意,每一个包目录下面都会有一个 __init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。
__init__.py
可以是空文件,也可以有Python代码,因为 __init__.py
本身就是一个模块,而它的模块名就是mycompany。
2.00 作用域
模块示例
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
mython12
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@File: test2
@Time: 2019/1/10 19:10
@Description:
"""
__author__='ludq1'
def test():
"""
test函数
Returns:
"""
print("test")
if __name__=='__main__':
test()
在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过 _
前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
类似 __xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的 __author__
, __name__
就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__
访问,我们自己的变量一般不要用这种变量名;
类似 _xxx
和 __xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如 _abc
, __abc
等;
之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。
2.01 安装第三方模块
在Python中,安装第三方模块,是通过包管理工具pip/pip3完成的。
2.02 导入模块 import 与 from...import
# 导入整个 module
import somemodule1,module2,...
# 导入module 中特定的函数
from somemodule import somefunction
# 从某个模块中导入某个函数
from somemodule import firstfunc, secondfunc, thirdfunc
# 将某个模块中的全部函数导入
from somemodule import *
注:
(1) 模块中那些由单一下划线(_)开头的名字不能导入
(2) 不管你执行了多少次import 一个模块只会被导入一次
2.1 当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?
当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在 sys模块的path变量中
如果我们要添加自己的搜索目录,有两种方法:
一是直接修改 sys.path
,添加要搜索的目录:
import sys
sys.path.append('/Users/michael/my_py_scripts')
这种方法是在运行时修改,运行结束后失效。
第二种方法是设置环境变量 PYTHONPATH
,该环境变量的内容会被自动添加到模块搜索路径中。
设置方式与设置Path环境变量类似。
注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。
七. 语法5 - 自定义函数
1. 函数定义
def printinfo1( name, age ):
"""
可选的函数说明文档
"""
print('Hi, %s, your age is %d.' % (name, age))
# 默认参数示例
def printinfo2( name, age = 35 ):
"""
可选的函数说明文档
"""
print('Hi, %s, your age is %d.' % (name, age))
# 函数调用
printinfo1('Jackie',40)
printinfo1(age=40,name="Jackie")
printinfo2('Jackie')
printinfo2(name='Jackie')
定义 默认参数 要牢记一点: 默认参数 必须指向 不变对象 !
def add_end(L=[]):
L.append('END')
return L
add_end()
# 输出 ['END']
# 但是,再次调用add_end()时,结果就不对了:
add_end()
# 输出 ['END', 'END']
add_end()
# 输出 ['END', 'END', 'END']
上面的例子原因解释如下:
Python函数在定义的时候,默认参数L的值就被计算出来了,即 []
,
因为默认参数L也是一个变量,它指向对象 []
,
每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的 []
了。
2. 不定长参数
加了星号(*
)的变量名会存放所有未命名的变量参数。
如果在函数调用时没有指定参数,它就是一个空元组。
我们也可以不向函数传递未命名的变量
def printinfo3( arg1, *vartuple ):
print('arg1:',arg1)
print('otherArgs:',vartuple)
# 调用printinfo3 函数
printinfo3(1,2,3)
aTuple=(1,2,3)
printinfo3(*aTuple);
# 输出
"""
arg1: 1
otherArgs: (2, 3)
"""
# 更一般的用法是
def printinfo4(*vartuple,arg1='arg1'):
print('arg1:',arg1)
print('otherArgs:',vartuple)
# 调用printinfo3 函数
printinfo4(1,2,3,arg1='a')
aTuple=(1,2,3)
printinfo4(*aTuple,arg1='a');
printinfo4(*aTuple);
# 输出
"""
arg1: arg1
otherArgs: (1, 2, 3)
"""
3. 不定长参数2
def printinfo5( arg1, **kwargs ):
print('arg1:',arg1)
print('otherArgs:',kwargs)
# 调用printinfo5 函数
printinfo5(1,arg2=2,arg3=3)
aDict={'arg1':1,'arg2':2,'arg3':3}
printinfo5(**aDict);
# 输出
"""
arg1: 1
otherArgs: {'arg2': 2, 'arg3': 3}
"""
aDict={'arg2':2,'arg3':3}
printinfo5(1,**aDict);
# 输出同上
# 更一般的用法是
def printinfo6( **kwargs ):
print('args:',kwargs)
# 调用printinfo6 函数
aDict={'arg1'=1,'arg2'=2,'arg3'=3}
printinfo6(**aDict)
3.2 命名关键字参数
def person(name, age, *, city, job):
print(name, age, city, job)
# 调用
person('Jack', 24, city='Beijing', job='Engineer')
# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了
def person(name, age, *args, city, job):
print(name, age, args, city, job)
# 错误的调用
person('Jack', 24, 'Beijing', 'Engineer')
# 这是由于调用时缺少参数名city和job,Python解释器把这4个参数均视为位置参数,但person()函数仅接受2个位置参数。
4. 匿名函数 lambda 表达式
sum = lambda arg1, arg2: arg1 + arg2;
5. 返回None
不带参数值的 return语句
返回 None
6. 返回多个值.
def test():
return 1,2
# 其实返回多个值,就是返回一个 tuple,等价于返回 `return (1,2)`
x,y=test();
print(x,y);
# 输出 1,2
aa=test()
print(aa)
# 输出 (1,2)
7. 变量作用域
查找顺序: L(局部) –> E(闭包函数外的函数中) –> G(全局) –>B(内建作用域)
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,
其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问
在函数内部虽然可以访问全局变量,但是无法更改全局变量,
例如:
# 这是一个全局变量
total = 0;
# 可写函数说明
def sum( arg1, arg2 ):
#返回2个参数的和.
# total在这里是局部变量.
total = arg1 + arg2
# 如果是 total=total+arg1+arg2 会报错, 这是因为系统查找当前作用域找不到total,使用未定义的 total 变量也会出错
print("函数内是局部变量 : ", total)
return total;
#调用sum函数
sum( 10, 20 );
# 输出30
print("函数外是全局变量 : ", total)
# 输出0
# 如果要想修改全局变量,需要使用 global ,例如
def sum( arg1, arg2 ):
#返回2个参数的和.
# 声明 total 为全局变量 或 将全局变量 total 引用到此处
global total
total = arg1 + arg2;
print("函数内是局部变量 : ", total)
return total;
如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal
关键字
八. 异常
1. 示例
该示例让用户输入一个数字,如果输入不正确则提示后继续等待用户输入
count=0
while True:
try:
x = int(input("Please enter a number: "))
count+=1
if count>2:
break
# 捕获单个异常
except ValueError as e:
print("Oops! That was no valid number:",e,". Try again ")
# 捕获多个异常(KeyboardInterrupt 是 Ctrl+C, EOFError 是 Ctrl+Z)
except (ValueError,KeyboardInterrupt,EOFError) as e:
print("Oops! That was someError:",e,". Try again ")
# 捕获任意的异常
except Exception as e:
print("Oops! That was someError:",e,". Try again ")
# 捕获任意的异常
except:
print("Oops! That was someError. Try again ")
# 没有异常的时候执行,提前break时代码不会执行到此处
else:
print("no error")
# 无论如何都会执行, 提前break时代码也会执行到此处
# 用于表示无论何时都会执行的"清理行为"
finally:
print("finally")
2. 示例2:
try:
f = open('foo.txt', 'r')
except IOError:
print('cannot open', arg)
else:
print('foo.txt', 'has', len(f.readlines()), 'lines')
f.close()
2.1 打印调用栈
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
3. 抛出异常 raise
raise NameError('HiThere')
4. 自定义异常
继承 Exception 类
6. 预定义的"清理行为": 使用 with
例如下面的代码
for line in open("foo.txt"):
print(line, end="")
# 以上这段代码的问题是,当执行完毕后,文件会保持打开状态,并没有被关闭
# 使用with,就算在处理过程中出问题了,文件 f 总是会关闭
f=open("foo.txt")
print(f.closed)
# 输出 False
with f:
raise NameError('HiThere')
print(f.closed)
# 输出 True
7. 调试
7.1 print
7.2 assert :
python -O
来关闭assert
7.3 logging:
import logging;
logging.basicConfig(level=logging.INFO)
logging.info('someMsg')
7.4 pdb: 单步调试
python -m pdb err.py
# 输入 `n` 单步执行代码;
# 任何时候都可以输入命令 `p 变量名` 来查看变量;
# q 结束调试
7.5 使用IDE
最后,当然最爽的还是使用 IDE:
目前比较好的Python IDE有PyCharm:
另外,Eclipse加上pydev插件也可以调试Python程序。
8. 单元测试
假设你写了一个模块 my_module.py
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
class Student(object):
_name = 'defaultname'
@property
def name(self):
return self._name
@name.setter
def name(self, value):
# some code
self._name = value
def print_name(self):
print('my name is ', self.name)
编辑一个文件(例如名为 my_module_test.py
)
import unittest
from my_module import Student
# 需要继承 unittest.TestCase
class TestMyModule(unittest.TestCase):
# 以test开头的方法就是测试方法,
# 不以test开头的方法不被认为是测试方法,测试的时候不会被执行
def test_init(self):
stu = Student()
self.assertEqual(stu.name, 'defaultname')
self.assertTrue(isinstance(stu, object))
def test_attrerror(self):
stu = Student()
with self.assertRaises(Exception):
value = stu.namexx
# setUp 和 tearDown 这两个方法会分别在每调用一个测试方法的前后分别被执行
def setUp(self):
print('setUp...')
def tearDown(self):
print('tearDown...')
# 最简单的运行方式是判断是否是 main进程
if __name__ == '__main__':
unittest.main()
另一种运行方式是
python -m unittest my_module_test
9. 文档测试
就是直接按照文档进行测试
直接上代码
# mydict2.py
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__':
import doctest
doctest.testmod()
运行该脚本 什么输出也没有。
这说明我们编写的doctest运行都是正确的。
如果程序有问题,比如把 __getattr__()方法
注释掉,再运行就会报错:
网友评论