如下内容是学习《Head First 设计模式》第二部分《观察者模式》所得,主要就是一些原文的摘抄和少量自己的总结。 观察者模式定义 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。 OO 原则 为交互对象之间的松耦合设计而努力。 吐槽 作者直接使用 Java 标准库里的 Observer 和 Observable 等东东来讲,虽然阅读起来无障碍……但是。好吧,也没啥好但是的。 书中示例的 C++ 实现版源码 这个 Solution 的 ObserverPattern 工程里。 书中示例的类图 Visio 原图见『这里』:
如下内容是学习《Head First 设计模式》第一部分《设计模式入门》所得,主要就是一些原文的摘抄和少量自己的总结。 OO 原则是我们的目标,设计模式是我们的做法。 OO 原则 封装变化。 多用组合,少用继承。 针对接口编程,不针对实现编程。 在这一部分,书中通过讲解对一个简单 SimUDuck 根据新需求的重新设计,不断优化过程中,顺理成章地将策略模式讲解清楚了,策略模式「严肃」定义: 策略模式定义了算法族,分别封装起来让它们之间可以相互替换,此模式让算法的变化独立于使用算法的用户。 这个模式的存在以前上面的设计原则第一条直接刷新了我之前对 OO 设计的一个认识: 抽象出不变的作为基类,将不同的特化到子类中去。 这个认识本身也没错,但是显然需要结合封装变化的设计原则使用才能适用更多场景,设计出弹性更好的系统来。 书中的示例代码都是使用 java,我用 C++ 将策略模式示例重新实现了一下,源码放在这个 Solution 的 StrategyPattern 工程里。 UML 类图如下,Visio 原图见『这里』:
有如下三个文件 header.h #pragma once namespace NS1 { static int var = 10; } void print_var(); src.cpp #include <stdio.h> #include "header.h" void print_var() { printf("%d\n", NS1::var); } main.cpp #include <stdio.h> #include "header.h" int main() { printf("%d\n", NS1::var); NS1::var = 0; print_var(); return 0; } 这三个文件是尝试的基础。 Situation A: 将 header.h 里的 var 的 static 去掉,发现编译通过,但是链接时提示: main.obj : error LNK2005: "int NS1::var" (?var@NS1@@3HA) 已经在 src.obj 中定义,fatal error LNK1169: 找到一个或多个多重定义的符号。 Situation B: 还原 static,编译通过,运行生成的 EXE,输出:10 10 与预期的10 0不符。 根据 static 对变量的作用域的影响,推断应该是预编译过程中 NS1::var 分别被引入了 src.cpp 和 main.cpp,而在两个源文件中的 NS1::var 非同一个变量,而且其作用域分别为各自所在的 cpp 文件。 求证: 将两个 cpp 文件中的 printf 语句都改为 printf("%d address is : 0x%X\n", NS1::var, &NS1::var); 输出为: 10 address is : 0x3C8004 10 address is : 0x3C8000 可见两个源文件中的 var 非同一个。
case 两个工程 Proj1 和 Proj2,同时包含 demo.cpp,其中 Proj1 在工程配置里预定义宏 MACRO_PROJ1,Proj2 在工程配置里预定义宏 MACRO_PROJ2,两个工程的中间文件输出目录为同一个,文件 demo.cpp 内容如下: #include <stdio.h> int main() { #ifdef MACRO_PROJ1 printf("output by proj1"); #elif defined MACRO_PROJ2 printf("output by proj2"); #endif return 0; } 然后编译两工程生成 Proj1.exe 和 Proj2.exe,期望的结果是 Proj1.exe 输出output by proj1,Proj2 输出output by proj2,但是……意外发生了: 会发现一定的概率下,两个 exe 输出的内容相同,至于是output by proj1还是output by proj2则比较随机。 analysis 在出问题的情况下,既然 Proj1.exe 和 Proj2.exe 输出一致,那么可以推测生成两个 exe 的源中间文件 demo.obj 是一样的,明明在两个工程里根据宏定义,预编译过后的源代码是不一样的,怎么会出现生成的 obj 文件一样的情况呢?联想到编译器的「懒惰」特性,推测出发生问题的情况如下: 假设首先编译 Proj1,那么预编译过后,源文件里生效的应该是printf("output by proj1");这一行,生成 demo.obj,然后链接生成 Proj1.exe;然后在编译 Proj2 时,编译器会先对比 demo.cpp 和 demo.obj 的时间戳,发现 demo.obj 的修改时间比较新,那么就不用重新编译,就将之前生成的 demo.obj 直接用于链接生成了 Proj2.exe。 confirmation 更改 Proj1 与 Proj2 两个工程的中间文件输出目录为两个不同的目录,问题不再发生。 Done!
问题描述 在 Windows Vista+ 系统下,若 EXE 文件名中包含有「install」、「update」或「setup」等字样,可能出现如下问题: 每次软件运行完退出后会弹出「程序兼容性助手」(Program Compatibility Assistant, 简称 PCA),提示软件未正确安装。 在 Vista+ 的操作系统下任务栏右键该程序缺少「将此程序锁定到任务栏」和软件名同名项。 程序名 运行时任务栏右键 a.exe setup.exe 你的程序没打算要求管理员权限的,但是运行的时候却弹 UAC 了。 完全相同的两个 EXE 文件,名字不一样: 问题分析 简而言之,上述现象发生的原因是 Windows Vista+ 系统的「安装程序检测」机制认为文件名中包含「install」、「update」或「setup」等字样,且没有在 Manifest 文件中显式指定 requestedExecutionLevel 的 32 位可执行程序是安装包,会主动为安装包弹出 UAC 提权申请,而「程序兼容性助手」会监控安装包的执行情况,如果它没有在「添加或删除程序」中创建一个条目,那「程序兼容性助手」会认为该安装包没有成功完成,在安装包结束后即弹出「程序兼容性助手」提示用户该程序可能安装不正确。 MSDN 关于「程序兼容性助手」的相关问答: What is the detection logic, and how does PCA know that the setup failed due to version problems? PCA does not specifically look for the setup’s failing due to version problems. The logic used by PCA is to detect if a setup did not complete successfully. It monitors a program detected as setup by Windows Vista and Windows Server 2008 and checks whether the program registers an entry in Add or Remove Programs (ARP). If no entries are created in ARP, PCA concludes that the installer did not complete successfully. It will then wait for the install program to terminate before displaying the UI. If it is an uninstaller, the detection looks for whether an entry was deleted from ARP. How does PCA get information about the setup programs? PCA relies on the User Account Control (UAC) feature in Windows Vista and Windows Server 2008 to know whether a program is a setup program. UAC includes detection for setup programs and will make sure the detected setup programs will be run as Administrator, which includes getting administrative credentials or confirmation from the user before launching the program. 原文链接:Application Compatibility: Program Compatibility Assistant (PCA) MSDN 关于「安装程序检测」的相关介绍: Installer Detection only applies to: 32 bit executables Applications without a requestedExecutionLevel Interactive processes running as a Standard User with UAC enabled Before a 32 bit process is created, the following attributes are checked to determine whether it is an installer: Filename includes keywords like “install,” “setup,” “update,” etc. Keywords in the following Versioning Resource fields: Vendor, Company Name, Product Name, File Description, Original Filename, Internal Name, and Export Name Keywords in the side-by-side application manifest embedded in the executable Keywords in specific StringTable entries linked in the executable Key attributes in the resource file data linked in the executable Targeted sequences of bytes within the executable 原文链接:New UAC Technologies for Windows Vista 另外,微软的一个 PPT 介绍了「安装程序检测」和它可能产生的误判,以及解决的办法,给出的方案是内嵌 Manifest 或者外置一个名为「MyApp.exe.manifest」的文件: Installer Detection - Microsoft 参考: Application Manifest Application Compatibility: UAC: Standard User Changes 解决方案 问题 1: 如下三项任选其一: 一、给程序改个名字,不要包含「install」、「update」和「setup」字样。 二、为可执行文件添加类似如下的 Manifest 文件,指定程序兼容 Win7 与 Vista(或更高版本的当前系统)。 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!--The ID below indicates application support for Windows Vista --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!--The ID below indicates application support for Windows 7 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> </application> </compatibility> </assembly> 三、程序运行时在注册表项 HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Compatibility Assistant\Persisted 下写入以可执行文件全路径为名,值为 REG_DWORD 类型的 1 的项。 问题 2 和 3: 这是 Windows Vista+ 系统对「安装包」的「特殊待遇」,如果你正在做安装包,那你应该不在乎这些;如果你正在做的不是「安装包」,那么将程序改名吧!去掉 install,去掉 update,去掉 setup,世界从此清净了。 结论 如果你正在做的是安装包,那么遵循 Windows Vista+ 系统对安装包的一致规范,主动要求以管理员权限执行,并在安装任务成功完成后在「添加或删除程序」里添加新的条目。 如果不是在做安装包,那么将程序改名能避免 Windows Vista+ 系统的误判导致的问题。
禁止 Win7 下窗口被拖到顶部时自动飘到左上角 去掉窗口的 WS_THICKFRAME 风格 将最小化或者不是在最前面的窗口激活到前面 SwitchToThisWindow 禁止双击标题栏最大化窗口 屏蔽 WM_NCLBUTTONDBLCLK 消息 禁止 XP 下右键任务栏的「最大化」菜单项 去掉窗口的 WS_MAXIMIZEBOX 风格 在任务管理器下「应用程序」标签栏不显示程序,但是在「进程」里显示进程 一个可能的原因是窗口标题为空 在 XP 任务栏右键无菜单,在 Win7 任务栏右键只有一个关闭项 给窗口添加 WS_SYSMENU 风格 程序的系统托盘图标右键菜单不消失 在menu.TrackPopupMenu前调用SetForegroundWindow(m_hWnd) 子窗口显示 / 不显示任务栏图标 通过SetWindowLong修改窗口的扩展风格,WS_EX_APPWINDOW 是显示,WS_EX_TOOLWINDOW 是不显示。
Mozilla 自带的 Profile 支持实际上是非常给力的,看看 %appdata%/Mozilla/Firefox 下的文件就能知道,这里存放着许多的信息,它们提供了很多东西,例如: 可以通过 Profiles/…default/chrome 下的 userchrome.css 文件来定制自己的 firefox 外观,比如用 #appmenu-button { display:none !important;} 这样一句就可以去掉 firefox 左上角难看的按钮菜单 安装的 extensions,Addon 放在这里 证书相关、组件列表、cookie、插件列表等等都可以在这里找到 等等等等。 合理利用这些文件和信息我们可以在自己内嵌 Gecko 的程序里方便地做一些事情而不用自己去操作和维护一些配置项和数据。 在自己编译完 xulrunner-1.9.2 后尚能找到编译好的 profdirserviceprovidersa_s.lib 来用于 WinEmbed 工程的移植,最近编译了 xulrunner-10.0.2 后发现就没这么幸运了,没有生成这个东东。在头疼了一番后决定自己做一个工程来生成它。根据 xulrunner 源码 mozilla-release/profile/dirserviceprovider 下的几个 makefile 来做就可以了。 直接讲过程吧。 一、编译出需要的 Lib 文件 新建一个 Win32 静态 Lib 空工程,将 mozilla-release/profile/dirserviceprovider 目录下的几个 .h 和 .cpp 文件都添加进工程,将 path/to/xulrunner-10.0.2-sdk/include 添加到工程的附加包含目录,为工程添加预处理器定义 XP_WIN 和 XPCOM_GLUE 这两项。如果一切顺利的话这时候你已经可以顺利地编译出这个 lib 文件了。在 http://download.csdn.net/detail/mzlogin/4382847 可以下载到我的 Demo 工程,如果想编译,需要将上面所讲的 path/to/xulrunner-10.0.2-sdk/include 修改成你本机上 xulrunner sdk 的相应路径。 二、将 Lib 链接进自己内嵌 Gecko 的程序 自写一个启用 profile 的函数,我这里直接使用 WinEmbed 例子里提供的 StartupProfile 函数: nsresult StartupProfile() { nsCOMPtr<nsIFile> appDataDir; nsresult rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR, getter_AddRefs(appDataDir)); if (NS_FAILED(rv)) return rv; appDataDir->AppendNative(nsCString("MozillaDemo")); nsCOMPtr<nsILocalFile> localAppDataDir(do_QueryInterface(appDataDir)); nsCOMPtr<nsProfileDirServiceProvider> locProvider; NS_NewProfileDirServiceProvider(PR_TRUE, getter_AddRefs(locProvider)); if (!locProvider) return NS_ERROR_FAILURE; rv = locProvider->Register(); if (NS_FAILED(rv)) return rv; return locProvider->SetProfileDir(localAppDataDir); } 其中的”MozillaDemo”即是你的 profile 文件夹的名字,可以根据你的喜好改动,在这里会是 %appdata%/Mozilla/MozillaDemo 在初始化 Gecko 运行环境的过程里调用完 XRE_InitEmbedding2 之后,添加 if (NS_FAILED(StartupProfile())) { result = 8; } else { 在此文件开始添加 #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceDefs.h" #include "nsProfileDirServiceProvider.h" 在合适的地方添加 #pragma comment(lib, "编译出的 lib") 顺利的话,就大功告成了,运行一个你的内嵌 Gecko 程序然后去 %appdata%/Mozilla/MozillaDemo 看看吧~有图有真相哦。
目录 Win7 不按 Shift,右键显示 “在此处打开命令窗口 (W)” Win7 搜索文件内容 将 Caps Lock 映射为 Ctrl 将任务栏库图标变成打开计算机 Win10 x64 下使用 ComMonitor Win10 下 Chrome 最新版崩溃 Win7 不按 Shift,右键显示 “在此处打开命令窗口 (W)” 图上的这条右键命令一般在 Win7 下是需要 Shift + 右键在弹出菜单里才能看到的,怎么省掉这个 Shift,直接就能出来呢? 先说方法: 将如下代码保存为 .reg 文件然后执行即可。 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\cmd] "Extended"=- [HKEY_CLASSES_ROOT\Drive\shell\cmd] "Extended"=- [HKEY_CLASSES_ROOT\LibraryFolder\background\shell] @="none" [HKEY_CLASSES_ROOT\LibraryFolder\background\shell\cmd] @="@shell32.dll,-8506" "NoWorkingDirectory"="" "Extended"=- [HKEY_CLASSES_ROOT\LibraryFolder\background\shell\cmd\command] @="cmd.exe /s /k pushd \"%V\"" 再说原理: 普通文件夹右键 将注册表 HKEY_CLASSES_ROOT\Directory\Background\shell\cmd 下的键 Extended 改名或者删除 磁盘分区右键 将注册表 HKEY_CLASSES_ROOT\Drive\shell\cmd 下的键 Extended 改名或者删除 库文件夹右键 在注册表 HKEY_CLASSES_ROOT\LibraryFolder\background 下建立和第一条的 Directory\Background 中相同的键值 Win7 搜索文件内容 控制面板 – 索引选项 – 高级 – 文件类型 – 找到你想要搜索内容的文件后缀名,点中它,然后选中下面的「为属性和文件内容添加索引」。 将 Caps Lock 映射为 Ctrl 注:经验证此方法也适用于 Win10,但是完成后需要重启。 因为个人习惯输入大写字母时使用「Shift + 字母」的方式,所以 Caps Lock 键并没有什么用,而且经常使用 Vim,偶尔使用 Emacs,都需要频繁地按 Ctrl 键,在 Mac OS X 下已经将 Caps Lock 键映射为 Ctrl,为了统一体验和按键方便,也需要在 Windows 下做一个映射。 先说方法: 将如下代码保存为 .reg 文件然后执行即可。 Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] "Scancode Map"=hex:00,00,00,00,00,00,00,00,02,00,00,00,1d,00,3a,00,00,00,00,00 再说原理: Scancode Map 这个键值的讲解实例参见 Keyboard and mouse class drivers (Windows Drivers),我们这里填写的值 00000000 00000000 02000000 1d003a00 00000000 可以对应理解如下: 值 说明 0x00000000 Header: 版本。全部设置为 0。 0x00000000 Header: 标志。全部设置为 0。 0x00000002 2 条映射条目(包括结尾的 null 条目)。 0x003a001d Caps Lock –> Left Ctrl (0x3a –> 0x1d). 0x00000000 终止符,即 null 条目。 将任务栏库图标变成打开计算机 右键任务栏库图标,右键弹出菜单里的“Windows 资源管理器”,单击“属性”。 在弹出对话框里将“目标”一栏的 %windir%\explorer.exe 改为 %windir%\explorer.exe ,,即加上一个空格一个逗号。 参考:如何将Win7/Win8任务栏库图标变为打开计算机 Win10 x64 下使用 ComMonitor ComMonitor 算是比较好用的串口调试工具了,但是已经很久没有更新,在 Win10 下无法直接使用,打开会弹出错误提示: 没有找到 C:\Windows\system32\msrd3x43.dll 要在 Win10 64 位操作系统下正常使用 ComMonitor 的步骤是: 下载 msrd3x43.dll,放到 C:\Windows\SysWOW64 下; 右键 ComMonitor.exe – 属性 – 兼容性 – 以兼容模式运行这个程序,选择 “Windows XP (Service Pack 2)”,应用。 我使用的 ComMinotor v4.5 版本及 msrd3x43.dll 文件可以到百度网盘下载: https://pan.baidu.com/s/1nuDa0JJ Win10 下 Chrome 最新版崩溃 突然有一天就什么也打不开了,不断的报 XX 插件崩溃,反正是所有插件都崩溃,设置页面都进不去。 解决方法: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome] "RendererCodeIntegrityEnabled"=dword:00000000 将以上内容保存为 chrome.reg 文件然后运行。
3-1 变量在第一次被赋值时自动声明。在赋值时解释器会根据语法和右侧的操作数来决定新对象的类型。 3-2 函数总是返回一个值,显式 return 的值或者 None。返回的值的类型也是动态确定。 3-3 因为变量名 xxx 对 Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。 3-4 可以,使用 “;” 3-5 可以,使用 “" 或者括号 3-6 (a)x = 1, y = 2, z = 3 (b)z = 2, x = 3, y = 1 3-7 40XL, $aving$, 0x40L, thisIsn’tAVar, big-daddy, 2hot2touch, counter-1 不合法。不是以字母或者下划线开头,或者有非字母下划线数字的字符。print, if 是关键字。 3-10 用异常处理取代对 os.path.exists() 的调用: import os ls = os.linesep while True: fname = raw_input('input a filename:') # if os.path.exists(fname): # print "Error: '%s' already exists" % fname # else: # break try: fobj = open(fname, 'r') except IOError, e: break; else: print "Error: '%s' already exists" % fname fobj.close() all = [] print "\nEnter lines ('.' by itself to quit).\n" while True: entry = raw_input('>') if entry == '.': break else: all.append(entry) fobj = open(fname, 'w') fobj.writelines(['%s%s' % (x, ls) for x in all]) fobj.close() print 'Done!' 用 os.path.exists() 取代异常处理方法: import os fname = raw_input('Enter filename:') print #try: # fobj = open(fname, 'r') #except IOError, e: # print '*** file open error:', e #else: # for eachLine in fobj: # print eachLine, # fobj.close() if os.path.exists(fname): fobj = open(fname, 'r') for eachLine in fobj: print eachLine, fobj.close() else: print 'this file not exists' 3-11 import os fname = raw_input('Enter filename:') print #try: # fobj = open(fname, 'r') #except IOError, e: # print '*** file open error:', e #else: # for eachLine in fobj: # print eachLine, # fobj.close() if os.path.exists(fname): fobj = open(fname, 'r') for eachLine in fobj: print eachLine.strip() fobj.close() else: print 'this file not exists' 3-12 """to read or make a file""" import os def makeTextFile(): '''make a file''' ls = os.linesep while True: fname = raw_input('input a filename:') # if os.path.exists(fname): # print "Error: '%s' already exists" % fname # else: # break try: fobj = open(fname, 'r') except IOError, e: break; else: print "Error: '%s' already exists" % fname fobj.close() all = [] print "\nEnter lines ('.' by itself to quit).\n" while True: entry = raw_input('>') if entry == '.': break else: all.append(entry) fobj = open(fname, 'w') fobj.writelines(['%s%s' % (x, ls) for x in all]) fobj.close() print 'Done!' def readTextFile(): '''read a file''' fname = raw_input('Enter filename:') print #try: # fobj = open(fname, 'r') #except IOError, e: # print '*** file open error:', e #else: # for eachLine in fobj: # print eachLine, # fobj.close() if os.path.exists(fname): fobj = open(fname, 'r') for eachLine in fobj: print eachLine.strip() fobj.close() else: print 'this file not exists' def main(): '''main menu''' while True: print '1.Read a file' print '2.Make a file' print 'x.exit' myStr = raw_input('input your choice:') if myStr == '1': readTextFile() elif myStr == '2': makeTextFile() elif myStr == 'x': break if __name__ == '__main__': main() 3-13 Windows 下 curses 模块貌似无法正常使用…… 迂回的实现思路是将文件内容按行读取到一个列表里, 然后让用户修改每一行, 最后让用户选择是否保存, 保存则重写文件, 不保存则退出。
2-2 (a) 计算 1 + 2 * 4 (b) 无输出 (c) 一样。无输出语句。 (d) 单独执行无输出,在交互解释器里执行输出结果。 (e)print 1 + 2 * 4 2-4 (a) str = raw_input('input a str:') print str (b) str = raw_input('input a int:') print int(str) 2-5 (a) i = 0 while i <= 10: print i, i += 1 (b) for eachNum in range(11): print eachNum, 2-6 num = raw_input('input a num: ') if num > 0: print '正数', elif num < 0: print '负数', else: print '0', 2-7 myStr = raw_input('input a str: ') i = 0 while i < len(myStr): print myStr[i], i += 1 myStr = raw_input('input a str: ') for c in myStr: print c, 2-8 aList = list() for i in range(5): num = raw_input('input a num : ') aList.append(int(num)) i = 0 myNum = 0 while i < len(aList): myNum += aList[i] i += 1 print 'myNum is : %d' % (myNum) 2-9 aTuple = [ 1, 2, 3, 5, 6] myNum = 0 for i in aTuple: myNum += i print 'average value is : %f ' % (float(myNum) / len(aTuple)) 2-10 b = bool(False) while b is False: num = int(raw_input('input a num among 1 to 100 : ')) if num >= 1 and num <= 100: print 'succeed!' break else: print 'input error!' 2-11 def display_menu(): """展示菜单""" print '1. 取五个数的和' print '2. 取五个数的平均值' print 'x. 退出' sel = str(raw_input('输入您的选项:')) return sel def tuple_sum(aTuple): """求和""" num = 0 for i in aTuple: num += i return num aTuple = (1, 2, 3, 4, 6) while True : sel = display_menu() if sel == str('1'): print 'num is : %d ' % tuple_sum(aTuple) elif sel == str('2'): print 'average is : %f ' % (float(tuple_sum(aTuple))/len(aTuple)) elif sel == str('x'): break else: continue 2-15 (a) num1 = int(raw_input('input num1:')) num2 = int(raw_input('input num2:')) num3 = int(raw_input('input num3:')) if num1 > num2: tmp = num2 num2 = num1 num1 = tmp if num2 > num3: tmp = num3 num3 = num2 num2 = tmp if num1 > num2: tmp = num2 num2 = num1 num1 = tmp print 'min to max is %d, %d, %d' % (num1, num2, num3) (b) num1 = int(raw_input('input num1:')) num2 = int(raw_input('input num2:')) num3 = int(raw_input('input num3:')) if num1 < num2: tmp = num2 num2 = num1 num1 = tmp if num2 < num3: tmp = num3 num3 = num2 num2 = tmp if num1 < num2: tmp = num2 num2 = num1 num1 = tmp print 'min to max is %d, %d, %d' % (num1, num2, num3) 2-16 fobj = open('hello.txt', 'r') for eachLine in fobj: print eachLine, fobj.close()