做系统去哪网站下载镜像,中企动力手机邮箱,邯郸网站设计怎么用,中华室内设计协会Python语言进阶
面向对象相关知识 三大支柱#xff1a;封装、继承、多态 例子#xff1a;工资结算系统。 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成from abc import ABCMeta, abstractmethodcl…Python语言进阶
面向对象相关知识 三大支柱封装、继承、多态 例子工资结算系统。 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成from abc import ABCMeta, abstractmethodclass Employee(metaclassABCMeta):员工(抽象类)def __init__(self, name):self.name nameabstractmethoddef get_salary(self):结算月薪(抽象方法)passclass Manager(Employee):部门经理def get_salary(self):return 15000.0class Programmer(Employee):程序员def __init__(self, name, working_hour0):self.working_hour working_hoursuper().__init__(name)def get_salary(self):return 200.0 * self.working_hourclass Salesman(Employee):销售员def __init__(self, name, sales0.0):self.sales salessuper().__init__(name)def get_salary(self):return 1800.0 self.sales * 0.05class EmployeeFactory:创建员工的工厂工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合staticmethoddef create(emp_type, *args, **kwargs):创建员工all_emp_types {M: Manager, P: Programmer, S: Salesman}cls all_emp_types[emp_type.upper()]return cls(*args, **kwargs) if cls else Nonedef main():主函数emps [EmployeeFactory.create(M, 曹操), EmployeeFactory.create(P, 荀彧, 120),EmployeeFactory.create(P, 郭嘉, 85), EmployeeFactory.create(S, 典韦, 123000),]for emp in emps:print(f{emp.name}: {emp.get_salary():.2f}元)if __name__ __main__:main()类与类之间的关系 is-a关系继承has-a关系关联 / 聚合 / 合成use-a关系依赖 例子扑克游戏。 经验符号常量总是优于字面常量枚举类型是定义符号常量的最佳选择from enum import Enum, uniqueimport randomuniqueclass Suite(Enum):花色SPADE, HEART, CLUB, DIAMOND range(4)def __lt__(self, other):return self.value other.valueclass Card:牌def __init__(self, suite, face):初始化方法self.suite suiteself.face facedef show(self):显示牌面suites [♠︎, ♥︎, ♣︎, ♦︎]faces [, A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K]return f{suites[self.suite.value]}{faces[self.face]}def __repr__(self):return self.show()class Poker:扑克def __init__(self):self.index 0self.cards [Card(suite, face)for suite in Suitefor face in range(1, 14)]def shuffle(self):洗牌随机乱序random.shuffle(self.cards)self.index 0def deal(self):发牌card self.cards[self.index]self.index 1return cardpropertydef has_more(self):return self.index len(self.cards)class Player:玩家def __init__(self, name):self.name nameself.cards []def get_one(self, card):摸一张牌self.cards.append(card)def sort(self, complambda card: (card.suite, card.face)):整理手上的牌self.cards.sort(keycomp)def main():主函数poker Poker()poker.shuffle()players [Player(东邪), Player(西毒), Player(南帝), Player(北丐)]while poker.has_more:for player in players:player.get_one(poker.deal())for player in players:player.sort()print(player.name, end: )print(player.cards)if __name__ __main__:main()说明上面的代码中使用了Emoji字符来表示扑克牌的四种花色在某些不支持Emoji字符的系统上可能无法显示。 对象的复制深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆 垃圾回收、循环引用和弱引用 Python使用了自动化内存管理这种管理机制以引用计数为基础同时也引入了标记-清除和分代收集两种机制为辅的策略。 typedef struct _object {/* 引用计数 */int ob_refcnt;/* 对象指针 */struct _typeobject *ob_type;
} PyObject;/* 增加引用计数的宏定义 */
#define Py_INCREF(op) ((op)-ob_refcnt)
/* 减少引用计数的宏定义 */
#define Py_DECREF(op) \ //减少计数if (--(op)-ob_refcnt ! 0) \; \else \__Py_Dealloc((PyObject *)(op))导致引用计数1的情况 对象被创建例如a 23对象被引用例如b a对象被作为参数传入到一个函数中例如f(a)对象作为一个元素存储在容器中例如list1 [a, a] 导致引用计数-1的情况 对象的别名被显式销毁例如del a对象的别名被赋予新的对象例如a 24一个对象离开它的作用域例如f函数执行完毕时f函数中的局部变量全局变量不会对象所在的容器被销毁或从容器中删除对象 引用计数可能会导致循环引用问题而循环引用会导致内存泄露如下面的代码所示。为了解决这个问题Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候对象被放在第一代中如果在第一代的垃圾检查中对象存活了下来该对象就会被放到第二代中同理在第二代的垃圾检查中对象存活下来该对象就会被放到第三代中。 # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收
# 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效
# 如果不想造成循环引用可以使用弱引用
list1 []
list2 []
list1.append(list2)
list2.append(list1)以下情况会导致垃圾回收 调用gc.collect()gc模块的计数器达到阀值程序退出 如果循环引用中两个对象都定义了__del__方法gc模块不会销毁这些不可达对象因为gc模块不知道应该先调用哪个对象的__del__方法这个问题在Python 3.6中得到了解决。 也可以通过weakref模块构造弱引用的方式来解决循环引用的问题。 魔法属性和方法请参考《Python魔法方法指南》 有几个小问题请大家思考 自定义的对象能不能使用运算符做运算自定义的对象能不能放到set中能去重吗自定义的对象能不能作为dict的键自定义的对象能不能使用上下文语法 混入Mixin 例子自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。 Pythonclass 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):自定义字典passmy_dict SetOnceDict()try:my_dict[username] jackfruedmy_dict[username] hellokittyexcept KeyError:passprint(my_dict)元编程和元类 对象是通过类创建的类是通过元类创建的元类提供了创建类的元信息。所有的类都直接或间接的继承自object所有的元类都直接或间接的继承自type。 例子用元类实现单例模式。 import threadingclass SingletonMeta(type):自定义元类def __init__(cls, *args, **kwargs):cls.__instance Nonecls.__lock threading.RLock()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.__instanceclass President(metaclassSingletonMeta):总统(单例类)pass面向对象设计原则 单一职责原则 SRP- 一个类只做该做的事情类的设计要高内聚开闭原则 OCP- 软件实体应该对扩展开发对修改关闭依赖倒转原则DIP- 面向抽象编程在弱类型语言中已经被弱化里氏替换原则LSP - 任何时候可以用子类对象替换掉父类对象接口隔离原则ISP- 接口要小而专不要大而全Python中没有接口的概念合成聚合复用原则CARP - 优先使用强关联关系而不是继承关系复用代码最少知识原则迪米特法则LoD- 不要给没有必然联系的对象发消息 说明上面加粗的字母放在一起称为面向对象的SOLID原则。 GoF设计模式 创建型模式单例、工厂、建造者、原型结构型模式适配器、门面外观、代理行为型模式迭代器、观察者、状态、策略 例子可插拔的哈希算法策略模式。 class StreamHasher():哈希摘要生成器def __init__(self, algmd5, size4096):self.size sizealg 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.update(buf)return self.hasher.hexdigest()def main():主函数hasher1 StreamHasher()with open(Python-3.7.6.tgz, rb) as stream:print(hasher1.to_digest(stream))hasher2 StreamHasher(sha1)with open(Python-3.7.6.tgz, rb) as stream:print(hasher2(stream))if __name__ __main__:main()迭代器和生成器 迭代器是实现了迭代器协议的对象。 Python中没有像protocol或interface这样的定义协议的关键字。Python中用魔术方法表示协议。__iter__和__next__魔术方法就是迭代器协议。 class Fib(object):迭代器def __init__(self, num):self.num numself.a, self.b 0, 1self.idx 0def __iter__(self):return selfdef __next__(self):if self.idx self.num:self.a, self.b self.b, self.a self.bself.idx 1return self.araise StopIteration()生成器是语法简化版的迭代器。 def fib(num):生成器a, b 0, 1for _ in range(num):a, b b, a byield a生成器进化为协程。 生成器对象可以使用send()方法发送数据发送的数据会成为生成器函数中通过yield表达式获得的值。这样生成器就可以作为协程使用协程简单的说就是可以相互协作的子程序。 def calc_avg():流式计算平均值total, counter 0, 0avg_value Nonewhile True:value yield avg_valuetotal, counter total value, counter 1avg_value total / countergen calc_avg()next(gen)print(gen.send(10))print(gen.send(20))print(gen.send(30))并发编程
Python中实现并发编程的三种方案多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验坏处在于并发的程序不容易开发和调试同时对其他程序来说它并不友好。
多线程Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码这个锁对于CPython是必须的因为CPython的内存管理并不是线程安全的因为GIL的存在多线程并不能发挥CPU的多核特性。 面试题进程和线程的区别和联系进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程线程 - 操作系统分配CPU的基本单位并发编程concurrent programming1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行2. 改善用户体验 - 让耗时间的操作不会造成程序的假死import globimport osimport threadingfrom PIL import ImagePREFIX thumbnailsdef generate_thumbnail(infile, size, formatPNG):生成指定图片文件的缩略图file, ext os.path.splitext(infile)file file[file.rfind(/) 1:]outfile f{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}img Image.open(infile)img.thumbnail(size, Image.ANTIALIAS)img.save(outfile, format)def main():主函数if not os.path.exists(PREFIX):os.mkdir(PREFIX)for infile in glob.glob(images/*.png):for size in (32, 64, 128):# 创建并启动线程threading.Thread(targetgenerate_thumbnail, args(infile, (size, size))).start()if __name__ __main__:main()多个线程竞争资源的情况。 多线程程序如果没有竞争资源处理起来通常也比较简单当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱说明临界资源就是被多个线程竞争的资源import timeimport threadingfrom concurrent.futures import ThreadPoolExecutorclass Account(object):银行账户def __init__(self):self.balance 0.0self.lock threading.Lock()def deposit(self, money):# 通过锁保护临界资源with self.lock:new_balance self.balance moneytime.sleep(0.001)self.balance new_balancedef main():主函数account Account()# 创建线程池pool ThreadPoolExecutor(max_workers10)futures []for _ in range(100):future pool.submit(account.deposit, 1)futures.append(future)# 关闭线程池pool.shutdown()for future in futures:future.result()print(account.balance)if __name__ __main__:main()修改上面的程序启动5个线程向账户中存钱5个线程从账户中取钱取钱时如果余额不足就暂停线程进行等待。为了达到上述目标需要对存钱和取钱的线程进行调度在余额不足时取钱的线程暂停并释放锁而存钱的线程将钱存入后要通知取钱的线程使其从暂停状态被唤醒。可以使用threading模块的Condition来实现线程调度该对象也是基于锁来创建的代码如下所示 多个线程竞争一个资源 - 保护临界资源 - 锁Lock/RLock多个线程竞争多个资源线程数资源数 - 信号量Semaphore多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Conditionfrom concurrent.futures import ThreadPoolExecutorfrom random import randintfrom time import sleepimport threadingclass Account:银行账户def __init__(self, balance0):self.balance balancelock threading.RLock()self.condition threading.Condition(lock)def withdraw(self, money):取钱with self.condition:while money self.balance:self.condition.wait()new_balance self.balance - moneysleep(0.001)self.balance new_balancedef deposit(self, money):存钱with self.condition:new_balance self.balance moneysleep(0.001)self.balance new_balanceself.condition.notify_all()def add_money(account):while True:money randint(5, 10)account.deposit(money)print(threading.current_thread().name, :, money, , account.balance)sleep(0.5)def sub_money(account):while True:money randint(10, 30)account.withdraw(money)print(threading.current_thread().name, :, money, , account.balance)sleep(1)def main():account Account()with ThreadPoolExecutor(max_workers15) as pool:for _ in range(5):pool.submit(add_money, account)for _ in range(10):pool.submit(sub_money, account)if __name__ __main__:main()多进程多进程可以有效的解决GIL的问题实现多进程主要的类是Process其他辅助的类跟threading模块中的类似进程间共享数据可以使用管道、套接字等在multiprocessing模块中有一个Queue类它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。 多进程和进程池的使用多线程因为GIL的存在不能够发挥CPU的多核特性对于计算密集型任务应该考虑使用多进程time python3 example22.pyreal 0m11.512suser 0m39.319ssys 0m0.169s使用多进程后实际执行时间为11.512秒而用户时间39.319秒约为实际执行时间的4倍这就证明我们的程序通过多进程使用了CPU的多核特性而且这台计算机配置了4核的CPUimport concurrent.futuresimport mathPRIMES [1116281,1297337,104395303,472882027,533000389,817504243,982451653,112272535095293,112582705942171,112272535095293,115280095190773,115797848077099,1099726899285419] * 5def is_prime(n):判断素数if n % 2 0:return Falsesqrt_n int(math.floor(math.sqrt(n)))for i in range(3, sqrt_n 1, 2):if n % i 0:return Falsereturn Truedef main():主函数with concurrent.futures.ProcessPoolExecutor() as executor:for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):print(%d is prime: %s % (number, prime))if __name__ __main__:main()重点多线程和多进程的比较。 以下情况需要使用多线程 程序需要维护许多共享的状态尤其是可变状态Python中的列表、字典、集合都是线程安全的所以使用线程而不是进程维护共享状态的代价相对较小。程序会花费大量时间在I/O操作上没有太多并行计算的需求且不需占用太多的内存。 以下情况需要使用多进程 程序执行计算密集型任务如字节码操作、数据处理、科学计算。程序的输入可以并行的分成块并且可以将运算结果合并。程序在内存使用方面没有任何限制且不强依赖于I/O操作如读写文件、套接字等。 异步处理从调度程序的任务队列中挑选任务该调度程序以交叉的形式执行这些任务我们并不能保证任务将以某种顺序去执行因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现由于执行时间和顺序的不确定因此需要通过回调式编程或者future对象来获取任务执行的结果。Python 3通过asyncio模块和await和async关键字在Python 3.7中正式被列为关键字来支持异步处理。 异步I/O - async / awaitimport asynciodef num_generator(m, n):指定范围的数字生成器yield from range(m, n 1)async def prime_filter(m, n):素数过滤器primes []for i in num_generator(m, n):flag Truefor j in range(2, int(i ** 0.5 1)):if i % j 0:flag Falsebreakif flag:print(Prime , i)primes.append(i)await asyncio.sleep(0.001)return tuple(primes)async def square_mapper(m, n):平方映射器squares []for i in num_generator(m, n):print(Square , i * i)squares.append(i * i)await asyncio.sleep(0.001)return squaresdef main():主函数loop asyncio.get_event_loop()future asyncio.gather(prime_filter(2, 100), square_mapper(1, 100))future.add_done_callback(lambda x: print(x.result()))loop.run_until_complete(future)loop.close()if __name__ __main__:main()说明上面的代码使用get_event_loop函数获得系统默认的事件循环通过gather函数可以获得一个future对象future对象的add_done_callback可以添加执行完成时的回调函数loop对象的run_until_complete方法可以等待通过future对象获得协程执行结果。 Python中有一个名为aiohttp的三方库它提供了异步的HTTP客户端和服务器这个三方库可以跟asyncio模块一起工作并提供了对Future对象的支持。Python 3.6中引入了async和await来定义异步执行的函数以及创建异步上下文在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。 import asyncioimport reimport aiohttpPATTERN re.compile(r\title\(?Ptitle.*)\\/title\)async def fetch_page(session, url):async with session.get(url, sslFalse) as resp:return await resp.text()async def show_title(url):async with aiohttp.ClientSession() as session:html await fetch_page(session, url)print(PATTERN.search(html).group(title))def main():urls (https://www.python.org/,https://git-scm.com/,https://www.jd.com/,https://www.taobao.com/,https://www.douban.com/)loop asyncio.get_event_loop()cos [show_title(url) for url in urls]loop.run_until_complete(asyncio.wait(cos))loop.close()if __name__ __main__:main()重点异步I/O与多进程的比较。 当程序不需要真正的并发性或并行性而是更多的依赖于异步处理和回调时asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时也应该考虑asyncio它很适合编写没有实时数据处理需求的Web应用服务器。 Python还有很多用于处理并行任务的三方库例如joblib、PyMP等。实际开发中要提升系统的可扩展性和并发性通常有垂直扩展增加单个节点的处理能力和水平扩展将单个节点变成多个节点两种做法。可以通过消息队列来实现应用程序的解耦合消息队列相当于是多线程同步队列的扩展版本不同机器上的应用程序相当于就是线程而共享的分布式消息队列就是原来程序中的Queue。消息队列面向消息的中间件的最流行和最标准化的实现是AMQP高级消息队列协议AMQP源于金融行业提供了排队、路由、可靠传输、安全等功能最著名的实现包括Apache的ActiveMQ、RabbitMQ等。
要实现任务的异步化可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列它使用分布式消息进行工作可以基于RabbitMQ或Redis来作为后端的消息代理。