一、知识点
知识点1:Iterable和Iterator
|-------------------|-------------------------------------------|------------------------------------------|
| | 可迭代对象 Iterable | 迭代器 Iterator |
| 定义 | 可以一次返回其成员的对象,这意味着这些对象可以通过for...in循环进行遍历 | 是表示数据流的对象,通过重复调用迭代器的*next()*方法返回数据流中的连续项 |
| 核心方法 | iter() | iter()和__next__() |
| 获取方式 | 直接使用可迭代对象本身 | 通过对可迭代对象调用 iter() 或 __iter__() 获得 |
| 状态 | 通常不保存迭代状态 | 保存当前的迭代状态(位置) |
| 遍历 | 不能直接使用 next() 遍历 | 可以使用 next() 逐个获取元素 |
| 遍历次数 | 可被多次遍历(每次创建新的迭代器) | 通常只能遍历一次(消耗型) |
| 示例类型 | 列表、元组、字符串、字典、集合 | iter(list) 返回的对象、生成器对象 |
| StopIteration | 不会直接引发 | 当元素耗尽时引发 StopIteration 异常 |
知识点2:map()和reduce()
python
map() 函数:
1. 两个参数: function and Iterable
2. 作用: 将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
3. 示例1: f(x)=x**2, 把f(x)作用到list[1,2,3,4,5,6,7,8,9],代码如下:
>>> def f(x):
return x*x
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
4、示例2:把list[1,2,3,4,5,6,7,8,9]转换为字符串,代码如下:
>>> list(map(str,[1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
python
reduce()函数:
1、两个参数:function和iterable
2、作用:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
3、示例1:对一个序列求和
>>> from functools import reduce
>>> def add(x, y):
return x + y
>>> reduce(add, [1, 3, 5, 7])
16
4、str转换为int
>>> from functools import reduce
>>> def f(x, y):
return x*10+y
>>> def strToInt(s):
num = {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9}
return num[s]
>>> reduce(f,map(strToInt,'1357'))
1357
相关练习:
1、利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
python
def f(name):
return name[0].upper() + name[1:].lower()
print(list(map(f,['adam','LISA','barT'])))
2、请编写一个prod()函数,可以接受一个list并利用reduce()求积:
python
from functools import reduce
def prod(x, y):
return x * y
print('3 * 5 * 7 * 9 =', reduce(prod,[3, 5, 7, 9]))
if reduce(prod,[3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
3、利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
python
from functools import reduce
def strToInt(s):
num = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
return num[s]
def f(x,y):
return x*10+y
def str2float(s):
return reduce(f,map(strToInt,s.split('.')[0]))+reduce(f,map(strToInt,s.split('.')[1]))/10**len(s.split('.')[1])
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
知识点3:filter()
python
filter()函数:
1、两个参数:函数和序列
2、作用:过滤
3、示例:保留奇数
>>> def is_odd(n):
return n % 2 == 1
>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
[1, 5, 9, 15]
相关练习:
1、回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
python
def is_palindrome(n):
s = str(n)
return s == s[::-1]
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
知识点4:闭包
使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常;但如果是对其赋值,则会把x当做函数counter()的局部变量
python
def createCounter():
x = 0
def counter():
nonlocal x
x = x + 1
return x
return counter
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
知识点5:slots
通过__slots__来限制实例能添加的属性。
python
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score' #报错:AttributeError: 'Student' object has no attribute 'score'
slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。
python
class Stu2(Stu):
pass
ss=Stu2()
ss.name='bbb'
ss.age=21
ss.score=98
print(ss.score) #输出:98
class Stu3(Stu):
__slots__ = ('name','gender')
sss=Stu3()
sss.name='ccc'
sss.gender='m'
sss.age=20
print(sss.name) #输出:ccc
print(sss.gender) #输出:m
print(sss.age) #输出:20
知识点6:@property
@property装饰器就是负责把一个方法变成属性调用的。
python
class Screen(object):
@property
def width(self):
return self.width
@width.setter #负责把该方法变成属性赋值
def width(self, value):
self._width = value
@property
def height(self):
return self.height
@height.setter
def height(self, value):
self._height = value
@property #只读属性,只定义getter方法,不定义setter方法就是一个只读属性
def resolution(self):
return self._width * self._height
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
知识点7:调试
错误信息:https://docs.python.org/3/library/exceptions.html#exception-hierarchy
1、print():把可能有问题的变量打印出来。
2、断言(assert):如果断言失败,assert语句本身就会抛出异常。(可通过-O参数关闭assert,如:python -O err.py)。
python
def foo(s):
n = int(s)
assert n != 0, 'n is zero!' #表达式n!=0应该是True,否则,后面代码出错。
return 10 / n
def main():
foo('0')
3、logging:和assert相比,logging不会抛出错误,而且可以输出到文件中。logging允许你指定记录信息的级别,有debug、info、warning、error等几个级别,当我们指定level=INFO时,其他级别的信息就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。另一个好处就是可以通过简单的配置,一条语句同时输出到不同的地方,如console和文件。
python
import logging
logging.basicConfig(level=logging.INFO)
4、调试器pdb:让程序单步运行,可随时查看运行状态。
以参数 -m pdb 启动后,pdb定位到下一步要执行的代码;输入命令 l 查看代码;输入命令 n 单步执行代码;任何时候都可以输入命令 p 变量名 来查看变量;输入命令 q 结束调试,退出程序。
pdb.set_trace() #在可能出错的地方放一个,就可以设置一个断点,运行代码时,程序会自动在该语句暂停并进入pdb调试环境,可以使用命令p 来查看变量,或用命令 c 继续运行。
5、IDE:支持调试的IDE
知识点8:单元测试
对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。
以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。
python
import unittest
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
if self.score >= 60 and self.score < 80:
return 'B'
elif self.score >= 80 and self.score <= 100:
return 'A'
elif self.score < 0 or self.score > 100:
raise ValueError
return 'C'
class TestStudent(unittest.TestCase):
def test_80_to_100(self):
s1 = Student('Bart', 80)
s2 = Student('Lisa', 100)
self.assertEqual(s1.get_grade(), 'A')
self.assertEqual(s2.get_grade(), 'A')
def test_60_to_80(self):
s1 = Student('Bart', 60)
s2 = Student('Lisa', 79)
self.assertEqual(s1.get_grade(), 'B')
self.assertEqual(s2.get_grade(), 'B')
def test_0_to_60(self):
s1 = Student('Bart', 0)
s2 = Student('Lisa', 59)
self.assertEqual(s1.get_grade(), 'C')
self.assertEqual(s2.get_grade(), 'C')
def test_invalid(self):
s1 = Student('Bart', -1)
s2 = Student('Lisa', 101)
with self.assertRaises(ValueError):
s1.get_grade()
with self.assertRaises(ValueError):
s2.get_grade()
if __name__ == '__main__':
unittest.main()

知识点9:正则表达式
在正则表达式中,如果直接给出字符,就是精确匹配。
search()方法用于在字符串中搜索正则表达式模式第一次出现的位置。
第一个参数是正则表达式模式,也就是你要描述的搜索规则。
找到后返回的范围是以下标0开始的。
python
import re
re.search(r"Fish","I Love Fish!")
<re.Match object; span=(7, 11), match='Fish'>
\d可以匹配一个数字
python
>>> re.search(r'\d', 'I love 123 FishC.com!')
<re.Match object; span=(7, 8), match='1'>
\w可以匹配一个字母或数字
.可以匹配任意字符
python
>>> re.search(r'.', 'I love FishC.com!')
<re.Match object; span=(0, 1), match='I'>
>>> re.search(r'co.', 'I love FishC.com!')
<re.Match object; span=(13, 16), match='com'>
反斜杠剥夺元字符(.)的特殊能力。
python
>>> re.search(r'\.', 'I love FishC.com!')
<re.Match object; span=(12, 13), match='.'>
*表示任意个字符(包括0个)
+表示至少一个字符
?表示0个或1个字符
{n}表示n个字符
{n,m}表示n-m个字符:
'00\d'可以匹配'007',但无法匹配'00A';'\d\d\d'可以匹配'010';'\w\w\d'可以匹配'py3';'py.'可以匹配'pyc'、'pyo'、'py!'等等- eg:\d{3}\s+\d{3,8}
\d{3}表示匹配3个数字,例如'010';\s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格;\d{3,8}表示3-8个数字,例如'1234567'
要做更精确地匹配,可以用[]表示范围,比如:
0-9a-zA-Z\\_\]可以匹配一个数字、字母或者下划线;
\[0-9a-zA-Z\\_\]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等;
\[a-zA-Z\\_\]\[0-9a-zA-Z\\_\]\*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
\[a-zA-Z\\_\]\[0-9a-zA-Z\\_\]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。
A\|B可以匹配A或B,所以(P\|p)ython可以匹配'Python'或者'python'。
\^表示行的开头,\^\\d表示必须以数字开头。
$表示行的结束,\\d$表示必须以数字结束。
使用 \[ \] 将任何内容包起来就是一个字符类,它的含义是你只要匹配这个字符类中的任何字符,结果就算作匹配。默认区分大小写。
```python
>>> re.search(r'[aeiou]', 'I love 123 FishC.com!')