Python基础学习笔记(二) | Dayu's Blog

Python基础学习笔记(二)

四、Python异常处理

0x01 简介

  • 异常处理就是为了防止程序运行异常时候结束掉了整个程序,比如你访问一个网站,访问不通了,这种情况出现异常导致程序被结束你肯定不希望,所以用try…except..进行捕获异常,对异常进行处理,目的就是不想在异常发生时结束了程序,所以需要在try中捕获。当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块, 执行完except后,如果有finally语句块,则执行finally语句块。

0x02 格式

1
2
3
4
5
6
7
8
9
10
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError:
print "Error: 没有找到文件或读取文件失败"
else: # 如果try正常执行则执行else,如果发生异常则不执行else
print "内容写入文件成功"
fh.close()
finally:
print "不管你有没有找到,都输出我"

0x03 常用操作

1
2
3
4
5
6
try:
<语句>
except BaseException,e #前面是异常类型,后面是接受异常信息的变量参数,也可以使用except,然后什么也不加
<发生异常执行语句>
except (IOError,KeyError):
print sys.exc_info() #虽然能看到错误信息,但不能定位哪里行

如果想知道except接什么的报错类型的话即先让他报错,然后看报的是什么错误,然后再在except后面加上对应的出错的异常类型。
备注: 以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。 同时也不建议使用过多异常处理,虽然程序会减少报错,但如果在开发前报错其实是个好事,如果用太多异常,导致上线产品前会有很多要修改的隐藏bug,只不过可能被 try…except:pass给掩耳盗铃了。

0x04 raise主动抛出异常

1
2
3
4
try:
rasieException("Invalid level!", level) # 触发异常后,后面的代码就不会再执行
except:
print '主动抛出'

ps:用户也可以自定义异常,但是要注意捕捉的异常名称要对应自定义的,比如exept “dota wrong”:。

0x05 采用traceback模块查看异常

1
2
3
4
5
6
import traceback
try:
rasie
except:
traceback.print_exc() #直接屏幕输出错误
traceback.print_exc(file=open('tb.txt','w+')) #错误保存到文件中

0x06 断言

assert和if语句类似,是用来检查一个条件,但不同的是如果它为真,就不做任何事。如果它为假,则会抛出AssertError异常。
即:

1
2
3
4
5
6
a = [1,2,3,4]
assert 1 in a #不执行任何事情
assert 5 in a #抛出AssertionError报错
等价于
if not 5 in a:
rasie AssertionError

用断言的场景:
1.断言应该使用在某种情况几乎不会发生的条件下(它是为了确保代码的正确性),目的是为了保证这种小概率事件发生时能尽早终止程序
2.异常应该使用在某些问题预见性的会出现,并且你可以创建自己的异常类

五、Python输入输出

0x01 print输出

1
2
3
4
5
6
7
8
9
10
11
12
Example: 
print 'a=%d,b=%s'%(a,b) #格式化输出

print '%.2f' % a #保留两位小数或者round(a,2)

print 'I am {}'.format('dota') #格式化输出
print "my name is {name}".format(name=name)

print 'I','LOVE','YOU' # print会依次打印每个字符串,遇到逗号“,”会输出一个空格

print 'I', #输出不换行,多加个逗号即可
print 'LOVE YOU'

0x02 输入

Input()和raw_input() 区别:

  • 查看Input源码发现input也是调用了raw_input,只是做了eval处理

    1
    2
    def input(prompt):
    return eval(raw_input(prompt))
  • raw_input:它把所有的输入都直接当作一串字符,于是就可以不用加引号,只保留原始的输入,不作其他(特殊)处理,所以所得到的输入都是字符串

  • input:会去做额外的处理,比如 1 + 2,其会帮你去计算为3,而不是字符串本身的“1+2”。对应的,想要输入字符串的话,也就要自己加上对应的引号,表示所输入的是字符串类型的值了。因此总的来说一句话就是输入为数字时应该为input,输入字符串时用raw_input

0x03 密码输入

1
2
3
4
import getpass
user = getpass.getuser()
pwd = getpass.getpass("enter password for user %s: " % user)
print user, pwd

0x04 输出重定向

1
2
3
4
5
6
echo print '\hello\' > dota.py
#命令行输出重定向

with open('1.txt','a+') as fr:
print >> fr,'dota'
# print输出重定向

0x05 遇到print即换行

单独输出一个print 和print ‘\n’效果一样

0x06 pprint格式化输出

1
2
from pprint import pprint
pprint xxxx

六、Python模块

0x01 导入模块 [ 模块如果在顶层导入则作用域是全局的,如果在函数中导入是局部的。]

1
2
3
4
5
6
import module,module2,module3                    #正常导入,相当于拿车

from module import name,name2,name3 / * #从模块中导入对应的模块属性,相当于从车里拿水,面包等。

import requests as visiturl #当导入的模块名字长,你不喜欢或者导入的模块或者模块的属性已经在代码中使用
from requests import get as dota
  • 注意点1:
    不建议from module import *,因为这样有时会污染名称空间,比如导入的模块中的一个函数名跟当前代码中函数名冲突,这样就会被污染;那么为什么import module也是导入全部属性不会被污染呢,因为import module导入后访问属性是通过模块名.属性,从而避免了名称空间冲突。
    比如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from requests import get
    def get(x):
    x = x+1
    print x
    a=get(2)
    ```python
    输出则为3,即先执行了导入语句,但是接下来的函数名冲突覆盖了,导致get属性功能变了。

    * 注意点2: 一个模块导入只被加载一次,比如你导入了很多次import sys,其次他只执行一次;但是如果你想重新导入一个已经存在的模块,可以使用内置重载函数

reload(sys)

1
2
3
4
5
6
7
即:这个模块被重新导入了一次,不过需要注意的是使用reloads时候,前提该模块已经import module过。
* 注意点3
如果有特殊情况,在函数中导入相关模块的话,意味着导入的这个模块只适用在当前这个函数的作用域,局部变量。其次意味着只有执行了这个函数才会导入这个模块,否则不会导入。
```python
def a():
import requests
pass

  • 注意点4:人性化的优雅导入模块,优雅报错。

    1
    2
    3
    4
    5
    import requests
    try:
    import pymongo
    except:
    print '您缺少pymongo,请输入pip install pymongo安装。'
  • 注意点5:
    Python寻找模块的顺序
    当前进程根目录>pythonpaht环境变量指定路径>python标准库列表>路径文件(.pth)保存的目录。

  • 注意点6:
    嵌套导入
    比如我在A中导入了全部B,B中存在的模块即被我导入了,我就可以在A中使用B的模块。

0x02 作用域

locals()和globals()分别返回调用者局部和全局名称空间的字典。

1
2
3
4
5
6
for g in range(1,11): 
for h in range(1,11):
if not(globals()["a%d%d" % (g, h)] == 0):
count=count+1
ps:["a%d%d" % (g, h)]==0就相当于在{全局变量这个字典中}["agh"] == 0
比如:print globals()的全局变量是{'a11':0},这样他就确定到了a11的value是否是0,判断其是否在全局变量中

0x03 包

包就是是一个包含init.py 文件(这个文件定义了包的属性和方法)的目录,该目录下一定得有这个 init.py文件和其它模块或子包,当一个包作为模块导入的时候,实际上导入了它的init.py文件,假如init.py为空,那么仅仅导入包是什么都做不了的。如果采用from package.module import *的话,我们就要在init.py中加入all变量,从而使不同的操作系统在完全导入的时候能自己来取决不同操作系统对应的文件。

0x04 其他

sys.path.append() 添加模块路径列表
sys.modules 查看所有模块以及路径,返回字典
from future import 你希望的新功能 ,虽然可以写 import future 但是没用,因此只能采用 from … import …形式。
如果pip安装失败,可以试试apt-get install python-xxxx

七、Python函数

0x01 简介

函数就是为了方便重复使用相同的一段程序。比如:我们去餐厅吃饭,跟服务员点了菜,过了一会儿,服务员把做好的菜端上来。餐厅的厨房就可以看作是一个函数,我们点的菜单,就是给这个函数的参数;厨师在厨房里做菜的过程就是这个函数的执行过程;做好的菜是返回结果,返回到我们的餐桌上。换个例子函数也就是个你招来的工人。你给他一些材料,告诉他怎么用这些材料拼装,然后他负责把拼装好的成品交给你。 材料就是函数的参数,成品是函数的输出,而怎么拼装就是你写的函数体代码了。

0x02 定义函数

1
2
3
4
def dota( parameters ):
"函数_文档字符串"
function_suite
return [expression]

定义函数时,前提确定函数名和参数个数;如果有必要,可以先对参数的数据类型做检查。函数中任何地方的return被执行到的时候,这个函数就会结束,停止执行函数内余下的语句。return并不是必须的,当没有return,或者return后面没有返回值时,函数将自动返回None; return也可以返回多个值,用逗号分隔,相当于返回一个tuple。return语句就是将结果返回到调用的地方,并把程序控制权一起返回。

0x03 常用内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
abs(-1)
max([1,2,3])
min([1,2,3])
round(1)//1.0
dir(xx)
type(s)
callable(funcname) #判断函数是否可调用
isinstance(x,list/int) #判断类型
cmp('dota','dota') #比较
zip()
1. zip函数同时遍历两个数组
>>> A=[1,2,3]
>>> B=[4,5,6]
>>> bb=zip(A,B)
<zip object at 0x01EF08F0>
>>> for i in bb:
print(i)
(1, 4)
(2, 5)
(3, 6)
>>> for i,v in bb:
print(i,v)
-----------------------------------
2. 通过zip构造字典
>>> key=['username','pwd']
>>> values=['nini','1qaz']
>>> bb=dict(zip(key,values))
>>> print(bb)
{'pwd': '1qaz', 'username': 'nini'}

0x04 函数参数

  • 关键字参数
    调用时指定参数的名称,且与函数声明时的参数名称一致。使用关键字参数允许函数调用时参数的顺序与声明时不一致,仅根据参数的指定进行赋值。

    1
    2
    3
    4
    5
    6
    def foo(x, y):
    print 'x is %s' % x
    print 'y is %s' % y
    if __name__ == '__main__':
    foo(1, 2) # 标准调用
    foo(y = 1, x = 2) # 关键字调用
  • 默认参数
    在函数声明时,指定形参的默认值,调用时可不传入改参数(使用默认值)。
    为什么需要默认参数,比如程序员遇到刚拿到的api接口传入的参数不太确定时候就可以使用默认参数,默认参数就像安装软件那样,很多情况你对这个软件功能不太明白情况下都是采用默认安装。

    1
    2
    3
    4
    5
    def tax(cost, rate = 0.17):
    print cost * (1 + rate)
    if __name__ == '__main__':
    tax(1000) # rate使用默认值0.17
    tax(1000, 0.05) # rate指定为0.05
  • 非关键字可变长参数(元组)F(arg1)
    上面俩个方式是有多少个形参,就传进去多少个实参,但有时候会不确定有多少个参数,则此时第三种方式就比较有用,它以一个加上形参名的方式来表示这个函数的实参个数不定,可能为0个也可能为n个。注意一点是,不管有多少个,在函数内部都被存放在以形参名为标识符的元组中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def a(*x):
    if len(x) == 0:
    print 'None'
    else:
    print x
    >>>a(1)
    (1,) #存放在元组中
    >>>a(1,2,3)
    (1,2,3)
    >>> a(m=1,y=2,z=3)
    Traceback (most recent call last):
    File "<pyshell#16>", line 1, in -toplevel
    a(m=1,y=2,z=3)
    TypeError: a() got an unexpected keyword argument 'm'
  • 关键字可变长参数(字典) F(*arg1) 形参名前加俩个表示,参数在函数内部将被存放在以形式名为标识符的字典中,这时调用函数的方法则需要采用arg1=value1,arg2=value2这样的形式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def a(**x):
    if len(x) ==0:
    print 'None'
    else:
    print x
    >>>> a()
    None
    >>> a(x=1,y=2)
    {'y':2,'x':'1'}
  • 各种参数调用的顺序

    1
    2
    def dota(a,b=1,*c,**d):
    pass
  • 向函数中传入元组和字典对象

    1
    newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12})

0x05 函数对象 vs 函数调用

无论是把函数赋值给新的标识符,还是作为参数传递给新的函数,针对的都是函数对象本身,而不是函数的调用。用一个更加简单,但从外观上看,更容易产生混淆的例子来说明这个问题。例如定义了下面这个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
def func():
return "hello,world"
ref1 = func #将函数对象赋值给ref1
ref2 = func() #调用函数,函数的返回值赋值给ref1
type(ref1)
function
type(ref2)
str
通过内建的callable函数,可以进一步验证ref1是可调用的,而ref2是不可调用的,
callable(ref1)
True
callable(ref2)
False

0x06 内嵌函数

1
2
3
4
5
6
def dota1():
print 'do dota1'
def dota2():
print 'do dota2'
dota2()
f = dota1()

0x07 闭包

要形成闭包,首先得有一个嵌套的函数,即函数中定义了另一个函数,闭包则是一个集合,它包括了外部函数的局部变量,这些局部变量在外部函数返回后也继续存在,并能被内部函数引用。

1
2
3
4
5
6
def dota1(x):
def dota2(y):
return x+y
return dota2
dota = dota1(1)(2)
print dota