删除列表的重复元素:
list(set(list_1))//先转化成集合,再换回数组
列表类似更强大的数组。(python列表不能实现取某一列的功能,而numpy的array数字可以)
元组:
① 只可访问,不可修改
② 访问方法同列表
③ 拼接,重复,关系,逻辑,成员关系操作符都同列表
④ tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
序列:列表、元组、字符串
dict.item()
方法,需要返回值需要用dict.vallue()
方法1.输入:input()。eg: a = input(‘请输入:’)
注意:input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数
函数式编程:
返回函数:返回的不是结果,而是函数,当调用此函数时才会计算最终结果。
闭包:返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
类和对象
set
元素是不可变对象,自动过滤重复对象
删除:remove()方法
增加:add()方法
指向是不会变的,但是指向的元素可以是可变对象,
元组是不可变对象,不能增删改,但是元素可以是可变对象,例如元组的元素可以是列表
对于不可变对象,调用自身的方法不会改变自身,相当于新建了一个对象,如:replace()方法
函数可以起别名
空函数:pass语句
函数可以同时返回多个值,但其实就是一个tuple,不过可以省略括号
位置参数:个数顺序得一一对应
默认参数:必选参数在前,默认参数在后。 定义默认参数要牢记一点:默认参数必须指向不变对象
可变参数:传入参数个数可变。定义函数时,在参数前加一个*
,函数内部自动组装成一个tuple,其余代码不变。如果已经有一个list或tuple,调用可变参数,则在list或tuple前面加一个*
号,把list或tuple的元素变成可变参数传进去
关键字参数:允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。定义函数时,**在参数前加一个**
,**如果已经有一个dict,调用关键字参数,则在dict前面加一个**
号,把dict的元素变成关键字参数传进去他,但不会改变原来的dict。
命名关键字参数:限制关键字参数。命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。
def person(name, age, *, city, job):
print(name, age, city, job)
调用方法:命名关键字参数必须传入参数名
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
如果函数定义中已经有可变参数,则后面的命名关键字参数不需要再加特殊分隔符*
防止栈溢出——尾递归优化:尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
for ... in 可迭代对象
如果要对list
实现类似Java那样的下标循环怎么办?Python内置的enumerate
函数可以把一个list
变成索引-元素对,这样就可以在for
循环中同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
python的循环可以同时引用两个变量,比如下面的代码:
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
[表达式 for in ]
可以多层循环,也可以加if判断,可以使用多个变量
if
在for
后时,不能加else
; 是筛选条件
if
在for
前时,必须加else
; 是表达式
要创建一个generator,有很多种方法。
方法一:只要把一个列表生成式的[]
改成()
,就创建了一个generator:
(表达式 for in )
可以通过next()
函数获得generator的下一个返回值,但基本不用(太蠢),正确方法应该是用for
循环 :
g = (x * x for x in range(10))
for n in g:
print(n)
方法二:generator函数
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
注:generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到
return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
generator函数的调用实际返回一个generator对象
调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。
匿名函数lambda是指一类无需定义标识符(函数名)的函数或子程序。
lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。
lambda [arg1 [,arg2,.....argn]]:expression
冒号前是参数,可以有多个,用逗号隔开,冒号右边的为表达式(只能为一个)。其实lambda返回值是一个函数的地址,也就是函数对象
函数本身也可以赋值给变量,即:变量可以指向函数。
函数名其实就是指向函数的变量
定义:一个函数接收另一个函数作为参数,这种函数就称之为高阶函数。
输入:两个参数,一个是函数,一个是Iterable
,
输出:返回Iterator
。将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list。
输入:两个参数,一个是函数,一个是Iterable
,
输出:返回计算结果。reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
输入:两个参数,一个是函数,一个是Iterable
,
输出:返回Iterator
。filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
输入:三个参数 —— 序列,key
函数,reserve(默认不反转)
sorted()
函数也是一个高阶函数,它还可以接收一个key
函数来实现自定义的排序,key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序,然后sorted()
函数按照keys进行排序,并按照对应关系返回list相应的元素。
例如:忽略大小写来比较字符串(只要都变成小写/大写就能实现)
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
使用闭包时,对外层变量赋值前,需要先使用nonlocal
声明该变量不是当前函数的局部变量。不然会把它当做局部变量,报错。
代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
本质上,decorator就是一个返回函数的高阶函数
我们要借助Python的@语法
,把decorator置于函数的定义处:把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数
当函数的参数个数太多,需要简化时,使用
functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数
eg:实现转换成二进制的整数
int2 = functools.partial(int, base=2)
模块 —— py.文件,命名不要使用中文、特殊字符
包 —— 模块的集合(必须包含__init__.py模块,否则是普通的目录)
导入模块:import
作用域:
__xxx__
可以被直接引用,但是有特殊用途,如__author__
,__name__``__doc__
,我们自己的变量一般不要用这种变量名;_xxx
或__xxx
安装第三方模块,是通过包管理工具pip
完成的
类 :class 类名(父类)
getattr()
函数,setattr()
以函数及hasattr()
函数: (内置函数)我们可以直接操作一个对象的状态绑定属性:
>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael
绑定方法:
>>> def set_age(self, age): # 定义一个函数作为实例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25
为了给所有实例都绑定方法,可以给class绑定方法:
>>> def set_score(self, score):
... self.score = score
...
>>> Student.set_score = set_score
__slots__()
:
给class限制属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
注:__slots__()
只对当前类的实例起限制作用,对其子类不起作用
@property
装饰器:把一个方法变成属性调用的,既能检查参数,又可以用类似属性这样简单的方式来访问类的变量
把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property本身又创建了另一个装饰器@score.setter
,负责把一个setter
方法变成属性赋值,也可以不用setter
方法,只用@property
,就变成只读属性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth//age只读属性
然后就变成可控的属性了(检查参数),调用方法如:
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
要特别注意:属性的方法名不要和实例变量重名(会无限递归)
通过多重继承,一个子类就可以同时获得多个父类的所有功能。
对于需要Runnable功能的动物,就多继承一个Runnable,例如Dog:
class Dog(Mammal, Runnable):
pass
MixIn
这种形如__xxx__
的变量或者函数名就要注意,这些在Python中是有特殊用途的
__slots__
:限制类的属性__len__()
方法:让class作用于len()函数__str__
()方法:打印实例时返回更好看的字符串:>>> class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)
__repr__
()返回程序开发者看到的字符串,也就是说,__repr__
()是为调试服务的。直接显示变量调用的不是__str__(),而是__repr__()
>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>
解决办法:修改完__str__
()后加一个__repr__ = __str__
__iter__
:我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
__getitem__()
方法:Fib实例虽然能作用于for循环,但是把它当成list来使用还是不行,要表现得像list那样按照下标取出元素,需要实现__getitem__()方法class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
这样就可以像列表一样[下标]访问元素了
>>> f = Fib()
>>> f[0]
1
__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素。
__getattr__()
:动态返回一个属性class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99
__call__
:将对象转换成“可调用对象”(即可以当做函数)class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
调用方式如下:
>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.
callable()
函数: 判断一个对象是否是“可调用”对象。
“可调用”对象:函数,定义的带有__call__()
的类实例
Enum
类: 枚举类,每个常量都是class的一个唯一实例from enum import Enum
class XX(Enum): //继承Enum类
#调用枚举成员的 3 种方式
print(Color.red)
print(Color['red'])
print(Color(1))
#调取枚举成员中的 value 和 name
print(Color.red.value)
print(Color.red.name)
#遍历枚举类中所有成员的 2 种方式
for color in Color:
print(color)
type的两种用法:
type(obj)//obj为对象或类
type(name, bases, dict)//name为类名,bases为所有父类(元组类型),dict为属性和方法
try...except...finally...
的错误处理机制
-O
参数,这样assert语句相当于pass语句def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
assert的意思是,表达式n != 0应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。
如果断言失败,assert语句本身就会抛出AssertionError:
$ python err.py
Traceback (most recent call last):
···
AssertionError: n is zero!
import logging
logging.basicConfig(level=logging.INFO)
将日志消息输出到文件中的实现方法很简单,只需要设置 logging.basicConfig() 函数中的 filename 关键字参数即可
4. pdb:单步运行(不好用,直接用IDE的断点调试更方便)
为了编写单元测试,我们需要引入Python自带的unittest模块
import unittest
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase
继承。
class TestDict(unittest.TestCase):
...
以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
由于
unittest.TestCase
提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual()
,还有assertRaises()
if __name__ == '__main__':
unittest.main()
然后就可以当做正常的脚本运行
$ python mydict_test.py
-m unittest
直接运行单元测试:$ python -m unittest mydict_test
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试。
可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。
设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:
注释+导入模块doctest
当我们编写注释时,如果写上这样的注释:
def abs(n):
'''
Function to get absolute value of number.
Example:
>>> abs(1)
1
>>> abs(-1)
1
>>> abs(0)
0
'''
return n if n >= 0 else (-n)
无疑更明确地告诉函数的调用者该函数的期望输入和输出。
if __name__=='__main__':
import doctest
doctest.testmod()
并且,Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用…表示中间一大段烦人的输出。
同步IO,异步IO
f = open(文件名,标识符)'
方法 | 特点 | 适用条件 |
---|---|---|
read() | 一次性全部读取 | 文件很小 |
readlines() | 每次读取一行内容 | 配置文件 |
read(size) | 指定大小 | 不能确定文件大小 |
写文件
‘w’
‘wb’
f.close()
try ... finally
来实现:with
语句来自动帮我们调用close()方法:with open('/path/to/file', 'r') as f:
print(f.read())
这和前面的try … finally是一样的,但是代码更佳简洁,并且不必调用f.close()方法。
StringIO顾名思义就是在内存中读写str。
要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
getvalue()方法用于获得写入后的str。
操作二进制数据,就需要使用BytesIO。
BytesIO实现了在内存中读写bytes,我们创建一个BytesIO,然后写入一些bytes:
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
Python内置的os
模块可以直接调用操作系统提供的接口函数。
先导入os
模块:import os
name
uname()
os.environ
——环境变量
os.environ.get(key)
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
导入pickle模块:import pickle
序列化:pickle.dumps()
方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object
反序列化:可以先把内容读到一个bytes,然后用pickle.loads()
方法反序列化出对象,也可以直接用pickle.load()
方法从一个file-like Object中直接反序列化出对象。
json
模块:
如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。
Python内置的json模块提供了非常完善的Python对象到JSON
格式的转换。
json模块的dumps()
和loads()
函数
对于操作系统来说,一个任务就是一个进程(Process),进程内的“子任务”称为线程(Thread)。
多任务的实现有3种方式:
多进程模式;
多线程模式;
多进程+多线程模式。
全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。
可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。
用于匹配字符串
可以实现:切片(re.split()
),分组
在正则表达式中,如果直接给出字符,就是精确匹配。
\d:一个数字
\w:一个字母或数字
.:任意字符
要匹配变长的字符:
*:任意个字符(包括0个)
+:至少一个字符
?:0个或1个字符
{n}:n个字符
{n,m}:n-m个字符
要做更精确地匹配,可以用[]表示范围
进阶:
要做更精确地匹配,可以用[]表示范围
A|B
可以匹配A或B
^ 表示行的开头,^ \d表示必须以数字开头。
$ 表示行的结束,\d$表示必须以数字结束。
re
模块,match()
方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None.
del x