自学python-语句
编程第一步
斐波纳契数列
#!/usr/bin/python3
# Fibonacci series: 斐波纳契数列
# 两个元素的总和确定了下一个数
a, b = 0, 1
while b < 10:
print(b)
a, b = b, a+b
a,b=b,a+b
是先计算右边,再赋值给左边
end关键字
关键字end可以用于将结果输出到同一行,或者在输出的末尾添加不同的字符
条件控制
一般形式
#根据缩进划分代码块
if cond1:
state1
elif condition_2:
state2
else:
state3
python没有switch-case语句
题外话:input("点击 enter 键退出")
age = int(input("请输入你家狗狗的年龄: "))
判断运算符与其他语言一样
if语句可嵌套
循环语句
while语句一般形式
while 判断条件(condition):
statements
没有do…while循环
无限循环,ctrl+c
退出循环,在服务器上客户端的实时请求非常有用
while…else一般形式
while <expr>:
<statement(s)>
else:
<additional_statement(s)>
简单语句可以将一句代码直接写在:
后
for语句一般形式
for <variable> in <sequence>:
<statements>
else:
<statements>
break跳出当前循环体
range函数
内置函数,能生成数列
range(m,n,step):[m,n),step增量或步长,只有一个参数,默认为n,m=0,step=1
break,continue,循环中的else
break是跳出循环,对应的循环 else 块将不执行
continue是跳到下一次判断语句
pass语句
空语句,保持程序结构的完整性,做占位语句
迭代器与生成器
迭代器
是python最强大的功能之一,访问集合元素方式之一
是一个可以记住遍历的位置的对象
从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
list=[1,2,3,4]
it=iter(list) #创建迭代器对象
next(it) #输出迭代器的下一个元素
迭代器对象可以使用for进行遍历
#!/usr/bin/python3
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
或者用next遍历
#!/usr/bin/python3
import sys # 引入 sys 模块
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration:#next出错处理,即没有下一个
sys.exit()#退出解释器,os._exit()(一般用于在线程中退出)直接退出,此处抛出异常,供捕获进行清理工作,用于在主线程中退出
创建一个迭代器
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
。
类都有一个构造函数,Python 的构造函数为 __init__()
, 它会在对象初始化的时候执行。
__iter__()
方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__()
方法并通过 StopIteration 异常标识迭代的完成。
__next__()
方法(Python 2 里是 next())会返回下一个迭代器对象。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__()
方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
for x in myiter:
print(x)
生成器
使用了 yield 的函数被称为生成器(generator)
生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
使用 yield 实现斐波那契数列
#!/usr/bin/python3
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
对比
迭代器是由创建对象,iter()返回,生成器是由函数返回。迭代器也是属于创建的对象类型(type),所以可以调用内置next函数
生成器返回的type就是生成器了
两种next的实现是明显不同的
函数
定义一个函数
def funcname(params):
#文档字符串用于存放函数说明
statement
return [表达式]#不带则表示返回None
在 python 中,类型属于对象,变量是没有类型的,=
左边变量是没有类型的,只是一个引用,右边对象是有类型的。
参数传递
可更改(mutable)和不可更改(immutable)对象
list,dict可更改,其他都不可更改
不可变类型:重新赋值,生成新对象,引用变换。
可变:list,dict内部部分值改变
参数传递:不可变类型是传递变量的值,函数改变值,原始值不会改变。可变就类似引用传递,函数改变值,原始值也会改变。
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
参数类型
必需参数
以正确的顺序传入函数,调用时的数量必须和声明时的一样
关键字参数
允许函数调用时参数的顺序与声明时不一致:func( param = " ")
默认参数
def func( param1, param1 = value1 ):
不定长参数
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
function_suite
return [expression]
加了星号 ***** 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数:func(param1,param2,param3) print(var_args_tuple)>>>(param2,param3)
,元组也可以为空
参数带两个星号 ******基本语法如下:
def functionname([formal_args,] **var_args_dict ):
"函数_文档字符串"
function_suite
return [expression]
加了两个星号 ** 的参数会以字典的形式导入:func(1, a=2,b=3) print(var_args_dict)>>>{'a': 2, 'b': 3}
声明函数时,参数中星号 ***** 可以单独出现:def f(a,b,*,c):
,但这种情况下 ***** 后的参数必须用关键字传入:f(1,2,c=3)
否则报错TypeError: f() takes 2 positional arguments but 3 were given
匿名函数
使用 lambda 来创建匿名函数
lambda的主体是一个表达式,拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数
题外话:C或C++的内联函数,只能写一行的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda不等同于此
lambda [arg1 [,arg2,.....argn]]:expression
强制位置参数
Python3.8 新增, /
用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。
在以下的例子中,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 或 f 要求为关键字形参:
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
数据结构
列表
可变,区别于字符串和元组
方法补充:remove(x)(删除值为x的第一个元素,无则返回i错误),pop([i])(删除指定位置元素,无则返回最后一个元素)
当作堆栈使用
后进先出,append()
方法添加到堆栈顶。不指定索引的pop()
方法可以释放堆栈顶
当做队列使用
队列里第一加入的元素,第一个取出来popleft()
。效率不高,因为所有其他的元素都得一个一个地移动。
from collections import deque #双向队列,操作很像list ,拥有更低的时间和空间复杂度
queue = deque(["Eric", "John", "Michael"])
推导式
列表推导式提供了从序列创建列表的简单途径。for 之后跟一个表达式,然后有零到多个 for 或 if 子句
例:[3*x for x in list]
,[str(round(355/113, i)) for i in range(1, 6)]
,[x*y for x in list1 for y in list2]
嵌套列表
例:将3X4的矩阵列表转换为4X3列表
[[row[i] for row in matrix] for i in range(4)]
等价于
transposed = []
for i in range(4):
transposed.append([row[i] for row in matrix])
#或者
transposed = []
for i in range(4):
# the following 3 lines implement the nested listcomp
transposed_row = []
for row in matrix:
transposed_row.append(row[i])
transposed.append(transposed_row)
推导式的for从右往左嵌套
del删除
del list[]或者del list
元组和序列
在输入时可能有或没有括号
集合
无序不重复元素的集,基本功能包括关系测试(-,|,&,^
)和消除重复元素,也支持推导式
字典
以关键字为索引,dict()
直接从键值对元组列表[(,),(,),(,)]
中构建字典
如果关键字只是简单的字符串,使用关键字参数指定键值对:dict(a=value1,b=value2)
遍历
在字典中遍历时:for k, v in dict.items():
在序列中遍历时,索引位置和对应值可以使用 enumerate()
函数同时得到:for i, v in enumerate(list):
同时遍历两个或更多的序列,可以使用 zip() 组合:for a, b in zip(list1, list2):
反向遍历:reverse()
顺序遍历:sorted()
,不改变原值
模块
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入。使用 python 标准库的方法相同。
import语句
import module1[, module2[,... moduleN]#把命令放在脚本的顶端
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。
搜索路径是一个解释器会先进行搜索的所有目录的列表。
可在下图操作后查看搜索路径
一个模块只会被导入一次,不管执行了多少次import
自定义模块:要在搜索路径创建py文件,这样做并没有把py文件中直接定义的函数名称写入到当前符号表里,可以使用模块名称来访问函数module.func
,如果你打算经常使用一个函数,你可以把它赋给一个本地的名称funcname=module.func
from…import语句
from modname import name1[, name2[, ... nameN]]
将模块里的某些函数引入进来
from…import*:把一个模块的所有内容全都导入到当前的命名空间,然而这种声明不该被过多地使用。这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, 不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。
深入模块
模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。
1、一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,被导入的模块的名称将被放入当前操作的模块的符号表中
2、可以使用 import 直接把模块内(函数,变量的)名称导入到当前操作模块。导入的方法不会把被导入的模块的名称放在当前的字符表中
__name__
属性
一个模块被另一个程序第一次引入时,其主程序将运行。
每个模块都有一个__name__
属性,当其值是’__main__
'时,表明该模块自身在运行,否则是被引入。
如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__
属性来使该程序块仅在该模块自身运行时执行。
if __name__ == '__main__':
print('程序自身在运行') #引入时运行
else:
print('我来自另一模块') #引入时不运行
dir()函数
内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回
如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称
标准模块
Python 本身带着一些标准的模块库
有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。
这些组件会根据不同的操作系统进行不同形式的配置
一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:
包
是一种管理 Python 模块命名空间的形式,采用"点模块名称"。比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。不用担心不同库之间的模块重名的情况。
类似分层的文件系统:包-》模块-》函数,变量
目录只有包含一个叫做 __init__.py
的文件才会被认作是一个包
导入方式:1.导入包里特定模块import package.module
,访问package.module.func
2.导入特定模块from package import module
,访问module.func
3.直接导入一个函数或者变量from package.module import func
,访问func
记忆:访问就是抄下import后面
从包中导入*
Python 会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来。
因为Windows是一个大小写不区分的系统,在 Windows平台上工作的就不好
为了解决这个问题,包作者需要提供一个精确的包的索引
如果包定义文件 __init__.py
存在一个叫做 __all__
的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入:__all__ = ["echo", "surround", "reverse"]
。在更新包之后保证 __all__
也更新了
如果 __all__
真的没有定义,那么使用from package import *
这种语法的时候,就不会导入包package里的任何子模块。他只是把包package和它里面定义的所有内容导入进来(可能运行__init__.py
里定义的初始化代码)。
这会把 __init__.py
里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。
通常不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。如果不同模块之间有相同的函数命名,最后导入的会覆盖前面的,也就是说只会调用到最后导入进的函数
from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。
无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"__main__
",一个Python应用程序的主模块,应当总是使用绝对路径引用。
包还提供一个额外的属性__path__
。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py
,得在其他__init__.py
被执行前定义。可以修改这个变量,用来影响包含在包里面的模块和子包。
这个功能并不常用,一般用来扩展包里面的模块。
输入与输出
输出格式美化
输出方式:表达式和print,文件对象的write
write:标准输出文件可以用 sys.stdout 引用
格式化输出值: str.format()
转化为string:str()(用户易读),repr(解释器易读)
repr:repr() 函数可以转义字符串中的特殊字符(和r相似),参数可以是 Python 的任何对象
题外话:rjust(n[, fillchar]) 字符串函数,右对齐,用空格(或者fillchar)填补达到n长度,ljust,center类似,zfill(n)在数字左边填补0达到n
str.format()基本用法:1.使用了关键字参数,2.位置及关键字参数可以任意的结合
print('{0} 和 {1}'.format('Google', 'baidu'))
print('{name}网址: {site}'.format(name='百度', site='www.baidu.com'))
!a (使用 ascii()), !s (使用 str()) 和 !r (使用 repr()) 可以用于在格式化某个值之前对其进行转化
print('常量 PI 的值近似为: {!r}。'.format(math.pi))
可选项 : 和格式标识符可以跟着字段名。 这就允许对值进行更好的格式化。
print('常量 PI 的值近似为 {0:.3f}。'.format(math.pi))
在 : 后传入一个整数, 可以保证该域至少有这么多的宽度。
print('{0:10} ==> {1:10d}'.format(name, number))
如果你有一个很长的格式化字符串, 而你不想将它们分开, 在格式化时通过变量名而非位置。
最简单的就是传入一个字典, 然后使用方括号 [] 来访问键值
table = {'Google': 1,'Taobao': 2}
print(' Google: {0[Google]:d}; Taobao: {0[Taobao]:d}'.format(table))
也可以通过在 table 变量前使用 ** 来实现相同的功能:
table = {'Google': 1, 'Taobao': 2}
print('Google: {Google:d}; Taobao: {Taobao:d}'.format(**table))
旧式字符串格式化
% 操作符也可以实现字符串格式化
print('常量 PI 的值近似为:%5.3f。' % math.pi)
这种旧式的格式化最终会从该语言中移除
读取键盘输入
Python提供了 input() 内置函数从标准输入读入一行文本。
input 可以接收一个Python表达式作为输入,并将运算结果返回。
读和写文件
open() 将会返回一个 file 对象,基本语法格式如下:
open(filename, mode)
mode:决定了打开文件的模式:只读,写入,追加等。。这个参数是非强制的,默认文件访问模式为只读®。
模式 | 描述 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。同上 |
w+ | 打开一个文件用于读写。同上 |
wb+ | 以二进制格式打开一个文件用于读写。同上 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。同a |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。同a+ |
模式 | r | r+ | w | w+ | a | a+ |
---|---|---|---|---|---|---|
读 | + | + | + | + | ||
写 | + | + | + | + | + | |
创建 | + | + | + | + | ||
覆盖 | + | + | ||||
指针在开始 | + | + | + | + | ||
指针在结尾 | + | + |
文件对象的方法
f.read()
调用 f.read(size), 这将读取一定数目的数据, 然后作为字符串或字节对象返回。size 是一个可选的数字类型的参数。 当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
f.readline()
会从文件中读取单独的一行。换行符为 ‘\n’。f.readline() 如果返回一个空字符串, 说明已经已经读取到最后一行。
f.readlines()
返回该文件中包含的所有行(以字符串列表返回,每个字符串后有\n)。如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。
另一种方式是迭代一个文件对象然后读取每行:
for line in f:
print(line, end='')
如果要写入一些不是字符串的东西, 那么将需要先进行转换。
f.tell()
返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。
f.seek()
如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数。
from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,from_what 值为默认为0。offset是移动字符数
- seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
- seek(x,1) : 表示从当前位置往后移动x个字符
- seek(-x,2):表示从文件的结尾往前移动x个字符
f.close
当你处理完一个文件后, 调用 f.close() 来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常。
当处理一个文件对象时, 使用 with 关键字是非常好的方式。在结束后, 它会帮你正确的关闭文件。 而且写起来也比 try - finally 语句块要简短:
with open('/tmp/foo.txt', 'r') as f:
read_data = f.read()
检验是否关闭:f.closed==True
pickle模块
实现了基本的数据序列和反序列化。
通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。
通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
基本接口:
pickle.dump(obj, file, [,protocol])
有了 pickle 这个对象, 就能对 file 以读取的形式打开:
x = pickle.load(file)
**注解:**从 file 中读取一个字符串,并将它重构为原来的python对象。
file: 类文件对象,有read()和readline()接口。
import pickle
output = open('data.pkl', 'wb')
# Pickle dictionary using protocol 0.
pickle.dump(data1, output)
# Pickle the list using the highest protocol available.
pickle.dump(datalist, output, -1)
#使用pickle模块从文件中重构python对象
pkl_file = open('data.pkl', 'rb')
data1 = pickle.load(pkl_file)
data2 = pickle.load(pkl_file)
格式化精度,对齐方式
%f 保留小数点后面六位有效数字 float
%e 保留小数点后面六位有效数字
%g 在保证六位有效数字的前提下,使用小数方式,否则使用科学计数法。
print('%.2g' % 1111.1111) # 取2位有效数字,自动转换为科学计数法1.1e+03
%s 字符串输出 string
%10s 右对齐,占位符 10位
%-10s 左对齐,占位符 10 位
%.2s 截取 2 位字符串
%10.2s,10 位占位符,截取两位字符串。
<
:左对齐>
:右对齐^
:居中=
:在正负号(如果有的话)和数字之间填充,该对齐选项仅对数字类型有效。它可以输出类似+0000120
这样的字符串。
>>> print("|",format("RUNOOB","*>30"),"|") #左对齐
| ************************RUNOOB |
>>> print("|",format("RUNOOB","*^30"),"|") #居中对齐
| ************RUNOOB************ |
>>> print("|",format("RUNOOB","*<30"),"|") #右对齐
| RUNOOB************************ |
file方法
open()
Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。
完整的语法格式为:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
参数说明:
-
file: 必需,文件路径(相对或者绝对路径)。
-
mode: 可选,文件打开模式
-
buffering: 设置缓冲
-
encoding: 一般使用utf8
-
errors: 报错级别
-
newline: 区分换行符
-
closefd: 传入的file参数类型
-
opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。
上面的mode表添加如下
模式 | 描述 |
---|---|
t | 文本模式 (默认)。 |
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
b | 二进制模式。 |
+ | 打开一个文件进行更新(可读可写)。 |
U | 通用换行模式(Python 3 不支持)。 |
默认为文本模式,如果要以二进制模式打开,加上 b 。
file对象
file.flush()(刷新文件内部缓冲,直接把内部缓冲区的数据写入文件),file.fileno()(返回一个整型的文件描述符),file.isatty()(是否连接到一个终端设备),file.next()(返回文件下一行,python3不支持),file.read(size)(返回指定字节数),file.readline([size])(读取整行,包括“、\n”)
file.readlines([sizeint])读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
file.truncate([size])从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
file.write(str)将字符串写入文件,返回的是写入的字符长度。
file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。
os
os 模块提供了非常丰富的方法用来处理文件和目录。
以下是比较能理解且平常能用到的函数
os.access(path,mode),path:文件路径,mode:F_OK,R_OK,W_OK,X_OK——是否存在,可读,可写,可执行
os.chdir(path)(改变当前工作目录)
os.chomd(path,mode),修改权限,Unix可用
os.chown(path, uid, gid),更改文件所有者,Unix可用,uid: 所属用户ID,gid :所属用户组 ID
os.closerange(fd_low, fd_high) 关闭所有文件描述符(open的返回值),从 fd_low (包含) 到 fd_high (不包含), 错误会忽略
os.dup(fd) 复制文件描述符 fd
os.getcwd() 输出当前目录
错误和异常
Python 有两种错误:语法错误和异常。
assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
运行期检测到的错误被称为异常。
异常处理
try/except语句
一个 try 语句可能包含多个except子句,一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出(raise)。
try/except...else
必须放在所有的 except 子句之后,else 子句将在 try 子句没有发生任何异常的时候执行。
使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。
try-finally
无论是否发生异常都将执行最后的代码。
抛出异常
raise语法格式如下:
raise [Exception [, args [, traceback]]]
raise 唯一的一个参数指定了要被抛出的异常。必须是一个异常的实例或者是异常的类( Exception 的子类)。
如果不想去处理异常,那么一个简单的 raise 语句就可以再次把它抛出。
自定义异常
可以通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承。
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类,大多数的异常的名字都以Error
结尾,就跟标准的异常命名一样。
定义清理行为以及预定义的清理行为
如果一个异常在 try 子句里(或者在 except 和 else 子句里)被抛出,而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后被抛出。
一些对象定义了标准的清理行为,无论系统是否成功的使用了它,一旦不需要它了,那么这个标准的清理行为就会执行。
关键词 with 语句可以保证对象在使用完之后一定会正确的执行他的清理方法:
with open("myfile.txt") as f:
for line in f:
print(line, end="")
就算在处理过程中出问题了,文件 f 总是会关闭。
assert断言
用于判断一个表达式,在表达式条件为 false 的时候触发异常。
断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况
语法格式如下:
assert expression [, arguments]
以下实例判断当前系统是否为 Linux,如果不满足条件则直接触发异常,不必执行接下来的代码:
import sys
assert ('linux' in sys.platform), "该代码只能在 Linux 下执行"
# 接下来要执行的代码
面向对象
Python从设计之初就已经是一门面向对象的语言
面向对象技术介绍此处不再详细介绍
Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
对象可以包含任意数量和类型的数据。
**实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
类对象
类有一个名为__init__()
的特殊方法(构造方法),该方法在类实例化时会自动调用。
__init__()
方法可以有参数,参数通过 __init__()
传递到类的实例化操作上。
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
self代表类的实例,而非类
类的方法与普通的函数只有一个区别——它们必须有一个额外的第一个参数名称, 一般它的名称是 self。self 不是 python 关键字,我们把他换成 其他 也是可以正常执行的。
继承
class DerivedClassName(BaseClassName1):
<statement-1>
.
.
.
<statement-N>
BaseClassName(示例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时:
class DerivedClassName(modname.BaseClassName):
多继承
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
父类的顺序:若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
方法重写
super() 函数是用于调用父类(超类)的一个方法。是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
以下是 super() 方法的语法:
super(type[, object-or-type])
- type – 类。
- object-or-type – 类,一般是 self
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx
代替 super(Class, self).xxx
类定义外调用父类函数也是super(type[, object-or-type])
,self改成类对象
**子类重写__init__()
方法又需要调用父类的方法:**使用super关键词:
super(子类,self).__init__(参数1,参数2,....)
父类名称.__init__(self,参数1,参数2,...)
class Son(Father):
def __init__(self, name):
super(Son, self).__init__(name)
类属性与方法
私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
类的专有方法
__init__
: 构造函数,在生成对象时调用__del__
: 析构函数,释放对象时使用__repr__
: 打印,转换__setitem__
: 按照索引赋值__getitem__
: 按照索引获取值__len__
: 获得长度__cmp__
: 比较运算__call__
: 函数调用__add__
: 加运算__sub__
: 减运算__mul__
: 乘运算__truediv__
: 除运算__mod__
: 求余运算__pow__
: 乘方
运算符重载
#!/usr/bin/python3
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)
命名空间和作用域
命名空间
命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。
命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
一般有三种命名空间:内置名称(built-in names)——Python 语言内置的名称,全局名称(global names)——模块中定义的名称,局部名称(local names)——函数中定义的名称
命名空间查找顺序:局部的命名空间 -> 全局命名空间 -> 内置命名空间。找不到就查找并引发一个 NameError 异常。
命名空间的生命周期:命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。因此,我们无法从外部命名空间访问内部命名空间的对象。
作用域
作用域就是一个 Python 程序可以直接访问命名空间的正文区域。
在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
Python的作用域一共有4种,分别是:
有四种作用域:L(Local):最内层,包含局部变量,E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。G(Global):当前脚本的最外层,B(Built-in): 包含了内建的变量/关键字等。
规则顺序: L –> E –> G –> B。
全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
global 和 nonlocal关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。
def fun1():
global num # 需要使用 global 关键字声明
print(num)
num = 123
print(num)
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num)
inner()
print(num)
标准库概览
操作系统接口
建议使用 import os
风格而非 from os import *
。这样可以保证随操作系统不同而有所变化的 os.open() 不会覆盖内置函数 open()。
针对日常的文件和目录管理任务,:shutil 模块提供了一个易于使用的高级接口:
>>> import shutil
>>> shutil.copyfile('data.db', 'archive.db')
>>> shutil.move('/build/executables', 'installdir')
文件通配符
glob模块提供了一个函数用于从目录通配符搜索中生成文件列表:
>>> import glob
>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']
命令行参数
通用工具脚本经常调用命令行参数。这些命令行参数以链表形式存储于 sys 模块的 argv 变量。
>>> import sys
>>> print(sys.argv)
['demo.py', 'one', 'two', 'three']
错误输出重定向和程序终止
sys 还有 stdin,stdout 和 stderr 属性,即使在 stdout 被重定向时,后者也可以用于显示警告和错误信息。
>>> sys.stderr.write('Warning, log file not found starting a new one\n')
大多脚本的定向终止都使用 “sys.exit()”。
字符串正则匹配
re模块为高级字符串处理提供了正则表达式工具。
>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
'cat in the hat'
如果只需要简单的功能,应该首先考虑字符串方法
数学
math
模块为浮点运算提供了对底层C函数库的访问,random
提供了生成随机数的工具。
访问 互联网
有几个模块用于访问互联网以及处理网络通信协议。其中最简单的两个是用于处理从 urls 接收的数据的urllib.request
以及用于发送电子邮件的 smtplib
:
日期和时间
datetime
模块为日期和时间处理同时提供了简单和复杂的方法。
支持日期和时间算法的同时,实现的重点放在更有效的处理和格式化输出。
该模块还支持时区处理
数据压缩
以下模块直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile。
性能度量
from timeit import Timer
有些用户对了解解决同一问题的不同方法之间的性能差异很感兴趣。Python 提供了一个度量工具,为这些问题提供了直接答案。
测试模块
doctest
模块提供了一个工具,扫描模块并根据程序中内嵌的文档字符串执行测试。
[‘primes.py’, ‘random.py’, ‘quote.py’]
### 命令行参数
通用工具脚本经常调用命令行参数。这些命令行参数以链表形式存储于 sys 模块的 argv 变量。
```python
>>> import sys
>>> print(sys.argv)
['demo.py', 'one', 'two', 'three']
错误输出重定向和程序终止
sys 还有 stdin,stdout 和 stderr 属性,即使在 stdout 被重定向时,后者也可以用于显示警告和错误信息。
>>> sys.stderr.write('Warning, log file not found starting a new one\n')
大多脚本的定向终止都使用 “sys.exit()”。
字符串正则匹配
re模块为高级字符串处理提供了正则表达式工具。
>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
'cat in the hat'
如果只需要简单的功能,应该首先考虑字符串方法
数学
math
模块为浮点运算提供了对底层C函数库的访问,random
提供了生成随机数的工具。
访问 互联网
有几个模块用于访问互联网以及处理网络通信协议。其中最简单的两个是用于处理从 urls 接收的数据的urllib.request
以及用于发送电子邮件的 smtplib
:
日期和时间
datetime
模块为日期和时间处理同时提供了简单和复杂的方法。
支持日期和时间算法的同时,实现的重点放在更有效的处理和格式化输出。
该模块还支持时区处理
数据压缩
以下模块直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile。
性能度量
from timeit import Timer
有些用户对了解解决同一问题的不同方法之间的性能差异很感兴趣。Python 提供了一个度量工具,为这些问题提供了直接答案。
测试模块
doctest
模块提供了一个工具,扫描模块并根据程序中内嵌的文档字符串执行测试。
unittest
模块不像 doctest模块那么容易使用,不过它可以在一个独立的文件里提供一个更全面的测试集:
总结
稀里糊涂地过了一遍菜鸟教程,学习了面向对象,函数,文件,输入输出,错误和异常,以及操作系统的操作,基本内容是学完了,再找点题练练手,边练边学吧