@WillireamAngel
2017-08-26T07:06:24.000000Z
字数 23402
阅读 2540
python
print()可以实现数字和字符串打印,之间插入,可以实现字符串间空格;
>>> name = input()Michael>>> name'Michael'
name=input('please enter your name:')print('hello', name)please enter your name:michaelhello michael
变量不仅可以为整数或浮点数,还可以是字符串。通过使用Input+提示,可以实现更加优化的界面。
#是注释,当语句以冒号:结尾时,缩进的语句视为代码块。0x前缀+ 0-9 + a-f 1.23×10^9=1.23e^9) "" '' \用于标示既包含'又包含"的情况,转义字符加在'和"上:I\'m| 转义字符 | 描述 |
|---|---|
| (在行尾时) | 续行符 |
| \ | 反斜杠符号 |
| \' | 单引号 |
| \" | 双引号 |
| \a | 响铃 |
| \b | 退格(Backspace) |
| \e | 转义 |
| \000 | 空 |
| \n | 换行 |
| \v | 纵向制表符 |
| \t | 横向制表符 |
| \r | 回车 |
| \f | 换页 |
| \b | 退格(Backspace) |
| \e | 转义 |
| \000 | 空 |
| \oyy | 八进制数yy代表的字符,例如:\o12代表换行 |
| \xyy | 十进制数yy代表的字符,例如:\x0a代表换行 |
| \other | 其它的字符以普通格式输出 |
利用r''表示''不转义,Python允许用'''...'''的格式表示多行内容。
4.布尔值:True or False两种值,and&or¬运算,布尔值运算常用在条件判断中。
5.空值:None一个特殊的空值,与零无关。
6.变量:英文大小写+数字+_,Python属于变量不固定类型语言(动态语言)
7.常量:全部大写的变量表示:PI(π)(暂未实现π的书写)
Python除法:/浮点数除法,//地板除保留整数
3. 字符串和编码
1.ASCII编码;
2.GB2312编码;
3.Unicode编码;
4.UTF-8编码(可变长度);
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
文件读取与保存:
网页Unicode转换:
5.Python字符串:Python3.x以Unicode编码
(1)ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符。
(2)Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
(3)Python中的bytes类型用b''或者b""表示。
(4)以Unicode表示的str通过encode(编码类型)方法可以编码为指定的bytes,
>>> 'ABC' .encode('utf-8')b'ABC'
(5)纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。
(6)要把bytes变为str,就需要用decode()
>>> b'ABC' .decode('ascii')'ABC'
(7)len()函数计算的是str的字符数,计算bytes的字节数。
>>> len('中文')2>>> len(b'\xe4\xb8\xad\xe6\x96\x87')6
6.格式化:利用%实现
| %d | 整数 |
|---|---|
| %f | 浮点数 |
| %s | 字符串 |
| %x | 十六进制整数 |
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)'Hi, Michael, you have $1000000.'
其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数。
当不确定使用哪个转义符时,%s永远起作用,它会把任何数据类型转换为字符串。
>>> 'Age: %s. Gender: %s' % (25, True)'Age: 25. Gender: True'
当存在想表示%为一个普通字符时,利用%%
>>> 'growth rate: %d %%' %7'growth rate: 7 %'
7.注释Python程序和编码
#!/usr/bin/env python3# -*- coding: utf-8 -*-
作业:
小明的成绩从去年的72分提升到了今年的85分,请计算小明成绩提升的百分点,并用字符串格式化显示出'xx.x%',只保留小数点后1位
# -*-coding: utf-8 -*-s1= 72s2= 85r=(s2-s1)/s1*100print('%.1f%%' %r)
.apend追加元素 .insert(i, 元素)指定位置插入元素 .pop()删除list末尾的元素 .pop(i)删除指定索引位置元素 p[1]或者s[2][1]格式,实现多维数组索引
classmates=['michael','bob','ova']classmates.pop(1)print(classmates[0])print(classmates[-2])print(classmates)L=[]print(len(L))
运行结果:
michaelmichael['michael', 'ova']0
2.tuple(元组):初始化列表指向不可变(没有append和insert等方法),但嵌套列表可变,其它呈现方式与list相同。
定义若干元素tuple:
>>> t=(1.2)>>> t1.2
定义空元素tuple:
>>> t=()>>> t()
定义一个元素tuple(单独元素做运算优先,加,表示单元素)
>>> t=(1)>>> t1
>>> t=(1,)>>> t(1,)
调整嵌套list
>>> t = ('a', 'b', ['A', 'B'])>>> t[2][0] = 'X'>>> t[2][1] = 'Y'>>> t('a', 'b', ['X', 'Y'])
:,执行从前到后,当判断为True后就停止运行
if <条件判断1>:<执行1>elif <条件判断2>:<执行2>elif <条件判断3>:<执行3>else:<执行4>
判断条件的简写:
if x:print('True')
只要x是非零数值、非空字符串、非空list等,就判断为True,否则为False。
2.input
使用input来读取用户输入,可以自己输入,但是input()返回的数据类型是str,要与所比较的类型匹配,需要一些函数来完成。
s = input('birth:')age = int(s)if age >= 6:print('Teenager')else:print('Kid')
range(n)函数是生成从0到n-1的序列 range(n1,n2,d):生成从n1到n2间隔为d的序列 Hello, xxx!
# -*- coding: utf-8 -*-L = ['Bart', 'Lisa', 'Adam']for n in L:print('Hello, %s!' %(n))
in判断,输出True/False;get方法,如果key不存在,可以返回None,或者自己指定的value. add``remove等算法添加元素,多个set还可实现集合运算(&:交集,|:并集) 
>>> a = 'abc'>>> a.replace('a', 'A')'Abc'>>> a'abc'
:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。 pass语句
def nop():pass
pass用作占位符,暂不运行函数。
3.TypeErrorPython解释器,数据类型检查用isinstance()实现
def my_abs(x):if not isinstance(x, (int, float)):raise TypeError('bad operand type')if x >= 0:return xelse:return -x
>>> my_abs('A')Traceback (most recent call last):File "<pyshell#17>", line 1, in <module>my_abs('A')File "<pyshell#16>", line 3, in my_absraise TypeError('bad operand type')TypeError: bad operand type
显示了错误类型
4.import math语句表示导入math包,并允许后续代码引用math包里的sin、cos等函数。
函数可以同时返回多个值,但其实就是一个tuple(语法上,返回tuple可以省略括号)。
import mathdef move(x, y, step, angle=0):nx = x + step * math.cos(angle)ny = y - step * math.sin(angle)return nx, ny
def power(x, n):s = 1while n > 0:n = n - 1s = s * xreturn s
def power(x, n):s = 1while n > 0:n = n - 1s = s * xreturn s
第二个条件循环未闭合,return无条件退出函数,所以第一个函数起作用而第二个函数没有作用。
2.默认参数
默认参数可以简化函数的调用。
设置默认参数时,必选参数在前,默认参数在后;变化大的参数放在前面,变化小的参数放后面。
使用默认参数时,当传入参数设为定值时,传入值与设定值相同时,可以直接省略;当传入值与设定值不同时,可直接补充。
def vab(name, gender, age=6, city = 'beijing'):print('name:', name)print('gender:', gender)print('age:', age)print('city:', city)print(vab('Tom','A'))print(vab('Sarah','F',7))print(vab('Mary','B',city ='Tianjing'))
3.可变参数:
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,函数代码完全不变。调用该函数时,可以传入任意个参数(包括0个参数)
4.关键字参数:
**kw是关键字参数,kw接收的是一个dict。
5.命名关键字参数:
命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。如果含有可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符。
6.调用函数时传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2,3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})。
使用*args 和 **kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。
6.参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
| order | 表示 | 含义 |
|---|---|---|
| 位置参数 | x | 与位置有关的参数,从前向后调用 |
| 默认参数 | c=0 | 设定默认值,可改变默认值 |
| 可变参数 | *args | 传入多个参数,接收一个tuple |
| 命名关键字参数 | ,*,(含有可变参数可忽略) | 限制传入关键字参数的名称 |
| 关键字参数 | **kw | 传入多个参数,接收一个dict |
5. 递归函数:函数内部调用自身函数
def fact(n):if n==1:return 1return n * fact(n - 1)
1.使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。函数调用通过栈(stack)实现,栈的大小是有限的,所以需要防止栈溢出。
2.针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
3.Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
def move(n, A, B, C):if(n == 1):print(A,"->",C)returnmove(n-1, A, C, B)move(1, A, B, C)move(n-1, B, A, C)
L[n1:n2:d]:L可为list、tuple、字符串等 n1;当选取全部元素,可省略n1、n2;当d=1时,可省略:d;实际上可以实现负值倒数取值(最后一个值为-1)
L = list(range(100))a = L[:10:2]print(a)
运行结果:
[0, 2, 4, 6, 8]
for循环的遍历list or tuple实现,称为迭代。 for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
>>> d = {'a': 1, 'b': 2, 'c': 3}>>> for key in d:print(key)acb
2.迭代对象判断:collections模块的Iterable类型判断。具体实现如下:
>>> from collections import Iterable #引入模块类型>>> isinstance([1,2,5],Iterable) #isinstance(object,type),对象是否为类型,返回布尔值True #返回结果
3.将list变成索引-元素对:enumerate函数。
for ** in emumerate(L,n):L为列,n为初始序列值,默认为0,省略,n
代码示例:
L = [(1, 1), (2, 4), (3, 9)]for i,value in enumerate(L,2):print(i,value)for item in enumerate(L,2):print(item)
运行结果:
2 (1, 1)3 (2, 4)4 (3, 9)(2, (1, 1))(3, (2, 4))(4, (3, 9))
>>> [x * x for x in range(1, 11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
加入判断if
>>> [x * x for x in range(1, 11) if x % 2 == 0] # %表示余[4, 16, 36, 64, 100]
if实现:
L = ['Hello', 'World', 18, 'Apple', None]L1 = [s.lower() for s in L if isinstance(s,str)]print(L1) #if判断后面自动设置为布尔值为true的选项
加入多重循环
>>> [m + n for m in 'ABC' for n in 'XYZ'] #+的含义是组合['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
列出目录名的列表实现
>>> import os #导入模块>>> [d for d in os.listdir('.')] #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表,这个列表以字母顺序,不包括 '.' 和'..' 文件(即使有)。['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'python.pdb', 'python3.dll', 'python36.dll', 'python36.pdb', 'python36_d.dll', 'python36_d.pdb', 'python3_d.dll', 'pythonw.exe', 'pythonw.pdb', 'pythonw_d.exe', 'pythonw_d.pdb', 'python_d.exe', 'python_d.pdb', 'Scripts', 'tcl', 'Tools', 'vcruntime140.dll']
字符串变成小写:
>>> L = ['HELLO']>>> [s.lower() for s in L]['hello']
StopIteration,可使用for循环实现
def fib(max):n, a, b = 0, 0, 1while n < max:print(b)a, b = b, a + bn = n + 1return 'done'
2.将print改成yield关键字:yield功能类似于return,返回生成器。变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
捕获返回值:
g = fib(6)>>> while True:... try:... x = next(g)... print('g:', x)... except StopIteration as e:... print('Generator return value:', e.value)... break#try...except...可以跳过异常继续执行程序#break 跳出循环
try: # 可能会出现异常的一段代码command_1 # 如果command_1出现异常,则不执行command_1以及之后的语句command_2 # command_1如果正常,则会执行except: # try中任意一行语句出现异常,直接跳转至except,程序继续运行command_3command_4
杨辉三角实践:
# -*- coding: utf-8 -*-def triangles(): #命名函数L = [1] #引入首个listwhile True: #无报错执行yield L #创建generatorL = [1]+[L[x]+L[x+1] for x in range(len(L)-1)]+[1] #generator执行完成(第一次执行)后,进行下一步执行,定义新的L,并且引用原来的L list数值,由于原来的引用使用x+1实现,故需要长度-1n = 0for t in triangles():print(t)n = n + 1 #每次print,n=n+1if n == 10: #比较对象是否相等(判断)break
>>> from collections import Iterator>>> isinstance((x for x in range(10)), Iterator)True
函数式编程,可归结为面向对象的编程,思想接近于数学计算,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
1. 高阶函数
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
变量可以指向函数,函数名也是变量,常用的函数在import builtins模块中,常用的函数引用可以通过以下的方式实现:
import builtins; builtins.abs = 10
1.map/reduce
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
map(function,[list])
reduce把一个函数作用在一个序列上,结果继续和序列的下一个元素做累积计算:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
首字母大写的实现:
1.直接运用函数:
def normalize(name):return name.capitalize()L1=['adam','LISA','barT']L2=list(map(normalize,L1))print(L2)
2.利用普通函数lower和upper实现:
def normalize(name):return name[:1].upper() + name[1:].lower()L1 = ['adam', 'LISA', 'barT']L2 = list(map(normalize, L1))print(L2)
if __name__ == '__main__'的用法:
当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
实现字符串转换为浮点数:
from functools import reducedef str2float(s):def fn(x, y):return x * 10 + yn = s.index('.') #调用index函数,实现浮点数查找s1 = list(map(int, [x for x in s[:n]]))s2 = list(map(int, [x for x in s[n+1:]]))return reduce(fn, s1) + reduce(fn, s2) /10 **len(s2)print('\'123.4567\'=', str2float('123.4567'))
2.filter
filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
寻找素数:
def _odd_iter():n = 1while True:n = n + 2yield ndef _not_divisible(n):return lambda x: x % n > 0 #lambda是一个参数函数实现的函数,定义一个匿名函数来实现,类似于`def...return...`def primes():yield 2it = _odd_iter() # 初始序列while True:n = next(it) # 返回序列的第一个数yield nit = filter(_not_divisible(n), it) # 构造新序列# 打印1000以内的素数:for n in primes():if n < 1000:print(n)else:break
寻找回数:
# -*- coding: utf-8 -*-def is_palindrome(n):s = str(n)for i in range(len(s)):if s[i] ==s[len(s)-1-i]:return Trueelse:return Falseoutput = filter(is_palindrome, range(1, 1000))print(list(output))
3.sorted
sorted可对list进行排序,也可引入key=function进行排序
>>> sorted([36, 5, -12, 9, -21], key=abs)[5, 9, -12, -21, 36]
反向排序,引入reverse=True
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)['Zoo', 'Credit', 'bob', 'about']
调用组内元素:
# -*- coding: utf-8 -*-def by_name(t):return t[0]L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]L2 = sorted(L, key=by_name)print(L2)
lambda表示匿名函数,匿名函数避免了函数名冲突。 wrapper()函数的参数定义是(*args, **kw),,因此,wrapper()函数可以接受任意参数的调用。 __name__等属性。 functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。 int(x, base=n):将字符串转换为n进制数。一个.py文件就称之为一个模块(Module)。
按目录来组织模块的方法,称为包(Package)。
一个包的度量在于存在一个可有无内容的__init__.py,该文件本身就是一个模块,而它的模块名就是包名。
创建模块时,不要与python自带模块冲突。
1. 使用模块
sys.argv:一个元组,项为用户输入的参数,此参数从程序外部输入的,而非代码本身。
作用域:
正常的函数和变量名是公开的(public);
类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,不是不能应用,只是编程习惯上不引用。
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
2. 安装第三方模块
pip install labname
添加自身搜索路径:
1.直接修改路径,设置添加路径,在运行时修改,运行结束后失效:
>>> import sys>>> sys.path.append('/Users/michael/my_py_scripts')
2.使用环境变量方法添加相关路径,类似于添加path。
面向对象
将程序视作一组对象集合,程序设计是一系列消息在对象中传播,难点在于抽象出类(Class),利用类建立实例(Instance)。
面向对象的三大特点:数据封装、继承和多态。
面向过程
程序视作一系列命令集合,即一组函数的顺序执行。
class关键字:
class Student(object):pass
>>> type(abs)<class 'builtin_function_or_method'>
>>> type('abs') ==strTrue
>>> isinstance(h, Dog)True
>>> isinstance((1, 2, 3), (list, tuple, str))True
>>> import types>>> dir(types)['AsyncGeneratorType', 'BuiltinFunctionType', 'BuiltinMethodType', 'CodeType', 'CoroutineType', 'DynamicClassAttribute', 'FrameType', 'FunctionType', 'GeneratorType', 'GetSetDescriptorType', 'LambdaType', 'MappingProxyType', 'MemberDescriptorType', 'MethodType', 'ModuleType', 'SimpleNamespace', 'TracebackType', '_GeneratorWrapper', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ag', '_calculate_meta', '_collections_abc', '_functools', 'coroutine', 'new_class', 'prepare_class']
>>> hasattr(types, 'y')False>>> setattr(types, 'y', 9)>>> getattr(types, 'y')9>>> types.y9
__slots__
>>> def set_score(self, score):self.score = score>>> Student.set_score = set_score
2.使用__slots__
利用__slots__可以对实例的属性进行限制,使它只具有我们设定的属性:
>>> class Student(object):__slots__ = ('name', 'age')>>> s = Student()>>> s.name = 'Michael'>>> s.age = 25>>> s.score = 99Traceback (most recent call last):File "<pyshell#22>", line 1, in <module>s.score = 99AttributeError: 'Student' object has no attribute 'score'
使用__slots__时,需要特别注意,__slots__定义的属性仅对当前类实例有效,继承的子类是不起作用的,除非在子类也定义__slots__,子类允许定义的属性就是自身的和父类的。
>>> class Student(object):__slots__ = ('name', 'age')>>> class Graduate(Student):__slots__ = ('name', 'dream')>>> g = Graduate()>>> g.age = 96>>> g.name = 'Will'>>> g.dream = 'Forever'>>> g.end = 1Traceback (most recent call last):File "<pyshell#38>", line 1, in <module>g.end = 1AttributeError: 'Graduate' object has no attribute 'end'
@property @property的主要作用是广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。 @property装饰器负责把一个方法变成属性调用。
# _*_ coding: utf-8 _*_class Screen(object):@propertydef width(self):return self._width@propertydef height(self):return self._height@propertydef resolution(self):return self.width * self.height@width.setterdef width(self,value):if not isinstance(value, int):raise ValueErrror('width must be an interger!')if value < 0:raise ValueError('width must > 0!')self._width = value@height.setterdef height(self,value):if not isinstance(value, int):raise ValueErrror('height must be an interger!')if value < 0:raise ValueError('height must > 0!')self._height = values = Screen()s.width = 1024s.height = 768print(s.resolution)assert s.resolution == 786432, '1024 * 768 = %d ?' % s.resolution
class College(object):passclass Student(College):passclass Teacher(College):passclass Study(object):def study(self):print('student..')class Teach(object):def teach(self):print("teach...")class Man(Teacher, Teach):pass
由于Python允许使用多重继承,因此,MixIn就是一种常见的设计。
只允许单一继承的语言(如Java)不能使用MixIn的设计。
4. 定制类
try...except...else&if...raise ...else&try...finally区别:
try...except...else:当没有异常发生时,else后的语句将被执行;如果在try部份引发了名为'**'的异常,则执行except后面的代码;
if...raise ...else:如果if判断错误,则会引发raise后面异常;
try...finally:无论是否发生错误,都要执行finally后面的代码。
Enum实现为枚举类型定义一个class类型,每个常量都是class的一个唯一实例。 @unique装饰器可以帮助我们检查保证没有重复值。 value属性则是自动赋给成员的int常量,默认从1开始计数。type()函数可以查看一个类型或变量的类型,也可以创建新的类型; type('Hello', (object,), dict(hello=fn)) metaclass:先定义metaclass,就可以创建类,最后创建实例。
__new__()方法接收到的参数依次是:
当前准备创建的类的对象;类的名字;类继承的父类集合;类的方法集合。
__new__(cls, name, bases, attrs)
ORM框架:Object Relational Mapping(对象-关系映射)
try...except...finally... pdb.set_trace():我们只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点;setUp与tearDownre模块就带了很多示例代码 同步IO&异步IO:使用异步IO的效率会高于同步IO;
stream:流
1. 文件读写
读文件:open(),使用close()关闭;
file-like Object:含有read()方法的对象,StringIO就是内存中创建的file-like Object,常用作临时缓冲。
二进制文件:rb模式打开:
f = open('/Users/michael/gbk.txt', 'rb')
字符编码:非utf-8编码文本,传入encoding参数:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
写文件:调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件
使用with语句操作文件IO是个好习惯。
2. StringIO和BytesIO
StringIO:内存读写str,getvalue()方法用于获得写入后的str。
BytesIO:内存中操作bytes。
3. 操作文件和目录
Python的os模块封装了操作系统的目录和文件操作,要注意这些函数有的在os模块中,有的在os.path模块中。
os.name获取操作系统类型,posix说明系统是Linux、Unix或Mac OS X,nt,就是Windows系统。
环境变量:os.environ,获取环境变量:os.environ.get('key')
4. 序列化
Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。
json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。
| JSON类型 | Python类型 |
|---|---|
| {} | dict |
| [] | list |
| "string" | str |
| 1234.56 | int或float |
| true/false | True/False |
| null | None |
对于操作系统来说,一个任务就是一个进程(Process)。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
多任务的实现有3种方式:
- 多进程模式;
- 多线程模式;
- 多进程+多线程模式。
线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
多进程和多线程的程序涉及到同步、数据共享的问题,编写起来更复杂。
1. 多进程
在Unix/Linux下,可以使用fork()调用实现多进程。
要实现跨平台的多进程,可以使用multiprocessing模块。
要启动大量的子进程,可以利用进程池Pool的方式批量创建子进程。
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。
进程间通信是通过Queue、Pipes等实现的。
在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。
2. 多线程
Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。
由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。
多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
3. ThreadLocal
一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
4. 进程 vs. 线程
要实现多任务,通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常是一个Master,多个Worker。
如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker。
如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker。
多进程模式的最大优点是稳定性高,缺点是创建进程的代价大。
多线程的效率比多进程高,但多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。
线程切换,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。
任务分为计算密集型和IO密集型:
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。最好用C语言编写。
IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
单线程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。
5. 分布式进程
Python的multiprocessing模块不但支持多进程,其中managers子模块还支持把多进程分布到多台机器上。
正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
在正则表达式中,如果直接给出字符,就是精确匹配。用\d可以匹配一个数字,\w可以匹配一个字母或数字,.可以匹配任意字符。
要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符。
对于一些特殊字符,需要使用转义符。
要做更精确地匹配,可以用[]表示范围。A|B可以匹配A或B。^表示行的开头,$表示行的结束。
re模块,包含所有正则表达式的功能。
切分字符串:如果用户输入了一组标签,需要用正则表达式来把不规范的输入转化成正确的数组:re.split
用()表示的就是要提取的分组(Group),group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串。
正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符,采用加?方法就可以让匹配采用非贪婪匹配。
datetime是模块,datetime模块还包含一个datetime类,通过from datetime import datetime导入的才是datetime这个类。 import datetime,则必须引用全名datetime.datetime。 datetime.now()返回当前日期和时间,其类型是datetime。 epoch time,记为0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。datetime类型转换为timestamp只需要简单调用timestamp()方法。 fromtimestamp() datetime.strptime() strftime() +和-运算符,不过需要导入timedelta datetime,通过astimezone()方法,可以转换到任意时区。collections模块提供了一些有用的集合类,可以根据需要选用。 defaultdict。 OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key。 struct模块来解决bytes和其他二进制数据类型的转换。pack函数把任意数据类型变成'bytes'。 count()会创建一个无限的迭代器,ctrl+c退出; cycle()会把传入的一个序列无限重复下去; repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数; takewhile()等函数根据条件判断来截取出一个有限的序列; chain()可以把一组迭代对象串联起来,形成一个更大的迭代器; groupby()把迭代器中相邻的重复元素挑出来放在一起; itertools模块提供的全部是处理迭代功能的函数,它们的返回值不是list,而是Iterator,只有用for循环迭代的时候才真正计算。urllib的request模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应。 ProxyHandler来处理。 User-Agent头就是用来标识浏览器的。PIL:Python Imaging Library,是Python平台事实上的图像处理标准库了。PIL提供了操作图像的强大功能,可以通过简单的代码完成复杂的图像处理。
virtualenv为应用提供了隔离的Python运行环境,解决了不同应用间多版本的冲突问题。
Python内置的Tkinter可以满足基本的GUI程序的要求,如果是非常复杂的GUI程序,建议用操作系统原生支持的语言和库来编写。
python支持的第三方图形库:Tk、wxWidgets、Qt、GTK。
一封电子邮件的旅程就是:
发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人
SMTP发送邮件
Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件。
构造一个邮件对象就是一个Message对象,如果构造一个MIMEText对象,就表示一个文本邮件对象,如果构造一个MIMEImage对象,就表示一个作为附件的图片,要把多个对象组合起来,就用MIMEMultipart对象,而MIMEBase可以表示任何对象。
Message
+- MIMEBase
+- MIMEMultipart
+- MIMENonMultipart
+- MIMEMessage
+- MIMEText
+- MIMEImage
POP3收取邮件
用Python的poplib模块收取邮件分两步:第一步是用POP3协议把邮件获取到本地,第二步是用email模块把原始邮件解析为Message对象,然后,用适当的形式把邮件内容展示给用户即可。
Connection对象和Cursor对象操作数据。 Connection对象和Cursor对象都正确地被关闭,否则,资源就会泄露。 try:...except:...finally:...用来确保出错的情况下也关闭掉Connection对象和Cursor对象。commit()提交事务; %s。asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。 asyncio提供了完善的异步IO支持; coroutine中通过yield from完成; coroutine可以封装成一组Task然后并发执行。asyncio实现了TCP、UDP、SSL等协议,aiohttp则是基于asyncio实现的HTTP框架。