Skip to content

HelloiWorld/PythonPracticeDemo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

本demo练习题目来源廖雪峰老师的Python教程

目录

Python基础

字符串和编码

小明的成绩从去年的72分提升到了今年的85分,请计算小明成绩提升的百分点,并用字符串格式化显示出'xx.x%',只保留小数点后1位:

s1 = 72 s2 = 85 r = 100 * (s2 - s1) / s1 print('%.1f%%' % r) 

条件判断

小明身高1.75,体重80.5kg。请根据BMI公式(体重除以身高的平方)帮小明计算他的BMI指数,并根据BMI指数: 低于18.5:过轻 18.5-25:正常 25-28:过重 28-32:肥胖 高于32:严重肥胖 用if-elif判断并打印结果:

height = 1.75 weight = 80.5 bmi = weight / (height * height) print("bmi %f" % bmi) if bmi < 18.5: print("过轻") elif bmi < 25: print("正常") elif bmi < 28: print("过重") elif bmi < 32: print("肥胖") else: print("严重肥胖") 

-->

bmi 26.285714 过重 

循环

请利用循环依次对list中的每个名字打印出Hello, xxx!:

L = ['Bart', 'Lisa', 'Adam'] for name in L: print("Hello, %s!" % name) 

-->

Hello, Bart! Hello, Lisa! Hello, Adam! 

函数

调用函数

请利用Python内置的hex()函数把一个整数转换成十六进制表示的字符串:

n1 = 255 n2 = 1000 print("hex n1: %s" % hex(n1)) print("hex n2: %s" % hex(n2)) 

-->

hex n1: 0xff hex n2: 0x3e8 

定义函数

请定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程: ax2 + bx + c = 0 的两个解。
提示:计算平方根可以调用math.sqrt()函数:    

import math def quadratic(a, b, c): if not isinstance(a, (int, float)) or not isinstance(b, (int, float)) or not isinstance(c, (int, float)): raise TypeError('bad operand type') if b == 0: if a * c < 0: x1 = math.sqrt(-(a / c)) x2 = -math.sqrt(-(a / c)) return x1, x2 elif c == 0: return 0 else: raise ValueError('no answer') elif a == 0: x = -(c / b) return x else: if b * b - 4 * a * c < 0: return ValueError('no answer') else: x1 = (-b + math.sqrt(b * b - 4 * a * c)) / (2 * a) x2 = (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a) return x1, x2 # 测试 print('quadratic(1.5, 3, 1) =', quadratic(1.5, 3, 1)) print('quadratic(0, 2, 1) =', quadratic(0, 2, 1)) print('quadratic(1, 2, 2) =', quadratic(1, 2, 2)) print('quadratic(1, 3, -4) =', quadratic(1, 3, -4)) 

-->

quadratic(1.5, 3, 1) = (-0.42264973081037427, -1.5773502691896255) quadratic(0, 2, 1) = -0.5 quadratic(1, 2, 2) = no answer quadratic(1, 3, -4) = (1.0, -4.0) 

函数的参数

以下函数允许计算两个数的乘积,请稍加改造,变成可接收一个或多个数并计算乘积:

def product(*numbers): product = 1; for number in numbers: product = product * number return product # 测试 print('product(5) =', product(5)) print('product(5, 6) =', product(5, 6)) print('product(5, 6, 7) =', product(5, 6, 7)) print('product(5, 6, 7, 9) =', product(5, 6, 7, 9)) 

-->

product(5) = 5 product(5, 6) = 30 product(5, 6, 7) = 210 product(5, 6, 7, 9) = 1890 

递归函数

汉诺塔的移动可以用递归函数非常简单地实现。 请编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A、B、C中第1个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的方法,例如:

def move(n, a, b, c): if n == 1: print(a, '-->', c) return move(n-1,a,c,b) #将A柱的n-1个盘移到B柱 print(a, '-->', c) move(n-1,b,a,c) #将过渡柱子B上n-1个圆盘B移动到目标柱子C # 测试 # 期待输出: # A --> C # A --> B # C --> B # A --> C # B --> A # B --> C # A --> C move(3, 'A', 'B', 'C') 

-->

A --> C A --> B C --> B A --> C B --> A B --> C A --> C 

高级特性

切片

利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法:

def trim(s): if s == '': return s while s != '' and s[0] == ' ': s = s[1:] while s != '' and s[-1] == ' ': s = s[:-1] return s # 利用递归思路的写法 def trim2(s): if s == '': return s if s[0] == ' ': return trim2(s[1:]) if s[-1] == ' ': return trim2(s[:-1]) return s # 测试 print('trim(\'hello \') =', trim('hello ')) print('trim(\' hello\') =', trim(' hello')) print('trim(\' hello \') =', trim(' hello ')) print('trim(\' hello world \') =', trim(' hello world ')) print('trim(\'\') =', trim('')) print('trim(\' \') =', trim(' ')) 

-->

trim('hello ') = hello trim(' hello') = hello trim(' hello ') = hello trim(' hello world ') = hello world trim('') = trim(' ') = 

迭代

请使用迭代查找一个list中最小和最大值,并返回一个tuple:

def findMinAndMax(L): if len(L) > 0: min = L[0] max = L[0] for v in L: if v < min: min = v if v > max: max = v return min, max return (None, None) # 测试 print('findMinAndMax([]) =', findMinAndMax([])) print('findMinAndMax([7]) =', findMinAndMax([7])) print('findMinAndMax([7, 1]) =', findMinAndMax([7, 1])) print('findMinAndMax([7, 1, 3, 9, 5]) =', findMinAndMax([7, 1, 3, 9, 5])) 

-->

findMinAndMax([]) = (None, None) findMinAndMax([7]) = (7, 7) findMinAndMax([7, 1]) = (1, 7) findMinAndMax([7, 1, 3, 9, 5]) = (1, 9) 

列表生成式

使用列表生成式将英文字母全部转换为小写,通过添加if语句保证列表生成式能正确地执行:

L1 = ['Hello', 'World', 18, 'Apple', None] L2 = [s.lower() for s in L1 if isinstance(s, str)] print('lower([\'Hello\', \'World\', 18, \'Apple\', None]) =' , L2) 

-->

lower(['Hello', 'World', 18, 'Apple', None]) = ['hello', 'world', 'apple'] 

生成器

包含yield关键字的函数表明是一个generator,在调用next()时遇到yield会中断,在下次运行时再从上次返回的yield处继续执行

杨辉三角定义如下:

 1 / \ 1 1 / \ / \ 1 2 1 / \ / \ / \ 1 3 3 1 / \ / \ / \ / \ 1 4 6 4 1 / \ / \ / \ / \ / \ 1 5 10 10 5 1 

把每一行看做一个list,试写一个generator,不断输出下一行的list:

def triangles(): L = [1] while True: yield L #print('previous yield: L =', L) L = L + [0,] # L.append(0)这样写会导致输出每一项都多一个0 #print('after append(0): L =', L) #print('[L[i-1] for i in range(len(L))] =', [L[i - 1] for i in range(len(L))]) #print('[L[i] for i in range(len(L))] =', [L[i] for i in range(len(L))]) L = [L[i - 1] + L[i] for i in range(len(L))]] #print('after triangles: L =', L) # 不使用列表生成式的写法: def triangles2(): ret = [1] while True: yield ret for i in range(1, len(ret)): ret[i] = pre[i] + pre[i - 1] ret.append(1) pre = ret[:] # 测试 # 期待输出: # [1] # [1, 1] # [1, 2, 1] # [1, 3, 3, 1] # [1, 4, 6, 4, 1] # [1, 5, 10, 10, 5, 1] # [1, 6, 15, 20, 15, 6, 1] # [1, 7, 21, 35, 35, 21, 7, 1] # [1, 8, 28, 56, 70, 56, 28, 8, 1] # [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] n = 0 results = [] for t in triangles(): print(t) results.append(t) n = n + 1 if n == 10: break print('results =', results) 

-->

[1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] [1, 5, 10, 10, 5, 1] [1, 6, 15, 20, 15, 6, 1] [1, 7, 21, 35, 35, 21, 7, 1] [1, 8, 28, 56, 70, 56, 28, 8, 1] [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] results = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1], [1, 7, 21, 35, 35, 21, 7, 1], [1, 8, 28, 56, 70, 56, 28, 8, 1], [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]] 

函数式编程

高阶函数

map/reduce

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是: reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:

lower()upper()capitalize()title()swapcase()这几个方法分别用来将字符串转换为小写、大写字符串、将字符串首字母变为大写、将每个首字母变为大写以及大小写互换,这几个方法都是生成新字符串,并不对原字符串做任何修改
 replace()用来替换字符串中指定字符或子字符串的所有重复出现,每次只能替换一个字符或字符串。该方法并不修改原字符串,而是返回一个新字符串。

def normalize(name): return name.title() # 测试 L1 = ['adam', 'LISA', 'barT'] L2 = list(map(normalize, L1)) print(L2) 

-->

['Adam', 'Lisa', 'Bart'] 

Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:

from functools import reduce def prod(L): def fn(x, y): return x * y return reduce(fn, L) # return reduce(lambda x, y: x * y, L) # 测试 print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9])) 

-->

3 * 5 * 7 * 9 = 945 

利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:

from functools import reduce def str2float(s): dot = s.find('.') def fn(x, y): return x * 10 + y def fd(x): return x / 10**(len(s)-dot-1) if dot == -1: return reduce(fn, map(int, s)) return reduce(fn, map(int, s[:dot])) + fd(reduce(fn, map(int, s[dot+1:]))) # 测试 print('str2float(\'123.456\') =', str2float('123.456')) 

-->

str2float('123.456') = 123.456 

filter

回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:

def is_palindrome(n): int2str = str(n) str2int = int(int2str[::-1]) return n == str2int # 测试 output = filter(is_palindrome, range(1, 1000)) print('1~1000:', list(output)) 

-->

1~1000: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999] 

sorted

假设我们用一组tuple表示学生名字和成绩: L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] 请用sorted()对上述列表分别按名字排序:
再按成绩从高到低排序:

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] def by_name(t): return t[:][0] def by_score(t): return t[:][-1] # 测试 L2 = sorted(L, key=by_name) print('按名字排序:', L2) L3 = sorted(L, key=by_score, reverse=True) print('按成绩从高到低排序:', L3) 

-->

按名字排序: [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)] 按成绩从高到低排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)] 

返回函数

利用闭包返回一个计数器函数,每次调用它返回递增整数:

# 使用生成器迭代 def createCounter(): def g(): n = 1 while True: yield n n = n + 1 it = g() def counter(): return next(it) return counter ## python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量 ## 内层函数能访问外层函数的变量,但不能修改它的指向 def createCounter2(): count = [0] def counter(): count[0] += 1 # 用list存储,这样地址不变就可以修改其内容了 return count[0] return counter ## nonlocal关键字用来在函数或其他作用域中修改外层(非全局)变量 ## global关键字则是用于修改全局变量 def createCounter3(): count = 0 def counter(): nonlocal count # 允许修改外部变量,在我的理解相当于__block关键字 count += 1 return count return counter # 测试 counterA = createCounter() print('counterA: ', counterA(), counterA(), counterA(), counterA(), counterA()) counterB = createCounter2() print('counterB: ', counterB(), counterB(), counterB(), counterB(), counterB()) counterC = createCounter3() print('counterC: ', counterC(), counterC(), counterC(), counterC(), counterC()) 

-->

counterA: 1 2 3 4 5 counterB: 1 2 3 4 5 counterC: 1 2 3 4 5 

匿名函数

请用匿名函数改造下面的代码:

def is_odd(n): return n % 2 == 1 L = list(filter(is_odd, range(1, 20))) 

改造后:

L = list(filter(lambda x: x % 2 == 1, range(1, 20))) print(L) 

-->

[1, 3, 5, 7, 9, 11, 13, 15, 17, 19] 

装饰器

请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

import time, functools def metric(fn): @functools.wraps(fn) def wrapper(*args, **kw): start_time = time.time() fn(*args, **kw) end_time = time.time() runtime = end_time - start_time print('%s executed in %s ms' % (fn.__name__, runtime * 1000)) return fn(*args, **kw) return wrapper # 测试 @metric def fast(x, y): time.sleep(0.0012) return x + y; @metric def slow(x, y, z): time.sleep(0.1234) return x * y * z; f = fast(11, 22) s = slow(11, 22, 33) print('f =', f) print('s =', s) 

-->

fast executed in 1.5931129455566406 ms slow executed in 123.92115592956543 ms f = 33 s = 7986 

面向对象编程

访问限制

请把下面的Student对象的gender字段对外隐藏起来,用get_gender()和set_gender()代替,并检查参数有效性:

class Student(object): def __init__(self, name, gender): self.name = name self.gender = gender 

修改后:

class Student(object): def __init__(self, name, gender): self.name = name self.__gender = gender def get_gender(self): return self.__gender def set_gender(self, gender): if gender == 'male' or gender == 'female': # if gender in ('male','female'): self.__gender = gender else: raise ValueError('bad gender') # 测试 bart = Student('Bart', 'male') print('bart.get_gender() =', bart.get_gender()) bart.set_gender('female') print('after bart.set_gender(\'female\'), bart.get_gender() =', bart.get_gender()) 

-->

bart.get_gender() = male after bart.set_gender('female'), bart.get_gender() = female 

实例属性和类属性

为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

class Student(object): count = 0 # 这里是类变量,是静态变量(只初始化一次,然后存在内存中便于修改访问),类似于oc中的static关键字 def __init__(self, name): self.name = name Student.count += 1 # 修改时必须修改类属性 # 测试 print('Student.count =', Student.count) bart = Student('Bart') print('Student.count =', Student.count) lisa = Student('Bart') print('Student.count =', Student.count) 

-->

Student.count = 0 Student.count = 1 Student.count = 2 

面向对象高级编程

使用@property

请利用@property给一个Screen对象加上width和height属性,以及一个只读属性resolution:

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 def resolution(self): return self._width * self._height # 测试 s = Screen() s.width = 1024 s.height = 768 print('resolution =', s.resolution) 

-->

resolution = 786432 

使用枚举类

把Student的gender属性改造为枚举类型,可以避免使用字符串:

from enum import Enum, unique class Gender(Enum): Male = 0 Female = 1 class Student(object): def __init__(self, name, gender): self.name = name self.gender = gender 

改造后,只允许通过枚举类设置gender

@unique class Gender(Enum): Male = 'male' #并不一定非要用数字 Female = 'female' class Student(object): def __init__(self, name, gender): self.name = name self.gender = gender @property def gender(self): return self._gender @gender.setter def gender(self, value): if isinstance(value, Gender): self._gender = value else: raise TypeError('bad type gender') # 测试 bart = Student('Bart', Gender.Male) print('bart.gender =', bart.gender) print('bart.gender.value =', bart.gender.value) bart2 = Student('Bart2', 'Gender.Male') 

-->

bart.gender = Gender.Male bart.gender.value = male Traceback (most recent call last): ... TypeError: bad type gender 

错误调试和测试

错误处理

运行下面的代码,根据异常信息进行分析,定位出错误源头,并修复:

from functools import reduce def str2num(s): return int(s) def calc(exp): ss = exp.split('+') ns = map(str2num, ss) return reduce(lambda acc, x: acc + x, ns) def main(): r = calc('100 + 200 + 345') print('100 + 200 + 345 =', r) r = calc('99 + 88 + 7.6') print('99 + 88 + 7.6 =', r) main() 

错误:

 File "test7_错误调试和测试_错误处理.py", line 10, in str2num return int(s) ValueError: invalid literal for int() with base 10: ' 7.6' 

说明是将7.6转成int类型出错,修改str2num():方法即可

def str2num(s): try: return int(s) except ValueError as e: try: return float(s) except: raise ValueError('not a number') 

单元测试

对Student类编写单元测试,结果发现测试不通过,请修改Student类,让测试通过:

import unittest #class Student(object): # def __init__(self, name, score): # self.name = name # self.score = score # def get_grade(self): # if self.score >= 60: # return 'B' # if self.score >= 80: # return 'A' # return 'C' # 修改后 class Student(object): def __init__(self, name, score): self.name = name self.score = score def get_grade(self): if self.score < 0 or self.score > 100: raise ValueError('bad value of score') if self.score >= 80: return 'A' elif self.score >= 60: return 'B' else: 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() 

-->

.... ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK 

文档测试

对函数fact(n)编写doctest并执行:

def fact(n): ''' Calculate 1*2*...*n >>> fact(1) 1 >>> fact(10) 3628800 >>> fact(-1) Traceback (most recent call last): ... ValueError ''' if n < 1: raise ValueError() if n == 1: return 1 return n * fact(n - 1) if __name__ == '__main__': import doctest doctest.testmod() 

测试:将

if n < 1: raise ValueError() 

注释,打印结果如下:

********************************************************************** File "test7_错误调试和测试_文档测试.py", line 15, in __main__.fact Failed example: fact(-1) Expected: Traceback (most recent call last): ... ValueError Got: Traceback (most recent call last): File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/doctest.py", line 1330, in __run compileflags, 1), test.globs) File "<doctest __main__.fact[2]>", line 1, in <module> fact(-1) File "test7_错误调试和测试_文档测试.py", line 24, in fact return n * fact(n - 1) File "test7_错误调试和测试_文档测试.py", line 24, in fact return n * fact(n - 1) File "test7_错误调试和测试_文档测试.py", line 24, in fact return n * fact(n - 1) [Previous line repeated 990 more times] File "test7_错误调试和测试_文档测试.py", line 22, in fact if n == 1: RecursionError: maximum recursion depth exceeded in comparison ********************************************************************** 1 items had failures: 1 of 3 in __main__.fact ***Test Failed*** 1 failures. 

IO编程

文件读写

请将本地一个文本文件读为一个str并打印出来:

# 绝对路径 fpath = r'/Users/pengzk/Desktop/PythonPracticeDemo/practice/test8_IO编程_文件读写.py' # 相对路径 # fpath = r'./test8_IO编程_文件读写.py' with open(fpath, 'r') as f: s = f.read() print(s) 

-->

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # IO编程->文件读写 # 练习 # 请将本地一个文本文件读为一个str并打印出来: # 绝对路径 fpath = r'/Users/pengzk/Desktop/PythonPracticeDemo/practice/test8_IO编程_文件读写.py' # 相对路径 # fpath = r'./test8_IO编程_文件读写.py' with open(fpath, 'r') as f: s = f.read() print(s) 

操作文件和目录

1.利用os模块编写一个能实现dir -l输出的程序。

import os from datetime import datetime def output_dir_detail(pwd = os.path.abspath('.')): for i in os.listdir(pwd): fname = i fsize = os.path.getsize(i) fmtime = str(datetime.fromtimestamp(os.path.getmtime(i))).split('.')[0] flag = '/' if os.path.isdir(i) else '' print('%10d %s %s%s' % (fsize, fmtime, fname, flag)) # 测试 output_dir_detail() 

-->

6148 2018-02-28 18:41:58 .DS_Store 1048 2018-02-25 18:53:05 test1_基础.py 2477 2018-02-25 18:52:57 test2_函数.py 820 2018-02-26 10:52:36 test3_高级特性_切片.py 1740 2018-02-26 10:51:34 test3_高级特性_生成器.py 615 2018-02-26 10:53:04 test3_高级特性_迭代.py 249 2018-02-27 10:01:41 test4_函数式编程_匿名函数.py 720 2018-02-27 10:01:33 test4_函数式编程_装饰器.py 1427 2018-02-26 10:52:27 test4_函数式编程_返回函数.py 2106 2018-02-27 16:29:25 test4_函数式编程_高阶函数.py 657 2018-02-27 10:02:01 test5_面向对象编程_实例属性和类属性.py 908 2018-02-26 18:28:30 test5_面向对象编程_访问限制.py 683 2018-02-27 12:10:18 test6_面向对象高级编程_使用@property.py 1015 2018-02-27 15:20:26 test6_面向对象高级编程_使用枚举类.py 1790 2018-02-28 11:17:43 test7_错误调试和测试_单元测试.py 503 2018-02-28 11:07:53 test7_错误调试和测试_文档测试.py 656 2018-02-28 11:02:13 test7_错误调试和测试_错误处理.py 914 2018-03-02 10:10:55 test8_IO编程_序列化.py 1160 2018-02-28 18:49:26 test8_IO编程_操作文件和目录.py 376 2018-02-28 18:50:24 test8_IO编程_文件读写.py 

2.编写一个程序,能在当前目录以及当前目录的所有子目录下查找文件名包含指定字符串的文件,并打印出相对路径。

import os def find_file_with_name(name, dir = '.', result = []): for x in os.listdir(dir): if os.path.isdir(x): find_file_with_name(name, os.path.join('%s' % dir, os.path.relpath(x))) continue if name in x: #加了条件os.path.isfile(x)就搜不到??? result.append(os.path.join('%s' % dir, os.path.relpath(x))) return result # 测试 for i in find_file_with_name('test8'): print(i) 

    -->

./test8_IO编程_序列化.py ./test8_IO编程_操作文件和目录.py ./test8_IO编程_文件读写.py 

序列化

对中文进行JSON序列化时,json.dumps()提供了一个ensure_ascii参数,观察该参数对结果的影响:

import json obj = dict(name='小明', age=20) s = json.dumps(obj, ensure_ascii=True) #默认,中文会转成Unicode编码 s2 = json.dumps(obj, ensure_ascii=False) # 测试 print('s = %s' % s) print('s2 = %s' % s2) 

-->

s = {"name": "\u5c0f\u660e", "age": 20} s2 = {"name": "小明", "age": 20} 

正则表达式

请尝试写一个验证Email地址的正则表达式。版本一应该可以验证出类似的Email:
someone@gmail.com
bill.gates@microsoft.com

import re reg_email = r'^([0-9a-zA-Z]+[\.\_]?[0-9a-zA-Z]*)@[0-9a-zA-Z\.\-]+\.[a-zA-Z]{2,4}$' def is_valid_email(addr): m = re.compile(reg_email) if m.match(addr) is None: return False return True # 测试: print('is_valid_email(\'1234567890@qq.com\') = %s' % is_valid_email('1234567890@qq.com')) print('is_valid_email(\'someone@163.com\') = %s' % is_valid_email('someone@163.com')) print('is_valid_email(\'someone@gmail.com\') = %s' % is_valid_email('someone@gmail.com')) print('is_valid_email(\'bill.gates@microsoft.com\') = %s' % is_valid_email('bill.gates@microsoft.com')) print('is_valid_email(\'bob#example.com\') = %s' % is_valid_email('bob#example.com')) print('is_valid_email(\'mr-bob@example.com\') = %s' % is_valid_email('mr-bob@example.com')) 

-->

is_valid_email('1234567890@qq.com') = True is_valid_email('someone@163.com') = True is_valid_email('someone@gmail.com') = True is_valid_email('bill.gates@microsoft.com') = True is_valid_email('bob#example.com') = False is_valid_email('mr-bob@example.com') = False 

版本二可以提取出带名字的Email地址:
<Tom Paris> tom@voyager.org => Tom Paris
bob@example.com => bob

reg_name_of_email = r'^<([\w\s]+)>[\s]*([0-9a-zA-Z]+[\.\_]?[0-9a-zA-Z]*)@[0-9a-zA-Z\.\-]+\.[a-zA-Z]{2,4}$' def name_of_email(addr): m = re.match(reg_email, addr) if m: return m.group(1) m = re.match(reg_name_of_email, addr) if m: return m.group(1) return None         # 测试: print('name_of_email(\'<Tom Paris> tom@voyager.org\') = %s' % name_of_email('<Tom Paris> tom@voyager.org')) print('name_of_email(\'tom@voyager.org\') = %s' % name_of_email('tom@voyager.org')) 

-->

name_of_email('<Tom Paris> tom@voyager.org') = Tom Paris name_of_email('tom@voyager.org') = tom 

常用内建模块

datetime

假设你获取了用户输入的日期和时间如2015-1-21 9:01:30,以及一个时区信息如UTC+5:00,均是str,请编写一个函数将其转换为timestamp:

import re from datetime import datetime, timezone, timedelta def to_timestamp(dt_str, tz_str): m_dt = re.match(r'\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}', dt_str) # 没判断时间超过60的情况,太长了 if m_dt is None: raise AttributeError('bad arg dt_str') m_tz = re.match(r'UTC([+-])(\d+):([0-9]{2})', tz_str) if m_tz is None: raise AttributeError('bad arg tz_str') dt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') # 格式化输入字符串 symbol = m_tz.group(1) hour = int(m_tz.group(2)) hour_int = int(symbol + str(hour)) minute = int(m_tz.group(3)) minute_int = int(symbol + str(minute)) tz_utc = timezone(timedelta(hours=hour_int, minutes=minute_int)) # 创建时区 dt_utc = dt.replace(tzinfo=tz_utc) # 设置为指定UTC时间 return dt_utc.timestamp() # 把datetime转换为timestamp # 测试: t1 = to_timestamp('2015-6-1 08:10:30', 'UTC+7:00') assert t1 == 1433121030.0, t1 t2 = to_timestamp('2015-5-31 16:10:30', 'UTC-09:00') assert t2 == 1433121030.0, t2 print('ok') 

base64

请写一个能处理去掉=的base64解码函数:

import base64 def safe_base64_decode(s): while len(s) % 4 != 0: # python3中必须要对类型作判断 https://thief.one/2017/04/18/1/ if isinstance(s, bytes): s = s + b'=' else: s = s + '=' return base64.b64decode(s) # 测试: assert b'abcd' == safe_base64_decode(b'YWJjZA=='), safe_base64_decode('YWJjZA==') assert b'abcd' == safe_base64_decode(b'YWJjZA'), safe_base64_decode('YWJjZA') print('ok') 

struct

请编写一个bmpinfo.py,可以检查任意文件是否是位图文件,如果是,打印出图片大小和颜色数。

import base64, struct bmp_data = base64.b64decode('Qk1oAgAAAAAAADYAAAAoAAAAHAAAAAoAAAABABAAAAAAADICAAASCwAAEgsAAAAAAAAAAAAA/3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9/AHwAfAB8AHwAfAB8AHwAfP9//3//fwB8AHwAfAB8/3//f/9/AHwAfAB8AHz/f/9//3//f/9//38AfAB8AHwAfAB8AHwAfAB8AHz/f/9//38AfAB8/3//f/9//3//fwB8AHz/f/9//3//f/9//3//f/9/AHwAfP9//3//f/9/AHwAfP9//3//fwB8AHz/f/9//3//f/9/AHwAfP9//3//f/9//3//f/9//38AfAB8AHwAfAB8AHwAfP9//3//f/9/AHwAfP9//3//f/9//38AfAB8/3//f/9//3//f/9//3//fwB8AHwAfAB8AHwAfAB8/3//f/9//38AfAB8/3//f/9//3//fwB8AHz/f/9//3//f/9//3//f/9/AHwAfP9//3//f/9/AHwAfP9//3//fwB8AHz/f/9/AHz/f/9/AHwAfP9//38AfP9//3//f/9/AHwAfAB8AHwAfAB8AHwAfAB8/3//f/9/AHwAfP9//38AfAB8AHwAfAB8AHwAfAB8/3//f/9//38AfAB8AHwAfAB8AHwAfAB8/3//f/9/AHwAfAB8AHz/fwB8AHwAfAB8AHwAfAB8AHz/f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//38AAA==') def bmp_info(data): head_data = data[:30] info = struct.unpack('<ccIIIIIIHH', head_data) is_windows_bmp = info[0] == b'B' and info[1] == b'M' width = info[-4] if is_windows_bmp else 0 height = info[-3] if is_windows_bmp else 0 color = info[-1] if is_windows_bmp else 0 return { 'width': width, 'height': height, 'color': color } # 测试 bi = bmp_info(bmp_data) assert bi['width'] == 28 assert bi['height'] == 10 assert bi['color'] == 16 print('ok') 

hashlib

根据用户输入的口令,计算出存储在数据库中的MD5口令:

import hashlib def calc_md5(password): return hashlib.md5(password.encode('utf-8')).hexdigest() # 设计一个验证用户登录的函数,根据用户输入的口令是否正确,返回True或False: db = { 'michael': 'e10adc3949ba59abbe56e057f20f883e', 'bob': '878ef96e86145580c38c87f0410ad153', 'alice': '99b1c2188db85afee403b1536010c2c9' } def login(user, password): if user in db: return db[user] == calc_md5(password) return False # 测试: assert login('michael', '123456') assert login('bob', 'abc999') assert login('alice', 'alice2008') assert not login('michael', '1234567') assert not login('bob', '123456') assert not login('alice', 'Alice2008') print('ok') 

根据用户输入的登录名和口令模拟用户注册,计算更安全的MD5:

import hashlib, random def get_md5(s): return hashlib.md5(s.encode('utf-8')).hexdigest() class User(object): def __init__(self, username, password): self.username = username self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)]) self.password = get_md5(password + self.salt) db = { 'michael': User('michael', '123456'), 'bob': User('bob', 'abc999'), 'alice': User('alice', 'alice2008') } def login(username, password): if username in db: user = db[username] return user.password == get_md5(password + user.salt) return False # 测试: assert login('michael', '123456') assert login('bob', 'abc999') assert login('alice', 'alice2008') assert not login('michael', '1234567') assert not login('bob', '123456') assert not login('alice', 'Alice2008') print('ok') 

hmac

将上一节的salt改为标准的hmac算法,验证用户口令:

import hmac, random def hmac_md5(key, s): return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'MD5').hexdigest() class User(object): def __init__(self, username, password): self.username = username self.key = ''.join([chr(random.randint(48, 122)) for i in range(20)]) self.password = hmac_md5(self.key, password) db = { 'michael': User('michael', '123456'), 'bob': User('bob', 'abc999'), 'alice': User('alice', 'alice2008') } def login(username, password): user = db[username] return user.password == hmac_md5(user.key, password) # 测试: assert login('michael', '123456') assert login('bob', 'abc999') assert login('alice', 'alice2008') assert not login('michael', '1234567') assert not login('bob', '123456') assert not login('alice', 'Alice2008') print('ok') 

itertools

计算圆周率可以根据公式:
利用Python提供的itertools模块,我们来计算这个序列的前N项和:

import itertools def pi(N): ' 计算pi的值 ' # step 1: 创建一个奇数序列: 1, 3, 5, 7, 9, ... odd_numbers_1 = itertools.count(1, 2) # step 2: 取该序列的前N项: 1, 3, 5, 7, 9, ..., 2*N-1. odd_numbers_2 = itertools.takewhile(lambda x: x <= 2 * N - 1, odd_numbers_1) # step 3: 添加正负符号并用4除: 4/1, -4/3, 4/5, -4/7, 4/9, ... # 先想到的方法,利用cycle方法有更好的写法 # symbol = -1 # def get_odd_number_3(x): # nonlocal symbol # symbol = -symbol # return symbol * 4 / x # odd_numbers_3 = map(get_odd_number_3, odd_numbers_2) symbol_cycles = itertools.cycle([1, -1]) odd_numbers_3 = map(lambda x: next(symbol_cycles) * 4 / x, odd_numbers_2) # step 4: 求和: return sum(odd_numbers_3) # 测试: print(pi(10)) print(pi(100)) print(pi(1000)) print(pi(10000)) assert 3.04 < pi(10) < 3.05 assert 3.13 < pi(100) < 3.14 assert 3.140 < pi(1000) < 3.141 assert 3.1414 < pi(10000) < 3.1415 print('ok') 

-->

3.0418396189294032 3.1315929035585537 3.140592653839794 3.1414926535900345 ok 

urllib

利用urllib读取JSON,然后将JSON解析为Python对象:

from urllib import request import json def fetch_data(url): req = request.Request(url) with request.urlopen(req) as f: # print('Status:', f.status, f.reason) # for k, v in f.getheaders(): # print('%s: %s' % (k, v)) # print('Data:', f.read().decode('utf-8')) data = f.read().decode('utf-8') return json.loads(data) # 测试 URL = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=json' data = fetch_data(URL) print(data) assert data['query']['results']['channel']['location']['city'] == 'Beijing' print('ok') 

-->

{'query': {'count': 1, 'created': '2018-03-05T10:56:02Z', 'lang': 'en-US', 'results': {'channel': {'units': {'distance': 'mi', 'pressure': 'in', 'speed': 'mph', 'temperature': 'F'}, 'title': 'Yahoo! Weather - Beijing, Beijing, CN', 'link': 'http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/', 'description': 'Yahoo! Weather for Beijing, Beijing, CN', 'language': 'en-us', 'lastBuildDate': 'Mon, 05 Mar 2018 06:56 PM CST', 'ttl': '60', 'location': {'city': 'Beijing', 'country': 'China', 'region': ' Beijing'}, 'wind': {'chill': '34', 'direction': '170', 'speed': '18'}, 'atmosphere': {'humidity': '29', 'pressure': '1023.0', 'rising': '0', 'visibility': '16.1'}, 'astronomy': {'sunrise': '6:41 am', 'sunset': '6:11 pm'}, 'image': {'title': 'Yahoo! Weather', 'width': '142', 'height': '18', 'link': 'http://weather.yahoo.com', 'url': 'http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif'}, 'item': {'title': 'Conditions for Beijing, Beijing, CN at 06:00 PM CST', 'lat': '39.90601', 'long': '116.387909', 'link': 'http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/', 'pubDate': 'Mon, 05 Mar 2018 06:00 PM CST', 'condition': {'code': '32', 'date': 'Mon, 05 Mar 2018 06:00 PM CST', 'temp': '41', 'text': 'Sunny'}, 'forecast': [{'code': '32', 'date': '05 Mar 2018', 'day': 'Mon', 'high': '48', 'low': '21', 'text': 'Sunny'}, {'code': '28', 'date': '06 Mar 2018', 'day': 'Tue', 'high': '42', 'low': '24', 'text': 'Mostly Cloudy'}, {'code': '28', 'date': '07 Mar 2018', 'day': 'Wed', 'high': '44', 'low': '28', 'text': 'Mostly Cloudy'}, {'code': '32', 'date': '08 Mar 2018', 'day': 'Thu', 'high': '46', 'low': '27', 'text': 'Sunny'}, {'code': '32', 'date': '09 Mar 2018', 'day': 'Fri', 'high': '53', 'low': '25', 'text': 'Sunny'}, {'code': '32', 'date': '10 Mar 2018', 'day': 'Sat', 'high': '58', 'low': '28', 'text': 'Sunny'}, {'code': '34', 'date': '11 Mar 2018', 'day': 'Sun', 'high': '54', 'low': '29', 'text': 'Mostly Sunny'}, {'code': '32', 'date': '12 Mar 2018', 'day': 'Mon', 'high': '62', 'low': '34', 'text': 'Sunny'}, {'code': '34', 'date': '13 Mar 2018', 'day': 'Tue', 'high': '66', 'low': '38', 'text': 'Mostly Sunny'}, {'code': '30', 'date': '14 Mar 2018', 'day': 'Wed', 'high': '64', 'low': '41', 'text': 'Partly Cloudy'}], 'description': '<![CDATA[<img src="http://l.yimg.com/a/i/us/we/52/32.gif"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Sunny\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Mon - Sunny. High: 48Low: 21\n<BR /> Tue - Mostly Cloudy. High: 42Low: 24\n<BR /> Wed - Mostly Cloudy. High: 44Low: 28\n<BR /> Thu - Sunny. High: 46Low: 27\n<BR /> Fri - Sunny. High: 53Low: 25\n<BR />\n<BR />\n<a href="http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2151330/">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n<BR />\n]]>', 'guid': {'isPermaLink': 'false'}}}}}} ok 

XML

请利用SAX编写程序解析Yahoo的XML格式的天气预报,获取天气预报:
https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=xml
参数woeid是城市代码,要查询某个城市代码,可以在weather.yahoo.com搜索城市,浏览器地址栏的URL就包含城市代码。

from xml.parsers.expat import ParserCreate from urllib import request class DefaultSaxHandler(object): def __init__(self): self.city = None self.forecast = [] def start_element(self, name, attrs): # print('sax:start_element: %s, attrs: %s' % (name, str(attrs))) if 'city' in attrs: self.city = attrs['city'] if 'forecast' in name: fc = dict(date=attrs['date'], high=attrs['high'], low=attrs['low']) self.forecast.append(fc) def end_element(self, name): # print('sax:end_element: %s' % name) pass def char_data(self, text): # print('sax:char_data: %s' % text) pass def parseXml(xml_str): # print(xml_str) handler = DefaultSaxHandler() parser = ParserCreate() parser.StartElementHandler = handler.start_element parser.EndElementHandler = handler.end_element parser.CharacterDataHandler = handler.char_data parser.Parse(xml_str) return { 'city': handler.city, 'forecast': handler.forecast } # 测试: URL = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=xml' with request.urlopen(URL, timeout=4) as f: data = f.read() result = parseXml(data.decode('utf-8')) print(result) assert result['city'] == 'Beijing' print('ok') 

-->

{'city': 'Beijing', 'forecast': [{'date': '05 Mar 2018', 'high': '48', 'low': '21'}, {'date': '06 Mar 2018', 'high': '42', 'low': '24'}, {'date': '07 Mar 2018', 'high': '44', 'low': '28'}, {'date': '08 Mar 2018', 'high': '46', 'low': '27'}, {'date': '09 Mar 2018', 'high': '53', 'low': '25'}, {'date': '10 Mar 2018', 'high': '58', 'low': '28'}, {'date': '11 Mar 2018', 'high': '54', 'low': '29'}, {'date': '12 Mar 2018', 'high': '62', 'low': '34'}, {'date': '13 Mar 2018', 'high': '66', 'low': '38'}, {'date': '14 Mar 2018', 'high': '64', 'low': '41'}]} ok 

HTMLParser

找一个网页,例如https://www.python.org/events/python-events/
用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间、名称和地点。

from urllib import request from html.parser import HTMLParser from html.entities import name2codepoint def fetch_data(url): req = request.Request(url) with request.urlopen(req) as f: return f.read().decode('utf-8') class MyHTMLParser(HTMLParser): def __init__(self): super(MyHTMLParser, self).__init__() self.flag = None self.event_info = {} self.event_list = [] def handle_starttag(self, tag, attrs): # print('starttag: %s, attrs: %s' % (tag, str(attrs))) if ('class', 'event-title') in attrs: self.flag = 'title' if 'time' == tag: self.flag = 'time' if ('class', 'event-location') in attrs: self.flag = 'location' def handle_endtag(self, tag): # print('</%s>' % tag) # pass self.flag = '' def handle_startendtag(self, tag, attrs): # print('<%s/>' % tag) pass def handle_data(self, data): # print(data) if self.flag in ('title', 'time', 'location'): self.event_info[self.flag] = data if len(self.event_info) == 3: self.event_list.append(self.event_info) self.event_info = {} # if self.flag == 'title': # self.event_info['title'] = data # self.flag = 0 # # if self.flag == 'time': # self.event_info['time'] = data # self.flag = 0 # # if self.flag == 'location': # self.event_info['location'] = data # self.flag = 0 # self.event_list.append(self.event_info) # self.event_info = {} def handle_comment(self, data): # print('<!--', data, '-->') pass def handle_entityref(self, name): # print('&%s;' % name) pass def handle_charref(self, name): # print('&#%s;' % name) pass def print_info(self): for n in self.event_list: print('title: %s' % n['title']) print('time: %s' % n['time']) print('location: %s' % n['location']) print('-----------------------------------------------------') # 测试: URL = 'https://www.python.org/events/python-events/' data = fetch_data(URL) # print(data) parser = MyHTMLParser() parser.feed(data) parser.print_info() 

-->

title: PyCon SK 2018 time: 09 March – 12 March location: Bratislava, Slovakia ----------------------------------------------------- title: PythonCamp 2018 - Cologne time: 07 April – 09 April location: GFU Cyrus AG, Am Grauen Stein 27, 51105 Köln, Germany ----------------------------------------------------- title: PyCon IT 9 time: 19 April – 23 April location: Hotel Mediterraneo - Lungarno del Tempio, 44, 50121 Firenze FI, Italy ----------------------------------------------------- title: PyDays Vienna time: 04 May – 06 May location: FH Technikum Wien, Hoechstaedtplatz 6, Vienna, Austria ----------------------------------------------------- title: GeoPython 2018 time: 07 May – 10 May location: Basel, Switzerland ----------------------------------------------------- title: PyCon US 2018 time: 09 May – 18 May location: Cleveland, Ohio, USA ----------------------------------------------------- title: PyCon Belarus 2018 time: 24 Feb. – 25 Feb. location: Minsk, Belarus ----------------------------------------------------- title: PyCon PH 2018 time: 24 Feb. – 26 Feb. location: Makati City, Metro Manila, Philippines ----------------------------------------------------- 

常用第三方模块

Pillow

示例代码

图形界面

示例代码

网络编程

TCP编程

服务端示例代码
客户端示例代码

UDP编程

服务端示例代码
客户端示例代码

电子邮件

SMTP发送邮件

示例代码

POP3收取邮件

示例代码

访问数据库

使用SQLite

请编写函数,在Sqlite中根据分数段查找指定的名字:

import os, sqlite3 db_file = os.path.join(os.path.dirname(__file__), 'test15.db') if os.path.isfile(db_file): os.remove(db_file) # 初始数据: conn = sqlite3.connect(db_file) cursor = conn.cursor() cursor.execute('create table user(id varchar(20) primary key, name varchar(20), score int)') cursor.execute(r"insert into user values ('A-001', 'Adam', 95)") cursor.execute(r"insert into user values ('A-002', 'Bart', 62)") cursor.execute(r"insert into user values ('A-003', 'Lisa', 78)") cursor.close() conn.commit() conn.close() def get_score_in(low, high): ' 返回指定分数区间的名字,按分数从低到高排序 ' try: conn = sqlite3.connect(db_file) cursor = conn.cursor() cursor.execute(r'select name from user where score between ? and ? order by score', (low, high)) names = [name[0] for name in cursor.fetchall()] finally: cursor.close() conn.close() return names # 测试: assert get_score_in(80, 95) == ['Adam'], get_score_in(80, 95) assert get_score_in(60, 80) == ['Bart', 'Lisa'], get_score_in(60, 80) assert get_score_in(60, 100) == ['Bart', 'Lisa', 'Adam'], get_score_in(60, 100) print('Pass') 

使用MySQL

示例代码

使用SQLAlchemy

示例代码

Web开发

使用Web框架

示例代码

使用模板

示例代码

异步IO

async/await

示例代码

aiohttp

示例代码

实战

webapp

example

to be continued...

About

learn python 3

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors