Python学习笔记(二)

Python学习笔记(二)

  • 数据结构和算法
    • 简单排序算法与查找算法
    • heapq, itertools
    • collections
    • 常用算法
  • 函数的使用方法
  • 面向对象
    • 封装、继承与多态
    • 类与类之间的关系:继承,关联/聚合/合成,依赖
    • 对象的复制
    • 垃圾回收、循环利用和弱引用
    • 魔法属性和方法
    • 混入(Mixin)
    • 元编程和元类
    • 面向对象设计原则
  • 迭代器和生成器

学习笔记自 https://github.com/jackfrued/Python-100-Days

数据结构和算法

  • O ( c ) O(c) O(c) 布隆过滤器,哈希存储
  • O ( l o g 2 n ) O(log_2n) O(log2n) 二分查找
  • O ( n ) O(n) O(n) 顺序查找,桶排序
  • O ( n ∗ l o g 2 n ) O(n * log_2n) O(nlog2n) 高级排序算法(归并排序、快速排序)
  • O ( n 2 ) O(n^2) O(n2) 简单排序算法(选择排序、插入排序、冒泡排序)
  • O ( n 3 ) O(n^3) O(n3) Floyd算法,矩阵乘法运算
  • O ( 2 n ) O(2^n) O(2n) 汉诺塔
  • O ( n ! ) O(n!) O(n!) 旅行经销商问题

简单排序算法与查找算法

"""简单选择排序"""
def select_sort(origin_items, comp=lambda x, y: x < y):
	items = origin_items[:]
	for i in range(len(items) - 1):
		min_index = 1
		# 找到第i个以后的最小值,并与i比较
		for j in range(i + 1, len(items)):
			if comp(items[j], items[min_index]):
				min_index = j
		items[i], items[min_index] = items[min_index], items[i]
	return items
"""高质量冒泡排序(搅拌排序)"""
"""定向冒泡排序,以双向在序列中进行排序"""
def bubble_sort(origin_items, comp=lambda x, y: x > y):
	items = origin_items[:]
	for i in range(len(items) - 1):
		swapped = False
		for j in range(i, len(items) - 1 - i):
			if comp(items[j], items[j + 1]):
				items[j], items[j + 1] = items[j + 1], items[j]
				swapped = True
		if swapped:
			swapped = False
			for j in range(len(items) - 2 - i, i, -1):
				if comp(items[j - 1], items[j]):
					items[j], items[j - 1] = items[j - 1], items[j]
					swapped = True
		if not swapped:
			break
	return items
"""归并排序(分治法)"""
def merge_sort(items, comp=lambda x, y: x <= y):
	if len(items) < 2return items[:]
	mid = len(items) // 2
	left = merge_sort(items[:mid], comp)
	right = merge_sort(items[mid:], comp)
	return merge(left, right, comp)

def merge(items1, items2, comp):
	"""合并(将两个有序的列表合并成一个有序的列表)"""
	items = []
	index1, index2 = 0, 0
	while index1 < len(items1) and index2 < len(items2):
		if comp(items1[index1], items2[index2]):
			items.append(intems[index1]
			index1 += 1
		else:
			intems.append(items2[index2])
			index2 += 1
	items += items1[index1:]
	items += items2[index2:]
	return items
"""顺序查找"""
def seq_search(items, key):
	for index, item in enumerate(items):
		if item == key:
			return index
	return -1
"""折半查找"""
def bin_search(items, key):
	start, end = 0, len(items) - 1
	while start <= end:
		mid = (start + end) // 2
		if key > items[mid]:
			start = mid + 1
		elif key < items[mid]:
			end = mid - 1
		else:
			return mid
	return -1

heapq, itertools

"""
从列表中找出最大的或最小的N个元素
堆结构
"""
import heapq

list_1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
list_2 = [
	{'name': 'IBM', 'shares': 100, 'price': 91.1},
	{'name': 'AAPL', 'shares': 50, 'price': 543.22},
	{'name': 'FB', 'shares': 200, 'price': 21.09},
	{'name': 'HPQ', 'shares': 35, 'price': 31.75},
	{'name': 'YHOO', 'shares': 45, 'price': 16.35},
	{'name': 'ACME', 'shares': 75, 'price': 115.65},
]	
print(heapq.nlargest(3, list1))
print(heapq.nsmallest(3, list1))
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
import itertools

itertools.permutations('ABCD') # 排列
itertools.combinations('ABCDE', 3) # 组合
itertools.product('ABCD', '123') # 笛卡尔积

collections

"""找出序列中出现次数最多的元素"""
from collections import Counter

words = [
	'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'the', 'eyes',
	'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', 'eyes', "don't", 'look',
	'around', "you're", 'under'
]
counter = Counter(words)
print(counter.most_common(3))

常用算法

  • 穷举法 (暴力破解法)
  • 贪婪法 (在对问题求解时,做出当前看来最好的选择,快速找到满意解,而非最优解)
  • 分治法 (复杂问题分为子问题,再把子问题分为子问题,直到可以求解,最终将子问题的解合并为原问题解)
  • 回溯法 (试探法,按选优条件向前搜索)
  • 动态规划 (将待解问题分为若干子问题,先求解并保存这鞋子问题的解)

穷举法——百钱百鸡和五人分鱼

""" 公鸡5元1只,母鸡3元1只,小鸡1元3只;用100元买100只鸡,问公鸡、母鸡、小鸡各多少只"""
for x in range(100 // 5):
	for y in range(100 // 3):
		z = 100 - x - y
		if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0:
			print(x, y, z)

"""
A, B, C, D, E五人在某天夜里合伙捕鱼,最后匹配不看各自睡觉
第二天A第一个醒来,将鱼分为5份,扔掉多余的1条,拿走自己的一份
B第二个醒来,也将鱼分为5份,扔掉多余的1条,拿走自己的一份
然后C,D,E依次醒来也按照同样的方式分鱼
问他们至少捕了多少条鱼
"""
fish = 6
while True:
	total = fish
	enough = True
	for _ in range(5):
		if (total - 1) % 5 == 0:
			total = (total - 1) % 5
		else:
			enough = False
			break
	if enough:
		print(fish)
		break
	fish += 5

贪婪法

"""
假设小偷有一个背包,最多能够装20公斤赃物,他闯入一户人家,发现如下表所示的物品。
电脑 $200 20kg
收音机 $20 4kg
钟 $175 10kg
花瓶 $50 2kg
书 $10 1kg
油画 90 9kg
很显然,他不能把所有物品都装进背包
"""
class Thing(object):
	def __init__(self, name, price, weight):
		self.name = name
		self.price = price
		self.weight = weight
	
	@property
	def value(self):
		"""价格重量比"""
		return self.price / self.weight
	
def input_thing():
	name_str, price_str, weight_str = input().split()
	return name_str, int(price_str), int(weight_str)

def main():
	max_weight, num_of_things = map(int, input().split())
	all_things = []
	for _ in range(num_of_things):
		all_things.append(Thing(*input_thing())
	all_things.sort(key=lambda x: x.value, reverse=True)
	total_weight = 0
	total_price = 0
	for thing in all_things:
		if total_weight + thing.weight <= max_weight:
			print(f'小偷拿走了{thing.name}')
			total_weight += thing.weight
			total_price += thing.price
	print(f'总价值:{total_price}美元')

if __name__ == '__main__':
	main()

回溯法——骑士巡逻

import sys
import time

SIZE = 5
total = 0

def print_board(board):
	for row in board:
		for col in row:
			print(str(col).center(4), end='')
		print()

def patrol(board, row, col, step=1):
	if row >= 0 and row < SIZE 
			col >=0 and col < SIZE and \
		board[row][col] == 0:
		board[row][col] = step
		if step == SIZE * SIZE:
			global total
			total += 1
			print(f'第{total}种走法:')
			print_board(board)
		patrol(board, row - 2, col - 1, step + 1)
		patrol(board, row - 1, col - 2, step + 1)
		patrol(board, row + 1, col - 2, step + 1)
		patrol(board, row + 2, col - 1, step + 1)
		patrol(board, row + 2, col + 1, step + 1)
		patrol(board, row + 1, col + 2, step + 1)
		patrol(board, row - 1, col + 2, step + 1)
		patrol(board, row - 2, col + 1, step + 1)
		board[row][col] = 0

def main():
	board = [[0] * SIZE for _ in range(SIZE)]
	patrol(board, SIZE-1, SIZE-1)

if __name__ == "__main__":
	main()

动态规划:适用于有重叠子问题和最优子结构性质的问题

"""斐波那契数列"""
def fib(num, temp={}):
	if num in (1, 2):
		return 1
	try:
		return temp[num]
	except KeyError:
		temp[num] = fib(num - 1) + fib(num - 2)
		return temp[num]
"""
子列表元素之和最大值
子列表 列表中索引连续的元素构成的列表
"""
def main():
	items = list(map(int, input().split()))
	size = len(items)
	overall, partial = {}, {}
	overall[size - 1] = partial[size - 1] = items[size - 1]
	for i in range(size - 2, -1, -1):
		partial[i] = max(items[i], partial[i + 1] + items[i])
		overall[i] = max(partial[i], overall[i + 1])
	print(overall[0])

函数的使用方法

  • 函数
    • 函数可赋值给变量
    • 函数可以作为函数的参数
    • 函数可以作为函数的返回值
  • 高阶函数(filter, map及其替代品)
items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
items2 = [x ** 2 for x in range(1, 10) if x % 2]
  • 位置参数、可变参数、关键字参数、命名关键字参数
  • 参数的元信息
  • 匿名参数和内联参数的用法(lambda函数)
  • 闭包和作用于的问题
    • Python搜索变量的顺序LEGB (Local --> Embedded --> Global --> Built-in)
    • global: 声明或定义全局变量,直接作用于现有的全局作用域的变量或定义一个变量放至全局作用域
    • nonlocal:声明使用嵌套作用域的变量,嵌套作用域必须存在改变量否则报错。
  • 装饰器函数(使用和取消)
"""自定义装饰函数的装饰器"""
def record_time(func):
	@wraps(func)
	def wrapper(*args, **kwargs):
		start = time()
		result = func(*args, **kwargs)
		print(f'{func.__name__}: {time() - start}s.')
		return result

	return wrapper

如果装饰器不希望与print函数耦合,可以编写带参数的装饰器。

from functools import wraps
from time import time

def record(output):
	def decorate(func):
		@wraps(func)
		def wrapper(*args, **kwargs):P
			start = time()
			result = func(*args, **kwargs)
			output(func.__name__, time() - start)
			return result
		return wrapper
	return decorate
"""自定义装饰器类"""
from functools import warps
from time import time

class Record(object):
	def __init__(self, output):
		self.output = output
	
	def __call__(self, func):
		@wraps(func)
		def wrapper(*args, **kwargs):
			start = time()
			result = func(*args, **kwargs)
			self.output(func.__name__, time() - start())
			return result
		return wrapper

由于对带装饰功能的函数添加了==@wraps==装饰器,可以通过func.__wrapped__方式获得被装饰之前的函数或类来取消装饰器的作用

例子如下:

from functools import wraps
from threading import Lock

def singleton(cls):
	instances = {}
	locker = Lock()
	
	@wraps(cls)
	def wrapper(*args, **kwargs):
		with locker:
			if cls not in instances:
				instances[cls] = cls(*args, **kwargs)
		return instances[cls]
	
	return wrapper

@singleton
class President():
	pass

以上代码中用到了闭包(closure)

面向对象

封装、继承与多态

类与类之间的关系:继承,关联/聚合/合成,依赖

对象的复制

垃圾回收、循环利用和弱引用

Python使用了自动化内存管理,以引用计数为基础,同时也引入了标记-清除分代收集两种机制为辅的策略。

导致引用计数+1的情况:
- 对象被创建
- 对象被引用
- 对象作为一个参数传入函数中
- 对象作为一个元素存储在容器中
导致引用计数-1的情况:
- 对象的别名被显示销毁,如 del a
- 对象的别名被赋予新的对象
- 一个对象离开它的作用域
- 对象所在容器被销毁,或从容器中删除该对象

引用计数可能会导致循环引用问题,而循环引用会导致内存泄露。

list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

为解决这个问题,Python引入“标记-清除”和“分代收集”。

在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活,该对象则被放到第二代中。同理,如果在第二代的垃圾检查中对象存活,该对象则被放到第三代中。

以下情况会导致垃圾回收:
- 调用gc.collect()
- gc模块的计数器达到阈值
- 程序退出

如果gc模块循环引用中两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象。
也可以通过weakref模块构造弱引用的方式来解决循环引用的问题

魔法属性和方法

参考《Python魔法方法指南》

混入(Mixin)

"""
自定义字典限制只有在指定的key不存在时才能在字典中设置键值对
"""
class SetOnceMappingMixin():
	"""自定义混入类"""
	__slots__ = ()

	def __setitem__(self, key, value):
		if key in self:
			raise KeyError(str(key) + ' already set')
		return super().__setitem__(key, value)

class SetOnceDict(SetOnceMappingMixin, dict):
	"""自定义字典"""
	pass

my_dict = SetOnceDict()
try:
	my_dict['username'] = 'jackfrued'
	my_dict['username'] = 'hellokitty'
except KeyError:
	pass
print(my_dict)

元编程和元类

"""用元类实现单例模式"""
import threading

class SingletonMeta(type):
	"""自定义元类"""
	
	def __init__(cls, *args, **kwargs):
		cls.__instance = None
		cls.__lock = threading.Lock()
		super().__init__(*args, **kwargs)
	
	def __call__(cls, *args, **kwargs):
		if cls.__instance is None:
			with cls.__lock:
				if cls.__instance is None:
					cls.__instance = super().__call__(*args, **kwargs)
		return cls.__instance

class President(metaclass=SingletonMeta):
	pass

面向对象设计原则

  • 单一职责原则(SRP):类的设计要高内聚
  • 开闭原则(OCP):软件实体应该对扩展开发对修改关闭
  • 依赖倒转原则(DIP):面向抽象编程
  • 里氏替换原则(LSP):任何时候可以用子类对象替换掉父类对象
  • 接口隔离原则(ISP):接口要小而专不要大而全
  • 合成聚合复用原则(CARP):有限使用强关联关系而不是继承关系复用代码
  • 最少知识原则(LoD):不要给没有必然联系的对象发消息
class StreamHasher():
	"""哈希摘要生成器"""
	def __init__(self, alg='md5', size=4096):
		self.size = size
		alg = alg.lower()
		self.hasher = getattr(__import__('hashlib'), alg.lower())()
	
	def __call__(self, stream):
		return self.to_digest(stream)
	
	def to_digest(self, stream):
		"""生成十六进制形式的摘要"""
		for buf in iter(lambda: stream.read(self.size), b''):
			self.hasher.updata(buf)
		return self.hasher.hexdigest()

def main():
	hasher1 = StreamHasher()
	with open('Python-3.7.1.tgz', 'rb') as stream:
		print(hasher1.to_digest(stream))
	hasher2 = StreamHasher('sha1')
	with open('Python-3.7.1.tgz', 'rb') as stream:
		print(hasher2(stream))

if __name__ == "__main__":
	main()

迭代器和生成器

  • 相关魔术方法 iter__和__next
  • 创建生成器的方式:生成器表达式,yield关键字
    def fib(num):
    	"""生成器"""
    	for _ in range(num):
    		a, b = b, a + b
    		yield a
    """
    带yield的函数是一个生成器,该生成器有一个函数next(),输出生成器生成的下一个值。
    next()开始的地方是上一次next()停止的地方
    """
    class Fib(object):
    	"""迭代器"""
    	def __init__(self, num):
    		self.num = num
    		self.a, self.b = 0, 1
    		self.idx = 0
    	
    	def __iter__(self):
    		return self
    	
    	def __next__(self):
    		if self.idx < self.num:
    			self.a, self.b = self.b, self.a + self.b
    			self.idx += 1
    			return self.a
    		raise StopIteration()
    

你可能感兴趣的:(python)