- 02. *args 和 **kwargs
- 03. 调试
- 04. 生成器
- 05. Map,Filter 和 Reduce
- 06. set 数据结构
- 07. 三元运算符
- 08. 装饰器
- 09. Global和Return
- 10. 对象变动 Mutation
- 11. __slots__魔法
- 12. 虚拟环境
- 13. 容器
- 14. 枚举
- 15. 对象自省
- 16. 推导式
- 17. 异常
- 18. lambda表达式
- 19. 一行式
- 20. For - Else
- 21. 使用C扩展
- 22. open函数(文件读取)
- 23. 目标Python
- 24. 协程
- 25. 函数缓存
- 26. 上下文管理器
02. *args 和 **kwargs
一个函数定义,如果并不知道函数使用者会传递多少个参数,这种场景下使用和
两个关键字
*argv
:用来发送一个非键值对的可变数量参数列表
def test_ver_args(f_arg,*argv):
print("first normal arg:",f_arg)
for arg in argv:
print("through *argv:",arg)
test_ver_args("yahoo","google","amazon","apple")
输出如下:
first normal arg: yahoo
through *argv: google
through *argv: amazon
through *argv: apple
**argv
:允许将不定长度的键值对,作为参数传递给一个函数
def test_ver_2args(**argv):
for key,value in argv.items():
print("{0}={1}".format(key,value))
test_ver_2args(name="lijun",age=9,gender="male")
输出如下:
name=lijun
age=9
gender=male
02.1 使用 *args 和 **kwargs 来调用函数
- args和kwargs参数的个数,必须与函数中接受参数个数一致,否则会有编译错误
def test_args_kwargs(arg1,arg2,arg3):
print("arg1",arg1)
print("arg2",arg2)
print("arg3",arg3)
args = ("two",3,True)
test_args_kwargs(*args)
print("----------------------------")
kwargs = {"arg3":3,"arg2":2,"arg1":"One"}
test_args_kwargs(**kwargs)
arg1 two
arg2 3
arg3 True
----------------------------
arg1 One
arg2 2
arg3 3
那么如果你想在函数里同时使用所有这三种参数, 顺序是这样的: some_func(fargs, *args, **kwargs) 详细参考16.5 参数组合
02.2 什么场景下使用
下面的内容没有完全理解!
什么时候使用它们? 这还真的要看你的需求而定。 最常见的用例是在写函数装饰器的时候(会在另一章里讨论)。
此外它也可以用来做猴子补丁(monkey patching)。猴子补丁的意思是在程序运行时 (runtime)修改某些代码。 打个比方,你有一个类,里面有个叫get_info的函数会调用一 个API并返回相应的数据。如果我们想测试它,可以把API调用替换成一些测试数据。例 如:
import someclass
def get_info(self, *args):
return "Test data"
someclass.get_info = get_info
参考:
03. 调试
- 从命令行运行
添加-m pdb
在py文件前面,直接在第一行就进行调试模式
C:\Users\61041150\Desktop\20180203\shell\pyData>python -m pdb pylearn.py
> c:\users\61041150\desktop\20180203\shell\pydata\pylearn.py(1)<module>()
-> def test_ver_args(f_arg,*argv):
(Pdb)
- 从脚本内部运行
import pdb
def make_bread(m):
i = m
x = m +1
pdb.set_trace()
return "I don't have time"
print(make_bread(3))
运行后效果如下,可以打印内存中参数的值,另外还能执行计算:
> c:\users\61041150\desktop\20180203\shell\pydata\pylearn.py(40)make_bread()
-> return "I don't have time"
(Pdb) i
3
(Pdb) m
3
(Pdb) x
4
(Pdb) i = m + 2
(Pdb) i
5
- 在pdb模式中,输入如下command:
c
: 继续执行
w
: 显示当前正在执行的代码行的上下文信息
a
: 打印当前函数的参数列表
s
: 执行当前代码行,并停在第一个能停的地方(相当于单步进入)
n
: 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)
单步跳过(next)和单步进入(step)的区别在于, 单步进入会进入当前行调用的函数内 部并停在里面, 而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的 下一行。
04. 生成器
关于迭代,生成器等,可以参考20. 迭代
04.1 可迭代对象(Iterable)
Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法,或者定义了 可以支持下标索引的__getitem__方法(这些双下划线方法会在其他章节中全面解释), 那么它就是一个可迭代对象。 简单说,可迭代对象就是能提供迭代器的任意对象。那迭代 器又是什么呢?
04.2 迭代器(Iterator)
任意对象,只要定义了next(Python2) 或者__next__方法,它就是一个迭代器。就这么 简单。现在我们来理解迭代(iteration)
04.3 迭代(Iteration)
用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一 个循环来遍历某个东西时,这个过程本身就叫迭代。现在既然我们有了这些术语的基本理 解,那我们开始理解生成器吧。
04.4 生成器(Generators)
生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在 内存中,而是在运行时生成值。你通过遍历来使用它们,要么用一个“for”循环,要么将它 们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而, 它们并不返回一个值,而是yield(暂且译作“生出”)一个值。这里有个生成器函数的简单 例子:
def generator_function(m):
for i in range(m):
print("yield0:{}".format(i))
yield i
for i in generator_function(4):
print("print:{}".format(i))
- 输出结果如下:
yield0:0
print:0
yield0:1
print:1
yield0:2
print:2
yield0:3
print:3
上面的例子可以看出,生成器也是一种迭代器,可以用于for循环,每一次迭代,生成器被调用一次,即print一次yield0,再yield一个值供外面print,下一次迭代再重复上述过程。 所以生成的值并不是存储在内存中,而是需要的时候直接计算。
def generator_function():
for i in range(4):
print("yield0:{}".format(i))
yield i
g = generator_function()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
```python
输出如下:
```python
yield0:0
0
yield0:1
1
yield0:2
2
yield0:3
3
【未完】
05. Map,Filter 和 Reduce
05.1 Map
Map会将一个函数映射到一个输入列表的所有元素上。
规范为 map(function_to_apply, list_of_inputs)
,大多数时候使用匿名函数lambda替代函数。
items = [1,2,3,4,5]
squared = []
for i in items:
squared.append(i*i)
print(squared)
squared1 = [i*i for i in items]
squared2 = list(map(lambda x: x**2, items))
print(squared1)
print(squared2)
print("--------------------------")
def multi(x):
return x*x
def add(x):
return x+x
funcs = [multi,add]
for i in items:
value = map(lambda x: x(i), funcs)
print(list(value))
输出如下:
[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]
--------------------------
[1, 2]
[4, 4]
[9, 6]
[16, 8]
[25, 10]
上面 value = map(lambda x: x(i), funcs) ,将函数名存储到一个list funcs中,将函数名作为参数传递给匿名函数,示例如下:
def multi(x):
return x*x
def add(x):
return x+x
funcs = [multi,add]
value = map(lambda x: x(10), funcs)
print(list(value))
输出:[100, 20]
下面是使用函数的形式,后面迭代器的元素,依次作为参数提供给前面的f函数
def f(x):
return x**3
lista = [1,2,3,4,5,6]
r = map(f,lista)
print(list(r))
输出如下:
[1, 8, 27, 64, 125, 216]
05.2 filter
顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列 表,符合要求即函数映射到该元素时返回值为True.
number_list = range(-5,5)
above_than_zero = filter(lambda x:x>0,number_list)
print(list(above_than_zero))
输出如下:
[1, 2, 3, 4]
与上面一样,除了匿名函数,还可以接受函数,如下判断偶数的函数,另外,filter()与map()一样,返回的都是迭代器,分别为filter对象和map对象,需要用list取出其中的元素。
def check_even(x):
if x%2 == 0:
return True
else:
return False
even_number_list = filter(check_even,number_list)
print(list(even_number_list))
print((even_number_list))
输出如下:
[-4, -2, 0, 2, 4]
<filter object at 0x0150F230>
05.3 Reduce
当需要对一个列表进行一些计算并返回结果时,Reduce 是个非常有用的函数。
举个例 子,当你需要计算一个整数列表的乘积时。 通常在 python 中你可能会使用基本的 for 循环来完成这个任务。
现在我们来试试 reduce:
from functools import reduce
product = reduce(lambda x,y:x*y,[1,2,3,4])
print(product)
print("---------------")
def testfunc(x,y):
print("x:{0},y:{1}".format(x,y))
return x*y
product1 = reduce(testfunc,[1,2,3,4])
print(product1)
输出为
24
---------------
x:1,y:2
x:2,y:3
x:6,y:4
24
可以看到testfunc每次接受两个参数,第一次是前两个,第二次前一次的结果和第三个,第三次也是前一次的结果和第四个reduce中的函数需要接受两个参数。
06. set 数据结构
set集合不能包含重复的元素,没有顺序。
some_list = ["A","B","C","A","B"]
duplicates = []
for i in some_list:
if i not in duplicates:
duplicates.append(i)
print("original:{}".format(some_list))
print("-------------")
print("method0:{}".format(duplicates))
print("-------------")
duplicates1 = set([x for x in some_list])
print("method1:{}".format(duplicates1))
print("-------------")
duplicates2 = set([x for x in some_list if some_list.count(x) > 1])
print("count>1:{}".format(duplicates2))
- 输出如下,另外,注意
some_list.count(x) > 1
的用法,能计算list中的元素个数:
original:['A', 'B', 'C', 'A', 'B']
-------------
method0:['A', 'B', 'C']
-------------
method1:{'A', 'C', 'B'}
-------------
count>1:{'A', 'B'}
- set集合的交集与并集,
{}
可以用于定义集合
setA = set(["A","B","C","D"])
setB = {"A","B","E","F"}
print("intersection:{}".format(setA.intersection(setB)))
print("difference(A-B):{}".format(setA.difference(setB)))
print("difference(B-A):{}".format(setB.difference(setA)))
输出如下:
intersection:{'B', 'A'}
difference(A-B):{'C', 'D'}
difference(B-A):{'E', 'F'}
07. 三元运算符
三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(not)的条件判 断,在Python 2.4以上才有了三元操作。
它允许用简单的一行快速判断,而不是使用复杂的多行if语句。 这在大多数时候非常有 用,而且可以使代码简单可维护。
is_fat = True
state = "fat" if is_fat else "not fat"
print(state)
上述输出fat
08. 装饰器
09. Global和Return
- global是全局变量
global变量意味着我们可以在函数以外的区域都能访问这个 变量。比较下面两个程序:
def add1(x,y):
return x+y
print(add1(3,5))
print("-----------")
def add2(x,y):
global result
result = x + y
add2(3,5)
print(result)
- 输出如下:
8
-----------
8
不过,在实际的编程时,你应该试着避开global关键字,它只会让生活变得艰难,因为它引入了多余的变量到全局作用域了。
- 多个return值
如果我们要返回多个值,有如下多种方式,尽量避免使用第一种global变量的方式,第三种方式最smart
def profile1():
global name
global age
name = "lj-1"
age = "18-1"
def profile2():
name = "lj-2"
age = "18-2"
return (name,age)
def profile3():
name = "lj-3"
age = "18-3"
return name,age
profile1()
print("{0}:{1}".format(name,age))
name2 = profile2()[0]
age2 = profile2()[1]
print("{0}:{1}".format(name2,age2))
name3,age3 = profile3()
print("{0}:{1}".format(name3,age3))
输出如下:
lj-1:18-1
lj-2:18-2
lj-3:18-3
10. 对象变动 Mutation
参考 16.2 默认参数中,默认参数必须指向不变的对象的例子。
在Python中当函数被定义时,默认参数只会运算一次,而不是每次被调用时都会重新运算。你应该永远不要定义可变类型的 默认参数。
foo = ["hi"]
print(foo)
bar = foo
bar += [",bye"]
print(foo)
print("--------------------")
def add_to(num,target=[]):
target.append(num)
return target
print(add_to(1))
print(add_to(2))
print(add_to(3))
print("--------------------")
def add_to2(element,target=None):
if target is None:
target = []
target.append(element)
return target
print(add_to2(1))
print(add_to2(2))
print(add_to2(3))
输出为
['hi']
['hi', ',bye']
--------------------
[1]
[1, 2]
[1, 2, 3]
--------------------
[1]
[2]
[3]
11. __slots__魔法
在进入正文前,先看看python的动态属性和动态方法绑定,即一个对象在创建完毕后,还能给它追加属性和方法。 实例代码:
class Student(object):
def __init__(self,name):
self.name = name
#
bob = Student("bob")
bob.age = 24
print("{0}:{1}".format(bob.name,bob.age))
#
def showname(name):
print("my name is:{}".format(name))
bob.showname = showname
bob.showname(bob.name)
运行后结果:
bob:24
my name is:bob
默认情况下Python用一个字典来保存一个对象的实例 属性。这非常有用,因为它允许我们在运行时去设置任意的新属性。然而,对于有着已知属性的小类来说,它可能是个瓶颈。这个字典浪费了很多内存。 Python不能在对象创建时直接分配一个固定量的内存来保存所有的属性。因此如果你创建 许多对象(我指的是成千上万个),它会消耗掉很多内存。
- 使用__slots__来告诉Python不要使 用字典,而且只给一个固定集合的属性分配空间。
class Student2(object):
__slot__ = ["name"]
def __init__(self,name):
self.name = name
wangling = Student2("wangling")
wangling.age = 24
print("{0}:{1}".format(wangling.name,wangling.age))
输出结果为: wangling:24
Python在构建类的时候,会检查__slots__变量是否存在,如果存在,为slots变量建立member_descriptor,并放入tp_dict里面,并且这个类的tp_dictoffset为0,它的实例将不会有自己的__dict__。
虽然上面的实例中也有动态的属性追加,但是估计是加到member_descriptor
中,而不是每个实例中。
既然使用__slots__
既能减少内存使用,也能实现动态属性追加,那是不是什么情况都需要使用呢?不是!只有在实例非常多的情况下,使用它才最有优势。
Warning: Don’t prematurely optimize and use this everywhere! It’s not great for code maintenance, and it really only saves you when you have thousands of instances.
12. 虚拟环境
13. 容器
python附带一个模块,包含了很多数据类型,名字叫做collections,这里讨论:
- defaultdict
- counter
- deque
- namedtuple
- enum.Enum
defaultdict
我个人使用defaultdict较多,与dict类型不同,你不需要检查key是否存在,所以我 们能这样做:
from collections import defaultdict
colors = (
("1","red"),
("2","blue"),
("2","dark blue"),
("3","yellow"),
("3","pink"),
)
favourite_colors = defaultdict(list)
for name,color in colors:
favourite_colors[name].append(color)
print(favourite_colors)
输出为:
defaultdict(<class 'list'>, {'1': ['red'], '2': ['blue', 'dark blue'], '3': ['yellow', 'pink']})
- 在一个字典中对一个键进行嵌套赋值时,如果这个键不存 在,会触发keyError异常。 defaultdict允许我们用一个聪明的方式绕过这个问题。
首先分享一个使用dict触发KeyError的例子
some_dict = {}
some_dict["colors"]["favourite"] = "yellow"
错误如下:
Traceback (most recent call last):
File "pylearn.py", line 3, in <module>
some_dict["colors"]["favourite"] = "yellow"
KeyError: 'colors'
然后提供一个使用defaultdict的解决方案。
import collections
tree = lambda:collections.defaultdict(tree)
some_dict = tree()
some_dict["colors"]["favourite"] = "yellow"
import json
print(json.dumps(some_dict))
输出为:{"colors": {"favourite": "yellow"}}
但是如果是非嵌套形式的话,是可以直接赋值的,如下:
some_dict = {}
some_dict["colors"] = "yellow"
print(some_dict)
输出为:{'colors': 'yellow'}
counter
Counter是一个计数器,它可以帮助我们针对某项数据进行计数。比如它可以用来计算每个人喜欢多少种颜色:
from collections import Counter
colors = (
("1","red"),
("2","blue"),
("2","blue"),
("3","blue"),
("9","blue"),
("3","pink"),
)
favs = Counter((str(color)+"-"+str(num)) for num, color in colors)
favs2 = Counter(color for num, color in colors)
print(favs)
print(favs2)
输出如下:
Counter({'blue-2': 2, 'red-1': 1, 'blue-3': 1, 'blue-9': 1, 'pink-3': 1})
Counter({'blue': 4, 'red': 1, 'pink': 1})
我们也可以在利用它统计一个文件,例如:
with open('filename', 'rb') as f:
line_count = Counter(f)
print(line_count)
deque
deque提供了一个双端队列,可以从头尾两端添加和删除元素。 示例程序如下:
from collections import deque
d = deque()
d.append("1")
d.append("2")
print(d)
print(d[1])
print("------------------------")
d1 = deque(range(5))
print(d1)
x = d1.popleft()
y = d1.pop()
print("popleft:{0},pop:{1}".format(x,y))
输出结果如下:
deque(['1', '2'])
2
------------------------
deque([0, 1, 2, 3, 4])
popleft:0,pop:4
我们也可以限制这个列表的大小,当超出你设定的限制时,数据会从对队列另一端被挤出 去(pop)。
from collections import deque
d = deque(maxlen=8)
m = [d.append(x) for x in range(8)]
print(m)
print(d)
print("-----------------")
m = [d.append(x) for x in range(9,12)]
print(d)
输出如下:
[None, None, None, None, None, None, None, None]
deque([0, 1, 2, 3, 4, 5, 6, 7], maxlen=8)
-----------------
deque([3, 4, 5, 6, 7, 9, 10, 11], maxlen=8)
还可以从任一端扩展这个队列中的数据:
from collections import deque
d = deque([1,2,3,4,5])
d.extendleft(["a","b"])
print(d)
d.extend(["A","B"])
print(d)
输出为:
deque(['b', 'a', 1, 2, 3, 4, 5])
deque(['b', 'a', 1, 2, 3, 4, 5, 'A', 'B'])
namedtuple
介绍命名tuple之前,先看看普通元祖,一个元组是一个不可变的列表,你可以存储一个数据的序列,它和命名元组 (namedtuples)非常像,但有几个关键的不同。 主要相似点是都不像列表,你不能修改元组中的数据。为了获取元组中的数据,你需要使 用整数作为索引:
tuple1 = ("x",100)
print(tuple1[1])
上述代码输出100
,如果试着修改tuple的话,如tuple1[1] = 200
,则会出错TypeError: 'tuple' object does not support item assignment
。
那namedtuples是什么呢?它把元组变成一个针对简单任务的容器。你不必使用整数索引来访问一个namedtuples的数据。你可以像字典(dict)一样访问namedtuples, 但namedtuples是不可变的。
from collections import namedtuple
Animal = namedtuple("Animal","name age type")
perry = Animal(name="perry",age=31,type="cat")
print(perry)
print(perry.name)
输出如下:
Animal(name='perry', age=31, type='cat')
perry
现在可以看到,我们可以用名字对tuple进行访问了,一个命名元组(namedtuple)有两个必须的参数,分别是元组名称和字段名称。
namedtuple的每个实例没有对象字典,所以它们很轻量,与普通的元组比,并不 需要更多的内存。这使得它们比字典更快。但是要记住它是一个元组,属性值在namedtuple中是不可变的。
另外,namedtuple还有两点:
- 本身是元组,仍然可以通过数字下标访问
- 可以将一个命名元组转换为字典
from collections import namedtuple
Animal = namedtuple("Animal","name age type")
perry = Animal(name="perry",age=31,type="cat")
print(perry)
print(perry[2])
print(perry._asdict())
输出如下:
Animal(name='perry', age=31, type='cat')
cat
OrderedDict([('name', 'perry'), ('age', 31), ('type', 'cat')])
enum.Enum (Python 3.4+)
另一个有用的容器是枚举对象,它属于enum模块,存在于Python 3.4以上版本中。
让我们回顾一下上一个’Animal’命名元组的例子。 它有一个type字段,问题是,type是一个字符串。 那么问题来了,万一程序员输入了Cat,因为他按到了Shift键,或者输入了’CAT’,甚 至’kitten’?
枚举可以帮助我们避免这个问题,通过不使用字符串。考虑以下这个例子:
from collections import namedtuple
from enum import Enum
class Species(Enum):
cat = 1
dog = 2
horse = 3
owl = 4
#
kitten = 1
puppy = 2
Animal = namedtuple("Animal","name age type")
perry = Animal(name="Perry",age=31,type=Species.cat)
tom = Animal(name="Tom",age=4,type=Species.dog)
charlie = Animal(name="Charlie",age=2,type=Species.kitten)
print(perry.type == charlie.type)
print(perry.type)
print(perry.type == tom.type)
print("{0}:{1}:{2}".format(Species(1),Species["cat"],Species.cat))
输出如下:
True
Species.cat
False
Species.cat:Species.cat:Species.cat
14. 枚举
枚举(enumerate)是Python内置函数。它的用处很难在简单的一行中说明,但是大多数的新人,甚至一些高级程序员都没有意识到它。 它允许我们遍历数据并自动计数。
- 接收参数
setA = set(["A","B","C","D"])
for index,value in enumerate(setA):
print("{0}:{1}".format(index,value))
print("-------------")
listA = ["A","B","C","D"]
# 可选参数允许我们定制从哪个数字开始枚举
for index,value in enumerate(listA,1):
print("{0}:{1}".format(index,value))
print("-------------")
# 创建包含索引的元组列表
indexlistA = list(enumerate(listA,101))
print(indexlistA)
输出如下,注意两次的输出set的结果不同,说明set中的数据是无序的。
C:\Users\61041150\Desktop\20180203\shell\pyData>python pylearn.py
0:B
1:D
2:C
3:A
-------------
1:A
2:B
3:C
4:D
-------------
[(101, 'A'), (102, 'B'), (103, 'C'), (104, 'D')]
C:\Users\61041150\Desktop\20180203\shell\pyData>python pylearn.py
0:A
1:C
2:B
3:D
-------------
1:A
2:B
3:C
4:D
-------------
[(101, 'A'), (102, 'B'), (103, 'C'), (104, 'D')]
15. 对象自省
自省(introspection),在计算机编程领域里,是指在运行时来判断一个对象的类型的能力。 它是Python的强项之一。Python中所有一切都是一个对象,而且我们可以仔细勘察那些对 象。
- dir
>>> listA = [1,2,3]
>>> dir(listA)
上面的自省给了我们一个列表对象的所有方法的名字。当你没法回忆起一个方法的名字, 这会非常有帮助。如果我们运行dir()而不传入参数,那么它会返回当前作用域的所有名 字。
- type与id
>>> type(listA)
<class 'list'>
>>> type(" ")
<class 'str'>
>>> type(2)
<class 'int'>
>>> id(listA)
24097976
- inspect模块
inspect模块也提供了许多有用的函数,来获取活跃对象的信息。比方说,你可以查看一 个对象的成员,只需运行:
>>> import inspect
>>> print(inspect.getmembers(str))
16. 推导式
参考 21. 列表生成式
列表推导式(list comprehensions)
如果我们要生成一个list的平方,可能会用下面for循环的方式,但是如果用列表推倒式,一行就搞定:
squared1 = []
for x in range(5):
squared1.append(x**2)
print(squared1)
#
squared2 = [x**2 for x in range(5)]
print(squared2)
都输出 [0, 1, 4, 9, 16]
。
同样,通过列表推倒式也可以用于过滤元素,输出[0, 3, 6, 9, 12]
:
mul = [i for i in range(15) if i % 3 is 0]
print(mul)
当然上面的也可以用map和filter来实现,但是列表推倒式还是更简明:
print(list(filter(lambda x:x%3==0,range(15))))
print(list(map(lambda x:x**2,range(5))))
分别输出为[0, 3, 6, 9, 12]
和[0, 1, 4, 9, 16]
。
字典推导式(dict comprehensions)
示例如下:
# 1
mcase = {"a":10,"b":34,"A":7,"z":3,"Z":10}
mcase_frequency = {k.lower():mcase.get(k.lower(),0) + mcase.get(k.upper(),0) for k in mcase.keys()}
print(mcase_frequency)
# 2
mcase_change = {v:k for k,v in mcase_frequency.items()}
print(mcase_change)
上面# 1
,将同一个字母,但是大小写不同的,其所对应的数字求和了:
k.lower()
是生成的字典的key,后面根据key去计算其大小写后取对应的数字,有可能没有对应的大写或是小写,则默认为0for k in mcase.keys()
这个是循环器,将字典的key取出来,大写A小写a虽然被计算两次,但因为数据结构是字典,最终只会保留一个
上面的# 2
,将字典的key和value进行了反转。
集合推导式(set comprehensions)
它们跟列表推导式也是类似的。 唯一的区别在于它们使用大括号{}。 举个例子:
squared_set = {x**2 for x in range(-5,6)}
print(squared_set)
squared_list = [x**2 for x in range(-5,6)]
print(squared_list)
输出如下:
{0, 1, 4, 9, 16, 25}
[25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25]
17. 异常
最基本的异常处理是 try/except从句,将可能触发异常的代码放到try语句中,而处理异常的代码在except中实现,如下是一个简单的例子:
try:
file = open("text.txt","rb")
except IOError as e:
print("An IOError occurred:\n{}".format(e.args[-1]))
当一个文件不存存在时会触发异常,输出结果如下:
An IOError occurred:
No such file or directory
处理多个异常
通常有如下的三种方式来处理异常:
- 将多个异常放在一个元组里面
try:
file = open("text.txt","rb")
except (IOError,EOFError) as e:
print("An Error occurred:\n{}".format(e.args[-1]))
- 使用多个except语句
try:
file = open("text.txt","rb")
except EOFError as e:
print("An EOFError occurred.")
raise e
except IOError as e:
print("An IOError occurred.")
raise e
- 不显式指定异常名,捕获所有异常
try:
file = open("text.txt","rb")
except Exception:
print("An error occurred.")
raise
上面通过raise
显式引发异常,异常被触发后输出如下:
An error occurred.
Traceback (most recent call last):
File "pylearn.py", line 2, in <module>
file = open("text.txt","rb")
FileNotFoundError: [Errno 2] No such file or directory: 'text.txt'
finally从句
try…except..finally异常处理从句中,finally中代码是不管异常是否触发都将会被执行,这样可以用在脚本执行完的清理工作。
try:
file = open("test.txt","rb")
print("Open Successfull!")
except IOError as e:
print("An IOError occurred: {}".format(e.args[-1]))
finally:
print("This would be printed whether or not an exception occurred")
- 没有触发异常,正常打开文件时的输出:
Open Successfull!
This would be printed whether or not an exception occurred
- 触发异常,没有找到可以打开的文件时的输出:
An IOError occurred: No such file or directory
This would be printed whether or not an exception occurred
try/else从句
类似于for/else,感觉不是很常用的else语句,如果想在没有触发异常的时候执行一些代码,可以很简单的通过else从句来实现。 可能会有这样的疑问,既然只是想让代码在没有触发异常时被执行,为什么不直接放在try中呢?答案是如果放在try中这部分代码的异常会被捕获,而放在else中,不会被捕获。
实际的使用场景中,不知道什么case需要这样使用。
举例如下:
- try正常执行,没有触发异常,所以else会被执行:
try:
print("try: I am sure no exception is going to occur!")
# 1
# file = open("test1.txt","rb")
except Exception as e:
print("except: exception")
else:
print("else: This would only run if no exception occurs,and an error would not be caught")
# 2
# file = open("test1.txt","rb")
finally:
print("finally: This would be printed in every case.")
输出为:
try: I am sure no exception is going to occur!
else: This would only run if no exception occurs,and an error would not be caugh
t
finally: This would be printed in every case.
- try异常结束,被except捕获异常,else不会被执行:
try:
print("try: I am sure exception is going to occur!")
# 1
file = open("test1.txt","rb")
except Exception as e:
print("except: exception")
else:
print("else: This would only run if no exception occurs,and an error would not be caught")
# 2
# file = open("test1.txt","rb")
finally:
print("finally: This would be printed in every case.")
输出如下:
try: I am sure exception is going to occur!
except: exception
finally: This would be printed in every case.
- try正常结束,else被执行,但是else中的异常不会被捕获,而是直接触发异常:
try:
print("try: I am sure no exception is going to occur!")
# 1
# file = open("test1.txt","rb")
except Exception as e:
print("except: exception")
else:
print("else: This would only run if no exception occurs,and an error would not be caught")
# 2
file = open("test1.txt","rb")
finally:
print("finally: This would be printed in every case.")
输出如下:
try: I am sure no exception is going to occur!
else: This would only run if no exception occurs,and an error would not be caught
finally: This would be printed in every case.
Traceback (most recent call last):
File
"pylearn.py", line 10, in <module>
file = open("test1.txt","rb")
FileNotFoundError: [Errno 2] No such file or directory: 'test1.txt'
else从句只会在没有异常的情况下执行,而且它会在finally语句之前被执行。
18. lambda表达式
add = lambda x,y:x+y
print(add(3,5))
a = [(1,2),(4,1),(9,10),(13,-3)]
a.sort(key=lambda x:x[1])
print(a)
print("-------------")
# 列表并行排序
list1 = (1,2,3,4)
list2 = (3,4,5,6)
data = zip(list1,list2)
print(type(data))
for x,y in data:
print("{0}:{1}".format(x,y))
#data.sort()
#list1,list2 = map(lambda t:list(t),zip(*data))
输出如下:
8
[(13, -3), (4, 1), (1, 2), (9, 10)]
-------------
<class 'zip'>
1:3
2:4
3:5
4:6
19. 一行式
- 简易Web Server
# Python 2
python -m SimpleHTTPServer
# Python 3
python -m http.server
```python
- 漂亮打印 `pprint`
```python
from pprint import pprint
my_dict = {"name":"lj","age":20,"male":True,"wife":"wanling"}
pprint(my_dict)
print(my_dict)
输出如下:
{'age': 20, 'male': True, 'name': 'lj', 'wife': 'wanling'}
{'name': 'lj', 'age': 20, 'male': True, 'wife': 'wanling'}
这种方法在字典上更为有效。
此外,如果你想快速漂亮的从文件打印出json数据,那么可以这么做:cat file.json | python -m json.tool
- 脚本性能分析
这可能在定位你的脚本中的性能瓶颈时,会非常奏效,python -m cProfile my_script.py
- 列表扁平
import itertools
a_list = [[1,2],[3,4],[5,6]]
print(list(itertools.chain.from_iterable(a_list)))
print(list(itertools.chain(*a_list)))
输出如下:
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
另外,关于一行式的构造器和CSV转换JSON,现在不知道有何用处,暂时跳过。
20. For - Else
for循环是每个程序语言中最基本的控制语句,但是大部分人不知道在python中for还有一个else子句,else子句会在循环正常结束(即不是被break中断)时被调用。常见的应用场景为,如果我们跑一个循环,查找一个元素,一旦找到就break,这个for循环有两个场景会停止循环:
- 找到元素,break
- 没找到元素,for循环完毕
如果我们想知道到底是如何停止循环的,即确认到底有没有找到元素,一个方法是做一个标记,在break前标记,另一个方式就是使用else子句,for else结构如下:
for item in container:
if search_something(item):
# Found it!
process(item)
break
else:
# Didn't find anything..
not_found_in_container()
实例:
listA = [1,4,5,6]
myelement = 40
# 方法1
founder = False
for i in listA:
if i == myelement:
founder = True
if founder == False:
print("not found!")
else:
print("found!")
#
print("-------------------")
# 方法2
for i in listA:
if i == myelement:
print("found!")
break
else:
print("not found!")
输出如下:
not found!
-------------------
not found!
上面的例子好像没有什么说服力,看下面的例子,直接将质数一起打印出来:
for i in range(2,10):
for n in range(2,i):
if i%n == 0:
print("{0}: {0} / {1}".format(i,n))
break
else:
print(" prime:{}".format(i))
输出如下:
prime:2
prime:3
4: 4 / 2
prime:5
6: 6 / 2
prime:7
8: 8 / 2
9: 9 / 3
21. 使用C扩展
22. open函数(文件读取)
参考:文件读写
下面是一段文件读取的极简代码,存在如下的问题:
- 如果文件open不成功,最后的close就不会被调用,那么这个文件的句柄就不会被释放
- 需要自己显式的关闭文件
f = open("photo.jpg","r+")
jpgdata = f.read()
f.close()
使用with语句就能避免上述问题,示例如下:
如下程序中读取一个文件,检测它是否是JPG(提示:这些文件头部以字节FF D8开始),把对输入文件的描述写入一个文本文件。
import io
with open("photo.jpg","rb") as inf:
jpgdata = inf.read()
if jpgdata.startswith(b"\xff\xd8"):
text = u"This is a JPEG file (%d bytes long) \n"
else:
text = u"This is a random file (%d bytes long) \n"
with io.open("summary.txt","w",encoding="utf-8") as outf:
outf.write(text % len(jpgdata))
关于open的第二个参数,有如下常用参数:
r
,读取r+
,读取并写入w
,覆盖写入文件a
,文件末尾追加内容
23. 目标Python
24. 协程
python中的协程和生成器很相似但又稍有不同,主要区别为:
- 生成器是数据的生产者
- 协程是数据的消费者
例如下面的例子,这样做不仅快而且不会给内存带来压力,因为我们所需要的值都是动态生成,而不是将他们存储在列表中。
def fib():
a,b = 0,1
while a < 100:
yield a
a,b = b,a+b
for i in fib():
print(i)
更概括的说上面的例子中使用yield便可获得一个协程,协程会消费掉发送给它的值。
def grep(pattern):
print("Searching for",pattern)
while True:
line = (yield)
if pattern in line:
print(line)
search = grep("coroutine")
next(search)
search.send("I love you")
search.send("Dont you love me?")
search.send("I love coroutine instead")
输出结果为:
Searching for coroutine
I love coroutine instead
在这里yield成了一个协程,它将不再包含任何初始值,相反要从外部传值给它,我们可以通过send()方法向它传值,发送的值会被yield接收。
为什么要运行next()方法呢?这样做正是为了启动一个协程,就像协程中包含的生成器并不是立刻执行,而是通过next()方法来响应send()方 法。因此,你必须通过next()方法来执行yield表达式。
可以通过调用close()方法来关闭一个协程。像这样:
search = grep('coroutine')
search.close()
如果在close之后继续使用send的话,会出现如下的error:
Traceback (most recent call last):
File "pylearn.py", line 17, in <module>
search.send("I love coroutine instead")
StopIteration