Python语言及其应用(第2版)
Bill Lubanovic
门佳 译
出版时间:2022年04月
页数:488
本书通俗易懂,阅读起来饶有乐趣,十分适合想快速获得Python应用经验的新手。第2版带你从基础知识出发,逐步迈向更复杂、更多样化的主题,帮助你全面构建Python知识体系。书中将教程与代码示例相结合,清晰解释了Python 3的各种常用概念。每章配有练习和参考答案,帮助你学以致用。
你将通过本书打下坚实的Python基础,并牢固掌握测试、调试、代码重用等开发技巧。第2版加入了新的代码示例和练习,还向你展示了如何使用丰富的Python包在商业分析、数据库和机器学习等领域开发应用。
● 学习简单的数据类型、基础的数学运算和文本操作
● 将数据整理技术与内建数据结构结合使用
● 探索函数等Python代码结构
● 使用模块和包来编写大型Python程序
● 深入理解对象、类和其他面向对象特性
● 学习关系数据库和NoSQL等数据存储技术
● 构建Web客户端、服务器、API和服务
● 管理程序、进程和线程等系统任务
● 理解并发和网络编程的基础知识
  1. 前言
  2. 第一部分 Python基础
  3. 第1章 Python初探
  4. 1.1 谜题
  5. 1.2 小程序
  6. 1.3 大程序
  7. 1.4 现实世界中的Python
  8. 1.5 Python和其他语言
  9. 1.6 为什么选择Python
  10. 1.7 为什么不选择Python
  11. 1.8 Python 2和Python 3
  12. 1.9 安装Python
  13. 1.10 运行Python
  14. 1.10.1 使用交互式解释器
  15. 1.10.2 使用Python文件
  16. 1.10.3 下一步
  17. 1.11 禅意时刻
  18. 1.12 后续内容
  19. 1.13 练习
  20. 第2章 数据:类型、值、变量和名称
  21. 2.1 Python数据皆是对象
  22. 2.2 类型
  23. 2.3 可变性
  24. 2.4 字面值
  25. 2.5 变量
  26. 2.6 赋值
  27. 2.7 变量是名称,不是位置
  28. 2.8 向多个名称赋值
  29. 2.9 为名称重新赋值
  30. 2.10 复制
  31. 2.11 选个好变量名
  32. 2.12 后续内容
  33. 2.13 练习
  34. 第3章 数字
  35. 3.1 布尔值
  36. 3.2 整数
  37. 3.2.1 字面整数
  38. 3.2.2 整数运算符
  39. 3.2.3 整数和变量
  40. 3.2.4 优先级
  41. 3.2.5 基数
  42. 3.2.6 类型转换
  43. 3.2.7 int类型有多大
  44. 3.3 浮点数
  45. 3.4 数学函数
  46. 3.5 后续内容
  47. 3.6 练习
  48. 第4章 if语句
  49. 4.1 使用#注释
  50. 4.2 使用\续行
  51. 4.3 使用if、elif和else进行比较
  52. 4.4 True是什么
  53. 4.5 使用in进行多重比较
  54. 4.6 新秀:我是海象
  55. 4.7 后续内容
  56. 4.8 练习
  57. 第5章 字符串
  58. 5.1 使用引号创建字符串
  59. 5.2 使用str()创建字符串
  60. 5.3 使用\转义
  61. 5.4 使用+拼接
  62. 5.5 使用*重复
  63. 5.6 使用[]获取字符
  64. 5.7 使用分片提取子串
  65. 5.8 使用len()获取字符串长度
  66. 5.9 使用split()分割字符串
  67. 5.10 使用join()合并字符串
  68. 5.11 使用replace()进行替换
  69. 5.12 使用strip()进行剥离
  70. 5.13 搜索和选择
  71. 5.14 大小写
  72. 5.15 对齐
  73. 5.16 格式化
  74. 5.16.1 旧式样:%
  75. 5.16.2 新式样:{}和format()
  76. 5.16.3 最新式样:f字符串
  77. 5.17 未完待续
  78. 5.18 后续内容
  79. 5.19 练习
  80. 第6章 while循环和for循环
  81. 6.1 while循环
  82. 6.1.1 使用break跳出循环
  83. 6.1.2 使用continue跳过本次循环
  84. 6.1.3 使用else检查break
  85. 6.2 使用for和in进行迭代
  86. 6.2.1 使用break跳出循环
  87. 6.2.2 使用continue跳过本次循环
  88. 6.2.3 使用else检查break
  89. 6.2.4 使用range()生成数字序列
  90. 6.3 其他迭代器
  91. 6.4 后续内容
  92. 6.5 练习
  93. 第7章 元组和列表
  94. 7.1 元组
  95. 7.1.1 使用逗号和()创建元组
  96. 7.1.2 使用tuple()创建元组
  97. 7.1.3 使用+拼接元组
  98. 7.1.4 使用*复制元素
  99. 7.1.5 比较元组
  100. 7.1.6 使用for和in迭代元组
  101. 7.1.7 修改元组
  102. 7.2 列表
  103. 7.2.1 使用[]创建列表
  104. 7.2.2 使用list()创建或转换列表
  105. 7.2.3 使用split()从字符串创建列表
  106. 7.2.4 使用[offset]获取列表项
  107. 7.2.5 使用切片获取列表项
  108. 7.2.6 使用append()在末尾添加列表项
  109. 7.2.7 使用insert()按偏移插入列表项
  110. 7.2.8 使用*复制所有列表项
  111. 7.2.9 使用extend()或+拼接列表
  112. 7.2.10 使用[offset]修改列表项
  113. 7.2.11 使用切片修改列表项
  114. 7.2.12 使用del按偏移删除列表项
  115. 7.2.13 使用remove()按值删除列表项
  116. 7.2.14 使用pop()按偏移获取列表项并删除
  117. 7.2.15 使用clear()删除所有列表项
  118. 7.2.16 使用index()按值查找列表项的偏移
  119. 7.2.17 使用in测试值
  120. 7.2.18 使用count()统计某个值出现的次数
  121. 7.2.19 使用join()将列表转换为字符串
  122. 7.2.20 使用sort()或sorted()重新排序列表项
  123. 7.2.21 使用len()获得列表长度
  124. 7.2.22 使用=赋值
  125. 7.2.23 使用copy()、list()或切片复制列表
  126. 7.2.24 使用deepcopy()复制所有内容
  127. 7.2.25 比较列表
  128. 7.2.26 使用for和in迭代列表
  129. 7.2.27 使用zip()迭代多个序列
  130. 7.2.28 使用推导式创建列表
  131. 7.2.29 列表的列表
  132. 7.3 元组和列表
  133. 7.4 不存在元组推导式
  134. 7.5 后续内容
  135. 7.6 练习
  136. 第8章 字典和集合
  137. 8.1 字典
  138. 8.1.1 使用{}创建字典
  139. 8.1.2 使用dict()创建字典
  140. 8.1.3 使用dict()进行转换
  141. 8.1.4 使用[key]添加或修改字典项
  142. 8.1.5 使用[key]或get()获取字典项
  143. 8.1.6 使用keys()获取所有的键
  144. 8.1.7 使用values()获取所有值
  145. 8.1.8 使用items()获取所有“键–值”对
  146. 8.1.9 使用len()获取长度
  147. 8.1.10 使用{**a, **b}合并字典
  148. 8.1.11 使用update()合并字典
  149. 8.1.12 使用del按键删除字典项
  150. 8.1.13 使用pop()按键获取字典项并将其删除
  151. 8.1.14 使用clear()删除所有字典项
  152. 8.1.15 使用in测试键是否存在
  153. 8.1.16 使用=赋值
  154. 8.1.17 使用copy()复制
  155. 8.1.18 使用deepcopy()复制所有内容
  156. 8.1.19 比较字典
  157. 8.1.20 使用for和in迭代字典
  158. 8.1.21 字典推导式
  159. 8.2 集合
  160. 8.2.1 使用set()创建集合
  161. 8.2.2 使用set()进行转换
  162. 8.2.3 使用len()获取集合元素个数
  163. 8.2.4 使用add()添加元素
  164. 8.2.5 使用remove()删除元素
  165. 8.2.6 使用for和in迭代集合
  166. 8.2.7 使用in测试元素
  167. 8.2.8 集合运算
  168. 8.2.9 集合推导式
  169. 8.2.10 使用frozenset()创建不可变集合
  170. 8.3 目前为止学过的数据结构
  171. 8.4 创建更复杂的数据结构
  172. 8.5 后续内容
  173. 8.6 练习
  174. 第9章 函数
  175. 9.1 使用def定义函数
  176. 9.2 使用圆括号调用函数
  177. 9.3 实参与形参
  178. 9.3.1 有用的None
  179. 9.3.2 位置实参
  180. 9.3.3 关键字实参
  181. 9.3.4 指定默认形参值
  182. 9.3.5 使用*拆分/汇集位置实参
  183. 9.3.6 使用**拆分/汇集关键字实参
  184. 9.3.7 仅关键字形参
  185. 9.3.8 可变实参和不可变实参
  186. 9.4 文档字符串
  187. 9.5 作为“头等公民”的函数
  188. 9.6 内部函数
  189. 9.7 匿名函数:lambda
  190. 9.8 生成器
  191. 9.8.1 生成器函数
  192. 9.8.2 生成器推导式
  193. 9.9 装饰器
  194. 9.10 名称空间和作用域
  195. 9.11 名称中_和__的用法
  196. 9.12 递归
  197. 9.13 异步函数
  198. 9.14 异常
  199. 9.14.1 使用try和except处理错误
  200. 9.14.2 编写自己的异常
  201. 9.15 后续内容
  202. 9.16 练习
  203. 第10章 对象和类
  204. 10.1 什么是对象
  205. 10.2 简单对象
  206. 10.2.1 使用class定义类
  207. 10.2.2 特性
  208. 10.2.3 方法
  209. 10.2.4 初始化
  210. 10.3 继承
  211. 10.3.1 从父类继承
  212. 10.3.2 覆盖方法
  213. 10.3.3 添加方法
  214. 10.3.4 使用super()获得父类的帮助
  215. 10.3.5 多重继承
  216. 10.3.6 mixin
  217. 10.4 为self正名
  218. 10.5 特性访问
  219. 10.5.1 直接访问
  220. 10.5.2 getter和setter
  221. 10.5.3 用于特性访问的属性
  222. 10.5.4 计算值属性
  223. 10.5.5 使用名称重整保护内部隐私
  224. 10.5.6 类和对象特性
  225. 10.6 方法的类型
  226. 10.6.1 实例方法
  227. 10.6.2 类方法
  228. 10.6.3 静态方法
  229. 10.7 鸭子类型
  230. 10.8 魔术方法
  231. 10.9 聚合和组合
  232. 10.10 何时使用对象或其他东西
  233. 10.11 具名元组
  234. 10.12 数据类
  235. 10.13 attrs
  236. 10.14 后续内容
  237. 10.15 练习
  238. 第11章 模块、包和赠品
  239. 11.1 模块和import语句
  240. 11.1.1 导入模块
  241. 11.1.2 使用别名导入模块
  242. 11.1.3 导入部分模块
  243. 11.2 包
  244. 11.2.1 模块搜索路径
  245. 11.2.2 相对导入和绝对导入
  246. 11.2.3 名称空间包
  247. 11.2.4 模块和对象
  248. 11.3 Python标准库
  249. 11.3.1 使用setdefault()和defaultdict()处理缺失的键
  250. 11.3.2 使用Counter()计数
  251. 11.3.3 使用OrderedDict()按键排序
  252. 11.3.4 栈+队列=双端队列
  253. 11.3.5 使用itertools迭代代码结构
  254. 11.3.6 使用pprint()美化输出
  255. 11.3.7 获得随机数
  256. 11.4 获取其他Python代码
  257. 11.5 后续内容
  258. 11.6 练习
  259. 第二部分 Python实践
  260. 第12章 数据处理
  261. 12.1 文本字符串:Unicode
  262. 12.1.1 Python 3 Unicode字符串
  263. 12.1.2 UTF-8
  264. 12.1.3 编码
  265. 12.1.4 解码
  266. 12.1.5 HTML实体
  267. 12.1.6 归一化
  268. 12.1.7 更多信息
  269. 12.2 字符串:正则表达式
  270. 12.2.1 使用match()查找起始匹配
  271. 12.2.2 使用search()查找首次匹配
  272. 12.2.3 使用findall()查找所有匹配
  273. 12.2.4 使用split()在匹配处分割
  274. 12.2.5 使用sub()替换匹配
  275. 12.2.6 模式:特殊字符
  276. 12.2.7 模式:使用修饰符
  277. 12.2.8 模式:指定match()的输出
  278. 12.3 二进制数据
  279. 12.3.1 bytes和bytearray
  280. 12.3.2 使用struct转换二进制数据
  281. 12.3.3 其他二进制数据工具
  282. 12.3.4 使用binascii()转换二进制/字符串
  283. 12.3.5 位运算符
  284. 12.4 珠宝类比
  285. 12.5 后续内容
  286. 12.6 练习
  287. 第13章 日历和时钟
  288. 13.1 闰年
  289. 13.2 datetime模块
  290. 13.3 使用time模块
  291. 13.4 读写日期和时间
  292. 13.5 所有的转换
  293. 13.6 替代模块
  294. 13.7 后续内容
  295. 13.8 练习
  296. 第14章 文件和目录
  297. 14.1 文件输入和输出
  298. 14.1.1 使用open()创建或打开文件
  299. 14.1.2 使用print()写入文本文件
  300. 14.1.3 使用write()写入文本文件
  301. 14.1.4 使用read()、readline()或readlines()读取文本文件
  302. 14.1.5 使用write()写入二进制文件
  303. 14.1.6 使用read()读取二进制文件
  304. 14.1.7 使用with自动关闭文件
  305. 14.1.8 使用seek()改变文件位置
  306. 14.2 内存映射
  307. 14.3 文件操作
  308. 14.3.1 使用exists()检查文件是否存在
  309. 14.3.2 使用isfile()检查文件类型
  310. 14.3.3 使用copy()复制文件
  311. 14.3.4 使用rename()重命名
  312. 14.3.5 使用link()或symlink()创建链接
  313. 14.3.6 使用chmod()改变文件权限
  314. 14.3.7 使用chown()改变文件所属权
  315. 14.3.8 使用remove()删除文件
  316. 14.4 目录操作
  317. 14.4.1 使用mkdir()创建目录
  318. 14.4.2 使用rmdir()删除目录
  319. 14.4.3 使用listdir()列出目录内容
  320. 14.4.4 使用chdir()改变当前目录
  321. 14.4.5 使用glob()列出匹配文件
  322. 14.5 路径名
  323. 14.5.1 使用abspath()获取路径名
  324. 14.5.2 使用realpath()获取符号链接路径名
  325. 14.5.3 使用os.path.join()构建路径名
  326. 14.5.4 使用pathlib
  327. 14.6 BytesIO和StringIO
  328. 14.7 后续内容
  329. 14.8 练习
  330. 第15章 进程和并发
  331. 15.1 程序和进程
  332. 15.1.1 使用subprocess创建进程
  333. 15.1.2 使用multiprocessing创建进程
  334. 15.1.3 使用terminate()终止进程
  335. 15.1.4 使用os获得系统信息
  336. 15.1.5 使用psutil获取进程信息
  337. 15.2 命令自动化
  338. 15.2.1 invoke
  339. 15.2.2 其他命令助手
  340. 15.3 并发
  341. 15.3.1 队列
  342. 15.3.2 进程
  343. 15.3.3 线程
  344. 15.3.4 concurrent.futures
  345. 15.3.5 绿色线程和gevent
  346. 15.3.6 twisted
  347. 15.3.7 asyncio
  348. 15.3.8 Redis
  349. 15.3.9 队列之外的选择
  350. 15.4 后续内容
  351. 15.5 练习
  352. 第16章 持久性存储
  353. 16.1 平面文本文件
  354. 16.2 填充式文本文件
  355. 16.3 表格式文本文件
  356. 16.3.1 CSV
  357. 16.3.2 XML
  358. 16.3.3 XML安全提示
  359. 16.3.4 HTML
  360. 16.3.5 JSON
  361. 16.3.6 YAML
  362. 16.3.7 tablib
  363. 16.3.8 Pandas
  364. 16.3.9 配置文件
  365. 16.4 二进制文件
  366. 16.4.1 填充式二进制文件和内存映射
  367. 16.4.2 电子表格
  368. 16.4.3 HDF5
  369. 16.4.4 TileDB
  370. 16.5 关系数据库
  371. 16.5.1 SQL
  372. 16.5.2 DB-API
  373. 16.5.3 SQLite
  374. 16.5.4 MySQL
  375. 16.5.5 PostgreSQL
  376. 16.5.6 SQLAlchemy
  377. 16.5.7 其他数据库访问包
  378. 16.6 NoSQL数据存储
  379. 16.6.1 dbm家族
  380. 16.6.2 memcached
  381. 16.6.3 Redis
  382. 16.6.4 文档数据库
  383. 16.6.5 时间序列数据库
  384. 16.6.6 图数据库
  385. 16.6.7 其他NoSQL
  386. 16.7 全文数据库
  387. 16.8 后续内容
  388. 16.9 练习
  389. 第17章 网络
  390. 17.1 TCP/IP
  391. 17.1.1 套接字
  392. 17.1.2 Scapy
  393. 17.1.3 Netcat
  394. 17.2 联网模式
  395. 17.3 “请求–回应”模式
  396. 17.3.1 ZeroMQ
  397. 17.3.2 其他消息工具
  398. 17.4 “发布–订阅”模式
  399. 17.4.1 Redis
  400. 17.4.2 ZeroMQ
  401. 17.4.3 其他“发布–订阅”工具
  402. 17.5 互联网服务
  403. 17.5.1 域名系统
  404. 17.5.2 Python电子邮件模块
  405. 17.5.3 其他协议
  406. 17.6 Web服务和API
  407. 17.7 数据序列化
  408. 17.7.1 使用pickle进行序列化
  409. 17.7.2 其他序列化格式
  410. 17.8 远程过程调用
  411. 17.8.1 XML-RPC
  412. 17.8.2 JSON-RPC
  413. 17.8.3 MessagePack-RPC
  414. 17.8.4 zerorpc
  415. 17.8.5 gRPC
  416. 17.8.6 twirp
  417. 17.9 远程管理工具
  418. 17.10 大数据
  419. 17.10.1 Hadoop
  420. 17.10.2 Spark
  421. 17.10.3 Disco
  422. 17.10.4 Dask
  423. 17.11 云
  424. 17.11.1 Amazon Web服务
  425. 17.11.2 Google Cloud
  426. 17.11.3 Microsoft Azure
  427. 17.11.4 OpenStack
  428. 17.12 Docker
  429. 17.13 后续内容
  430. 17.14 练习
  431. 第18章 Web
  432. 18.1 Web客户端
  433. 18.1.1 使用telnet进行测试
  434. 18.1.2 使用curl进行测试
  435. 18.1.3 使用httpie进行测试
  436. 18.1.4 使用httpbin进行测试
  437. 18.1.5 Python的标准Web库
  438. 18.1.6 标准库之外的requests
  439. 18.2 Web服务器
  440. 18.2.1 最简单的Python Web服务器
  441. 18.2.2 Web服务器网关接口(WSGI)
  442. 18.2.3 ASGI
  443. 18.2.4 Apache
  444. 18.2.5 NGINX
  445. 18.2.6 其他Python Web服务器
  446. 18.3 Web服务器框架
  447. 18.3.1 Bottle
  448. 18.3.2 Flask
  449. 18.3.3 Django
  450. 18.3.4 其他框架
  451. 18.4 数据库框架
  452. 18.5 Web服务和自动化
  453. 18.5.1 webbrowser
  454. 18.5.2 webview
  455. 18.6 Web API和REST
  456. 18.7 爬取数据
  457. 18.7.1 Scrapy
  458. 18.7.2 BeautifulSoup
  459. 18.7.3 requests-html
  460. 18.8 看场电影吧
  461. 18.9 后续内容
  462. 18.10 练习
  463. 第19章 成为Python主义者
  464. 19.1 关于编程
  465. 19.2 寻找Python代码
  466. 19.3 安装Python包
  467. 19.3.1 使用pip
  468. 19.3.2 使用virtualenv
  469. 19.3.3 使用pipenv
  470. 19.3.4 使用包管理器
  471. 19.3.5 从源代码安装
  472. 19.4 集成化开发环境
  473. 19.4.1 IDLE
  474. 19.4.2 PyCharm
  475. 19.4.3 IPython
  476. 19.4.4 Jupyter Notebook
  477. 19.4.5 JupyterLab
  478. 19.5 名称和文档
  479. 19.6 添加类型提示
  480. 19.7 测试
  481. 19.7.1 使用pylint、pyflakes 和pep8检查代码
  482. 19.7.2 使用unittest进行测试
  483. 19.7.3 使用doctest进行测试
  484. 19.7.4 使用nose进行测试
  485. 19.7.5 其他测试框架
  486. 19.7.6 持续集成
  487. 19.8 调试Python代码
  488. 19.8.1 使用print()
  489. 19.8.2 使用装饰器
  490. 19.8.3 使用pdb
  491. 19.8.4 使用breakpoint()
  492. 19.9 记录错误消息
  493. 19.10 优化
  494. 19.10.1 测量时间
  495. 19.10.2 算法和数据结构
  496. 19.10.3 Cython、NumPy和C扩展
  497. 19.10.4 PyPy
  498. 19.10.5 Numba
  499. 19.11 源代码控制
  500. 19.11.1 Mercurial
  501. 19.11.2 Git
  502. 19.12 分发你的程序
  503. 19.13 克隆本书
  504. 19.14 更多内容
  505. 19.14.1 图书
  506. 19.14.2 网站
  507. 19.14.3 团体
  508. 19.14.4 会议
  509. 19.14.5 Python相关的工作机会
  510. 19.15 后续内容
  511. 19.16 练习
  512. 第20章 Python的艺术
  513. 20.1 2D图形
  514. 20.1.1 标准库
  515. 20.1.2 PIL和Pillow
  516. 20.1.3 ImageMagick
  517. 20.2 3D图形
  518. 20.3 3D动画
  519. 20.4 图形用户界面
  520. 20.5 绘图、图形和可视化
  521. 20.5.1 Matplotlib
  522. 20.5.2 Seaborn
  523. 20.5.3 Bokeh
  524. 20.6 游戏
  525. 20.7 音频和音乐
  526. 20.8 后续内容
  527. 20.9 练习
  528. 第21章 工作中的Python
  529. 21.1 Microsoft Office套件
  530. 21.2 执行企业任务
  531. 21.3 处理企业数据
  532. 21.3.1 提取、转换和装载
  533. 21.3.2 数据验证
  534. 21.3.3 其他信息源
  535. 21.4 开源Python企业包
  536. 21.5 金融中的Python
  537. 21.6 商业数据安全
  538. 21.7 地图
  539. 21.7.1 格式
  540. 21.7.2 根据shapefile绘制地图
  541. 21.7.3 Geopandas
  542. 21.7.4 其他绘图包
  543. 21.7.5 应用和数据
  544. 21.8 后续内容
  545. 21.9 练习
  546. 第22章 Python的科学
  547. 22.1 标准库中的数学和统计
  548. 22.1.1 数学函数
  549. 22.1.2 处理复数
  550. 22.1.3 使用decimal精确计算浮点数
  551. 22.1.4 使用fractions执行有理数运算
  552. 22.1.5 使用array创建压缩序列
  553. 22.1.6 使用statistics进行简单的统计
  554. 21.1.7 矩阵乘法
  555. 22.2 科学Python
  556. 22.3 NumPy
  557. 22.3.1 使用array()创建数组
  558. 22.3.2 使用arange()创建数组
  559. 22.3.3 使用zeros()、ones()和random()创建数组
  560. 22.3.4 使用reshape()改变数组的形状
  561. 22.3.5 使用[]获取元素
  562. 22.3.6 数组的算术运算
  563. 22.3.7 线性代数
  564. 22.4 SciPy
  565. 22.5 SciKit
  566. 22.6 Pandas
  567. 22.7 Python和各个科学领域
  568. 22.8 后续内容
  569. 22.9 练习
  570. 附录A 写给初级程序员的计算机软硬件知识
  571. 附录B 安装Python 3
  572. 附录C 截然不同的async
  573. 附录D 习题答案
  574. 附录E 速查表
书名:Python语言及其应用(第2版)
作者:Bill Lubanovic
译者:门佳 译
国内出版社:人民邮电出版社
出版时间:2022年04月
页数:488
书号:978-7-115-58622-3
原版书书名:Introducing Python, Second Edition
原版书出版商:O'Reilly Media
Bill Lubanovic
 
Bill Lubanovic在长达40余年的软件开发生涯中积累了丰富的实战经验,开发过的项目跨越数据库、Web、分布式系统等多个领域。他曾负责用Python重新实现流行工具Wayback Machine。
Bill Lubanovic started developing software with Unix in the 1970s, GUIs in the 1980s, and the Web in the 1990s. He now does web visualization work for a wind energy company.