python 基本语法

1       python下载安装

1.1       下载地址:https://www.python.org/downloads/windows/ (windows版本)

 

 

1.2       双击exe文件进行安装

如下图,并按照圈中区域进行设置,切记要勾选打钩的框,然后再点击Customize installation进入到下一步:

 

图1-2-1

 

图1-2-2

 

图1-2-4

1.3       验证安装是否成功

WIN+R(快捷键)输入cmd打开命令行窗口,输入pyhton,显示如图1-3-1所示,说明安装成功。

 

图1-3-1

1.4       下载python IDE wing

1.4.1下载地址:http://wingware.com/downloads/wing-personal

 

图1-4-1

1.5       安装wing

安装路径最好安装在英文路径下,否则有可能会出错,安装基本都是点击下一步即可,这里就不一步步讲述了。

1.5.1安装完成后点击电脑左边的开始菜单,查看如图1-5-1所示的图标,双击图标运行wing IDE

 

图1-5-1

1.5.1         Wing的简单使用

如图1-6-1和1-6-2所示,

 

图1-6-1

 

图1-6-2

#coding=utf-8  #文件中含有中文注释需加上,必须将编码注释放在第一行或者第二行

注:

python相关模块下载

https://www.lfd.uci.edu/~gohlke/pythonlibs/

1.6       pycharm下载

访问:https://www.jetbrains.com/pycharm/download/#section=windows

1.7       Linux下安装

https://www.python.org/downloads/source/

[安装python依赖包]

yum -y install bzip2-devel libssl-dev libffi-devel python-devel libgsasl gcc cyrus-sasl-*

        yum install bzip2-devel

yum -y install libssl-dev

yum -y install libffi-devel

yum -y install python-devel

yum -y install libgsasl

yum -y install gcc

yum -y install cyrus-sasl-*

[查看是否安装]

yum list installed bzip2-devel ,libssl-dev, libffi-devel,python-devel, libgsasl,gcc, cyrus-sasl-*

官网下载Python-3.5.6.tgz,上传至服务器下/usr/lib目录(需gcc编译环境)

(1)    解压文件

执行命令:tar -xvf Python-3.5.6.tgz

 

(2)    查看解压后文件

执行命令:ls

 

(3)    创建安装文件路径

执行命令:mkdir /usr/local/python3

 

(4)    进入解压目录

执行命令:cd /usr/lib/ Python-3.5.6

(5)    编译

执行命令:./configure --prefix=/usr/local/python3

 

(6)    安装

执行命令:make

执行命令:make install

 

(7)    查看安装是否成功

执行命令:ls /usr/local/python3/

执行命令:/usr/local/python3/bin/python3

 

若/usr/local/python3/目录下有4目录,执行/usr/local/python3/bin/python3命令有

python3.5.6的字样说明安装成功

(8)    创建新版本的软连接

<1> 修改旧版本

执行命令:mv /usr/bin/python /usr/bin/python_bak

 

<2> 创建新的软连接

执行命令:ln -s /usr/local/python3/bin/python3 /usr/bin/python

 

<3> 检查python的版本

执行命令:python -V

       

(9)    pip下载安装

wget https://files.pythonhosted.org/packages/ae/e8/2340d46ecadb1692a1e455f13f75e596d4eab3d11a57446f08259dee8f02/pip-10.0.1.tar.gz

tar xf pip-10.0.1.tar.gz

cd pip-10.0.1

python setup.py install #安装

ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3

 

l  yum -y install bzip2-devel ,zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel

l  wget https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tgz

l  mkdir -p /usr/local/python3

l  tar -zxvf Python-3.6.3.tgz

l  cd Python-3.6.3

l  ./configure --prefix=/usr/local/python3

l  make && make install

l  ln -s /usr/local/python3/bin/python3 /usr/bin/python3  

l  ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3

 

1.8       pip镜像源配置

  • pip国内的一些镜像:

阿里云 https://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

豆瓣(douban) http://pypi.douban.com/simple/

清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/

中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/

  • 临时使用pip源

pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

  • 设置为默认

pip config set global.index-url http://mirrors.aliyun.com/pypi/simple

pip config set global.trusted-host mirrors.aliyun.com

  • 修改配置文件

Windows:直接在user目录中创建一个pip目录,如C:UsersoyueAppDataRoamingpip,新建文件pip.ini,内容如下:

[global]

index-url = http://mirrors.aliyun.com/pypi/simple

trusted-host = mirrors.aliyun.com

 

linux:修改 ~/.pip/pip.conf (没有就创建一个), 内容如下:

[global]

index-url = http://mirrors.aliyun.com/pypi/simple

trusted-host = mirrors.aliyun.com

2       pyton基本操作

2.1       六个标准类型

(1)Numbers(数字)

        Integer 整型

        Boolean 布尔型 True  False

        Long integer 长整型

        Floating point real number 浮点型

        Complex number 复数型

如:>>>com=23+1.2j   

>>>com.real  #该复数的实部

23

>>>com.imag #该复数的虚部

1.2

(2) String (字符串)

(3)   List (列表)

(4)   Tuple (元组)

(5)  Set  (集合)

(6)   Dictionary (字典)

2.2        Numbers

[example]

#!/usr/bin/env python

# -*- coding:utf-8 -*-

a=100         #整型

b=100.01      #浮点型

a1,b1,c1 = 10.1,90,True  #多个变量赋值

a2=b2=c2=1

print(a,b)     #10 100.01

print(a1,b1,c1)   #10.1 90 True

print(a2,b2,c2) #1 1 1

2.3       String (字符串)

>>>a=’abcdef’

>>>a[0]

‘a’

#字符串切片[start:end:step]

>>>a[0::2]  #取从0开始到结束,步长为2的字符串

‘ace’

>>>a[::-1]  #取反

‘fedcba’

2.3.1         常用方法

‘’.join([‘a’,’b’,’c’])     #’abc’ [将列表转换成字符串]

stc=" aBc_dEf"

stc.capitalize()   [首字母大写]

stc.lower()       [大写字符为小写,ASCII编码,也就是‘A-Z’有效]

stc.casefold()    [大写转换为小写,(非汉语或英文)]

stc.center(9,'*') ['*aBc_dEf*',原字符串居中,并使用特定字符填充新字符串(默认空格)]

stc.ljust(10,'*') [' aBc_dEf**',字符填充(左边对齐)]

stc.rjust(10,'*') ['** aBc_dEf',字符填充(右边对齐)]

stc.count('a')    [1,计算某个字符出现的次数]

stc.encode()      [b'aBc_dEf',压缩]

stc.find('a')     [0,否则返回-1]

stc. partition(‘_’) ('aBc', '_', 'dEf') 指定返回一个3元的元组

stc.zfill(12)     ['00000aBc_dEf', 定字符串的长度。原字符串右对齐,前面填充0]

stc.split()       [指定分隔符对字符串进行切片,默认为空格、换行( )、制表符( )等]

stc.strip('af')   [删除字符串中开头、结尾处字符,结果为' Bc_dE',默认 空格 ]

stc.rstrip('af')  [删除以af结尾的字符]

stc.lstrip('af')  [删除以af开头的字符]

stc.expandtabs(tabsize=8) [字符串中的 tab 符号(' ')转为空格,tab 符号(' ')默认的空格数是 8]

'ab er'.splitlines(True)       ['ab', 'er']# (' ',' ', ' ')分隔,True:包括换行符

{:>18,.2f}'.format(70305084.0)   [:冒号+空白填充+右对齐+固定宽度18+浮点精度.2+浮点数声明f]

  • isdigit()

True: Unicode数字,byte数字(单字节),全角数字(双字节),罗马数字

False: 汉字数字

Error: 无

  • isdecimal()

True: Unicode数字,,全角数字(双字节)

False: 罗马数字,汉字数字

Error: byte数字(单字节)

  • isnumeric()

True: Unicode数字,全角数字(双字节),罗马数字,汉字数字

False: 无

Error: byte数字(单字节)

2.4       列表

2.4.1基本操作

>>>alist=[1,2,3,4]

>>>alist[2:]    

[3, 4]

>>>alist[2:3]  

[3]

>>> alist[-3:-1]

[2, 3]

>>> alist.append(4)  [添加一个对象]

>>>alist

  [1, 2, 3, 4, 4]

>>>alist.count(4)    [返回一个对象在列表中出现的次数]

  2

>>>alist.extend([1,2])     [把序列seq的内容添加到列表末尾]

>>>alist

[1, 2, 3, 4, 4, 1, 2]

>>>alist.index(4,0,len(alist))   [返回alist[k]==obj的索引值,并k范围[0,len(alist)],否则引发错误]

3

>>>alist.index(3) [从列表中找出某个值第一个匹配项的索引位置]

>>>alist.remove(4) [从列表中删除对象obj]

>>>alist

[1, 2, 3, 4, 1, 2]

>>>del alist[1]

>>>alist

[1, 3, 4, 1, 2]

>>>alist.insert(0,'a')  [在索引位置插入对象]

>>>alist

['a', 1, 3, 4, 1, 2]         

>>>alist.pop(2)               [移除列表中的第3个元素(默认最后一个)]

>>>alist

['a', 1, 4, 1, 2]

>>>alist.reverse()   [列表反转]

>>>alist

[2, 1, 4, 1, 'a']

>>>len(alist)           [计算列表的长度]

  5

>>>all(alist) [检查alist中的所有项是否为True]

>>>any(alist) [检查alist中的任意项是否为True]

>>>a=[1,2,3]

>>>b=['a','b','c']

>>>b[1:1]=a

>>>b  

['a',1,2,3,'b','c']

>>> b[::-1]

 ['c', 'b', 3, 2, 1, 'a']

2.4.1         对列表中字典排序

d=[{'age':23,'name':';liming'},{'age':13,'name':'liming'},{'age':43,'name':'liming'},

{'age':31,'name':'liming'}]

(1)方法一

>>>d.sort(key=lambda obj:(obj.get('age')))

>>>d

  [{'age': 13, 'name': 'liming'}, {'age': 23, 'name': ';liming'}, {'age': 31, 'name': 'liming'}, {'age': 43, 'name': 'liming'}]

(2)方法二

>>>d=sorted(d,key= lambda x:x[‘select’])

  [{'age': 13, 'name': 'liming'}, {'age': 23, 'name': ';liming'}, {'age': 31, 'name': 'liming'}, {'age': 43, 'name': 'liming'}]

(3)方法三

>>>x = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

>>>sorted(x, key=operator.itemgetter('name'), reverse=True)

[{'age': 39, 'name': 'Homer'}, {'age': 10, 'name': 'Bart'}]

2.4.3列表解析

>>> [(x+1,y+1) for x in range(3) for y in range(5)] 

  [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2),

(2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]

[Example]

IndexList=[{'Field': 'ITEM_NAME', 'ColName': 'B'}, {'Field': 'TYPE', 'ColName': 'E'}, {'Field': 'NO', 'ColName': 'C'}]

 Fields = [ d['Field'] for d in IndexList if d['ColName'] != 'B']

 list(map(lambda x:'T1.'+x+'=T.'+x,Fields))

2.5       元祖

>>>app=('abc','def')

>>>app[:1]

('abc',)

>>>t=(1,2,4,[1,2,3])

>>>id(t)

  1223379404264

>>>t[-1][0]='ttt'

>>>t

(1, 2, 4, ['ttt', 2, 3])

>>>id(t)

  1223379404264

注:元组的值不能被修改

        不能单独的删除元组中的一个元素(del 元组名,删除元组)

        当元组里面只有一个元素时需要在元组分割符里加一个逗号

虽然元组本身不可变,但如果元组内嵌套了可变类型的元素,那么此元素的

修改不会返回新元组。

2.6       集合

>>>S1={'a','b','c'}

>>>S2={'a','b1'}

>>>S1|S2

{'c', 'b', 'a', 'b1'}

>>>S1&S2

{'a'}

>>>S1-S2

{'c', 'b'}

>>>S2-S1

{'b1'}

>>>set("123")

{'1', '3', '2'}

>>>frozenset("123")

frozenset({'1', '3', '2'})

2.7       字典

2.7.1基本操作

>>> dict(a="12",b="hello")

  {'b': 'hello', 'a': '12'}

>>> adict = { 'name':'lucy', 'age':'40'}

>>> dicts=dict((['a',1],['b',2]))  #创建字典

>>> adict['addr']='GD'        #添加一个项

>>> adict

{'addr': 'GD', 'age': '40', 'name': 'lucy'}

>>>adict['age'] =20           #更新一个数据项

>>>adict

{'addr': 'GD', 'age': 20, 'name': 'lucy'}

>>>dict.fromkeys(['a','b','c'],10)  #用于创建一个新字典

{'c': 10, 'b': 10, 'a': 10}

>>>adict.pop('age')  #删除指定键age

>>> adict

{'addr': 'GD', 'name': 'lucy'}

>>> adict.values()   #返回一个列表,包含字典中所有的值

dict_values(['GD', 'lucy'])

>>> adict.keys()    #返回一个列表,包含字典中所有的键

dict_keys(['addr', 'name'])

>>> adict.items()   #返回一个包含所有(键,值)元祖的列表

  dict_items([('addr', 'GD'), ('name', 'lucy')])

>>>adict.get('addr')  #返回一个键的值(若键不存在,返回错误)

  'GD'

>>> adict.update({'test':12})  #将一个字典的内容添加到另一个字典中

>>>adict

{'test': 12, 'addr': 'GD', 'age': '40', 'name': 'lucy'}

>>> adict1=adict.copy()         #浅复制

>>> adict.clear() #删除字典中的所有项或元素

>>>adict

  {}

>>> adict.setdefault('kk',90)  #检查字典中是否含有某键,如果存在,取它的值,

若不存在,就给这个键赋默认值并返回此值

  {'kk': 90}

[Example]

dict1 = {'a': 10, 'b': 8}

dict2 = {'d': 6, 'c': 4}

d={**dict1,**dict2} # {'a': 10, 'c': 4, 'd': 6, 'b': 8}

2.8       类

1)        _xxx: "单下划线 " 开始的成员变量叫做保护变量,意思是只有类对象(即类实例)和子类对象自己能访问到这些变量,需通过类提供的接口进行访问,不能用'from module import *'导入。

2)        __xxx:类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。)," 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

3)        __xxx__:系统定义名字,前后均有一个“双下划线” 代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。

2.8.1         魔术方法

2.8.1.1  __getitem__()、__call__()、__repr__()

__getitem__():可以让对象实现迭代功能,可以像访问列表那样调用

__call__():允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的

__repr__():不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了

class A:

    def __init__(self):

        print("init")

       

    def __getitem__(self,a):

        print("__getitem__")

       

    def __call__(self):

        print("call")

       

    def __repr__(self):

        return "__repr__"

       

a = A()

a()      #调用__call__()

a[1]     #调用__getitem__()

print(a)  #调用__repr__()

2.8.1.2  __new__、 __init__、 __del__

#!-*- coding:utf-8 -*-

class T:

def __new__(cls):

print('{}.__new__'.format(cls.__name__))

return super().__new__(cls)

   

def __init__(self):

print('{}.__init__'.format(self.__class__.__name__))

#return None 只能返回None

def __del__(self):

print('{}.__del__'.format(self.__class__.__name__))

             

t=T()

Result:

T.__new__

T.__init__

T.__del__

2.8.1.3  __new__与__init__区别

__new__:创建对象时调用,会返回当前对象的一个实例

__init__:创建完对象后调用,对当前对象的一些实例初始化,无返回值

1、  在类中,如果__new__和__init__同时存在,会优先调用__new__

2、  __new__方法会返回所构造的对象,__init__则不会。__init__无返回值

2.8.2         可迭代类

class MyRange:

    def __init__(self,n):

        self.i = 0

        self.n= n

       

    def __iter__(self):

        return self

   

    def __next__(self):

        if self.i <self.n:

            self.i +=1

            return self.i

        else :

            raise StopIteration

xrange = MyRange(4)

for i in xrange:

print(i)

Result:

1  2  3  4

class A:

        def __init__(self,arg):

                print "enter A"

                print "leave A"

        def add(self,x):

                return x+6

class B(A):

        def __init__(self):

                print "enter B"

                A.__init__(self,arg)

                print "leave B"

                #super().__init__(arg)

        def add(self,x):

                super().add(x)

2.8.3         多继承的例子

例1

class A(object):

    def __init__(self, x=0):

        print('A',x)

        self._x = x

class B(object):

    def __init__(self, y=0):

        print('B',y)

        self._y = y

class C(object):

    def __init__(self, z=0):

        print('C',z)

        self._z = z

class D(A,B,C):

    def __init__(self,x,y,z):

        print('D')

        super(D,self).__init__(x)

        super(A,self).__init__(y)

        super(B,self).__init__(z)

print(D.__mro__)

obj_d = D('x','y','z')

(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)

D

A x

B y

C z

注:

多重继承的继承顺序实际上使用的MRO算法来排序,可以输出mro查看继承顺序,例如上面代码的继承顺序为上面的代码输出结果

所以根据mro算法的继承顺序:

super(D,self).init(x) 就是去初始化D的上一个类,也就是去初始化A

super(A,self).init(y) 就是去初始化A的上一个类,也就是去初始化B

super(B,self).init(z) 就是去初始化B的上一个类,也就是去初始化C

例2

class D:

    def say_hello(self):

        print('D')

class B(D):

    def say_hello(self):

        print('B')

        super().say_hello()

       

class C(D):

    def say_hello(self):

        print('C')

        super().say_hello()

class A(B,C):

    def say_hello(self):

        print('A')

        super().say_hello()

       

print(A.__mro__)

a=A()

a.say_hello()

result:

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)

A

B

C

D

例3

class A:

    def go(self):

        print('go A go')

class B:

    def go(self):

        print('go B go')

class C(A):

    def go(self):

        super().go()

        print('go C go')

class D(B):

    def go(self):

        super().go()

        print('go D go')

class E(A):

    def go(self):

        super().go()

        print('go E go')

class F(C,E):

    def go(self):

        super().go()

        print('go F go')

class G(F,D):

    def go(self):

        super().go()

        print('go G go')

g=G()

g.go()

print(G.__mro__)

result:

go A go

go E go

go C go

go F go

go G go

(<class '__main__.G'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class 'object'>)

2.9       内置方法

2.9.1         方法列表

内置方法

描述

__init__(self,...)

初始化对象,在创建新对象时调用

__del__(self)

释放对象,在对象被删除之前调用

__new__(cls,*args,**kwd)

实例的生成操作

__str__(self)

在使用print语句时被调用

__repr__(self)

_str__ 方法其实调用了 __repr__ 方法

__getitem__(self,key)

获取序列的索引key对应的值,等价于seq[key]

__len__(self)

在调用内联函数len()时被调用

__cmp__(stc,dst)

比较两个对象src和dst

__getattr__(s,name)

获取属性的值

__setattr__(s,name,value)

设置属性的值

__delattr__(s,name)

删除name属性

__getattribute__()

__getattribute__()功能与__getattr__()类似

__gt__(self,other)

判断self对象是否大于other对象

__lt__(slef,other)

判断self对象是否小于other对象

__ge__(slef,other)

判断self对象是否大于或者等于other对象

__le__(slef,other)

判断self对象是否小于或者等于other对象

__eq__(slef,other)

判断self对象是否等于other对象

__call__(self,*args)

把实例对象作为函数调用

2.9.2       type() 函数如果你只有第一个参数则返回对象的类型,三个参数返回新的类型对象

  • 返回对象的类型

语法:type(object)

type( 12 ) == int # 判断类型是否相等

True

  • 返回一个对象

语法:type(name, bases, dict)

d = type("A",(object,),{"a":"hello","b":"world"})

d.a #hello

d.b #world

2.9.3         isinstance()

语法:isinstance(object, classinfo)

object -- 实例对象。

classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。

>>>a = 2

>>> isinstance (a,int) True

>>> isinstance (a,str) False

>>> isinstance (a,(str,int,list)) # 是元组中的一个返回 True True

2.9.4         enumerate()

语法:enumerate(sequence, [start=0])

参数:

sequence -- 一个序列、迭代器或其他支持迭代对象。

start -- 下标起始位置。

for i, element in enumerate((1,2,3,4),1):

print(i,element)

2.9.5         issubclass()

issubclass() 方法用于判断参数 class 是否是类型参数 classinfo 的子类。

语法:issubclass(class, classinfo)

参数:

class -- 类。

classinfo -- 类。

2.10    生成器与迭代器

对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数

迭代器有两个基本的方法:iter() 和 next()

如:

iter([1,2,3,4]) # 创建迭代器对象

2.10.1      生成器定义

在Python中,一边循环一边计算的机制,称为生成器:generator。

send():生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。

throw():用来想生成器函数传入一个异常,可以结束系统定义的异常,或者自定义的异常

2.10.2      为什么要有生成器

列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。

如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

2.10.3      如何创建生成器

l  第一种方法

只要把一个列表生成式的[]改成(),就创建了一个generator

>>> L = [x * x for x in range(10)]

>>> L

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

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

>>> g

<generator object <genexpr> at 0x000001DE8D5B21A8>

>>>g.__next__()

>>>next(g)

l  方法二

如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象

def gen():

    for i in range(5):

        yield i

print(type(gen()))    #<class 'generator'>   

for d in gen():

print(d)    #0 1 2 3 4

2.10.4      判断对象是否可迭代

原生函数iter(instance) 可以判断某个对象是否可迭代,它的工作流程大概分为以下3个步骤:

l  检查对象instance是否实现了__iter__方法,并调用它获取返回的迭代器(iterator)。

l  如果对象没有实现__iter__方法,但是实现了__getitem__方法,Python会生成一个迭代器。

l  如果上述都失败,则编译器则抛出TypeError错误,‘xxx' Object is not iterable

2.10.5      创建一个迭代器

把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__()

__iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。

__next__() 方法(Python 2 里是 next())会返回下一个迭代器对象

2.11    异常

2.11.1      1、try…except…else

try:

    fd = open('file','rb')

except IOError as err:

    print(err)

else:

print('else')

2.11.2      2、try…except…finally

try:

    fd = open('file','rb')

except IOError as err:

    print(err)

finally:

fd.close(fd)

2.11.3      触发异常(raise)

def CrossProduct(seqq,seq2):

        if not seq1 or not seq2:

                raise ValueError,”Sequence argements must be non-empty”

        return [(x1,x2) for x1 in seq2 for x2 in seq2]

2.11.4      异常类型

异常名称

描述

AttributeError

试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x

IOError

输入/输出异常;基本上是无法打开文件

ImportError

无法引入模块或包;基本上是路径问题或名称错误

IndentationError

语法错误(的子类) ;代码没有正确对齐

IndexError

下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]

KeyError

试图访问字典里不存在的键

KeyboardInterrupt

Ctrl+C被按下

NameError

尝试访问一个没有申明的变量

SyntaxError Python

代码非法,代码不能编译(个人认为这是语法错误,写错了)

TypeError

传入对象类型与要求的不符合

UnboundLocalError

试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它

ValueError

传入一个调用者不期望的值,即使值的类型是正确的

BaseException    所有异常的基类
SystemExit    解释器请求退出
KeyboardInterrupt    用户中断执行(通常是输入^C)
Exception    常规错误的基类
StopIteration    迭代器没有更多的值
GeneratorExit    生成器(generator)发生异常来通知退出
StandardError    所有的内建标准异常的基类
ArithmeticError    所有数值计算错误的基类
FloatingPointError    浮点计算错误
OverflowError    数值运算超出最大限制
ZeroDivisionError    除(或取模)零 (所有数据类型)
AssertionError    断言语句失败
AttributeError    对象没有这个属性
EOFError    没有内建输入,到达EOF 标记
EnvironmentError    操作系统错误的基类
IOError    输入/输出操作失败
OSError    操作系统错误
WindowsError    系统调用失败
ImportError    导入模块/对象失败
LookupError    无效数据查询的基类
IndexError    序列中没有此索引(index)
KeyError    映射中没有这个键
MemoryError    内存溢出错误(对于Python 解释器不是致命的)
NameError    未声明/初始化对象 (没有属性)
UnboundLocalError    访问未初始化的本地变量
ReferenceError    弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError    一般的运行时错误
NotImplementedError    尚未实现的方法
SyntaxError    Python 语法错误
IndentationError    缩进错误
TabError    Tab 和空格混用
SystemError    一般的解释器系统错误
TypeError    对类型无效的操作
ValueError    传入无效的参数
UnicodeError    Unicode 相关的错误
UnicodeDecodeError    Unicode 解码时的错误
UnicodeEncodeError    Unicode 编码时错误
UnicodeTranslateError    Unicode 转换时错误
Warning    警告的基类
DeprecationWarning    关于被弃用的特征的警告
FutureWarning    关于构造将来语义会有改变的警告
OverflowWarning    旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning    关于特性将会被废弃的警告
RuntimeWarning    可疑的运行时行为(runtime behavior)的警告
SyntaxWarning    可疑的语法的警告
UserWarning    用户代码生成的警告

2.12    函数

函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。

任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。

函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。

函数内容以冒号起始,并且缩进。

return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

arg=12

alist=[1,2,3]

adict={'a':1,'b':2}

def test(arg,*list,**adict):

    print(arg)

    print(list)

    print(adict)

if __name__ == '__main__':   

    test(arg,*alist,**adict)

    #12

    #(1, 2, 3)

#{'b': 2, 'a': 1}

2.13    命名空间和作用域

2.13.1      命名空间

命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

一般有三种命名空间:

内置名称(built-in names, Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。

全局名称(global names,模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

局部名称(local names,函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

命名空间的生命周期:

命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。因此,我们无法从外部命名空间访问内部命名空间的对象。

2.13.2      作用域

Python的作用域一共有4种,分别是:

L(Local:最内层,包含局部变量,比如一个函数/方法内部。

E(Enclosing:包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

G(Global:当前脚本的最外层,比如当前模块的全局变量。

B(Built-in: 包含了内建的变量/关键字等。,最后被搜索

规则顺序: L –> E –> G –>B。

2.13.3      global 和 nonlocal关键字

global关键字:用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。

nonlocal关键字:用来在函数或其他作用域中使用外层(非全局)变量。

#!/usr/bin/python3

num = 1

def fun1():

    global num  # 需要使用 global 关键字声明

    print(num)

    num = 123

    print(num)

fun1()

print(num)

输出结果:

1

123

123

【如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下实例】

#!/usr/bin/python3

def outer():

    num = 10

    def inner():

        nonlocal num   # nonlocal关键字声明

        num = 100

        print(num)

    inner()

    print(num)

outer()

以上实例输出结果:

100

100

3       常用函数

3.1       format

l  通过对象的属性

    class Names():

        name1='Kevin'

        name2='Tom'

print('hello {names.name1} iam {names.name2}'.format(names=Names))

#hello Kevin i am Tom

l  使用魔法参数

    args=['lu']

    kwargs = {'name1': 'Kevin', 'name2': 'Tom'}

    print('hello {name1} {} i am {name2}'.format(*args, **kwargs))  # hello Kevin i am Tom

l  格式转换

b、d、o、x分别是二进制、十进制、八进制、十六进制

数字

格式

输出

描述

3.1415926

{:.2f}

3.14

保留小数点后两位

3.1415926

{:+.2f}

3.14

带符号保留小数点后两位

2.71828

{:.0f}

3

不带小数

1000000

{:,}

1,000,000

以逗号分隔的数字格式

0.25

{:.2%}

25.00%

百分比格式

1000000000

{:.2e}

1.00E+09

指数记法

25

{0:b}

11001

转换成二进制

25

{0:d}

25

转换成十进制

25

{0:o}

31

转换成八进制

25

{0:x}

19

转换成十六进制

                                                   

l  对齐与填充

    数字        格式        输出        描述

    5          {:0>2}      05        数字补零 (填充左边, 宽度为2)

    5          {:x<4}      5xxx 数字补x (填充右边, 宽度为4)

    10        {:x^4}      x10x 数字补x (填充两边, 宽度为4)

    13        {:10} 13        右对齐 (默认, 宽度为10)

    13        {:<10}      13        左对齐 (宽度为10)

    13        {:^10}      13        中间对齐 (宽度为10)

l  转义{和}符号

    print('{{ hello {0} }}'.format('Kevin'))#{ hello Kevin }

l  format作为函数

    f = 'hello {0} i am {1}'.format

    print(f('Kevin','Tom'))#'hello Kevin i am Tom'

l  {}内嵌{}

    print('hello {0:>{1}} '.format('Kevin',10)) #hello      Kevin

l  叹号的用法

   !后面可以加s r a 分别对应str() repr() ascii()

    print("{!s}".format('2'))  #2

    print("{!r}".format('2'))  #'2'

3.2       print

print(*objects,sep=' ',end=' ',file=sys.stdout)

    objects -- 复数,表示可以一次输出多个对象。输出多个对象时,需要用 , 分隔。

    sep -- 用来间隔多个对象,默认值是一个空格。

    end -- 用来设定以什么结尾。默认值是换行符 ,我们可以换成其他字符串。

    file -- 要写入的文件对象

[Example]

    a,b=1,2

    print(a,b,sep="|",end='***') #1|2***

   

    f=open(path,'a')

    print('hello test',file=f)

    f.close()

3.3       map、filter、reduce

1.map

map(function, sequence):接收两个参数,一个是函数,一个是序列

>>>def square(x):

…  return x**2

>>>list(map(square,[1,2,3,4,5]))

  [1,4,9,16,25]

>>> alist = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数

>>> list(alist)

[1,4,9,16,25]

>>> list(map(int,'1234'))

  [1, 2, 3, 4]

# 提供了两个列表,对相同位置的列表数据进行相加

>>> d = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])

>>>list(d)

[3, 7, 11, 15, 19]

2.filter

filter(function, sequence):接收两个参数,一个是函数,一个是序列

def fun_filter(num):

if num>4:

        return num

    else:

        return 0

>>>alist= filter(fun_filter,[12,4,6,8,9])

>>>list(alist)

  [12, 6, 8, 9]

3 reduce

reduce函数,reduce函数会对参数序列中元素进行累积

from functools import reduce

>>>a=[13,4,5,78,23,5,7,10]

>>>def fun_reduce(x,y):

…     return x+y

>>>r=reduce(fun_reduce,a)

  145

>>> reduce(lambda x, y: x + y, [2, 3, 4, 5, 6], 1)

21

3.4       sort与sorted

sort方法用来对列表中元素进行排序,默认直接比较列表中元素的大小

>>>alist=[5,2,'a','6',9,0]

>>>alist.sort(key=str)

>>>alist

>>>[0, 2, 5, '6', 9, 'a']

sorted可以对任意的序列进行排序,排序不会影响原来的对象,而是返回一个新的对象

alist=[4,'3',9,'6',2]

d=sorted(alist,key=int)

[2, '3', 4, '6', 9]

 

from operator import itemgetter, attrgetter

d = {'data1':3, 'data2':1, 'data3':2, 'data4':4} 

sorted(d.items(), key=itemgetter(1), reverse=True)

[('data4', 4), ('data1', 3), ('data3', 2), ('data2', 1)]

3.5       文件操作

>>>fd = open(file_name,access_mode='r')

access_mode:

        r:读(默认)

        w:写

        r+、w+:以读写模式打开

        a:以追加的方式打开(若没有此文件则创建)

        ab:以二进制追加方式打开(若没有此文件则创建)

        rb:以二进制读模式打开

        wb:以二进制写模式打开

        rb+、wb+:以二进制读写模式打开

>>>fd.read()

>>>fd.readline()

>>>fd.readlines()

>>>fd.write()

>>>fd.seek(offset,whence)

offset:偏移量

whence:

         0 文件开头

         1 文件当前位置

         2 文件结尾

>>>fd.tell()  #显示当前文件指针的位置

模块

import os

os.name         #判断现在正在实用的平台,Windows 返回‘nt'; Linux 返回’posix'

os.system('ping http://www.baidu.com')      #执行系统命令

os.listdir("G:python")   #列出指定目录文件

os.makedirs(path)         #多层创建目录

os.mkdir(path)               #创建目录

os.remove()                    #删除指定文件

os.rmdir()                       #删除指定目录

os.walk(fileDir)               #递归查找文件

os.rename('src_test.txt', 'des_test.txt')         #重命名

os.getcwd()                    #获得当前工作的目录

os.path.sep                    #window下返回\

os.path.split()                #已元祖的形式返回路径的目录和文件名

os.path.isabs(path)        #判断是否为绝对路径

os.path.isdir(name)        #判断name是不是一个目录,name不是目录就返回false

os.path.isfile(name)       #判断name是不是一个文件,不存在name也返回false

os.path.exists(name)     #判断是否存在文件或目录name

os.path.getsize(name)   #获得文件大小,如果name是目录返回0

os.path.abspath('osTest.py') #返回绝对路径

os.path.dirname(path)          #返回目录路径

os.path.basename(path)       #返回文件名

os.path.join(path, name)       #连接目录和文件名

os.path.splitdrive('c:\windows')  #('c:', '\windows')

os.path.getatime(file)   #输出最近访问时间

os.path.getctime(file)   #输出文件创建时间

os.path.getmtime(file)  #输出最近修改时间

time.gmtime(os.path.getmtime(file))   #以struct_time形式输出最近修改时间

import time

time.sleep(1) #睡眠1秒

time.clock()    #[第一次输出的是程序运行时间第一次调用,而第二次之后的调用是自第一次调用以后到现在的运行时间]

time.asctime()         #Sat Nov 11 13:51:37 2017,没有参数,将会将time.localtime()作为参数传入

time.ctime()           #Sat Nov 11 13:51:37 2017,默认time.time()为参数

time.localtime(([secs])   #[time.struct_time(tm_year=2017, tm_mon=11, tm_mday=11, tm_hour=13, tm_min=51, tm_sec=37, tm_wday=5, tm_yday=315, tm_isdst=0)]

time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime())       #格式化的时间字符串,默认传入时间time.localtime()

time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X')  #把一个格式化时间字符串转化为struct_time

import datetime

datetime.datetime.now()       #2017-11-11 14:24:26.642000

datetime.date.today()            #获取当天日期

datetime.date.today() + datetime.timedelta(days=1)   #获取明天/前N天

dt = datetime.datetime.strptime("2012-09-12 21:08:12", "%Y-%m-%d %H:%M:%S")

>>>dt.year

  2012

>>>dt.date()

datetime.date(2012, 9, 12)

import sys

sys.exit(n)       #退出程序

sys.argv[0]    #获取外部向程序内部传递参数

sys.path      #获取指定模块搜索路径的字符串集合

sys.path.append('H:/youBest/python/SQLparse')

sys.path.append('/usr/src/CamProject')      #添加模块搜索路径

sys.path.insert(0,'/usr/src/CamProject')      #如果想放在第一位,可以执行

3.6       内置函数

>>>bin(10)  #二进制

  '0b1010'

>>>oct(7)   #八进制

  '0o7'

>>>hex(10)  #十进制

  '0xa'

>>>int(‘12’)  #整型

  12

>>>float(‘12’) #浮点型

  12.0

>>>min(1,3,34,56,6)

  1

>>>max(1,3,34,56,6)

  56

>>> sum([0,1,2])

  3

>>>chr(65)

  ‘A’

>>>ord(‘A’)

  65

>>>repr('hello')

  "'hello'"

>>>str(78)

  ‘78’

>>> eval("[1,2,'ok']")

  [1, 2, 'ok']

enumerate([1,2,3]) #将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列

hasattr(object, name)          #判断一个对象里面是否有name属性或者name方法

getattr(object, name[,default]) #获取对象object的属性或者方法

setattr(object, name, values)        #给对象的属性赋值,若属性不存在,先创建再赋值

[例子,test.py]

        class test:

                name="xiaohua"

                def run(self):

                       return "HelloWord"

        t=test()

        hasattr(t,'age') #False

        setattr(t, "age", "18")   #为属相赋值,并没有返回值

        getattr(t, "name") #打印xiaohua

        getattr(t, "run")() #运行run方法

       

        EngineModel = __import__('test')

        EngineClass = getattr(EngineModel,'test')

    EngineObj = EngineClass()

        EngineFun = getattr(EngineObj,'run')

        RetData = EngineFun() #执行函数

locals() 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行修改,修改的是拷贝,而对实际的局部名字空间中的变量值并无影响。

globals() 返回的是实际的全局名字空间,而不是一个拷贝与 locals 的行为完全相反,所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量的取值。

ascii(object) 返回一个对象可打印的 ASCII 编码的字符,生成的字符串和 Python 2 的 repr()返回的结果相似。

isinstance(object, classinfo) 判断object对象是不是classinfo类,如果 object 实参是 classinfo 实参的实例,或者是(直接、间接或 虚拟)子类的实例,则返回 true。

issubclass(class, classinfo) 判断class是否是classinfo的子类如果 class 是 classinfo 的子类(直接、间接或 虚拟 的),则返回 true。classinfo 可以是类对象的元组,此时 classinfo 中的每个元素都会被检查。其他情况,会触发 TypeError 异常。

enumerate(iterable, start=0) 返回一个枚举对象,enumerate()返回一个元组,里面包含一个计数值(从 start 开始,默认为 0)和通过迭代 iterable 获得的值。iterable 必须是一个序列,或 迭代器,或其他支持迭代的对象。

vars() 函数返回对象object的属性和属性值的字典对象。

memoryview() 函数返回给定参数的内存查看对象(Momory view)。

3.7       编码转码

bytes(‘hello’,encoding=’utf-8’)   #b’hello’

bytes.decode(b’hello’)         #hello

>>>import urllib.parse

>>> urllib.parse.quote('我们')  #'%E6%88%91%E4%BB%AC'

3.8       @修饰符

3.8.1          @staticmethod或@classmethod区别

@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用

        @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

        @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

[example]

        class A(object): 

    bar = 1 

    def foo(self): 

        print ('foo')  

               

    @staticmethod 

    def static_foo(): 

        print ('static_foo')  

        print (A.bar)  

    @classmethod 

    def class_foo(cls): 

        print ('class_foo') 

        print (cls.bar) 

        cls().foo()

               

        A.static_foo()  [static_foo 1]

        A.class_foo() [class_foo 1 foo]

3.8.2         @property

@property可以将python定义的函数“当做”属性访问,从而提供更加友好访问方式,但是有时候setter/deleter也是需要的。

l  只有@property表示只读。

l  同时有@property和@x.setter表示可读可写。

l  同时有@property和@x.setter和@x.deleter表示可读可写可删除

@property

        [是将它作为一个方法的装饰器来使用,这可以让你将一个类方法转变成一个类属性]

@abstractmethod

        1)子类必须全部实现重写父类的abstractmethod方法

        2)非abstractmethod方法可以不实现重写

        3)带abstractmethod方法的类不能实例化

        from abc import ABCMeta,abstractmethod, abstractproperty

        class Drawable(metaclass=ABCMeta):

                @abstractproperty

                def size(self):

                       pass

                @abstractmethod

                def draw(self, x, y, scale=1.0):

                       pass

                def double_draw(self, x, y):

                       self.draw(x, y, scale=2.0)

                      

        class Cicle(Drawable):

                def draw(self, x, y, scale=1.0):

                       print(x * scale, y * scale)

                @property

                def size(self):

                       return 'Cicle size'

        class Rectangle():

                """docstring for Cicle"""

                pass

        # 使用抽象类函数的register方法注册具体的class

        # 通过注册的类,可以直接实例化,但是无法访问抽象类的所有成员

        # 其实就是只是让isinstance、issubclass识别注册的类为抽象类的成员和实例

        Drawable.register(Rectangle)

        r = Rectangle()

        # Cicle如果没有override draw函数和size 属性,那么实例化的时候就会报错

        c = Cicle()

        c.draw(1, 2)

        c.double_draw(1, 2)

        c.size

3.9       修饰器

#!/usr/bin/env python3

#-*- coding:utf-8 -*-

import time

from functools import wraps

def wrap_time(fun):

    @wraps(fun)

    def fun_wrap(*args,**kwargs):

        start = time.time()

        ret = fun(*args,**kwargs)

        end = time.time()  

        take_time = '%.2f s'%(end-start)

print("function:{} args:{} kwargs:{} take_time:{}".format(fun.__name__,args,kwargs,take_time))

        return ret

    return fun_wrap

@wrap_time

def test(a,b):

    time.sleep(0.04)

return a+b

result=test(1,5)

Result:

function:test   args:(1, 5)        kwargs:{} take_time:0.04 s

6

3.10    正则表达式re

import re

元字符:. $ * + ? {} [] | ()

.   表示除了 之外的任意字符

*:指定前一个字符可以被匹配零次或更多次,而不是只有一次

+:表示前一个字符匹配一次或多次

?:表示将前一个字符重复0次或1次

d:匹配任何十进制数,相当于[0-9]

D:匹配任何非数字字符,相当于[^0-9]

s: 匹配任何空白字符,相当于[ fv]

S: 匹配任何非空白字符,相当于[^ fv]

w:匹配任何字母数字字符,相当于[a-zA-Z0-9_]

W:匹配任何非字母数字字符,相当于[^a-zA-Z0-9_]

re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)

re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)

re.S(DOTALL): 匹配所有字符

函数

  1. findall()【找到RE匹配的所有字串,并作为一个列表返回】

findall(pattern, string, flags=0)

a=r'abc'

re.findall(a,"abcyou")     ['abc']

re.findall('bc',"abcabcccc")            ['bc', 'bc']

re.findall("b[ab]c","bacbbccdbac") ['bac', 'bbc', 'bac']

s="hello world,hello boye"

re.findall("^hello",s)       [['hello'],匹配行首为“hello”的字符串]

re.findall('boye$',s) [['boye'],匹配行尾为‘boye’的字符串]

        re.findall(r'(.+)1','122111') ['2','1'] '1'意思是再匹配一次跟(.+)一样的数

  1. match()【匹配开头与要匹配字符的对象】

match(pattern, string, flags=0)

c.match('csvt hello')       [<_sre.SRE_Match object at 0xa087020>]

r=re.match(r'ab',"abdfsbab")

r.span()   [(0, 2),返回一个元组包含匹配(开始,结束)的位置]

r.start()    [0,返回匹配开始的位置]

        r.end()     [2,返回匹配结束的位置]

  1. search()【 搜索整个字符串并返回第一个成功的匹配】

d=re.search(r'hello','hello csvt hello')

d.group() [hello]

4.  finditer()【找到RE匹配的所有字串,作为一个迭代器返回】

                a=r'abc'

                re.finditer(a,"abcabcyouabc") [<callable-iterator object at 0xa08252c>]

  1. group()【返回被RE匹配的字符串】

a = "123abc456"

print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整体

print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123

s2='asfdxxIxxasdxxlovexxfddf'

f= re.search(r'xx(.+?)xxasdxx(.*?)xx', s2).group(2) 返回匹配的第二个()中内容

r=re.match(r'a',"aa")

       r.group() ['a']

6.  sub()【替换字符串】

        rs=r'c..t'【四个字符】

        re.sub(rs,'python','csvt cadat ccst')【'python casat python' 匹配四个字符】

  1. split()【分隔字符串】

r="a+b*09&c.e?fd"

re.split(r'[+&.?]',r)     ['a', 'b*09', 'c', 'e', 'fd']

re.split('(d+)' , 'dkjj23jjjj44') #匹配部分加上()  ['dkjj' , '23' , 'jjjj' , '44' , '' ]

条件匹配

1.(?:...)   用于使用'|'或后接数量词 (?:abc){2}                         abcabc

        如:re.findall('industr(?:y|yu)','industry industryu') ['industry', 'industry']

2.(?iLmsux)     只能用在开头,可选多个   (?i)abc                                    Abc

3.(?#...)           #后的内容作为注释被忽略 abc(?#comment)123           abc123

4.(?=...)           之后的字符串内容需要匹配       a(?=d)                   后面是数字的a

        re.findall('Windows(?=95|98|NT|2000)*?','Windows2000')  ['Windows']

                       表达式才能成功匹配

5.(?!...)     之后的内容需要不匹配          a(?!d)                    后面不是数字的a

        如:re.findall('Windows(?!95|98|NT|2000)','Windows56') 

        ['Windows']

6.(?<=...) 之前的内容需要匹配                   (?<=d)a         前面是数字的a

        如:re.findall(r'(?<=100) windows', '76100 windows')

        [' windows']

7. (?<!...) 之前的字符串内容需要不匹配   (?<!d)a          前面不是数字的a

        re.findall(r'(?<!100) windows', '200 windows') 

        [' windows']

4       常用模块

4.1       logging

#!/usr/bin/env python3

#-*- coding:utf-8 -*-

import logging

import traceback

logger = logging.getLogger("Mylogging")

log_format= '%(asctime)s %(filename)s [Line:%(lineno)d] %(levelname)s:%(message)s'

logging.basicConfig(filename=None, level=logging.DEBUG,

                    datefmt='%Y-%m-%d %H:%M:%S',

                    format=log_format,

                    filemode='a+')

logger.debug('this debug')

logger.info('this info')

logger.warning('this warning')

logger.error('this error')

Result:

2019-11-05 00:05:58 loggingTest.py [Line:14] DEBUG:this debug

2019-11-05 00:05:58 loggingTest.py [Line:15] INFO:this info

2019-11-05 00:05:58 loggingTest.py [Line:16] WARNING:this warning

2019-11-05 00:05:58 loggingTest.py [Line:17] ERROR:this error

参数

作用

%(levelno)s

打印日志级别的数值

%(levelname)s

打印日志级别的名称

%(pathname)s

打印当前执行程序的路径

%(filename)s

打印当前执行程序名

%(funcName)s

打印日志的当前函数

%(lineno)d

打印日志的当前行号

%(asctime)s

打印日志的时间

%(thread)d

打印线程ID

%(threadName)s

打印线程名称

%(process)d

打印进程ID

%(message)s

打印日志信息

4.1.1         将日志写入到文件

设置logging,创建一个FileHandler,并对输出消息的格式进行设置,将其添加到logger,然后将日志写入到指定的文件中

import logging

logger = logging.getLogger(__name__)

logger.setLevel(level = logging.INFO)

handler = logging.FileHandler("log.txt")

handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

handler.setFormatter(formatter)

logger.addHandler(handler)

logger.info("Start print log")

logger.debug("Do something")

logger.warning("Something maybe fail.")

logger.info("Finish")

4.1.2         将日志同时输出到屏幕和日志文件

logger中添加StreamHandler,可以将日志输出到屏幕上

import logging

logger = logging.getLogger(__name__)

logger.setLevel(level = logging.INFO)

handler = logging.FileHandler("log.txt")

handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

handler.setFormatter(formatter)

console = logging.StreamHandler()

console.setLevel(logging.INFO)

logger.addHandler(handler)

logger.addHandler(console)

logger.info("Start print log")

logger.debug("Do something")

logger.warning("Something maybe fail.")

logger.info("Finish")

logging有一个日志处理的主对象,其他处理方式都是通过addHandler添加进去,logging中包含的handler主要有如下几种

handler名称

位置

作用

StreamHandler

logging.StreamHandler

日志输出到流,可以是sys.stderr,sys.stdout或者文件

FileHandler

logging.FileHandler

日志输出到文件

BaseRotatingHandler

logging.handlers.BaseRotatingHandler

基本的日志回滚方式

RotatingHandler

logging.handlers.RotatingHandler

日志回滚方式,支持日志文件最大数量和日志文件回滚

TimeRotatingHandler

logging.handlers.TimeRotatingHandler

日志回滚方式,在一定时间区域内回滚日志文件

SocketHandler

logging.handlers.SocketHandler

远程输出日志到TCP/IP sockets

DatagramHandler

logging.handlers.DatagramHandler

远程输出日志到UDP sockets

SMTPHandler

logging.handlers.SMTPHandler

远程输出日志到邮件地址

SysLogHandler

logging.handlers.SysLogHandler

日志输出到syslog

NTEventLogHandler

logging.handlers.NTEventLogHandler

远程输出日志到Windows NT/2000/XP的事件日志

MemoryHandler

logging.handlers.MemoryHandler

日志输出到内存中的指定buffer

HTTPHandler

logging.handlers.HTTPHandler

通过"GET"或者"POST"远程输出到HTTP服务器

4.1.3         日志回滚

使用RotatingFileHandler,可以实现日志回滚

import logging

from logging.handlers import RotatingFileHandler

logger = logging.getLogger(__name__)

logger.setLevel(level = logging.INFO)

#定义一个RotatingFileHandler,最多备份3个日志文件,每个日志文件最大1K

rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3)

rHandler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

rHandler.setFormatter(formatter)

console = logging.StreamHandler()

console.setLevel(logging.INFO)

console.setFormatter(formatter)

logger.addHandler(rHandler)

logger.addHandler(console)

logger.info("Start print log")

logger.debug("Do something")

logger.warning("Something maybe fail.")

logger.info("Finish")

4.1.4         logging.config模块配置日志

#logger.conf

######################################################################

[loggers]

keys=root,example01,example02

[logger_root]

level=DEBUG

handlers=hand01,hand02

[logger_example01]

handlers=hand01,hand02

qualname=example01

propagate=0

[logger_example02]

handlers=hand01,hand03

qualname=example02

propagate=0

##########################################################################

import logging

import logging.config

logging.config.fileConfig("logger.conf")

logger = logging.getLogger("example01")

logger.debug('This is debug message')

logger.info('This is info message')

logger.warning('This is warning message')

4.2       os模块

命令

描述

os.system('ping http://www.baidu.com')

执行系统命令

os.listdir("G:python")

列出指定目录文件

os.makedirs(path)

多层创建目录

os.mkdir(path)

创建目录

os.remove()

删除指定文件

os.rmdir()

删除指定目录

os.walk(fileDir)

递归查找文件

os.rename('test', 'filetest.txt')

重命名

os.getcwd()

获得当前工作的目录

os.path.pardir

..

os.path.sep

window下返回\

os.path.isabs(path)

判断是否为绝对路径

os.path.isdir(name)      

判断name是不是一个目录,name不是目录就返回false

os.path.isfile(name)

判断name是不是一个文件,不存在name也返回false

os.path.exists(name)

判断是否存在文件或目录name

os.path.getsize(name)

获得文件大小,如果name是目录返回0

os.path.split('d:/h/d/he.go')

分离文件和路径 ('d:/h/d', 'he.go')

os.path.splitext(path)

分离扩展名 ('d:/h/d/he', '.go')

os.path.abspath('osTest.py')

返回绝对路径

os.path.dirname(path)

返回目录路径

os.path.basename(path)

返回文件名

os.path.join(path, name)

连接目录和文件名

4.3       time 和datetime 模块

方法

描述

time.sleep(1)

睡眠1秒

time.clock()

第一次输出的是程序运行时间第一次调用,而第二次之后的调用是自第一次调用以后到现在的运行时间

time.asctime()

Sat Nov 11 13:51:37 2017,没有参数,将会将time.localtime()作为参数传入

time.ctime()

Sat Nov 11 13:51:37 2017,默认time.time()为参数

time.localtime()

time.struct_time(tm_year=2019, tm_mon=11, tm_mday=4, tm_hour=22, tm_min=8, tm_sec=31, tm_wday=0, tm_yday=308, tm_isdst=0)

time.strftime("%Y-%m-%d",time.localtime(time.time()))

2019-01-01

time.strftime("%Y-%m-%d",time.gmtime())

格式化的时间字符串,默认传入时间time.localtime()

time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X')

把一个格式化时间字符串转化为struct_time

d=datetime.datetime(2018,12,1,12,12,10,900)

datetime.datetime(2018, 12, 1, 12, 12, 10, 900)

d.strftime('%X')

12:12:10

d.strftime('%Y-%m-%d')

2018-12-01

datetime.datetime.strftime(d,'%Y-%m-%d')

2018-12-01

datetime.datetime.fromtimestamp(time.time())

datetime.datetime(2019, 11, 4, 22, 17, 3, 406413)

datetime.datetime.strftime(

datetime.datetime.today(),'%Y-%m-%d')

2019-11-04

datetime.datetime.strftime(

datetime.datetime.now(),'%Y-%m-%d')

2019-11-04

dt=datetime.datetime.strptime('20181231', '%Y%m%d')

日期格式化,datetime.datetime(2018, 12, 31, 0, 0)

dt-datetime.timedelta(

days=1,hours=2,minutes=3,seconds=4)

datetime.datetime(2018, 12, 29, 21, 56, 56)

from dateutil.relativedelta import relativedelta

dt - relativedelta(months=months)

月份减1

dt.replace(day=1).strftime('%Y%m%d')

20181201

d=datetime.datetime.strptime('2018-11-10','%Y-%m-%d') - datetime.datetime.strptime('2018-10-10','%Y-%m-%d')

 

日期相减

d=datetime.datetime(2018,12,12)- datetime.datetime(2018,10,14)

 

d.days(31)

4.4       re(正则表达式)

import re

元字符:  . $ * + ? {} [] | ()

4.4.1         标志修饰符

修饰符

描述

re.I

使匹配对大小写不敏感

re.M

多行匹配,影响 ^ 和 $

re.S

使 . 匹配包括换行在内的所有字符

re.U

根据Unicode字符集解析字符。这个标志影响 w, W, , B

re.X

该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

re. L

字符集本地化。这个功能是为了支持多语言版本的字符集使用环境的, 如果在一个法语环境下使用,缺省设置下,不能匹配 "é" 或 "ç" 。 加上这 L 选项和就可以匹配了。不过这个对于中文环境似乎没有什么用,它仍然不能匹配中文字符

4.4.2         使用模式

模式

描述

^

匹配字符串的开头

$

匹配字符串的末尾

.

匹配任意字符,除了换行符

[...]

用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'

[^...]

不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符

*

匹配0个或多个的表达式

+

匹配1个或多个的表达式

?

匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式

{m,n}

表示至少重复m次,至多重复n次

d

匹配任何十进制数,相当于[0-9]

D

匹配任何非数字字符,相当于[^0-9]

s

匹配任何空白字符,相当于[ fv]

S

匹配任何非空白字符,相当于[^ fv]

w

匹配任何字母数字字符,相当于[a-zA-Z0-9_]

W

匹配任何非字母数字字符,相当于[^a-zA-Z0-9_]

z

匹配字符串结束



匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串

A   

匹配字符串开始

G

匹配最后匹配完成的位置

匹配一个换行符

匹配一个制表符

<number>

(?P=name)

只能用在开头,可选多个   (?i)abc     Abc

(?:…)

用于使用'|'或后接数量词

(?iLmsux)

(?#...)

#后的内容将作为注释被忽略

(?=…)

之后的字符串内容需要匹配

(?!…)

之后的内容需要不匹配

(?<=...)

之前的内容需要匹配

(?<!...)

之前的字符串内容需要不匹配

(?(id)yes-pattern|no-pattern)

对应id的子表达式如果匹配到内容,则这里匹配yes_exp,否则匹配no_exp

4.4.3         re.findall

搜索整个字符串,返回一个list

re.findall(pattern, string, flags=0)

  • 只打印括号内元素

secret="assjfkldsxxwoxxsdfsxsdfxxmexxousdxxhulixxdfs"

re.findall(r'xx(.+?)xx', secret)  #['wo', 'me', 'huli']

s2='asfdxxIxxasdxxlovexxfddf'

s3=re.findall(r'xx(.+?)xxasdxx(.*?)xx', s2)  #[('I', 'love')]

  • 非贪婪模式

r=r"ab+?"

re.findall(r,"abbbb")       #['ab']

re.findall(r'(.+)1','122111')  #['2','1'] '1'意思是再匹配一次跟(.+)一样的数

re.findall('industr(?:y|yu)','industry industryu')    #['industry', 'industry']

re.findall('Windows(?=95|98|NT|2000)*?','Windows2000')      #['Windows']

re.findall('Windows(?!95|98|NT|2000)','Windows56')       #['Windows']

re.findall(r'(?<=100) windows', '76100 windows')        #[' windows']

re.findall(r'(?<!100) windows', '200 windows')              # [' windows']

4.4.4         re.match

从开始位置开始匹配,如果开头没有则无

match(pattern, string, flags=0)

  • [example1]

r=re.match(r'ab',"abdfsbab")

r.span()   #(0, 2) 返回一个元组包含匹配(开始,结束)的位置

r.start()    #0 返回匹配开始的位置

r.end()     #2 返回匹配结束的位置

  • [example2]

line = "Cats are smarter than dogs"

matchObj = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)

if matchObj:

matchObj.groups()  # ('Cats', 'smarter')

matchObj.group()  # Cats are smarter than dogs

    matchObj.group(1)  # 'Cats'

    matchObj.group(2)  # 'smarter'

4.4.5         re. search

搜索整个字符串并返回第一个成功的匹配

d=re.search(r'hello','hello csvt hello')                          

d.group()  # hello

g=re.search("([0-9]*)([a-z]*)([0-9]*)","123abc456")

g.span()          # (0, 9)

g.groups()      # ('123', 'abc', '456')

g.group(1)     # '123'

4.4.6         re. finditer

找到RE匹配的所有字串,作为一个迭代器返回

content = '''email:12345678@163.com

    email:2345678@163.com

    email:345678@163.com

    '''   

    result_finditer = re.finditer(r"d+@w+.com", content)

    for d in result_finditer:

        print(d.group())      #12345678@163.com 2345678@163.com 345678@163.com

        print(d.start())        #6 33 59

4.4.7         re. group

返回被RE匹配的字符串

a = "123abc456"

re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整体

re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123

s2='asfdxxIxxasdxxlovexxfddf'

f= re.search(r'xx(.+?)xxasdxx(.*?)xx', s2).group(2) #返回匹配的第二个()中内容

r=re.match(r'a',"aa")

r.group() #'a'

4.4.8         re. sub

替换字符串

rs=r'c..t'   #四个字符

re.sub(rs,'python','csvt cadat ccst')  #'python casat python'      

4.4.9         re.split

split(pattern, string, maxsplit=0, flags=0)

maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。

r="a+b*09&c.e?fd"

re.split(r'[+&.?]',r)            #['a', 'b*09', 'c', 'e', 'fd']

re.split('(d+)' , 'dkjj23jjjj44') #匹配部分加上()  ['dkjj' , '23' , 'jjjj' , '44' , '' ]

4.5       random模块

random.random()方法用于生成一个0到1的随机浮点数:0<=n<1.0

random.uniform(a,b):用于生成一个指定范围内的随机浮点数,两格参数中,其中一个是上限,一个是下限。如果a>b,则生成的随机数n,即b<=n<=a;如果a>b,则a<=n<=b

random.randint(a,b):用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,生成的随机数n:a<=n<=b

random.randrange([start],stop[, step]):从指定范围内,按指定基数递增的集合中获取一个随机数。如:random.randrange(10,100,2),结果相当于从[10,12,14,16,...,96,98]序列中获取一个随机数

random.choice(sequence):参数sequence表示一个有序类型。sequence在python不是一种特定的类型,而是泛指一系列的类型。list,tuple,字符串都属于sequence

random.shuffle(x[, random]):用于将一个列表中的元素打乱

random.sample(sequence,k):从指定序列中随机获取指定长度的片段,sample函数不会修改原有序列

4.6       linecache(读取大文件)

linecache,这个模块也可以解决大文件读取的问题,并且可以指定读取哪一行

# 输出第2行

text = linecache.getline(filename, 2)

with open(file_name,’r’, encoding=’utf-8’) as fd:

        for line in fd:

                print(line)

4.7       urllib

urllib是一个包含几个模块来处理请求的库,分别是:

  • urllib.request 发送http请求
  • urllib.error 处理请求过程中,出现的异常。
  • urllib.parse 解析url
  • urllib.robotparser 解析robots.txt 文件

4.7.1         urllib.request

request.urlopen(url, data=None, timeout=10)

#url:  需要打开的网址

#data:Post提交的数据

#timeout:设置网站的访问超时时间

from urllib import request

import ssl

# 解决某些环境下报<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

ssl._create_default_https_context = ssl._create_unverified_context

response = request.urlopen(url, data=None, timeout=10)

page = response.read().decode('utf-8')

headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36'

}

req = request.Request(url,headers=headers)

response = request.urlopen(req)

#在urllib里面 判断是get请求还是post请求,就是判断是否提交了data参数

print(request.get_method())

4.7.2         Cookie

import http.cookiejar, urllib.request

# 1 创建CookieJar对象

cookie = http.cookiejar.CookieJar()

# 使用HTTPCookieProcessor创建cookie处理器,

handler = urllib.request.HTTPCookieProcessor(cookie)

# 构建opener对象

opener = urllib.request.build_opener(handler)

# 将opener安装为全局

urllib.request.install_opener(opener)

data = urllib.request.urlopen(url)

2 保存cookie为文本

import http.cookiejar, urllib.request

filename = "cookie.txt"

# 保存类型有很多种

## 类型1

cookie = http.cookiejar.MozillaCookieJar(filename)

## 类型2

cookie = http.cookiejar.LWPCookieJar(filename)

# 使用相应的方法读取

cookie = http.cookiejar.LWPCookieJar()

cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)

handler = urllib.request.HTTPCookieProcessor(cookie)

opener = urllib.request.build_opener(handler)

4.7.3         设置代理

import urllib.request

url = 'http://httpbin.org/ip'

proxy = {'http':'39.134.108.89:8080','https':'39.134.108.89:8080'}

proxies = urllib.request.ProxyHandler(proxy) # 创建代理处理器

# 创建特定的opener对象

opener = urllib.request.build_opener(proxies,urllib.request.HTTPHandler)

urllib.request.install_opener(opener) # 安装全局的opener 把urlopen也变成特定的opener

data = urllib.request.urlopen(url)

print(data.read().decode())

4.7.4         urllib.error

urllib.error可以接收有urllib.request产生的异常。urllib.error中常用的有两个方法,URLError和HTTPError。URLError是OSError的一个子类,

HTTPError是URLError的一个子类,服务器上HTTP的响应会返回一个状态码,根据这个HTTP状态码,我们可以知道我们的访问是否成功。

  • URLError

URLError产生原因一般是:网络无法连接、服务器不存在等

import urllib.error

import urllib.request

requset = urllib.request.Request('http://www.usahfkjashfj.com/')

try:

    urllib.request.urlopen(requset).read()

except urllib.error.URLError as e:

    print(e.reason)

else:

print('success')

  • HTTPError

HTTPError是URLError的子类,在你利用URLopen方法发出一个请求时,服务器上都会对应一个应答对象response,其中他包含一个数字“状态码”,

例如response是一个重定向,需定位到别的地址获取文档,urllib将对此进行处理。其他不能处理的,URLopen会产生一个HTTPError,对应相应的状态码,

HTTP状态码表示HTTP协议所返回的响应的状态。

 

from urllib import request, error

    try:

        response = request.urlopen('http://cuiqingcai.com/index.htm')

    except error.URLError as e:

        print(e.reason)

 

    # 先捕获子类错误

    try:

        response = request.urlopen('http://cuiqingcai.com/index.htm')

    except error.HTTPError as e:

        print(e.reason, e.code, e.headers, sep=' ')

    except error.URLError as e:

        print(e.reason)

    else:

        print('Request Successfully')

4.7.5         urllib.parse

from urllib import parse

1)        解析url

parse.urlparse('https://movie.douban.com/?a=hello')

#ParseResult(scheme='https', netloc='movie.douban.com', path='/', params='',

query='a=hello', fragment='')

2)        把元素串连成一个url

url = ('http', 'www.baidu.com', 'index', 'name=liming&sex=nan', 'hello', 'world')

parse.urlunparse(url)

'http://www.baidu.com/index;name=liming&sex=nan?hello#world'

3)        连接两个参数的url, 将第二个参数中缺的部分用第一个参数的补齐,如果第二个有完整的路径,则以第二个为主

parse.urljoin('https://movie.douban.com/test', 'index')

'https://movie.douban.com/index'

4)        压缩编码

data = { 'a': 'test','name': '魔兽'}

parse.urlencode(data)

'name=%E9%AD%94%E5%85%BD&a=test'

5)        解码

parse.unquote('%E9%AD%94%E5%85%BD')

'魔兽'

6)        用于分析URL中query组件的参数,返回一个key-value对应的字典格式

parse.parse_qs("FuncNo=9009001&username=1")

{'FuncNo': ['9009001'], 'username': ['1']}

7)        urllib.parse.quote(string, safe=’/’, encoding=None, errors=None)

第一个参数是URL,

第二个参数是安全的字符串,即在加密的过程中,该类字符不变。默认为“/”;

url="https://www.zhihu.com/question/50056807/answer/223566912"

urllib.parse.quote(url)

urllib.parse.quote(url,safe=":")

'https%3A//www.zhihu.com/question/50056807/answer/223566912'

'https:%2F%2Fwww.zhihu.com%2Fquestion%2F50056807%2Fanswer%2F223566912'

8)        urllib.parse.quote_plus(string, safe=”, encoding=None, errors=None)

url="https://www.zhihu.com/question/50056807/answer/223566912"

urllib.parse.quote_plus(url)

'https%3A%2F%2Fwww.zhihu.com%2F+question%2F50056807%2Fanswer%2F223566912'

4.7.6         其它

response.read().decode('utf-8')            

response.status                             [状态码]

response.getheaders()                   [响应头]

response.getheader('Date')           [获取响应头日期信息]

response.geturl()                           [返回请求的url]

response.info()                      [返回HTTPMessage对象,表示远程服务器返回的头信息]

response.getcode()                   [返回Http状态码]

response.readlines()     

response.readline()

urllib.request.quote('中文')                                         ['%E4%B8%AD%E6%96%87']

urllib.request.unquote('%E4%B8%AD%E6%96%87')     [中文]

url = 'http://www.baidu.com/index.php?username=guol'

urllib.request.urlparse(url)                                            [将网址解析成一个6元组]

urllib.request.urljoin('http://www.oschina.com/tieba','index.php') [http://www.oschina.com/index.php]

data=urllib.request.urlsplit(url)

data.netloc     ['www.baidu.com']

data.scheme   ['http']

data.query    ['username=guol']

data.path        ['/index.php']

4.8       urllib3

Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库。许多Python的原生系统已经开始使用urllib3。Urllib3提供了很多python标准库urllib里所没有的重要特性:

•线程安全

•连接池

•客户端SSL/TLS验证

•文件分部编码上传

•协助处理重复请求和HTTP重定位

•支持压缩编码

•支持HTTP和SOCKS代理

4.8.1         request

def request(self, method, url, fields=None, headers=None, **urlopen_kw):

  • 第一个参数method 必选,指定是什么请求,'get'、'GET'、'POST'、'post'、'PUT'、'DELETE'等,不区分大小写。
  • 第二个参数url,必选
  • 第三个参数fields,请求的参数,可选
  • 第四个参数headers 可选

urllib3主要使用连接池进行网络请求的访问,所以访问之前我们需要创建一个连接池对象,如下所示:

import urllib3

url = "http://httpbin.org"

http = urllib3.PoolManager();

r = http.request('GET',url+"/get")

print(r.data.decode())

print(r.status)

#带参数的get

r = http.request('get','http://www.baidu.com/s',fields={'wd':'周杰伦'})

print(r.data.decode())

4.8.2         设置代理

import urllib3

url = "http://httpbin.org"

headers = {

   'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'

}

proxy = urllib3.ProxyManager('http://101.236.19.165:8866',headers = headers)

r = proxy.request('get',url+"/ip")

print(r.data.decode())

4.9       requests模块

http://www.python-requests.org/en/master/

1.pip install requests [安装]  

2.http://www.lfd.uci.edu/~gohlke/pythonlibs/

import requests

requests.codes.ok

requests.request("GET",url)

requests.post(url, headers=headers,data=data)

requests.put(url)

requests.delete(url)

requests.head(url)

requests.options(url)

response= requests.get(url,headers=headers)

requests.get(url,params=data)              [传参]

requests.get(url,verify=False))               [不进行证书验证]

requests.get(url, cookies=cookies)        [发送cookies]

requests.get(url,timeout=1)                  [设置超时时间]

       

response.encoding = 'utf-8'    [获取/修改网页编码]

response.text         [文本的方式去显示]

respnse.json()        [解析json]

response.headers [响应头]

response.cookies   [cookies]

cookieData=[{'key':key,'value':value} for key,value in respnse.cookies.items()]

response.status_code[响应状态码]

response.content   [以字节的方式去显示,中文显示为字符]

respnse.history      [访问历史记录]

response.url

4.9.1         维持会话

        s=requests.Session()

        s.get('http://httpbin.org/cookie/set/number/1234')

        response =s.get('http://httpbin.org/cookie')

        response.text {'cookies':{'number':'1234'}}

4.9.2         证书验证

        import requests

        from requests.packages import urllib3

        urllib3.disable_warnings()

        response=requests.get('https:www.12306.cn',verify=False) #不使用证书

       response=requests.get('https:www.12306.cn',cert=('/path/server.crt','/path/key'))

       

4.9.3         设置访问代理

        proxies = {"http": "http://10.10.10.10:8888","https": "http://10.10.10.100:4444",}

        #proxies={'http':'http://user:password@127.0.0.1:9743/'}

        requests.get(url, proxies=proxies)

4.9.4         认证设置

        from requests.auth import HTTPBasicAuth

        r=requests.get(url,auth=HTTPBasicAuth('user','123')

4.9.5         异常处理

        from requests.exceptions import ReadTimeout,HTTPError,RequestException

        try:

                response=requests.get(url,timeout=1)

        except ReadTimeout:

                print('timeout')

        except HTTPError:

                print('http error')

        except RequestException:

                print('error')

       

4.9.6         下载图片

        r =requests.get('http://localhost:8080/static/images/bg3.jpg')

        with open('b.jpg','wb') as code:

                code.write(r.content)

from urllib import request

request.urlretrieve('https://puui.qpic.cn/qqvideo_ori/0/p08126fw9tj_360_204/0','H:/youBest/aa.jpg')

4.10    selenium

pip install selenium

http://chromedriver.storage.googleapis.com/index.html  #chrom浏览器驱动下载

4.10.1      Example

from selenium import webdriver

from selenium.webdriver.common.keys import Keys

option=webdriver.ChromeOptions()

option.add_argument('headless') # 设置option,页面不显示

chrome_options.add_argument('--disable-gpu')  #如果不加这个选项,有时定位会出现问题

option.add_argument('--user-data-dir=D: GoogleChrome Data') #设置成用户自己的数据目录

driver = webdriver.Chrome(executable_path=" Application/chromedriver.exe",

                          chrome_options=option)

driver.maximize_window() # 将浏览器最大化显示

browser.set_window_size(480, 800) #设置浏览器宽480、高800显示

driver.implicitly_wait(30)  # 隐性等待,最长等30秒

driver.get(“http://www.baidu.com”)

elem = driver.find_element_by_id("kw")

elem.clear()

elem.send_keys("python") #输入内容

elem.send_keys(Keys.RETURN) #按回车键

print (driver.page_source)

driver.quit()

l  常用方法

driver.save_screenshot(“baidu.png”)

driver.get_screenshot_as_file("baidu.png") #保存网页截图

driver. get_screenshot_as_png()   #获取二进制数据流

driver. get_screenshot_as_base64()     #base64编码原始数据

switch_to_window()

switch_to.frame("frameId")

driver.title

driver.current_url  #current_url 方法可以得到当前页面的URL

driver.back()         # 后退

driver.forward()    # 前进

ele_string = driver.find_element_by_xpath("//*[@id='1']/h3/a/em").text

driver.find_elements_by_xpath("//*/input[@type='radio']")

l  定位方法

方法

描述

find_element_by_id(self, id_)

id定位

find_element_by_name(self, name)

name定位

find_element_by_class_name(self, name)

class定位

find_element_by_tag_name(self, name)

tag定位

find_element_by_link_text(self, link_text)

link定位

find_element_by_partial_link_text(self, link_text)

partial_link定位

find_element_by_xpath(self, xpath)

xpath定位

find_element_by_css_selector(self, css_selector)

css定位

driver.find_elements("css selector",".mnav")

返回list

driver.find_element_by_id("kw").send_keys("selenium")

#通过name方式定位

driver.find_element_by_name("wd").send_keys("selenium")

#通过tag name方式定位

driver.find_element_by_tag_name("input").send_keys("selenium")

#通过class name方式定位

driver.find_element_by_class_name("s_ipt").send_keys("selenium")

#通过CSS方式定位

driver.find_element_by_css_selector("#kw").send_keys("selenium")

#通过xpath方式定位

driver.find_element_by_xpath("//input[@id='kw']").send_keys("selenium")

4.11    键盘事件

4.11.1      调用键盘按键操作需要引入 keys 包

from selenium.webdriver.common.keys import Keys

4.11.2      通过 send_keys()调用按键

send_keys("python")             #输入内容

send_keys(Keys.TAB)              # TAB

send_keys(Keys.ENTER)         # 回车

4.11.3      键盘组合键的用法

driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')  # ctrl+a 全选输入框内容

driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')  # ctrl+x 剪切输入框内容

4.12    鼠标事件

鼠标事件一般包括鼠标右键、双击、拖动、移动鼠标到某个元素上等等。

4.12.1      需要引入ActionChains类

from selenium.webdriver.common.action_chains import ActionChains

l  常用方法

perform()              #执行所有ActionChains 中存储的行为;

context_click()      #右击

double_click()      #双击

drag_and_drop()  #拖动

move_to_element()  #鼠标悬停

l  鼠标双击示例

#定位到要双击的元素

 qqq =driver.find_element_by_xpath("xxx")

#对定位到的元素执行鼠标双击操作

 ActionChains(driver).double_click(qqq).perform()

l  鼠标拖放示例

#定位元素的原位置

element = driver.find_element_by_name("source")

#定位元素要移动到的目标位置

target = driver.find_element_by_name("target")

#执行元素的移动操作

ActionChains(driver).drag_and_drop(element, target).perform()

4.13    多层框架/层级定位

#先找到到 ifrome1(id = f1)

driver.switch_to_frame("f1")

driver.switch_to_window("f1")

4.14    定位截图

driver = webdriver.Chrome()

    driver.get('http://stackoverflow.com/')

    driver.save_screenshot('screenshot.png')

    left = element.location['x']

    top = element.location['y']

    right = element.location['x'] + element.size['width']

    bottom = element.location['y'] + element.size['height']

    im = Image.open('screenshot.png')

    im = im.crop((left, top, right, bottom))

    im.save('screenshot.png')

4.15    pycurl

pip install pycurl

4.15.1      pycurl模块功能

pycurl.global_cleanup()清理curl环境,对应于libcurl中的curl_global_cleanup

pycurl.version显示pycurl版本信息以及libcurl,openSSL,zlib依赖项的版本信息,对应libcurl中的curl_version

pycurl.version_info()用元祖返回版本信息,对应libcurl中的curl_version_info

class pycurl.Curl创建一个新的curl对象,它对应libcurl中的句柄.

class pycurl.CurlMulti创建一个与libcurl中的句柄相对应的新curlMulti对象curlM

class pycurl.CurlShare创建一个与libcurl中的句柄相对应的新CurlShare对象curlSH

4.15.2      pycurl.Curl:创建一个新的curl对象

函数

描述

c = pycurl.Curl()

创建curl对象

(pycurl.CONNECTTIMEOUT, 5)

连接的等待时间,设置为0则不等待

(pycurl.TIMEOUT, 5)

请求超时时间

(pycurl.NOPROGRESS, 0)

是否屏蔽下载进度条,非0则屏蔽

(pycurl.MAXREDIRS, 5)

指定HTTP重定向的最大数

(pycurl.FORBID_REUSE, 1)

完成交互后强制断开连接,不重用

(pycurl.FRESH_CONNECT,1)

强制获取新的连接,即替代缓存中的连接

(pycurl.DNS_CACHE_TIMEOUT,60)

设置保存DNS信息的时间,默认为120秒

(pycurl.URL,"http://www.baidu.com")

指定请求的URL

(pycurl.USERAGENT,"Mozilla/5.2 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50324)")

配置请求HTTP头的User-Agent

(pycurl.HTTP_CODE)

返回的HTTP状态码

(pycurl.TOTAL_TIME)

传输结束所消耗的总时间

(pycurl.NAMELOOKUP_TIME)

DNS解析所消耗的时间

(pycurl.CONNECT_TIME)

建立连接所消耗的时间

(pycurl.PRETRANSFER_TIME)

从建立连接到准备传输所消耗的时间

(pycurl.STARTTRANSFER_TIME)

从建立连接到传输开始消耗的时间

(pycurl.REDIRECT_TIME)

重定向所消耗的时间

(pycurl.SIZE_UPLOAD)

上传数据包大小

(pycurl.SIZE_DOWNLOAD)

下载数据包大小

(pycurl.SPEED_DOWNLOAD)

平均下载速度

(pycurl.SPEED_UPLOAD)

平均上传速度

(pycurl.HEADER_SIZE)

HTTP头部大小

#!/usr/bin/env python3

#-*- coding:utf-8 -*-

# pip install pycurl

import pycurl

from io import BytesIO

def test_website(url):

    c = pycurl.Curl()

    buffer = BytesIO()  # 创建缓存对象

    c.setopt(c.WRITEDATA, buffer)  # 设置资源数据写入到缓存对象

    c.setopt(c.URL, url)  # 指定请求的URL

    c.setopt(c.MAXREDIRS, 5)  # 指定HTTP重定向的最大数

    c.perform()  # 执行

    http_code = c.getinfo(pycurl.HTTP_CODE)  # 返回的HTTP状态码

    dns_resolve = c.getinfo(pycurl.NAMELOOKUP_TIME)  # DNS解析所消耗的时间

    http_conn_time = c.getinfo(pycurl.CONNECT_TIME)  # 建立连接所消耗的时间

    http_pre_trans = c.getinfo(pycurl.PRETRANSFER_TIME)  # 从建立连接到准备传输所消耗的时间

    http_start_trans = c.getinfo(pycurl.STARTTRANSFER_TIME)  # 从建立连接到传输开始消耗的时间

    http_total_time = c.getinfo(pycurl.TOTAL_TIME)  # 传输结束所消耗的总时间

    http_size_download = c.getinfo(pycurl.SIZE_DOWNLOAD)  # 下载数据包大小

    http_size_upload = c.getinfo(pycurl.SIZE_UPLOAD)  # 上传数据包大小

    http_header_size = c.getinfo(pycurl.HEADER_SIZE)  # HTTP头部大小

    http_speed_downlaod = c.getinfo(pycurl.SPEED_DOWNLOAD)  # 平均下载速度

    http_speed_upload = c.getinfo(pycurl.SPEED_UPLOAD)  # 平均上传速度

    http_redirect_time = c.getinfo(pycurl.REDIRECT_TIME)  # 重定向所消耗的时间

    print('HTTP响应状态: %d' % http_code)

    print('DNS解析时间:%.2f ms' % (dns_resolve * 1000))

    print('建立连接时间: %.2f ms' % (http_conn_time * 1000))

    print('准备传输时间: %.2f ms' % (http_pre_trans * 1000))

    print("传输开始时间: %.2f ms" % (http_start_trans * 1000))

    print("传输结束时间: %.2f ms" % (http_total_time * 1000))

    print("重定向时间: %.2f ms" % (http_redirect_time * 1000))

    print("上传数据包大小: %d bytes/s" % http_size_upload)

    print("下载数据包大小: %d bytes/s" % http_size_download)

    print("HTTP头大小: %d bytes/s" % http_header_size)

    print("平均上传速度: %d k/s" % (http_speed_upload / 1024))

    print("平均下载速度: %d k/s" % (http_speed_downlaod / 1024))

if __name__ == '__main__':

    test_url = 'http://sports.sina.com.cn/'

test_website(test_url)

结果:

HTTP响应状态: 200

DNS解析时间:31.00 ms

建立连接时间: 78.00 ms

准备传输时间: 78.00 ms

传输开始时间: 109.00 ms

传输结束时间: 484.00 ms

重定向时间: 0.00 ms

上传数据包大小: 0 bytes/s

下载数据包大小: 368674 bytes/s

HTTP头大小: 690 bytes/s

平均上传速度: 0 k/s

平均下载速度: 743 k/s

5       网络编程

5.1       简单实例

客户端在建立一个TCP连接时一般需要两步,而服务器的这个过程需要四步,具体见下面的比较

步骤

TCP客户端

TCP服务器

第一步

建立socket对象

建立socket对象

第二步

调用connect()建立一个和服务器的连接

设置socket选项[可选]

第三步

None

绑定到一个端口

第四步

None

侦听连接

【Example】

class Server:

    """服务器"""

    def __init__(self,host='0.0.0.0',port=50007,numbers=5):

        self.host = host

        self.port = port

        self.socketfd = None

        self.create_socket = self.create_socket()

   

    def create_socket(self,numbers=5):

        #self.socketfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

                self.socketfd = socket.socket()

        self.socketfd.bind((self.host,self.port))

        self.socketfd.listen(numbers)

       

    def server(self):

        while True:

            conn,addr = self.socketfd.accept()

            data = conn.recv(1024)

            print("data=",data)

            send = input()

            conn.sendall(send.encode('utf-8'))

        conn.close()

       

class Client:

    """客户端"""

    def __init__(self,host='127.0.0.1',port=50007):

        self.host = host

        self.port = port

        self.socketfd = None

        self.create_socket()

       

    def create_socket(self):

        self.socketfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

        self.socketfd.connect((self.host,self.port))

       

    def client(self):

        cmd = 'hello client'

        self.socketfd.sendall(cmd.encode('utf-8'))

        data = self.socketfd.recv(1024)

        self.socketfd.close()

        return data

       

if __name__ == '__main__':

    #s = Server()

    #s.server()

   

    c= Client('192.168.227.129')

    d=c.client()

print(d)

5.1.1         问题

在绑定前调用setsockopt让套接字允许地址重用

bind前添加sk.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)

5.2       IO多路复用

IO多路复用中包括 select、pool、epoll,这些都属于同步,还不属于异步

5.2.1         select

select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

  select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。

  select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

  另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

5.2.2         poll

poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

 poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

  另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

5.2.3         epoll

直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

  epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

  epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

5.2.4   sellectpollepoll三者的区别

IO多路复用名称

最大文件描述符数量

文件描述符扫描

备注

select

1024

所有监视的文件描述符进行扫描(内部相当于一个for循环)

poll

X

epoll

X

X

事件的就绪通知方式

5.2.5         select语法

select(rlist, wlist, xlist, timeout=None)

select()方法接收并监控3个通信列表:

第一个rlist监控所有要进来的输入数据

第二个wlist是监控所有要发出去的输出数据

第三个监控异常错误数据

第四个设置指定等待时间,如果想立即返回,设为null即可,最后需要创建2个列表来包含输入和输出信息来传给select(),让select方法通过内核去监控,然后生成三个实例

5.2.5.1  select服务端代码实例

Python的select()方法直接调用操作系统的IO接口,它监控sockets,open files, and pipes(所有带fileno()方法的文件句柄)何时变成readable 和writeable, 或者通信错误,select()使得同时监控多个连接变的简单,并且这比写一个长循环来等待和监控多客户端连接要高效,因为select直接通过操作系统提供的C的网络接口进行操作,而不是通过Python的解释器。

import select,socket,queue

server = socket.socket()

server.bind(("localhost",9000))

server.listen(1000)

server.setblocking(False)       #设置为非阻塞

msg_dic = dict()     #定义一个队列字典

inputs = [server,]  #由于设置成非阻塞模式,accept和recive都不阻塞了,没有值就会报错,#因此最开始需要最开始需要监控服务端本身,等待客户端连接

outputs = []

while True:

#exceptional表示如果inputs列表中出现异常,会输出到这个exceptional中

#如果没有任何客户端连接,就会阻塞在这里

    readable,writeable,exceptional = select.select(inputs,outputs,inputs)

    for r in readable:    #没有个r代表一个socket链接

        if r is server:  #如果这个socket是server的话,就说明是是新客户端连接了

            conn,addr = r.accept()    #新连接进来了,接受这个连接,生成这个客户端实例

            print("来了一个新连接",addr)

#为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接就会被交给select去监听

            inputs.append(conn)     

             #初始化一个队列,后面存要返回给这个客户端的数据

            msg_dic[conn] = queue.Queue()

        else:  #如果不是server,就说明是之前建立的客户端来数据了

            data = r.recv(1024)

            print("收到数据:",data)

            msg_dic[r].put(data)      #收到的数据先放到queue里,一会返回给客户端

            outputs.append(r)         #为了不影响处理与其它客户端的连接 , 这里不立刻

#返回数据给客户端

            # r.send(data)

            # print("send done....")

    for w in writeable:        #要返回给客户端的链接列表

        data_to_client = msg_dic[w].get()

        w.send(data_to_client) #返回给客户端的源数据

        outputs.remove(w)       #确保下次循环的时候writeable,不返回这个

#已经处理完的这个连接了

    for e in exceptional:      #处理异常的连接

        if e in outputs:      #因为e不一定在outputs,所以先要判断

            outputs.remove(e)

        inputs.remove(e)  #删除inputs中异常连接

        del msg_dic[e]     #删除此连接对应的队列

【example】

# -*- coding: utf-8 -*-

import select

import socket

import datetime

response = b"Hello, World!"

sock = socket.socket()

# 需要设置socket选项时,需要先将socketlevel设置为SOL_SOCKET  SOL=socket option level

# SO_REUSEADDR代表重用地址reuse addr

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(("0.0.0.0", 9000))

sock.listen(5)

sock.setblocking(0)

inputs = [sock, ]

while True:

    print(datetime.datetime.now())

    rlist, wlist, errlist = select.select(inputs, [], [], 10)

    print(" >>> ", rlist, wlist, errlist)

    for s in rlist:

        if s == sock:

            con, addr = s.accept()

            # 将新的请求连接加入到监控列表中

            inputs.append(con)

            print('con=[%s],addr=[%s]'%(con,addr))

        else:

            # 对于其他的文件描述符要接收信息并返回

            try:

                data = s.recv(1024)

                if data:

                    s.send(response)

            finally:

                s.close()

                inputs.remove(s)

5.2.6         poll使用(Linux)

poll方法:

register,将要监控的文件描述符注册到poll中,并添加监控的事件类型

unregister,注销文件描述符监控

modify, 修改文件描述符监控事件类型

poll([timeout]),轮训注册监控的文件描述符,返回元祖列表,元祖内容是一个文件描述符及监控类型(

POLLIN,POLLOUT等等),如果设置了timeout,则会阻塞timeout秒,然后返回控列表,如果没有设置timeout 微秒,则会阻塞到有返回值为止。

# -*- coding: utf-8 -*

import select

import socket

import datetime

sock = socket.socket()

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(("localhost", 9000))

sock.listen(5)

# 设置为非阻塞

sock.setblocking(0)

poll = select.poll()

poll.register(sock, select.POLLIN)

connections = {}

while True:

    # 遍历被监控的文件描述符

    print(datetime.datetime.now())

    for fd, event in poll.poll(10000):

        if event == select.POLLIN:

            if fd == sock.fileno():

                # 如果是当前的sock,则接收请求

                con, addr = sock.accept()

                poll.register(con.fileno(), select.POLLIN)

                connections[con.fileno()] = con

            else:

                # 如果是监听的请求,读取其内容,并设置其为等待写监听

                con = connections[fd]

                data = con.recv(1024)

                if data:

                    print("%s accept %s" % (fd, data))

                    poll.modify(fd, select.POLLOUT)

        else:

            con = connections[fd]

            try:

                con.send(b"Hello, %d" % fd)

                print("con >>> ", con)

            finally:

                poll.unregister(con)

                connections.pop(fd)

                con.close()

5.2.7         epoll(Linux)

epoll相当于是linux内核支持的方法,而epoll主要是解决select,poll的一些缺点

²  数组长度限制

解决方案:fd上限是最大可以打开文件的数目,具体数目可以查看/proc/sys/fs/file-max。一般会和内存有关

²  需要每次轮询将数组全部拷贝到内核态

解决方案:每次注册事件的时候,会把fd拷贝到内核态,而不是每次poll的时候拷贝,这样就保证每个fd只需要拷贝一次。

²  每次遍历都需要列表线性遍历

解决方案:不再采用遍历的方案,给每个fd指定一个回调函数,fd就绪时,调用回调函数,这个回调函数会把fd加入到就绪的fd列表中,所以epoll只需要遍历就绪的list即可

# -*- coding: utf-8 -*-

import select

import socket

import datetime

EOL1 = b' '

EOL2 = b' '

response = b'HTTP/1.0 200 OK Date: Mon, 1 Jan 1996 01:01:01 GMT '

response += b'Content-Type: text/plain Content-Length: 13 '

response += b'Hello, world!'

sock = socket.socket()

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(("localhost", 9000))

sock.listen(5)

sock.setblocking(0)

epoll = select.epoll()

epoll.register(sock, select.EPOLLIN)

# 为了针对长连接的情况,增加请求和响应操作

connections = {}

requests = {}

responses = {}

try:

    while True:

        print(datetime.datetime.now())

        events = epoll.poll(5)

        print(events)

        for fd, event in events:

            if fd == sock.fileno():

                # 接收请求

                con, addr = sock.accept()

                con.setblocking(0)

                epoll.register(con, select.EPOLLIN | select.EPOLLET)

                connections[con.fileno()] = con

                requests[con.fileno()] = b''

                responses[con.fileno()] = response

            elif event & select.EPOLLIN:

                print("ssssssssssssss")

                con = connections[fd]

                requests[fd] += con.recv(1024)

                # 判断con是否已经完全发送完成

                if EOL1 in requests[fd] or EOL2 in requests[fd]:

                    epoll.modify(fd, select.EPOLLOUT)

                    print('-' * 40 + ' ' + requests[fd].decode()[:-2])

            elif event & select.EPOLLOUT:

                # 发送完成,将fd挂起

                con = connections[fd]

                byteswritten = con.send(responses[fd])

                # 将已发送内容截取,并判断是否完全发送完毕,已发送完毕,epoll挂起fd,fdshutdown

                responses[fd] = responses[fd][byteswritten:]

                if len(responses[fd]) == 0:

                    epoll.modify(fd, 0)

                    con.shutdown(socket.SHUT_RDWR)

            elif event & select.EPOLLHUP:

                # 处理挂起fd, epoll注销fd, 关闭socket, connections移除fd

                epoll.unregister(fd)

                connections[fd].close()

                del connections[fd]

finally:

    epoll.unregister(sock)

sock.close()

6       协程、线程、进程

6.1       协程

在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程。

协程的优点:

²  无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

²  无需原子操作锁定及同步的开销

²  方便切换控制流,简化编程模型

²  高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点:

²  无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

²  进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

6.1.1         Example

关于asyncio的一些关键字的说明:

l  event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数。

l  coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。

l  task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态。

l  future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别。

l  async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。

并发并行

import asyncio

import time

now = lambda :time.time()

@asyncio.coroutine

def do_some_work(x):

    print("Waiting:",x)

    yield from asyncio.sleep(x)

    #result1 = yield from func()

    return "Done after {}s".format(x)

start = now()

coroutine1 = do_some_work(1)

coroutine2 = do_some_work(2)

coroutine3 = do_some_work(4)

tasks = [

    asyncio.ensure_future(coroutine1),

    asyncio.ensure_future(coroutine2),

    asyncio.ensure_future(coroutine3)

]

loop = asyncio.get_event_loop() #  创建一个事件loop

loop.run_until_complete(asyncio.wait(tasks)) # 将协程加入到事件循环loop

for task in tasks:

    print("Task ret:",task.result())

print("Time:",now()-start)

Result:

Waiting: 1

Waiting: 2

Waiting: 4

Task ret: Done after 1s

Task ret: Done after 2s

Task ret: Done after 4s

Time: 4.002252817153931

# 需要先安装 aiohttp: pip install aiohttp
import asyncio
import aiohttp # 可以理解为一个支持异步 I/O 的 requests
async def fetch_page(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
# httpbin 这个网站能测试 http 请求和响应的各种信息
result = loop.run_until_complete(fetch_page('http://httpbin.org/get?a=2'))
    print(f"Args: {result.get('args')}")
    loop.close()
# 输出:Args: {'a': '2'}

 

6.2       线程

# -*- coding: utf-8 -*-

import threading

import time

def func(*arg,**kwargs):

    print('this is func arg:',arg)

    print('this is func kwargs:',kwargs)

    print('this is func thread name is:',threading.current_thread().name)

    print('this is func thread ident is:',threading.current_thread().ident)

    time.sleep(1)

    print('End!')

   

if __name__=='__main__':

    t = threading.Thread(target=func, name='thread1', args=(1,),kwargs={'a':1})

t.start()

Result:

this is func arg: (1,)

this is func kwargs: {'a': 1}

this is func thread name is: thread1

this is func thread ident is: 15516

End!

方法、属性

描述

threading.current_thread()

返回线程本身

t.ident

获取线程的标识符

t.start()

激活线程

t.getName()

获取线程的名称

t.name

获取或设置线程的名称

t.isAlive()

判断线程是否为激活状态

t.is_alive()

判断线程是否为激活状态

t.isDaemon()

判断是否为守护线程

t.daemon

判断是否为守护线程

threading.active_count()

当前存活线程数(包括主线程)

6.3       线程锁threading.RLock和threading.Lock

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。为了保证数据的准确性,引入了锁的概念。所以,可能出现如下问题:

例:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。

threading.RLock和threading.Lock 的区别:

 
   


RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

# -*- coding: utf-8 -*-

import threading

import time

globals_num = 0

#lock = threading.RLock()

lock = threading.Lock()

def Func():

    lock.acquire()  # 获得锁

    global globals_num

    globals_num += 1

    time.sleep(1)

    print(globals_num,threading.current_thread().name)

    lock.release()  # 释放锁

for i in range(10):

    t = threading.Thread(target=Func,name='thread{}'.format(i+1))

t.start()

Result:

1 thread1

2 thread2

3 thread3

4 thread4

5 thread5

6 thread6

7 thread7

8 thread8

9 thread9

10 thread10

6.4       线程事件threading.Event

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 isSet(): 当内置标志为True时返回True。

set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。

clear(): 将标志设为False。

wait([timeout]): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()

6.5       线程条件变量threading.Condition

一个condition变量总是与某些类型的锁相联系,这个可以使用默认的情况或创建一个,当几个condition变量必须共享和同一个锁的时候,是很有用的。锁是conditon对象的一部分:没有必要分别跟踪。

condition变量服从上下文管理协议:with语句块封闭之前可以获取与锁的联系。 acquire() 和 release() 会调用与锁相关联的相应的方法。

注意: notify()和notify_all()不会释放锁,也就是说,线程被唤醒后不会立刻返回他们的wait() 调用。除非线程调用notify()和notify_all()之后放弃了锁的所有权。

 
   


6.6      

 
   


线程定时器threading.Timer

6.7       线程信号量threading.Semaphore

信号量semaphore 是用于控制进入数量的锁。有哪些应用场景呢,比如说在读写文件的时候,一般只能只有一个线程在写,而读可以有多个线程同时进行,如果需要限制同时读文件的线程个数,这时候就可以用到信号量了(如果用互斥锁,就是限制同一时刻只能有一个线程读取文件)。又比如在做爬虫的时候,有时候爬取速度太快了,会导致被网站禁止,所以这个时候就需要控制爬虫爬取网站的频率。

# -*- coding:utf-8 -*-

import threading

import time

def fun(semaphore, num):

    # 获得信号量,信号量减一

    semaphore.acquire()

    print ("%s is running." % threading.current_thread().name)

    time.sleep(3)

    # 释放信号量,信号量加一

    semaphore.release()

    print ("%s is end." % threading.current_thread().name)

if __name__=='__main__':

    # 初始化信号量,数量为2

    semaphore = threading.Semaphore(2)

    # 运行4个线程

    for num in range(4):

        t = threading.Thread(target=fun,name='thread{}'.format(num+1)

                             ,args=(semaphore, num))

        t.start()

Result:

thread1 is running.

thread2 is running.

thread1 is end.

thread3 is running.

thread2 is end.

thread4 is running.

thread3 is end.

thread4 is end.

6.8       multiprocessing模块

6.8.1         使用Process模块创建进程

# -*- coding: utf-8 -*-

from multiprocessing import Process

def func(n):

    print("这里是子进程", n)

if __name__ == '__main__':

    p = Process(target=func, args=(1,))

    p.start()

print("这里是父进程")

结果:

这里是父进程

这里是子进程 1

注意:在windows操作系统中由于没有fork(linux操作系统中创建进程的机制) , 在创建进程的时候自动import启动它的这个文件 , 而在import的时候又执行了整个文件,因此如果将process()直接写在文件中就会无限递归创建子进程报错.必须把创建子进程的部分写在 if __name__ == '__main__':

6.8.2         进程池

import os

import sys

import time

from multiprocessing import Pool,Lock,Manager

def run_finished(name):

    #完成任务

    print('Task {0} pid {1} is running, parent id is {2}'.format(name, os.getpid(), os.getppid()))

    print('Task {0} end.'.format(name))

   

def run_task(name,lock):

    lock.acquire()

    with open('run_task','a') as fd:

        msg = 'Task {0} pid {1} is running, parent id is {2} '.format(name, os.getpid(), os.getppid())

        fd.write(msg)

    lock.release()

    return name

   

  

if __name__ == '__main__':

    manager = Manager()

    lock=manager.Lock()

    pool=Pool(processes=3)

    start = time.time()

    pool_list = []

    for i in range(1,10):

        res = pool.apply_async(run_task,(i,lock),callback=run_finished)

        pool_list.append(res)

    pool.close()

    pool.join()

    end = time.time()

    for res in pool_list:

        print(res.get())

       

    print("take time is %.2f s"%(end-start))

    print('All processes done!')

 
   


6.8.3         进程间的通信Queue(队列)

multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序

from multiprocessing import Queue

q=Queue(2) #初始化一个Queue对象,最多可接收三条put消息

q.put("消息1")

q.put("消息2")

print(q.full()) #True

try:  #因为消息列队已满下面的try都会抛出异常,等待2秒后再抛出异常

    q.put("消息4",True,2)

except:

print("消息列队已满,现有消息数量:%s"%q.qsize())

6.8.4         multiprocessing.Manager()

Python中进程间共享数据,处理基本的queue,pipe和value+array外,还提供了更高层次的封装。使用multiprocessing.Manager可以简单地使用这些高级接口。

Manager()返回的manager对象控制了一个server进程,此进程包含的python对象可以被其他的进程通过proxies来访问。从而达到多进程间数据通信且安全。

Manager支持的类型有list,dict,Namespace,Lock,RLock,Semaphore,

BoundedSemaphore,Condition,Event,Queue,Value和Array。

方法

描述

manager = Manager()

 

q = manager.Queue()

 

q.get、q.put()

 

lock = manager.Lock()

 

lock.acquire()、lock.release()

 

 

l  [Example1]

import multiprocessing

import time

def worker(d, key, value):

    d[key] = value

 

if __name__ == '__main__':

    mgr = multiprocessing.Manager()

    d = mgr.dict()

    jobs = [ multiprocessing.Process(target=worker, args=(d, i, i*2))

             for i in range(10)

             ]

    for j in jobs:

        j.start()

    for j in jobs:

        j.join()

    print ('Results:' )

    for key, value in enumerate(dict(d)):

        print("%s=%s" % (key, value))

        

# the output is :

# Results:

# 0=0

# 1=1

# 2=2

# 3=3

# 4=4

# 5=5

# 6=6

# 7=7

# 8=8

# 9=9

 

 

l  [Example2]

import os       

from multiprocessing import Process,Manager

# 这里实现的就是多个进程之间共享内存,并修改数据

# 这里不需要加锁,因为manager已经默认给你加锁了

 

def f(d,l):

    d[1] = '1'

    d['2'] = 2

    d[0.25] = None

    l.append(os.getpid())

    print(l)

 

if __name__ == '__main__':

    with Manager() as manager:

        d = manager.dict()  #生成一个字典

        l = manager.list(range(5))  #生成一个列表

        p_list = []

        for i in range(10):

            p = Process(target=f,args=(d,l))

            p.start()

            p_list.append(p)

        for res in p_list:

            res.join()

 

 

7       异步编程

python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病。然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了python性能方面的短板,如最新的微服务框架japronto,resquests per second可达百万级

python还有一个优势是库(第三方库)极为丰富,运用十分方便。asyncio是python3.4版本引入到标准库,python2x没有加这个库,毕竟python3x才是未来啊,哈哈!python3.5又加入了async/await特性。

同步/异步的概念

l  同步是指完成事务的逻辑,先执行第一个事务,如果阻塞了,会一直等待,直到这个事务完成,再执行第二个事务,顺序执行。。。

l  异步是和同步相对的,异步是指在处理调用这个事务的之后,不会等待这个事务的处理结果,直接处理第二个事务去了,通过状态、通知、回调来通知调用者处理结果。

7.1       asyncio(百万并发 )

import time

import asyncio

# 定义异步函数

async def hello():

    asyncio.sleep(1)

    print('Hello World:%s' % time.time())

 

def run():

    for i in range(5):

        loop.run_until_complete(hello())

 

loop = asyncio.get_event_loop()

if __name__ =='__main__':

run()

结果:

Hello World:1527595104.8338501

Hello World:1527595104.8338501

Hello World:1527595104.8338501

Hello World:1527595104.8338501

Hello World:1527595104.8338501

async def 用来定义异步函数,其内部有异步操作。每个线程有一个事件循环,主线程调用asyncio.get_event_loop()时会创建事件循环,你需要把异步的任务丢给这个循环的run_until_complete()方法,事件循环会安排协同程序的执行

7.2       aiohttp

如果需要并发http请求怎么办呢,通常是用requests,但requests是同步的库,如果想异步的话需要引入aiohttp。这里引入一个类,from aiohttp import ClientSession,首先要建立一个session对象,然后用session对象去打开网页。session可以进行多项操作,比如post, get, put, head等

基本用法:

async with ClientSession() as session:

async with session.get(url) as response:

import asyncio

from aiohttp import ClientSession

tasks = []

url = "https://www.baidu.com"

async def hello(url):

    async with ClientSession() as session:

        async with session.get(url) as response:

            response = await response.read()

            print(response)

if __name__ == '__main__':

    loop = asyncio.get_event_loop()

loop.run_until_complete(hello(url))

首先async def 关键字定义了这是个异步函数,await 关键字加在需要等待的操作前面,response.read()等待request响应,是个耗IO操作。然后使用ClientSession类发起http请求

8       第三方模块

8.1       打包py文件为exe可执行文件PyInstaller

1.     pip install PyInstaller [安装pyinstaller]  (http://www.pyinstaller.org/)

2.     (1)pyinstaller.py -F baidu.py(pyinstaller -F baidu.py)

    (2)pyinstaller -F -i tubiao123.ico myfile.py

        (3)pyinstaller -F -p D:installpythonLibsite-packages myfile.py

        -F, –onefile 打包成一个exe文件。

        -D, –onedir 创建一个目录,包含exe文件,但会依赖很多文件(默认选项)。

        -c, –console, –nowindowed 使用控制台,无界面(默认)

        -w, –windowed, –noconsole 使用窗口,无控制台

        -p, 指定第三方库目录

        -i,程序图标

3.生成了dist目录,目录中有*.exe文件

8.2       模块制作与安装

1.目录结构

├── setup.py

└──  pu

        ├── __init__.py

        ├──

        ├── suba

        │   ├── test.py

        │   └── __init__.py

        └── subb

                ├── Public.py

                └── __init__.py

[test.py]

        #!/usr/bin/env python

        #-*- coding:utf-8 -*-

        import sys

        #sys.path.append('../subb')

        #from Public import Log

        from pu.subb.Public import Log

        class Add:

                def __init__(self):

                       self.log=Log()

                      

                def add(self,a,b):

                       return a+b

               

                def print_error(self,msg):

                       self.log.error(msg)

               

        if __name__ == '__main__':  # 用来进行测试  

                add = Add()

                d=add.add(2,3)

                print(d)

                add.print_error('error')

2.新建setup.py

#from distutils.core import setup, find_packages

from setuptools import find_packages

from setuptools import setup

setup(

    name='pu',

    version='1.2.0',

    description="option excel",

    author='liboye',

    url="http://www.csdn.net",

    license="LGPL",

    packages= find_packages(),

        install_requires = ["openpyxl"],#安装依赖包

    #scripts=["scripts/test.py"],     

    py_modules=['pu.suba.test', 'pu.subb.Public']

       

        # 此项需要,否则卸载时报windows error,exe安装

        #zip_safe=False

)

3.构建模块

        python setup.py build

4.模块使用

        python setup.py install [安装]

        easy_install -m 包名      [模块卸载]

        python setup.py --help-commands [获取帮助]

        python setup.py sdist                    [为模块创建一个源码包(*.tar.gz)]

        python setup.py bdist_wininst              [dist目录生成一个exe文件]

        python setup.py bdist --help-formats[查看所有格式的支持]

5.setup.py各参数介绍:

--name           包名称

--version         包版本

--author  程序的作者

--author_email      程序的作者的邮箱地址

--url                        程序的官网地址

--license         程序的授权信息

--description 程序的简单描述

--platforms     程序适用的软件平台列表

--classifiers     程序的所属分类列表

--keywords             程序的关键字列表

--packages             需要处理的包目录(包含__init__.py的文件夹)

--py_modules        需要打包的python文件列表

--download_url      程序的下载地址

--cmdclass

--data_files     打包时需要打包的数据文件,如图片,配置文件等

--scripts          安装时需要执行的脚步列表

--package_dir         告诉setuptools哪些目录下的文件被映射到哪个源码包。一个例子:package_dir = {'': 'lib'},表示“root package”中的模块都在lib 目录中。

--requires               定义依赖哪些模块

--provides               定义可以为哪些模块提供依赖

--find_packages()   它默认在和setup.py同一目录下搜索各个含有 __init__.py的包。

--install_requires    需要安装的依赖包

--entry_points               动态发现服务和插件,是一种将包定义为插件的简单方法

console_scripts 指明了命令行工具的名称;在“redis_run = RedisRun.redis_run:main”中,

        等号前面指明了工具包的名称,等号后面的内容指明了程序的入口地址

# 注意:模块与函数之间是冒号:

entry_points={'console_scripts': [

        'redis_run = RedisRun.redis_run:main',

                ]}

               

【entry_points例子】

目录结构

├── setup.py

└── af

        ├── __init__.py

        └── app.py

       

1.新建[app.py]

        #!/usr/bin/env python

        #-*- coding:utf-8 -*-

        import tornado.ioloop

        import tornado.web

        import sys

        def hello():

                print('hello,this is tornado web!')

        class MainHandler(tornado.web.RequestHandler):

                def get(self):

                       self.write("Hello, tornado")

        def make_app():

                return tornado.web.Application([

                       (r"/", MainHandler),

                ])

        def run_main(*args, **kwargs):

                if len(sys.argv) >1:

                       for cmd in sys.argv[1:]:

                               if cmd.__contains__('--port='):

                                       port_parm = cmd.split('=')[-1]

                                       if port_parm.isdigit():

                                               port = int(port_parm)

                else:

                       port =9999

                app = make_app()

                app.listen(port)

                print('running 0.0.0.0:%s'%(port))

                tornado.ioloop.IOLoop.current().start()   

        if __name__ == "__main__":

                run_main()

2.新建[setup.py]

        from setuptools import find_packages

        from setuptools import setup

        setup(

                name='af',

                version='1.0.0',

                description="tornado web",

                author='liboye',

                url="http://www.csdn.net",

                license="LGPL",

                packages= find_packages(),

                #scripts=["scripts/test.py"],

                install_requires = ["tornado"],

                #py_modules=['af.app'],

                entry_points={'console_scripts': [

                       'af_run = af.app:run_main',

                ]}   

        )

3.构建: python setup.py build

4.安装: python setup.py install

5.启动web: af_run --host=9090

原文地址:https://www.cnblogs.com/boye169/p/13394560.html