python设计模式-适配器模式

问题:假设有一个软件系统,你希望它能在不改变现有代码的前提下和一个新的厂商类库搭配使用,但是这个新厂商所设计出来的接口不同于旧厂商的接口 这个问题和下图的问题类似 美国标准的插头🔌无法在欧洲标准的插座上使用,通常的做法是什么呢? 添加一个插头适配器,适配器的作用是将欧式插头转换成美式插座,以便于让美式插头可以使用。 解决方案 所以,面对一个有全新接口的类库而又不能改变现有代码时,最先想到的做法是,在这两个系统之间添加一个适配器。 简单的例子 有一个系统,需要一个鸭子🦆对象,但是现在只有一个火鸡🦃对象。鸭子和火鸡对象的功能简单描述如下: # 鸭子的简单描述 class Duck: def quack(self): # 会呱呱叫 print("Quack") def fly(self): # 飞的能力 print("I'm flying") # 火鸡的简单描述 class Turkey: def gobble(self): # 不会呱呱叫,只会咯咯叫 print("Gobble gobble") def fly(self): # 飞的能力 但是飞不远 print("I'm flying a short distance") 因为现在没有鸭子对象,只能那火鸡对象冒充。由于鸭子对象和火鸡对象功能不同,不能直接拿来用,现在就需要使用适配器来完成这个功能: class TurkeyAdapter(Duck): turkey = Turkey() # 这里实际使用的是火鸡对象 # 实现鸭子对象拥有的quack方法 def quack(self): self.turkey.gobble() def fly(self): # 假设火鸡比鸭子飞的短,为了模拟鸭子的动作,多飞几次 for i in range(5): turkey.fly() 接下来调用就可以像使用鸭子对象一样使用火鸡适配后的对象。 ...

2018-11-03 · 1 min · 112 words

SQLAlchemy in 空列表问题分析

SQLAlchemy in 空列表问题 问题场景 有model Account,SQLAlchemy 查询语句如下: query = Account.query.filter(Account.id.in_(account_ids)).order_by(Account.date_created.desc()) 这里 account_ids 如果为空,执行查询会有如下警告: /usr/local/lib/python2.7/site-packages/sqlalchemy/sql/default_comparator.py:35: SAWarning: The IN-predicate on "account.id" was invoked with an empty sequence. This results in a contradiction, which nonetheless can be expensive to evaluate. Consider alternative strategies for improved performance. return o[0](self, self.expr, op, *(other + o[1:]), **kwargs) 这里的意思是使用一个空的列表会花费较长的时间,需要优化以提高性能。 为什么会有这个提示呢?一个空列表为什么会影响性能呢? 首先打印 query 可得到如下 sql 语句: SELECT * // 字段使用 “*” 代替 FROM account WHERE account.id != account.id ORDER BY account.date_created DESC 会发现生成的语句中过滤条件是 WHERE account.id != account.id,使用 PostgreSQL Explain ANALYZE 命令, ...

2018-10-04 · 3 min · 542 words

使用github+travis将Python包部署到Pypi

我在 github 托管 Python 代码,然后将包发布到 Pypi,通常的操作步骤是,更新完代码将提交到 github ,然后手动将包更新到 pypi,这样比较繁琐,就想到了使用github+travis-ci 构建一个自动部署环境。 注册 pypi 访问https://pypi.org 点击Register注册账号,记住自己的用户名密码。 创建 setup.py 文件 setup.py 文件放置于包的根目录,示例内容如下: #!/usr/bin/env python from setuptools import setup, find_packages with open("README.md", "r") as fh: long_description = fh.read() with open('requirements.txt') as f: requirements = [l for l in f.read().splitlines() if l] setup(name="python-weixin", # 项目名 version="0.3.2", # 版本号 description="Python Weixin API client support wechat-app", #简介 long_description=long_description, # 长简介 这里使用的 readme 内容 long_description_content_type="text/markdown", license="BSD", # 授权 install_requires=requirements, # 依赖 author="gusibi", # 作者 author_email="[email protected]", # 邮箱 url="https://github.com/gusibi/python-weixin", # 地址 download_url="https://github.com/gusibi/python-weixin/archive/master.zip", packages=find_packages(), keywords=["python-weixin", "weixin", "wechat", "sdk", "weapp", "wxapp"], zip_safe=True) 以上特别需要注意的是 packages参数,用来申明你的包里面要包含的目录,这里使用setuptools自动决定要包含哪些包。 ...

2018-07-23 · 2 min · 308 words

Python 生成便签图片

最近有文字转图片的需求,但是不太想下载 APP,就使用 Python Pillow 实现了一个,效果如下: PIL 提供了 PIL.ImageDraw.ImageDraw.text 方法,可以方便的把文字写到图片上,简单示例如下: from PIL import Image, ImageDraw, ImageFont # get an image base = Image.open('Pillow/Tests/images/hopper.png').convert('RGBA') # make a blank image for the text, initialized to transparent text color txt = Image.new('RGBA', base.size, (255,255,255,0)) # get a font fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40) # get a drawing context d = ImageDraw.Draw(txt) # draw text, half opacity d.text((10,10), "Hello", font=fnt, fill=(255,255,255,128)) # draw text, full opacity d.text((10,60), "World", font=fnt, fill=(255,255,255,255)) out = Image.alpha_composite(base, txt) out.show() 为什么要计算文字的宽高呢?把文字直接写到背景图不可以么? ...

2018-07-08 · 3 min · 469 words

Python gRPC 入门

gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。 本文通过一个简单的 Hello World 例子来向您介绍 gRPC 。 gRPC 是什么? gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。 在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得我们能够更容易地创建分布式应用和服务。 gRPC 客户端和服务端可以在多种环境中运行和交互,并且可以用任何 gRPC 支持的语言来编写。 gRPC 支持 C++ Java Python Go Ruby C# Node.js PHP Dart 等语言 gRPC 默认使用 protocol buffers,这是 Google 开源的一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。 安装 Google Protocol Buffer 方法一(建议使用) 参考文档:gRPC Python Quickstart 1. 安装 gRPC python -m pip install grpcio # 或者 sudo python -m pip install grpcio # 在 El Capitan OSX 系统下可能会看到以下报错 $ OSError: [Errno 1] Operation not permitted: '/tmp/pip-qwTLbI-uninstall/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/six-1.4.1-py2.7.egg-info' # 可以使用以下命令 python -m pip install grpcio --ignore-installed 2. 安装 gRPC tools Python gPRC tools 包含 protocol buffer 编译器和用于从 .proto 文件生成服务端和客户端代码的插件 ...

2018-06-07 · 3 min · 626 words

newrelic python agent 源码分析-1

Newrelic 是APM(Application Performance Management)(应用性能管理/监控)解决方案提供商。项目中,通常用它来追踪应用的性能。最近看了一下 newrelic-python-agent 源码,这是查看源码过程中的一些记录。 目录结构 newrelic 目录结构如下: newrelic ├── admin # 常用命令 ├── api # 探针 ├── bootstrap ├── common ├── core ├── extras │ └── framework_django │ └── templatetags ├── hooks # 数据库 web 各个库的一些探针 │ ├── framework_tornado │ ├── framework_tornado_r3 │ └── framework_tornado_r4 ├── network ├── packages │ ├── requests │ │ └── packages │ │ ├── chardet │ │ └── urllib3 │ │ ├── packages │ │ │ └── ssl_match_hostname │ │ └── util │ └── wrapt └── samplers 命令 使用 newrelic-admin help 可以列出所有命令: ...

2018-05-16 · 3 min · 509 words

python设计模式-命令模式

命令模式 题目: 现在要做一个智能家居控制遥控器,功能如下图所示。 下图是家电厂商提供的类,接口各有差异,并且以后这种类可能会越来越多。 观察厂商提供的类,你会发现,好多类提供了 on()、off() 方法,除此之外,还有一些方法像 dim()、setTemperature()、setVolumn()、setDirection()。由此我们可以想象,之后还会有更多的厂商类,每个类还会有各式各样的方法。 如果我们把这些类都用到遥控器代码中,代码就会多一大堆的 if 语句,例如 if slot1 == Light: light.on() elif slot1 == Hottub: hottob.jetsOn() 并且更严重的是,每次有新的厂商类加进来,遥控器的代码都要做相应的改动。 这个时候我们就要把动作的请求者(遥控器)从动作的执行者(厂商类)对象中解耦。 如何实现解耦呢? 我们可以使用命令对象。利用命令对象,把请求(比如打开电灯)封装成一个特定对象。所以,如果对每个按钮都存储一个命令对象,那么当按钮按下的时候,就可以请求命令对象做相关的工作。此时,遥控器并不需要知道工作的内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。 下面我们拿餐厅点餐的操作来介绍下命令模式。 餐厅通常是这样工作的: 顾客点餐,把订单交给服务员 服务员拿了订单,把订单交给厨师。 厨师拿到订单后根据订单准备餐点。 这里我们把订单想象成一个用来请求准备餐点的对象, 和一般对象一样,订单对象可以被传递:从服务员传递到订单柜台,订单的接口只包含一个方法 orderUp()。这个方法封装了准备餐点所需的动作。 服务员的工作就是接受订单,然后调用订单的 orderUp() 方法,她不需要知道订单内容是什么。 厨师是一个对象,他知道如何准备准备餐点,是任务真正的执行者。 如果我们把餐厅想象成OO 设计模式的一种模型,这个模型允许将”发出请求的对象“和”接受与执行这些请求的对象“分隔开来。比如对于遥控器 API,我们要分隔开”发出请求的按钮代码“和”执行请求的厂商特定对象”。 回到命令模式我们把餐厅的工作流程图转换为命令模式的流程图:这里 client 对应上一张图的顾客,command 对应订单,Invoker 对应服务员,Receiver 对应的是厨师。 命令模式 先来看下命令模式的定义: 命令模式将”请求“封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。 通过上边的定义我们知道,一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将动作和接收者包进对象中。这个对象只暴露一个 execute() 方法,当此方法被调用时,接收者就会进行这些动作。 命令模式类图如下: 回到遥控器的设计:我们打算将遥控器的每个插槽,对应到一个命令,这样就让遥控器变成了调用者。当按下按钮,相应命令对象的 execute() 方法就会被调用,其结果就是接收者(例如:电灯、风扇、音响)的动作被调用。 命令模式还支持撤销,该命令提供和 execute() 方法相反的 undo() 方法。不管 execute() 做了什么,undo() 都会倒转过来。 ...

2018-01-09 · 3 min · 477 words

Python 字典

这一篇是《流畅的 python》读书笔记。主要介绍: 常见的字典方法 如何处理查不到的键 标准库中 dict 类型的变种 散列表的工作原理 泛映射类型 collections.abc 模块中有 Mapping 和 MutableMapping 这两个抽象基类,它们的作用是为 dict 和其他类似的类型定义形式接口。 标准库里所有映射类型都是利用 dict 来实现的,它们有个共同的限制,即只有可散列的数据类型才能用做这些映射里的键。 问题: 什么是可散列的数据类型? 在 python 词汇表(https://docs.python.org/3/glossary.html#term-hashable)中,关于可散列类型的定义是这样的: 如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现 __hash__() 方法。另外可散列对象还要有 __eq__() 方法,这样才能跟其他键做比较。如果两个可散列对象是相等的,那么它们的散列只一定是一样的 根据这个定义,原子不可变类型(str,bytes和数值类型)都是可散列类型,frozenset 也是可散列的(因为根据其定义,frozenset 里只能容纳可散列类型),如果元组内都是可散列类型的话,元组也是可散列的(元组虽然是不可变类型,但如果它里面的元素是可变类型,这种元组也不能被认为是不可变的)。 一般来讲,用户自定义的类型的对象都是可散列的,散列值就是它们的 id() 函数的返回值,所以这些对象在比较的时候都是不相等的。(如果一个对象实现了 eq 方法,并且在方法中用到了这个对象的内部状态的话,那么只有当所有这些内部状态都是不可变的情况下,这个对象才是可散列的。) 根据这些定义,字典提供了很多种构造方法,https://docs.python.org/3/library/stdtypes.html#mapping-types-dict 这个页面有个例子来说明创建字典的不同方式。 >>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3])) >>> d = dict([('two', 2), ('one', 1), ('three', 3)]) >>> e = dict({'three': 3, 'one': 1, 'two': 2}) >>> a == b == c == d == e True 除了这些方法以外,还可以用字典推导的方式来建造新 dict。 ...

2017-12-03 · 3 min · 568 words

python设计模式-单例模式

问题:现代化的巧克力工厂具备计算机控制的巧克力锅炉。锅炉做的事情就是把巧克力和牛奶融在一起,然后送到下一个阶段,以制成巧克力棒。下边是一个巧克力公司锅炉控制器的代码,仔细观察一下,这段代码有什么问题? class ChocolateBoiler(object): def __init__(self): self.empty = True self.boiled = False def fill(self): # 向锅炉填充巧克力和牛奶混合物 # 在锅炉内填充原料时,锅炉必须是空的。 # 一旦填入原料,就要把empty 和 boiled 标志设置好 if self.empty: self.empty = False self.boiled = False def drain(self): # 排出煮沸的巧克力和牛奶 # 锅炉排出时,必须是满的且煮沸的。 # 排出完毕empty 设置为 true if not self.empty and self.boiled: self.empty = True def boil(self): # 将颅内物煮沸 # 煮混合物时,锅炉内必须是满的且没有煮沸过 # 一旦煮沸,就把 boiled 设置为 true if not self.empty and not self.boiled: self.boiled = True 从代码可以看出,他们加入了多种判断,以防止不好的事情发生。如果同时存在两个ChocolateBoiler实例,那这么多判断岂不是失去作用了。那我们改如何实现这个需求呢?这个问题的核心是,我们要先判断实例是不是已经存在,如果存在就不再创建。 _chocolate_boiler_instance = None # 声明实例 def chocolate_boiler(): global _chocolate_boiler_instance # 使用全局变量 if _chocolate_boiler_instance is not None: # 判断是否存在,如果存在,直接返回 return _chocolate_boiler_instance else: # 如果不存在,创建一个新的 _chocolate_boiler_instance = ChocolateBoiler() return _chocolate_boiler_instance 现在我们需要获取 ChocolateBoiler 实例的时候只需要调用 chocolate_boiler 方法获取实例即可保证同时只有一个 ChocolateBoiler实例。 ...

2017-11-26 · 2 min · 418 words

python设计模式-建造者模式

问题:在上一篇python设计模式:抽象工厂模式中,我们尝试用抽象工厂模式规范化了 Pizza 原材料的供应以及 Pizza 的创建。但是我们忽略了一个问题,那就是每种 Pizza 的烘焙时间依赖于生面团的厚度和使用的配料,它们所需的时间是不一样的。那这时我们改如何处理呢? Pizza 的制作流程包括:准备(擀面皮、加佐料),然后烘烤、切片、装盒。这些有特定的顺序,不能错乱。 为了保证 生产 Pizza 的步骤不会出错,我们打算指派一个创建者,创建者用于控制 Pizza 的制作流程。 创建 Pizza 创建者 首先我们定义一个 Pizza class Pizza: def __init__(self, name): self.name = name self.dough = None self.sauce = None self.toppings = [] def prepare_dough(self, dough): self.dough = dough print(self.dough) print('preparing the {} dough of your {}...'.format(self.dough, self)) time.sleep(STEP_DELAY) print('Done with the {} dough'.format(self.dough)) def __str__(self): return self.name 然后我们抽象出一个创建者: class PizzaBuilder(object): name = None def __init__(self): self.progress = PIZZA_PROGRESS self.baking_time = 5 def prepare_dough(self): raise NotImplementedError() def add_sauce(self): raise NotImplementedError() def add_topping(self): raise NotImplementedError() def bake(self): raise NotImplementedError() def cut(self): raise NotImplementedError() def box(self): raise NotImplementedError() @property def pizza(self): return Pizza(self.name) 创建具体建造者 class NYStyleCheeseBuilder(PizzaBuilder): name = 'NY Style Sauce and Cheese Pizza' def prepare_dough(self): self.progress = PIZZA_PROGRESS[0] self.pizza.prepare_dough('thin') def add_sauce(self): print('adding the tomato sauce to your pizza..') self.pizza.sauce = 'tomato' time.sleep(STEP_DELAY) print('done with the tomato sauce') def add_topping(self): print('adding the topping (grated reggiano cheese) to your pizza') self.pizza.toppings.append(["Grated", "Reggiano", "Cheese"]) time.sleep(STEP_DELAY) print('done with the topping (grated reggiano cheese)') def bake(self): self.progress = PIZZA_PROGRESS[1] print('baking your pizza for {} seconds'.format(self.baking_time)) time.sleep(self.baking_time) def cut(self): self.progress = PIZZA_PROGRESS[2] print("Cutting the pizza into diagonal slices") def box(self): self.progress = PIZZA_PROGRESS[3] print("Place pizza in official PizzaStore box") 创建指挥者 class Waiter: # 指挥者 def __init__(self): self.builder = None def construct_pizza(self, builder): self.builder = builder # 一旦我们有了一个 pizza,需要做一些准备(擀面皮、加佐料),然后烘烤、切片、装盒 [step() for step in (builder.prepare_dough, builder.add_sauce, builder.add_topping, builder.bake, builder.cut, builder.box)] @property def pizza(self): return self.builder.pizza 完整代码参考:python-design-patter-builder ...

2017-11-12 · 2 min · 266 words