markdown中code生成图片的思路

最近在头条上写东西,遇到了一个比较烦的事情—编辑器不支持代码。这对于一个像我这样使用代码凑字数的人来说实在不是一个好的消息。但是等头条改进编辑器太遥远了,只能自己自足实现一个替代方案了–把代码替换成图片。 一段代码的时候,我随手截图,简单完成了; 两段代码的时候,我随手随手截图,也完成了; 三段代码的时候,我随手随手随手截图,强忍着完成了; 等我发现代码越来越多的时候,不能忍了。 懒惰是程序员的美德,不能再花费时间干这些事情了。我觉得要写个程序,把markdown 中的代码自动生成图片。 考虑了一下,大概需要做的工作是: 把markdown 中 “ ” 包换的代码提取出来(也可以使用工具先把markdown 转换成html 再解析html 取出code 把每一段code 分别生成图片 把图片对应的代码替换掉 想想还是很简单的。那就开始吧。 但是到第二步的时候遇到了问题,code 如何生成图片,生成什么样的图片? 首先code 需要保持原有的样式,如果能高亮那就更好了(嗯,高亮 生成图片的时候是把code 作为文字使用PIL(我使用python)写在背景上么,图片大小是多少,高亮怎么实现 算了,还是先把code 生成html,然后截取html页面吧。(这样html 还能使用 highlight.js 来实现高亮) 如何动态生成包含code 的html 页面呢? 如何把截取html 页面呢? 动态生成包含code 的html 页面有两个思路: 使用post 请求,把code 写入数据库(或者文件),然后返回id,再使用id GET 请求获取页面(需要存储,两次请求) 压缩code,把code 作为url参数,使用GET请求获取页面(可能会造成url太长的错误) 那如何截取html呢? 如果是python,可以使用pyqt,渲染html页面,截取webview。 如果使用node,可以使用 html2canvas。 大致流程如下: 哎,这一篇没有代码,就凑不了多少字。 最后,感谢女朋友支持和包容,比❤️ 也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程 内推时间

2019-06-13 · 1 min · 59 words

DynamoDB 最佳实践

面向DynamoDB的NoSQL设计 关系数据库设计和NoSQL之间的差异 关系型数据库可以灵活的查询数据,但是成本较高,高流量无法扩展 RDBMS 设计灵活,可以随时修改 NoSQL查询方式有限 需要对架构进行专门设计,以尽可能的加快查询速度。数据结构和需求高度相关,需要特制。 NoSQL设计的两个关键概念 需要先了解业务问题和应用程序的使用案例,然后再开始设计 应保留尽可能少的表。 了解NoSQL设计 三个基本属性 数据大小 了解一次存储和请求的数据量将有助于确定对数据进行分区的最有效方法。 数据形状 NoSQL 数据库不会在处理查询时重塑数据(如 RDBMS 系统所做的一样),而是整理数据以便数据在数据库中的形状与查询内容对应。这是加快速度并增强可扩展性的一个关键因素 数据速度 DynamoDB 通过增加可用于处理查询的物理分区的数量并通过跨这些分区有效分发数据来进行扩展。预先了解峰值查询负载可能有助于确定数据分区方式,从而最高效地使用 I/O 容量。 性能的一般准则 将相关数据放在一起 将相关数据集中放置到一个位置。将相关数据保留在最近位置会对成本和性能产生重大影响。 不是跨多个表分发相关数据项目,而是在 NoSQL 系统中尽可能紧密地保留相关项目。 作为一般规则,应在 DynamoDB 应用程序中保留尽可能少的表。 只需要一个表, 例外是涉及大量时间序列数据的情况或具有明显不同的访问模式的数据集 — 但这些都是例外。具有反向索引的单个表通常可启用简单查询来创建和检索应用程序所需的复杂层次数据结构。 使用排序顺序 可将相关项目组织起来并进行有效查询,前提是它们的键设计可促使它们一起排序 分发查询 您应该设计数据键以跨尽可能多的分区均匀分发流量,从而避免“热点”。 使用全局二级索引 通过创建特定的全局二级索引,可启用主表支持的查询以外的查询 设计并高效使用分区键的最佳实践 项目主键可以是仅分区键 也可以是分区键+排序键 高效使用突增容量 DynamoDB 当前可将未使用的读取和写入容量保留最多五分钟 (300 秒) 当读取或写入突增导致容量不足时使用。 DynamoDB适应性容量 DynamoDB 适应性容量 允许您的应用程序继续不受限地对热分区进行读写操作,前提是流量未超出表的配置的总容量或分区最大容量。自适应容量的工作原理是,自动增加分区的吞吐量容量来接收更多流量 示例表配置了 400 个写入容量单位 (WCU),这些容量单位均匀分布在 4 个分区中,每个分区每秒可以接收最多 100 个 WCU。分区 1、2 和 3 每秒接收的写入流量为 50 个 WCU。分区 4 每秒接收 150 个 WCU。此热分区可以在接受写入流量的同时仍具有未利用的突增容量,但是,它最终会限制每秒超过 100 个 WCU 的流量。 ...

2019-06-10 · 3 min · 538 words

每周分享第1期-常用表情包收录

每周分享,分享看到的一些有意思的文章和项目 1 中国表情包大集合 https://zhaoolee.github.io/ChineseBQB/ 表情包目录(共收录2298张表情包)Emoticon package directory (commonly included 2298 emoticon pack) 2 谷歌产品替代品 一批谷歌产品的替代方案 https://nomoregoogle.com/ 3 Redis作者:开源维护者的挣扎和无奈 英文作者是著名开源项目 Redis 的开发者 antirez。截至 2019-06-04 为止,Redis 项目在 GitHub 将近有 37,000 Star,Fork 数达 14000。 https://mp.weixin.qq.com/s/6C7-4Fp46rxfn0J34ebKyg 4 一个分享 GitHub 上 有趣、入门级的开源项目网站 https://hellogithub.com 5 Saber - 新一代静态网站生成系统 https://saber.land 6 star-history: 帮助用户查看 github 项目 star 数目的历史 star-history 帮助用户查看 github 项目 star 数目的历史, 判断项目发展情况,项目地址:https://github.com/timqian/star-history 7 一个分类收集 GitHub 开源项目的网站,并对项目的热度和活跃度进行分析 项目地址:https://www.ctolib.com/ 8 git 提交信息规范检测工具 git-commit-msg-linter 项目地址:https://www.npmjs.com/package/git-commit-msg-linter 9 编码一时爽,重写火葬场?这些公司都重写了软件,结局却不同。 生存,还是死亡,这是一个问题。重写,还是不重写,这是导致生存或死亡的另一个问题。 https://mp.weixin.qq.com/s/SqxPoIDPuvKlrlqZmubpXA ...

2019-06-07 · 1 min · 78 words

为什么json 不能使用 int64类型

json 简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。 JSON支持两种数据结构存在: 对象(object):一个对象包含一系列非排序的名称/值对(pair),一个对象以{开始,并以}结束。每个名称/值对之间使用 : 分割。 数组 (array):一个数组是一个值(value)的集合,一个数组以 [ 开始,并以]结束。数组成员之间使用 , 分割。 具体的格式如下: [value1, value2, value3] 名称/值(pair):名称和值之间使用 : 隔开,格式如下: {name:value} 名称必须是字符串类型; 值(value)必须是可以是字符串(string),数值(number),对象(object),有序列表(array),或者 false, null, true 的其中一种。 JSON的格式描述可以参考RFC 4627。 为什么JSON不支持 int64 类型? 通过上面的介绍有两个关键点: JSON 是基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集 JSON 支持number 类型 Javascript的数字存储使用了IEEE 754中规定的双精度浮点数数据类型,而这一数据类型能够安全存储 -(2^53-1) 到 2^53-1 之间的数值(包含边界值)。JSON 是Javascript 的一个子集,所以它也遵守这个规则。 ...

2019-06-03 · 1 min · 191 words

PostgreSQL jsonb 使用入门

json 类型 说明 根据RFC 7159中的说明,JSON 数据类型是用来存储 JSON(JavaScript Object Notation)数据的。这种数据也可以被存储为text,但是 JSON 数据类型的优势在于能强制要求每个被存储的值符合 JSON 规则。也有很多 JSON 相关的函数和操作符可以用于存储在这些数据类型中的数据 PostgreSQL支持两种 JSON 数据类型:json 和 jsonb。它们几乎接受完全相同的值集合作为输入。两者最大的区别是效率。json数据类型存储输入文本的精准拷贝,处理函数必须在每 次执行时必须重新解析该数据。而jsonb数据被存储在一种分解好的二进制格式中,因为需要做附加的转换,它在输入时要稍慢一些。但是 jsonb在处理时要快很多,因为不需要重新解析。 重点:jsonb支持索引 由于json类型存储的是输入文本的准确拷贝,存储时会空格和JSON 对象内部的键的顺序。如果一个值中的 JSON 对象包含同一个键超过一次,所有的键/值对都会被保留(** 处理函数会把最后的值当作有效值**)。 jsonb不保留空格、不保留对象键的顺序并且不保留重复的对象键。如果在输入中指定了重复的键,只有最后一个值会被保留。 推荐把JSON 数据存储为jsonb 在把文本 JSON 输入转换成jsonb时,JSON的基本类型(RFC 7159 )会被映射到原生的 PostgreSQL类型。因此,jsonb数据有一些次要额外约束。 比如:jsonb将拒绝除 PostgreSQL numeric数据类型范围之外的数字,而json则不会。 JSON 基本类型和相应的PostgreSQL类型 JSON 基本类型 PostgreSQL类型 注释 string text 不允许\u0000,如果数据库编码不是 UTF8,非 ASCII Unicode 转义也是这样 number numeric 不允许NaN 和 infinity值 boolean boolean 只接受小写true和false拼写 null (无) SQL NULL是一个不同的概念 json 输入输出语法 -- 简单标量/基本值 -- 基本值可以是数字、带引号的字符串、true、false或者null SELECT '5'::json; -- 有零个或者更多元素的数组(元素不需要为同一类型) SELECT '[1, 2, "foo", null]'::json; -- 包含键值对的对象 -- 注意对象键必须总是带引号的字符串 SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json; -- 数组和对象可以被任意嵌套 SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json; -- "->" 通过键获得 JSON 对象域 结果为json对象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 通过键获得 JSON 对象域 结果为text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->>'nickname' as nickname; nickname ----------- goodspeed -- "->" 通过键获得 JSON 对象域 结果为json对象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 通过键获得 JSON 对象域 结果为text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->>'nickname' as nickname; nickname ----------- goodspeed 当一个 JSON 值被输入并且接着不做任何附加处理就输出时, json会输出和输入完全相同的文本,而jsonb 则不会保留语义上没有意义的细节 ...

2019-05-30 · 10 min · 1984 words

创建高质量的代码--软件构建中的设计

《代码大全》读书笔记 太长不看版 软件构建中的设计 软件设计是一项明确的活动 设计中的挑战 软件设计一词意味着去构思、创造或发明一套方案,把一份计算机软件的规格说明书要求转变为可实际运行的软件。 设计就是把需求分析和编码调试连接在一起的活动。 好的高层词设计能够提供一个可以稳妥容纳多个较低层次设计的结构。 设计是一个险恶的问题 险恶(wicked)的问题就是那种只有通过解决或部分解决才能被明确的问题。 Tacoma Narrows 大桥是一个险恶问题的好例子,因为直到这座桥坍塌,工程师才知道不应该只考虑桥的负荷,还需要充分的考虑空气动力学因素(只有建造大桥,才能从中学到需要考虑额外的环节)。 设计是一个了无章法的过程(即使它能得处清爽的成果) 是因为在设计的过程中可能会采用很多错误的步骤,多次出错 因为设计的优劣差异往往非常微妙 因为不能判断设计是否足够好 设计就是确定取舍和调整顺序的过程 现实世界中,设计者工作的一个关键内容就是衡量彼此冲突的各项设计特性,并尽力在其中寻求平衡。响应速度优先和开发时间短优先得出的设计结果可能是不同的。 设计受到诸多限制 设计的要点一部分是在创造可能发生的事情,另一部分是在限制可能发生的事情。 如果一个人有无限空间和资源来建造房子,可能会建造出无法控制的建筑。正是因为有了限制,才得出了简单的结果。软件设计也是一样。 设计是不确定的 每个人设计的结果可能是不同的,并且可能用起来都不错。设计没有标准答案。 设计是一个启发式的过程 设计过程中充满了不确定性,因此设计技术也趋于具有探索性–“经验法则”或者“试试没准能行”–而不是保证能产生预期结果的可重复的过程。 设计是自然而然形成的 设计不是在谁的头脑中直接跳出来的,它是在不断的设计评估、非正式讨论、写试经验以及修改试验代码中演化和完善的。 关键的设计概念 软件的首要技术使命:管理复杂度 本质的难题和偶然的难题 偶然的难题可以理解为bug,编程语言笨拙的语法,等易于发现容易解决的问题。 本质的难题则比较复杂,本质上说,软件开发就是不断去发掘错综复杂,相互关连的整套概念的所有细节。本质困难就是: 要面对复杂、无序的现实世界; 精确而完成的识别出各种依赖关系和外部情况 设计出完全正确而不是大致正确的解决方案 。。。 管理复杂度的重要性 一个失败的项目如果是由于技术原因而失败,通常都是因为软件复杂度失控了。如果复杂度失控,那么软件就会变得极端复杂,没有人知道它能做什么,它出了问题如何解决。 管理复杂度是软件开发中最为重要的技术话题。 在软件架构层次上,可以通过把大的系统分解为多个子系统来降低问题的复杂度,多个简单的问题比一个复杂的大问题更容易理解。 子系统相互间应该减少依赖; 子系统的关注点应该是相互分离的。 如何应对复杂度 高代价、低效率的设计源于下面三种根源: 用复杂的方法解决简单的问题 用简单但错误的方法解决复杂的问题 用不恰当的复杂的方法解决复杂的问题 用下面的方法管理复杂度 把任何人在同一时间需要处理的本质复杂度降到最低 不要让偶然性的复杂度无谓的增长 理想的设计特征 最小的复杂度 易于维护 松散耦合 可扩展性 可重用性 高扇入:让大量的类使用某个给定的类。(意味着设计出的系统很好的利用了在较低层次上的工具类 低扇出:让一个类少量或始终的使用其他类。(高扇出(7个)意味着一个类过多的使用了其他类,可能会变得过于复杂 可移植性 精简性:没有多余的部分 层次性:比如一个新系统会用到很多设计不佳的旧系统,这时就应该为新系统编写一个负责同就代码交互的层(代理模式) 层次性能把低劣的代码紧闭起来 如果能最终抛弃或重构旧代码,旧不必修改处交互层之外的任何新代码。 标准技术:用到的外来的、古怪的东西越多,也越难理解。 设计的层次 1. 软件系统(Software System) 2. 分解为子系统或包(Division into Subsystems or Packages) 这一层的主要目的是确定如何把程序分为主要的子系统,并定义清楚允许各子系统如何使用其他子系统。 ...

2019-05-29 · 2 min · 305 words

并发模型:线程与锁

并发&并行 并发程序含有多个逻辑上的独立执行块,他们可以独立的并行执行,也可以串行执行。 并行程序解决问题的速度比串行程序快的多,因为其可以同时执行整个任务的多个部分。并行程序可能有多个独立执行块,也可能只有一个。 引用Rob Pike的经典描述就是: 并发是同一时间应对多件事情的能力; 并行是同一时间动手做多件事情的能力。 常见的并发模型有: 线程与锁 函数式编程 actor模型和通信顺序是进行(Communicating Sequential Processes, CSP) 数据级并行 lambda 架构 分离标识与状态模型 这篇主要介绍线程与锁模型 线程与锁模型 线程与锁模型是对底层硬件运行过程的形式化,非常简单直接,几乎所有的编程语言都对其提供了支持,且不对其使用方法加以限制(易出错)。 这篇文章主要使用python语言来演示线程与锁模型。文章结构来自《七周七并发模型》 互斥和内存模型 创建线程 from threading import Thread def hello_world(): print("Hello from new thread") def main(): my_thread = Thread(target=hello_world) my_thread.start() print("Hello from main thread") my_thread.join() main() 这段代码创建并启动了一个Thread实例,首先从start() 开始,my_thread.start() main()函数的余下部分一起并发执行。最后调用join() 来等待my_thread线程结束。 运行这段代码输出结果有几种: Hello from new thread Hello from main thread 或者 Hello from main thread Hello from new thread 或者 ...

2019-05-19 · 6 min · 1178 words

JWT RefreshToken 实践

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。 详细介绍可以查看这篇文章 理解JWT(JSON Web Token)认证及实践 JWT 特点 优点 体积小,因而传输速度快 传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输 严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。 支持跨域验证,可以应用于单点登录。 存在的问题 JWT 自身(在 payload 中)就包含了所有与用户相关的验证消息,所以通常情况下不需要保存。这种设计存在几个问题: Token不能撤销–客户端重置密码后之前的JWT依然可以使用(JWT 并没有过期或者失效 不支持refresh token,JWT过期后需要执行登录授权的完整流程 无法知道用户签发了几个JWT 针对第一个问题,可能的解决方法有: 保存JWT到数据库(或Redis),这样可以针对每个JWT单独校验 在重置密码等需要作废之前全部JWT时,把操作时间点记录到数据库(或Redis),校验JWT时同时判断此JWT创建之后有没有过重置密码等类似操作,如果有校验不通过 当然,这种解决方法都会多一次数据库请求,JWT自身可校验的优势会有所减少,同时也会影响认证效率。 这篇文章主要介绍解决第二个问题(不支持refresh token)的思路。 refresh token refresh token是OAuth2 认证中的一个概念,和OAuth2 的access token 一起生成,表示更新令牌,过期所需时间比access toen 要长,可以用来获取下一次的access token。 如果JWT 需要添加 refresh token支持,refresh token需要满足的条件有一下几项: 和JWT一起生成返回给客户端 有实效时间,有效时间比JWT要长 只能用来换取下一次JWT,不能用于访问认证 不能重复使用(可选) refresh token 获取流程 refresh token 使用流程 代码示例 import jwt import time # 使用 sanic 作为restful api 框架 def create_token(account_id, username): payload = { "iss": "gusibi.mobi", "iat": int(time.time()), "exp": int(time.time()) + 86400 * 7, "aud": "www.gusibi.mobi", "sub": account_id, "username": username, "scopes": ['open'] } token = jwt.encode(payload, 'secret', algorithm='HS256') payload['grant_type'] = "refresh" refresh_token = jwt.encode(payload, 'secret', algorithm='HS256') return True, { 'access_token': token, 'account_id': account_id, "refresh_token": refresh_token } # 验证refresh token 出否有效 def verify_refresh_token(token): payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及是否是refresh token,验证通过后生成新的token 以及 refresh_token if payload and payload.get('grant_type') == 'refresh': # 如果需要标记此token 已经使用,需要借助redis 或者数据库(推荐redis) return True, payload return False, None # 验证token 是否有效 def verify_bearer_token(token): # 如果在生成token的时候使用了aud参数,那么校验的时候也需要添加此参数 payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及不能是refresh token if payload and not payload.get('grant_type') == 'refresh': return True, payload return False, None 参考链接 理解JWT(JSON Web Token)认证及实践 理解OAuth 2.0[1] References [1] 理解OAuth 2.0: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html ...

2019-04-29 · 1 min · 204 words

Golang mod 入门

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为: 模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。 如何使用 Modules ? 把 golang 升级到 1.11(现在1.12 已经发布了,建议使用1.12) 设置 GO111MODULE GO111MODULE GO111MODULE 有三个值:off, on和auto(默认值)。 GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。 GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。 GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形: 当前目录在GOPATH/src之外且该目录包含go.mod文件 当前文件在包含go.mod文件的目录下面。 当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。 go mod golang 提供了 go mod命令来管理包。 go mod 有以下命令: 命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖) 如何在项目中使用 示例一:创建一个新项目 在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件 ➜ ~ mkdir hello ➜ ~ cd hello ➜ hello go mod init hello go: creating new go.mod: module hello ➜ hello ls go.mod ➜ hello cat go.mod module hello go 1.12 go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。 ...

2019-03-16 · 4 min · 689 words

python设计模式-状态模式

问题:有一个糖果公司需要设计一个糖果售卖机,控制流程如下图,需要怎么实现? 这是一个状态图,每个圆圈都是一种状态。很明显,有有25分钱、 没有25分钱、 售出糖果、 糖果售罄四个状态,同时也对应四个动作:投入25分钱,退回25分钱,转动曲柄和发放糖果。 那如何从状态图得到真正的代码呢? 简单代码实现如下: #! -*- coding: utf-8 -*- class GumballMachine: # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值 STATE_SOLD_OUT = 0 STATE_NO_QUARTER = 1 STATE_HAS_QUARTER = 2 STATE_SOLD = 3 state = STATE_SOLD_OUT def __init__(self, count=0): self.count = count if count > 0: self.state = self.STATE_NO_QUARTER def __str__(self): return "Gumball machine current state: %s" % self.state def insert_quarter(self): # 投入25分钱 if self.state == self.STATE_HAS_QUARTER: # 如果已经投过 print("You can't insert another quarter") elif self.state == self.STATE_NO_QUARTER: # 如果没有投过 self.state = self.STATE_HAS_QUARTER print("You inserted a quarter") elif self.state == self.STATE_SOLD_OUT: # 如果已经售罄 print("You can't insert a quarter, the machine is sold out") elif self.state == self.STATE_SOLD: # 如果刚刚买了糖果 print("Please wait, we're already giving you a gumball") def eject_quarter(self): # 退回25分 if self.state == self.STATE_HAS_QUARTER: print("Quarter returned") self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print("You haven't inserted a quarter") elif self.state == self.STATE_SOLD: print("Sorry, you alread turned the crank") elif self.state == self.SOLD_OUT: print("You can't eject, you haven't inserted") def turn_crank(self): # 转动曲柄 if self.state == self.STATE_SOLD: print("Turning twice doesn't get you another gumball") elif self.state == self.STATE_NO_QUARTER: print("You turned but there's no quarter") elif self.state == self.STATE_SOLD_OUT: print("You turned, but there are no gumballs") elif self.state == self.STATE_HAS_QUARTER: print("You turned...") self.state = self.STATE_SOLD self.dispense() def dispense(self): # 发放糖果 if self.state == self.STATE_SOLD: print("A gumball comes rolling out the slot") self.count -= 1 if self.count == 0: self.state = self.STATE_SOLD_OUT else: self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print("You need to pay first") elif self.state == self.STATE_SOLD_OUT: print("No gumball dispensed") elif self.state == self.STATE_HAS_QUARTER: print("No gumball dispensed") if __name__ == "__main__": # 以下是代码测试 gumball_machine = GumballMachine(5) # 装入5 个糖果 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() #投入25分钱 gumball_machine.eject_quarter() # 退钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.eject_quarter() # 退钱 print(gumball_machine) 这段代码有几个问题: ...

2018-12-31 · 4 min · 802 words