python实践设计模式(一)概述和工厂模式【转】

学习python有一段时间,之前就是看看书,最后发现只有实践才能真正学到东西,只有在解决问题中才能真正掌握。之前大牛也有用python实现各种设计模式的,我自己参考之前在学习用C#实现设计模式的经历,也想把23种模式逐一实践,从网上查了一些资料,才明白python虽然是面向对象的,但是和C#,java语言不太一样。影响设计方法不一样有以下几点:

1.python没有接口Interface的类型。

2.由于《Design Patterns—Elements of Reusable Object-Oriented Software》一书采用的是C++来讲述设计模式,因此访问控制符(public、protected和private等关键字)和静态成员方法(类方法)等都可以直接使用,但是这些特性在Python中都无法用到,原因是Python采了与C++完全不同的对象模式。

3.python不是强类型,弱化了多态的概念,一个对象很少只是一个类的实例,而是可以在运行时动态改变,支持相同请求的不同对象针对同一请求所触发的操作可能完全不同。

4.python代码生存在modules中而不必须在class中。

5.python有一些特殊语法运用了设计模式(如decorator ),可以直接应用。

有了以上的不同,就注定python在运用设计模式上与其他面向对象语言的不同,下面是我尝试的实现。

《Design Patterns》一书把设计模式分为了3大类,创建型模式(creational pattern)、结构型模式(structural pattern)和行为型模式(behavioral patterns)。

  一. 创建型模式(creational pattern)

对类的实例化过程进行了抽象,能够使软件模块做到与对象创建和组织的无关性。为了使体系结构更加清晰,一些软件在设计上要求当创建类的具体实例时,能够根据具体的语境来动态地决定怎样创建对象,创建哪些对象,以及怎样组织和表示这些对象,而创建型模式所要描述的就是该如何来解决这些问题。

创建型模式包括以下几种:

 

Simple Factory模式
专门定义一个类来负责创建其它类的实例,被创建的实例通常都具有共同的父类。

Factory Method模式
将对象的创建交由父类中定义的一个标准方法来完成,而不是其构造函数,究竟应该创建何种对象由具体的子类负责决定。

Abstract Factory模式
提供一个共同的接口来创建相互关联的多个对象。

 

Singleton模式
保证系统只会产生该类的一个实例,同时还负责向外界提供访问该实例的标准方法。
Builder模式
将复杂对象的创建同它们的具体表现形式(representation)区别开来,这样可以根据需要得到具有不同表现形式的对象。
Prototype模式
利用一个能对自身进行复制的类,使得对象的动态创建变得更加容易。

 

本次先实践Simple Factory模式,Factory Method模式和 Abstract Factory模式,其他模式后续会涉及。

1.Simple Factory模式

Simple Factory模式不是独立的设计模式,他是Factory Method模式的一种简单的、特殊的实现。他也被称为静态工厂模式,通常创建者的创建方法被设计为static方便调用,但是python没有static一说。所以可以把创建者也就是工厂设计为一个普通class或全局函数即可。如果是class还需要实例化才能调用工厂方法,而全局函数比较简单,比较接近静态工厂的简便特性。

 

Simple Factory模式包含以下角色部分,UML可参考下图:

 

1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。由一个具体类实现,通常该类的工厂方法是静态的。在python中工厂为一个普通class或全局函数。

2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。在python中抽象产品一般为父类。

3) 具体产品角色:工厂类所创建的对象就是此角色的实例。由一个具体类实现。

 

一个Simple Factory模式代码实例

 

 1 class car:
 2     '''interface as Product'''
 3     def drive(self):
 4         pass
 5 
 6 class BMW(car):
 7     '''Concrete Product'''
 8     def __init__(self,carname):
 9         self.__name=carname
10     def drive(self):
11         print "Drive the BMW as "+self.__name
12 
13 class Benz(car):
14     '''Concrete Product'''
15     def __init__(self,carname):
16         self.__name=carname
17     def drive(self):
18         print "Drive the Benz as "+self.__name
19 
20 class driver:
21     '''Factory also called Creator'''
22     def driverCar(self,name):
23         if name=="BMW":
24             return BMW("BMW")
25         elif name=="Benz":
26             return Benz("Benz")
27         else:
28             raise MyInputException(name)
29 
30 class MyInputException(Exception):  
31     def __init__(self, name):  
32         Exception.__init__(self)  
33         self.name = name
34  
35 
36 if __name__ == "__main__":
37     print "please input \"BMW\" or \"Benz\" :"
38     carname=raw_input()
39     dier=driver()
40     try:
41         d=dier.driverCar(carname)
42     except MyInputException,e:
43         print "input worry name "+e.name
44     else:
45         d.drive()

 

用全局函数改写工厂类,其他部分省略,变化部分如下:

 1 '''Factory also called Creator'''
 2 def driver(name):
 3     if name=="BMW":
 4         return BMW("BMW")
 5     elif name=="Benz":
 6         return Benz("Benz")
 7     else:
 8         raise MyInputException(name)
 9 
10 if __name__ == "__main__":
11     print "please input \"BMW\" or \"Benz\" :"
12     carname=raw_input()
13     try:
14         d=driver(carname)
15     except MyInputException,e:
16         print "input worry name "+e.name
17     else:
18         d.drive()

 

2. Factory Method工厂模式

工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。也就是工厂外面再封装一层。

1) 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。

2) 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3) 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在python中抽象产品一般为父类。

4) 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。由一个具体类实现。

 

一个Factory Method代码实例

 1 class car:
 2     '''interface as Product'''
 3     def drive(self):
 4         pass
 5 
 6 class BMW(car):
 7     '''Concrete Product'''
 8     def __init__(self,carname):
 9         self.__name=carname
10     def drive(self):
11         print "Drive the BMW as "+self.__name
12 
13 class Benz(car):
14     '''Concrete Product'''
15     def __init__(self,carname):
16         self.__name=carname
17     def drive(self):
18         print "Drive the Benz as "+self.__name
19 
20 class driver:
21     '''Factory also called Creator'''
22     def driverCar(self):
23         return car()
24 
25 class BMWdriver(driver):
26     '''Concrete Creator'''
27     def driverCar(self):
28         return BMW("BMW")
29 
30 class Benzdriver(driver):
31     '''Concrete Creator'''
32     def driverCar(self):
33         return Benz("Benz")    
34 
35 if __name__ == "__main__":
36     driver=BMWdriver()
37     car=driver.driverCar()
38     car.drive()
39     driver=Benzdriver()
40     car=driver.driverCar()
41     car.drive()

3. Abstract Factory模式

抽象工厂是工厂模式的进一步延伸,产品的类变的更加复杂,也就说产品不只是一个接口或父类而是有多个接口和父类了,形成了一个产品族的概念。模式的角色与Factory Method基本一样,UML图如下:

抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。 不过使用抽象工厂是有条件的:
1.系统中有多个产品族,而系统一次只可能消费其中一族产品
2.同属于同一个产品族的产品在一起使用,这一约束必须在系统的设计中体现出来。

简单的实现代码如下:

 1 class Newcar:
 2     '''Abstract Product'''
 3     def drive(self):
 4         pass
 5 
 6 class NewBMW(Newcar):
 7     '''Concrete Product'''
 8     def __init__(self,carname):
 9         self.__name=carname
10     def drive(self):
11         print "Drive the New BMW as "+self.__name
12 
13 class NewBenz(Newcar):
14     '''Concrete Product'''
15     def __init__(self,carname):
16         self.__name=carname
17     def drive(self):
18         print "Drive the New Benz as "+self.__name
19 
20 class Oldcar:
21     '''Abstract Product'''
22     def drive(self):
23         pass
24 
25 class OldBMW(Oldcar):
26     '''Concrete Product'''
27     def __init__(self,carname):
28         self.__name=carname
29     def drive(self):
30         print "Drive the Old BMW as "+self.__name
31 
32 class OldBenz(Oldcar):
33     '''Concrete Product'''
34     def __init__(self,carname):
35         self.__name=carname
36     def drive(self):
37         print "Drive the Old Benz as "+self.__name
38 
39 class driver:
40     '''Abstract Factory also called Creator'''
41     def driverNewCar(self):
42         return Newcar()
43     def driverOldCar(self):
44         return Oldcar()
45 
46 class BMWdriver(driver):
47     '''Concrete Factory or Creator'''
48     def driverNewCar(self):
49         return NewBMW("NewBMW")
50     def driverOldCar(self):
51         return OldBMW("OldBMW")
52 
53 class Benzdriver(driver):
54     '''Concrete Factory or Creator'''
55     def driverNewCar(self):
56         return NewBenz("NewBenz")
57     def driverOldCar(self):
58         return OldBenz("OldBenz")    
59 
60 if __name__ == "__main__":
61     driver=BMWdriver()
62     car=driver.driverNewCar()
63     car.drive()
64     car=driver.driverOldCar()
65     car.drive()
66     driver=Benzdriver()
67     car=driver.driverNewCar()
68     car.drive()
69     car=driver.driverOldCar()
70     car.drive()

未完待续……

msi安装包使用msiexec命令进行自动安装卸载命令【转】

1. 安装

msiexec /i ***.msi /qb /l*v ***.log
说明:/i表示安装,***.msi是MSI安装包的全路径。/qb表示安静安装(不需要用户点下一步),/l*v表示输出日志到 ***.log文件。

2. 卸载

msiexec /x {GUID} /q
说明:每个程序安装后,都会有一个GUID号,这个GUID号可以从Assembly.cs中找到。
/x 用于卸载一个程序。/q表示完全安静地安装。

msiexec /x ***.msi /q
说明:也可以通过MSI文件来卸载。其效果和通过GUID号来卸载是一样的。

3. 帮助

msiexec /Option [Optional Parameter]安装选项
安装或配置产品
/a 管理安装 – 在网络上安装产品
/j<u|m> [/t ] [/g ]
公布产品 – m 公布到所有用户,u 公布到当前用户
卸载产品
显示选项
/quiet
安静模式,无用户交互
/passive
无人参与模式 – 只显示进度栏
/q[n|b|r|f]
设置用户界面级别
n – 无用户界面
b – 基本界面
r – 精简界面
f – 完整界面(默认值)
/help
帮助信息
重新启动选项
/norestart
安装完成后不重新启动
/promptrestart
必要时提示用户重新启动
/forcerestart
安装后始终重新启动计算机
日志选项
/l[i|w|e|a|r|u|c|m|o|p|v|x|+|!|*]
i – 状态消息
w – 非致命警告
e – 所有错误消息
a – 操作的启动
r – 操作特定记录
u – 用户请求
c – 初始用户界面参数
m – 内存不足或致命退出信息
o – 磁盘空间不足消息
p – 终端属性
v – 详细输出
x – 额外调试信息
+ – 扩展到现有日志文件
! – 每一行刷新到日志
* – 记录所有信息,除了 v 和 x 选项
/log
与 /l* 相同
更新选项
/update [;Update2.msp]
应用更新
/uninstall [;Update2.msp] /package 删除产品的更新
修复选项
/f[p|e|c|m|s|o|d|a|u|v] 修复产品
p – 仅当文件丢失时
o – 如果文件丢失或安装了更旧的版本(默认值)
e – 如果文件丢失或安装了相同或更旧的版本
d – 如果文件丢失或安装了不同版本
c – 如果文件丢失或较验和与计算的值不匹配
a – 强制重新安装所有文件
u – 所有必要的用户特定注册表项(默认值)
m – 所有必要的计算机特定注册表项(默认值)
s – 所有现有的快捷键方式(默认值)
v – 从源运行并重新缓存本地安装包
设置公共属性
[PROPERTY=PropertyValue]

移动安全之Android安全检测工具大全【转】

测试工具集

Appie

轻量级的软件包, 可以用来进行基于Android的渗透测试, 不想使用VM的时候可以尝试一下.
https://manifestsecurity.com/appie

Android Tamer

可以实时监控的虚拟环境, 可以用来进行一系列的安全测试, 恶意软件检测, 渗透测试和逆向分析等.
https://androidtamer.com/

AppUse

AppSec Labs开发的Android的虚拟环境.
https://appsec-labs.com/AppUse/

Mobisec

移动安全的测试环境, 同样支持实时监控
http://sourceforge.net/projects/mobisec/

Santoku

基于Linux的小型操作系统, 提供一套完整的移动设备司法取证环境, 集成大量Adroind的调试工具, 移动设备取证工具, 渗透测试工具和网络分析工具等.
https://santoku-linux.com/

逆向工程和静态分析

APKInspector

带有GUI的安卓应用分析工具
https://github.com/honeynet/apkinspector/

APKTool

一个反编译APK的工具,能够将其代码反编译成smali或者java代码,并且能后对反编译后的代码重新打包
http://ibotpeaches.github.io/Apktool/

Dex2jar

Dex2jar可以将.dex文件转换成.class文件或是将apt文件转换成jar文件.
https://github.com/pxb1988/dex2jar

Oat2dex

Oat2dex顾名思义和上一个工具类似, 用以将.oat文件转化为.dex文件.
https://github.com/testwhat/SmaliEx

JD-Gui

用来反编译并分析class,jar
http://jd.benow.ca/

FindBugs + FindSecurityBugs

FindSecurityBugs是FindBugs的拓展, 可以对指定应用加载各种检测策略来针对不同的漏洞进行安全检查.
http://findbugs.sourceforge.net/
http://h3xstream.github.io/find-sec-bugs/

YSO-Mobile Security Framework

Mobile Security Framework (移动安全框架) 是一款智能、一体化的开源移动应用(Android/iOS)自动渗透测试框架,它能进行静态、动态的分析.python manage.py runserver 127.0.0.1:1337
https://github.com/ajinabraham/YSO-Mobile-Security-Framework

Qark

LinkedIn发布的开源静态分析工具QARK,该工具用于分析那些用Java语言开发的Android应用中的潜在安全缺陷.
https://github.com/linkedin/qark

AndroBugs

AndroBugs Framework是一个免费的Android漏洞分析系统,帮助开发人员或渗透测试人员发现潜在的安全漏洞, AndroBugs框架已经在多家公司开发的Android应用或SDK发现安全漏洞, Fackbook、推特、雅虎、谷歌安卓、华为、Evernote、阿里巴巴、AT&T和新浪等
https://github.com/AndroBugs/AndroBugs_Framework

Simplify

Simplify可以用来去掉一些android代码的混淆并还原成Classes.dex文件, 得到.dex文件后可以配合Dex2jar或者JD-GUI进行后续还原
https://github.com/CalebFenton/simplify

ClassNameDeobfuscator

可以通过简单的脚本来解析smali文件
https://github.com/HamiltonianCycle/ClassNameDeobfuscator

动态调试和实时分析

Introspy-Android

一款可以追踪分析移动应用的黑盒测试工具并且可以发现安全问题。这个工具支持很多密码库的hook,还支持自定义hook.
https://github.com/iSECPartners/Introspy-Android

Cydia Substrate

Cydia Substrate是一个代码修改平台.它可以修改任何主进程的代码,不管是用Java还是C/C++(native代码)编写的, 一款强大而实用的HOOK工具
http://www.cydiasubstrate.com/

Xposed Framework

Xposed框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作.
http://forum.xda-developers.com/xposed/xposed-installer-versions-changelog-t2714053

CatLog

Adroind日志查看工具, 带有图形界面
https://github.com/nolanlawson/Catlog

Droidbox

一个动态分析android代码的的分析工具
https://code.google.com/p/droidbox/

Frida

Frida是一款基于python + javascript 的hook与调试框架,通杀android\ios\linux\win\osx等各平台,相比xposed和substrace cydia更加便捷.
http://www.frida.re/

Drozer

Drozer 是一个强大的app检测工具,可以检测app存在的漏洞和对app进行调试。
https://www.mwrinfosecurity.com/products/drozer/

网络状态分析和服务端测试

Tcpdump

基于命令行的数据包捕获实用工具
http://www.androidtcpdump.com/

Wireshark

Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料
https://www.wireshark.org/download.html

Canape

可以对任何网络协议进行测试的工具
http://www.contextis.com/services/research/canape/

Mallory

中间人(MiTM)攻击工具, 可以用来监视和篡改网络内的移动设备和应用的网络流量数据. A Man in
https://intrepidusgroup.com/insight/mallory/

Burp Suite

Burp Suite 是用于攻击web 应用程序的集成平台。它包含了许多工具,并为这些工具设计了许多接口,以促进加快攻击应用程序的过程。所有的工具都共享一个能处理并显示HTTP 消息,持久性,认证,代理,日志,警报的一个强大的可扩展的框架
https://portswigger.net/burp/download.html

Proxydroid

Android ProxyDroid可以帮助的你设置Android设备上的全局代理(HTTP / SOCKS4 / SOCKS5).
https://play.google.com/store/apps/details?id=org.proxydroid

绕过Root检测和SSL的证书锁定

Android SSL Trust Killer

一个用来绕过SSL加密通信防御的黑盒工具, 功能支持大部分移动端的软件.
https://github.com/iSECPartners/Android-SSL-TrustKiller

Android-ssl-bypass

命令行下的交互式安卓调试工具, 可以绕过SSL的加密通信, 甚至是存在证书锁定的情况下
https://github.com/iSECPartners/android-ssl-bypass

RootCoak Plus

RootCloak隐藏root是一款可以对指定的app隐藏系统的root权限信息.
https://github.com/devadvance/rootcloakplus

其他安全相关的库

PublicKey Pinning

公钥锁定,
https://www.owasp.org/images/1/1f/Pubkey-pin-android.zip

Android Pinning

一个独立开发的用于实现Android证书锁定的库. A standalone library project for certificate pinning on Android.
https://github.com/moxie0/AndroidPinning

Java AES Crypto

一个用来加解密字符串的Android类, 目的是防止开发整使用不恰当的加密方式从而导致的安全风险
https://github.com/tozny/java-aes-crypto

Proguard

ProGuard是一个压缩、优化和混淆Java字节码文件的免费的工具,它可以删除无用的类、字段、方法和属性。可以删除没用的注释,最大限度地优化字节码文件。它还可以使用简短的无意义的名称来重命名已经存在的类、字段、方法和属性。常常用于Android开发用于混淆最终的项目,增加项目被反编译的难度.
http://proguard.sourceforge.net/

SQL Cipher

SQLCipher是一个开源的SQLite扩展, 提供使用256-bit的AES加密来保证数据库文件的安全.
https://www.zetetic.net/sqlcipher/sqlcipher-for-android/

Secure Preferences

用来加密Android上的Shared Preferences防止安全防护不足的情况下被窃取.
https://github.com/scottyab/secure-preferences

Trusted Intents

Library for flexible trusted interactions between Android apps.
https://github.com/guardianproject/TrustedIntents

每天一个linux命令(61):wget命令【转】

Linux系统中的wget是一个下载文件的工具,它用在命令行下。对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器。wget支持HTTP,HTTPS和FTP协议,可以使用HTTP代理。所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后退出系统,wget将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与,这省去了极大的麻烦。

wget 可以跟踪HTML页面上的链接依次下载来创建远程服务器的本地版本,完全重建原始站点的目录结构。这又常被称作”递归下载”。在递归下载的时候,wget 遵循Robot Exclusion标准(/robots.txt). wget可以在下载的同时,将链接转换成指向本地文件,以方便离线浏览。

wget 非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性.如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。

1.命令格式:

wget [参数] [URL地址]

2.命令功能:

用于从网络上下载资源,没有指定目录,下载资源回默认为当前目录。wget虽然功能强大,但是使用起来还是比较简单:

1)支持断点下传功能;这一点,也是网络蚂蚁和FlashGet当年最大的卖点,现在,Wget也可以使用此功能,那些网络不是太好的用户可以放心了;

2)同时支持FTP和HTTP下载方式;尽管现在大部分软件可以使用HTTP方式下载,但是,有些时候,仍然需要使用FTP方式下载软件;

3)支持代理服务器;对安全强度很高的系统而言,一般不会将自己的系统直接暴露在互联网上,所以,支持代理是下载软件必须有的功能;

4)设置方便简单;可能,习惯图形界面的用户已经不是太习惯命令行了,但是,命令行在设置上其实有更多的优点,最少,鼠标可以少点很多次,也不要担心是否错点鼠标;

5)程序小,完全免费;程序小可以考虑不计,因为现在的硬盘实在太大了;完全免费就不得不考虑了,即使网络上有很多所谓的免费软件,但是,这些软件的广告却不是我们喜欢的。

3.命令参数:

启动参数:

-V, –version 显示wget的版本后退出

-h, –help 打印语法帮助

-b, –background 启动后转入后台执行

-e, –execute=COMMAND 执行`.wgetrc’格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc

记录和输入文件参数:

-o, –output-file=FILE 把记录写到FILE文件中

-a, –append-output=FILE 把记录追加到FILE文件中

-d, –debug 打印调试输出

-q, –quiet 安静模式(没有输出)

-v, –verbose 冗长模式(这是缺省设置)

-nv, –non-verbose 关掉冗长模式,但不是安静模式

-i, –input-file=FILE 下载在FILE文件中出现的URLs

-F, –force-html 把输入文件当作HTML格式文件对待

-B, –base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀

–sslcertfile=FILE 可选客户端证书

–sslcertkey=KEYFILE 可选客户端证书的KEYFILE

–egd-file=FILE 指定EGD socket的文件名

下载参数:

–bind-address=ADDRESS 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)

-t, –tries=NUMBER 设定最大尝试链接次数(0 表示无限制).

-O –output-document=FILE 把文档写到FILE文件中

-nc, –no-clobber 不要覆盖存在的文件或使用.#前缀

-c, –continue 接着下载没下载完的文件

–progress=TYPE 设定进程条标记

-N, –timestamping 不要重新下载文件除非比本地文件新

-S, –server-response 打印服务器的回应

–spider 不下载任何东西

-T, –timeout=SECONDS 设定响应超时的秒数

-w, –wait=SECONDS 两次尝试之间间隔SECONDS秒

–waitretry=SECONDS 在重新链接之间等待1…SECONDS秒

–random-wait 在下载之间等待0…2*WAIT秒

-Y, –proxy=on/off 打开或关闭代理

-Q, –quota=NUMBER 设置下载的容量限制

–limit-rate=RATE 限定下载输率

目录参数:

-nd –no-directories 不创建目录

-x, –force-directories 强制创建目录

-nH, –no-host-directories 不创建主机目录

-P, –directory-prefix=PREFIX 将文件保存到目录 PREFIX/…

–cut-dirs=NUMBER 忽略 NUMBER层远程目录

HTTP 选项参数:

–http-user=USER 设定HTTP用户名为 USER.

–http-passwd=PASS 设定http密码为 PASS

-C, –cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许)

-E, –html-extension 将所有text/html文档以.html扩展名保存

–ignore-length 忽略 `Content-Length’头域

–header=STRING 在headers中插入字符串 STRING

–proxy-user=USER 设定代理的用户名为 USER

–proxy-passwd=PASS 设定代理的密码为 PASS

–referer=URL 在HTTP请求中包含 `Referer: URL’头

-s, –save-headers 保存HTTP头到文件

-U, –user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION

–no-http-keep-alive 关闭 HTTP活动链接 (永远链接)

–cookies=off 不使用 cookies

–load-cookies=FILE 在开始会话前从文件 FILE中加载cookie

–save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中

FTP 选项参数:

-nr, –dont-remove-listing 不移走 `.listing’文件

-g, –glob=on/off 打开或关闭文件名的 globbing机制

–passive-ftp 使用被动传输模式 (缺省值).

–active-ftp 使用主动传输模式

–retr-symlinks 在递归的时候,将链接指向文件(而不是目录)

递归下载参数:

-r, –recursive 递归下载--慎用!

-l, –level=NUMBER 最大递归深度 (inf 或 0 代表无穷)

–delete-after 在现在完毕后局部删除文件

-k, –convert-links 转换非相对链接为相对链接

-K, –backup-converted 在转换文件X之前,将之备份为 X.orig

-m, –mirror 等价于 -r -N -l inf -nr

-p, –page-requisites 下载显示HTML文件的所有图片

递归下载中的包含和不包含(accept/reject):

-A, –accept=LIST 分号分隔的被接受扩展名的列表

-R, –reject=LIST 分号分隔的不被接受的扩展名的列表

-D, –domains=LIST 分号分隔的被接受域的列表

–exclude-domains=LIST 分号分隔的不被接受的域的列表

–follow-ftp 跟踪HTML文档中的FTP链接

–follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表

-G, –ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表

-H, –span-hosts 当递归时转到外部主机

-L, –relative 仅仅跟踪相对链接

-I, –include-directories=LIST 允许目录的列表

-X, –exclude-directories=LIST 不被包含目录的列表

-np, –no-parent 不要追溯到父目录

wget -S –spider url 不下载只显示过程

4.使用实例:

实例1:使用wget下载单个文件

命令:

wget http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

以下的例子是从网络下载一个文件并保存在当前目录,在下载的过程中会显示进度条,包含(下载完成百分比,已经下载的字节,当前下载速度,剩余下载时间)。

实例2:使用wget -O下载并以不同的文件名保存

命令:

: wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080

说明:

wget默认会以最后一个符合”/”的后面的字符来命令,对于动态链接的下载通常文件名会不正确。

错误:下面的例子会下载一个文件并以名称download.aspx?id=1080保存

wget http://www.minjieren.com/download?id=1

即使下载的文件是zip格式,它仍然以download.php?id=1080命令。

正确:为了解决这个问题,我们可以使用参数-O来指定一个文件名:

wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080

实例3:使用wget –limit -rate限速下载

命令:

wget –limit-rate=300k http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

当你执行wget的时候,它默认会占用全部可能的宽带下载。但是当你准备下载一个大文件,而你还需要下载其它文件时就有必要限速了。

实例4:使用wget -c断点续传

命令:

wget -c http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

使用wget -c重新启动下载中断的文件,对于我们下载大文件时突然由于网络等原因中断非常有帮助,我们可以继续接着下载而不是重新下载一个文件。需要继续中断的下载时可以使用-c参数。

实例5:使用wget -b后台下载

命令:

wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

对于下载非常大的文件的时候,我们可以使用参数-b进行后台下载。

wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip

Continuing in background, pid 1840.

Output will be written to `wget-log’.

你可以使用以下命令来察看下载进度:

tail -f wget-log

实例6:伪装代理名称下载

命令:

wget –user-agent=”Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16″ http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

有些网站能通过根据判断代理名称不是浏览器而拒绝你的下载请求。不过你可以通过–user-agent参数伪装。

实例7:使用wget –spider测试下载链接

命令:

wget –spider URL

说明:

当你打算进行定时下载,你应该在预定时间测试下载链接是否有效。我们可以增加–spider参数进行检查。

wget –spider URL

如果下载链接正确,将会显示

wget –spider URL

Spider mode enabled. Check if remote file exists.

HTTP request sent, awaiting response… 200 OK

Length: unspecified [text/html]

Remote file exists and could contain further links,

but recursion is disabled — not retrieving.

这保证了下载能在预定的时间进行,但当你给错了一个链接,将会显示如下错误

wget –spider url

Spider mode enabled. Check if remote file exists.

HTTP request sent, awaiting response… 404 Not Found

Remote file does not exist — broken link!!!

你可以在以下几种情况下使用spider参数:

定时下载之前进行检查

间隔检测网站是否可用

检查网站页面的死链接

实例8:使用wget –tries增加重试次数

命令:

wget –tries=40 URL

说明:

如果网络有问题或下载一个大文件也有可能失败。wget默认重试20次连接下载文件。如果需要,你可以使用–tries增加重试次数。

实例9:使用wget -i下载多个文件

命令:

wget -i filelist.txt

说明:

首先,保存一份下载链接文件

cat > filelist.txt

url1

url2

url3

url4

接着使用这个文件和参数-i下载

实例10:使用wget –mirror镜像网站

命令:

wget –mirror -p –convert-links -P ./LOCAL URL

说明:

下载整个网站到本地。

–miror:开户镜像下载

-p:下载所有为了html页面显示正常的文件

–convert-links:下载后,转换成本地的链接

-P ./LOCAL:保存所有文件和目录到本地指定目录

实例11:使用wget –reject过滤指定格式下载

命令:
wget –reject=gif ur

说明:

下载一个网站,但你不希望下载图片,可以使用以下命令。

实例12:使用wget -o把下载信息存入日志文件

命令:

wget -o download.log URL

说明:

不希望下载信息直接显示在终端而是在一个日志文件,可以使用

实例13:使用wget -Q限制总下载文件大小

命令:

wget -Q5m -i filelist.txt

说明:

当你想要下载的文件超过5M而退出下载,你可以使用。注意:这个参数对单个文件下载不起作用,只能递归下载时才有效。

实例14:使用wget -r -A下载指定格式文件

命令:

wget -r -A.pdf url

说明:

可以在以下情况使用该功能:

下载一个网站的所有图片

下载一个网站的所有视频

下载一个网站的所有PDF文件

实例15:使用wget FTP下载

命令:

wget ftp-url

wget –ftp-user=USERNAME –ftp-password=PASSWORD url

说明:

可以使用wget来完成ftp链接的下载。

使用wget匿名ftp下载:

wget ftp-url

使用wget用户名和密码认证的ftp下载

wget –ftp-user=USERNAME –ftp-password=PASSWORD url

 

备注:编译安装

使用如下命令编译安装:

# tar zxvf wget-1.9.1.tar.gz

# cd wget-1.9.1

# ./configure

# make

# make install

python 迭代器 生成器【转】

1. 迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

1.1 使用迭代器的优点

对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。

迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。

迭代器有两个基本的方法

  • next方法:返回迭代器的下一个元素
  • __iter__方法:返回迭代器对象本身

下面用生成斐波那契数列为例子,说明为何用迭代器

代码1

 def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        print b 
        a, b = b, a + b 
        n = n + 1
直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。

代码2

 def fab(max): 
    L = []
    n, a, b = 0, 0, 1 
    while n < max: 
        L.append(b) 
        a, b = b, a + b 
        n = n + 1
    return L
代码2满足了可复用性的需求,但是占用了内存空间,最好不要。

代码3

对比

 for i in range(1000): pass
 for i in xrange(1000): pass

前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题

 class Fab(object): 
    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self): 
        return self 

    def next(self): 
        if self.n < self.max: 
            r = self.b 
            self.a, self.b = self.b, self.a + self.b 
            self.n = self.n + 1 
            return r 
        raise StopIteration()

执行

1
2
3
4
5
6
7
8
9
>>> for key in Fabs(5):
    print key
    
1
1
2
3
5

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数

1.2 使用迭代器

使用内建的工厂函数iter(iterable)可以获取迭代器对象:

1
2
3
4
>>> lst = range(5)
>>> it = iter(lst)
>>> it
<listiterator object at 0x01A63110>

使用next()方法可以访问下一个元素:

1
2
3
4
5
6
>>> it.next()
0
>>> it.next()
1
>>> it.next()
2

python处理迭代器越界是抛出StopIteration异常

1
2
3
4
5
6
7
8
9
10
11
12
>>> it.next()
3
>>> it.next
<method-wrapper 'next' of listiterator object at 0x01A63110>
>>> it.next()
4
>>> it.next()
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    it.next()
StopIteration

了解了StopIteration,可以使用迭代器进行遍历了

lst = range(5)
it = iter(lst)
try:
    while True:
        val = it.next()
        print val
except StopIteration:
    pass
结果
1
2
3
4
5
6
>>>
0
1
2
3
4

事实上,因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下

>>> a = (1, 2, 3, 4)
>>> for key in a:
    print key

    
1
2
3
4
首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

1.3 定义迭代器

下面一个例子——斐波那契数列

# -*- coding: cp936 -*-
class Fabs(object):
    def __init__(self,max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1  #特别指出:第0项是0,第1项是第一个1.整个数列从1开始
    def __iter__(self):
        return self
    def next(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.n = self.n + 1
            return r
        raise StopIteration()

print Fabs(5)
for key in Fabs(5):
    print key
结果
1
2
3
4
5
6
<__main__.Fabs object at 0x01A63090>
1
1
2
3
5

 

2. 生成器

带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明)

可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果

代码4

def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

执行

1
2
3
4
5
6
7
8
9
>>> for n in fab(5):
    print n
    
1
1
2
3
5

简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> f = fab(3)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    f.next()
StopIteration

return作用

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。例如

1
2
3
4
5
6
7
8
9
>>> s = fab(5)
>>> s.next()
1
>>> s.next()
Traceback (most recent call last):
  File "<pyshell#66>", line 1, in <module>
    s.next()
StopIteration

代码5  文件读取

 def read_file(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return
如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

常用的shell命令汇总,夯实Linux基础【转】

一、硬件篇

1、cpu相关

lscpu                #查看CPU的统计信息
cat /proc/cpuinfo    #查看CPU详细信息,如每个CPU的型号等

2、内存相关

free -m              #概要查看内存情况,这里的单位是MB
cat /proc/meminfo    #查看内存详细信息

3、磁盘相关

lsblk                    #查看硬盘的分区分布,显示很值观
df -lh                   #查看各分区的情况
cat /proc/partitions     #查看硬盘和分区
mount | column -t        #查看挂接的分区状态

4、网卡相关

lspci | grep -i 'eth'    #查看网卡硬件信息
ifconfig -a              #查看系统的所有网络接口
ethtool eth0             #如果要查看某个网络接口的详细信息,例如eth0的详细参数和指标

二、软件篇

1、内核相关

uname -a                 #查看版本当前操作系统内核信息
cat /proc/version        #查看当前操作系统版本信息
cat /etc/issue           #查看版本当前操作系统发行版本信息
cat /etc/redhat-release  #同上
cat /etc/SuSe-release    #suse系统下才可使用,同上
lsb_release -a           #用来查看Linux兼容性的发行版信息
lsmod                    #列出加载的内核模块

2、网络相关

ifconfig            #查看所有网络接口的属性
iptables -L         #查看防火墙设置
service iptables (start|stop|restart|status)    #服务管理内命令
route -n            #查看路由表
netstat -tnlp       #查看所有监听端口
netstat -antp       #查看所有已经建立的连接
netstat -s          #查看网络统计信息进程
netstat    -at      #列出所有tcp端口
netstat    -au      #列出所有udp端口
netstat -lt         #只列出所有监听tcp端口

3、系统管理

top                           #查看系统所有进程的详细信息,如CPU、内存等信息
mount                         #挂载远程目录、NFS、本地共享目录到Linux下
hostname                      #查看/修改计算机名
w                             #查看活动用户
id                            #查看指定用户信息
last                          #查看用户登录日志
cut -d: -f1 /etc/passwd       #查看系统所有用户
cut -d: -f1 /etc/group        #查看系统所有组
crontab -l                    #查看当前用户的计划任务
chkconfig --list              #列出所有系统服务
rpm -qa                       #查看所有安装的软件包
uptime                        #查看系统运行时间、用户数、负载

4、文件相关

ls -lht          #列出一个文件夹下所有文件及大小、访问权限
du -sh <dir>     #查看指定目录的大小
du -lh <dir>     #查看指定目录各文件的大小
ln -s            #建立软连接

5、进程相关

pstree -p pid            #查看一个进程的所有线程
pstree -a                #显示所有进程的所有详细信息,遇到相同进程名可以压缩显示
ps -ef                   #查看所有进程
kill -9 pid              #杀死进程
kill all test            #杀死进程
kill -9 `pgrep test`     #杀死进程
./test.sh &              #使进程后台运行
nohup ./test.sh &        #使进程后台运行

6、压缩解压缩相关

zip -r dir.zip dir file          #将目录dir、文件file等压缩到zip包
zip -re dir.zip dir file         #创建zip包,且加密
unzip dir.zip                    #解压
tar -zcvf dir.tar.gz dir file    #将目录dir、文件file等压缩到tar包
tar xf dir.tar.gz                #解压

7、screen命令

screen -S test        #创建一个名为test的screen
screen -r test        #打开名字为test的screen
screen -r pid         #打开进程号为pid的screen
screen -ls            #列出所有的screen
ctrl + a,d            #当在一个screen时,退出screen
ctrl + a,n            #当在一个screen时,切换到下一个窗口
ctrl + a,c            #当在一个screen时,创建一个新的窗口

8、scp命令

scp local_file remote_username@remote_ip:remote_dir        #拷贝本地文件到远程机器上
scp -r local_dir remote_username@remote_ip:remote_dir    #拷贝本地目录到远程机器上

9、软件包管理命令

//centos ,redhat系统
rpm -qa | grep app      #查找本机是否安装了app
rpm -ivh app.rpm        #安装app
yum install app         #在线安装app
yum update app          #更新app
rpm -e app              #删除已经安装的app
yum repolist            #列出可用的仓库
yum repolist all        #列出所有仓库
yum list all            #列出仓库中所有的软件包
yum make cache          #缓存远程仓库缓存信息

//suse,opensuse系统
zypper search app      #查找本机是否安装了app
zypper install app     #安装app
zypper update app      #更新app
zypper remove app      #删除app
zypper lr              #列出所有已定义的安装源
zypper ar              #添加新安装源
zypper rr              #删除指定的安装源
zypper mr              #修改指定的安装源

//ubuntu系统
apt-get install app    #安装
apt-get update app     #更新
apt-get remove app     #删除
apt-cache search app   #搜索软件包
dpkg -i app.deb        #假设你有app的deb包,直接安装

每天进步一点点之python:exec eval execfile compile介绍【转】

1、exec

exec将字符串str当成有效的Python代码来执行。提供给exec的代码的名称空间和exec语句的名称空间一样。
比如:
执行:exec “print ‘hello world!'”
结果就是会输出 :hello world!
但是上面的代码其实问题挺大的,一定要注意指定一个作用域。这样是为了防止干扰你的命名空间。
我执行了low=1,pow=1.
这样,在这个程序中引入math,里面的low和pow函数就无法使用了。干扰了。
看上面正确的做法
如果你是多个作用域,那么可以在in后面添加多个,比如:exec “pow =1” in scope,local,new_scope
2、eval
这是Python的求值,是一个内建函数。exec会执行一系列的Python语句,而eval会计算Python表达式(以字符串形式书写),并返回结果值。exec不返回任何对象,。
这个上面的写法也是不规范的,还是需要作用域。这样避免冲突。
eval可以有两个命名空间,一个全局一个局部,全局必须是字典,局部的可以是任何形式的映射。
注意,这个写法。空间是写在括号里面的,不像exec是in这样的关键字来表示。
3、execfile
这个顾名思义,就是执行一个py文件。也可以包含作用域。在括号里面。这也是一个内建函数
执行了abc.py
这个跟直接执行是一个样子的。
4、compile
当一个字符串被exec、eval()、execfile()执行的时候,解释器会先将他们编译为字节码,然后再执行,这个过程比较耗时。所以对某段代码进行执行多次的时候,最好是先预编译,可以提高效率。预编译就是用函数compile
如果你要是执行的eval的话,那么compile参数就该是eval了而不是exec。

每天进步一点点之Python基础教程笔记

第1章 快速改造:基础知识

1.4 数字和表达式

a. 整除:

整型 / 整型  结果还是整型(整除)

如果要实现整数除法,三种方法:

1) 将整数改成浮点数

2) 导入模块

 >>> from __future__ import division

3) 运行python 加上-Qnew参数

在将python的中的“/”更改为除法时:如果要使用整除可以用双斜线(包括小数的时候也是用于整除的)

1//2   1.0//2.0

取余:

python中取余数用的是%

b. 乘方 (**)  : 2**3 = 8

c. 长整形: 数字最后加个L(小写也行)

注意:正常整型的范围是在      2^31 <   int   <   -2^31

d. 八进制和十六进制:  010 = 8   、 0xAF = 175

补充:关于int函数的使用:

>>> int('010', 8)
  8
>>> int('0x10', 16)
  16
>>> int(0x10, 16)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: int() can't convert non-string with explicit base
>>> int(0x10)
  16

1.7 获取用户输入

input跟raw_input的区别:

在使用input的时候,python会假设用户输入的是合法的Python表达式

所以在输入一个字符串的时候就需要加上双引号,否则python会认为其是一个变量名

而raw_input是将输入的作为一个字符串处理的。

1.9 模块

1)   import math(照我的理解就是有一个math的文件)

在调用时, (math.函数)

2) from math import sqrt(文件math中有一个sqrt的函数)

在调用时,直接使用函数名就好  (函数)。但是这是要确定自己不会导入不同模块下的相同名字的函数。

3)from math import sqrt,

4)  from math import *

5)如果发生重名的情况为例以示区分,可以使用as子句。

>>> import math as foobar
>>> foobar.sqrt(4)

或者为函数提供别名:

>>> from math import sqrt as foobar
>>> foobar(4)

对于重名的函数可以:

from module1 import open as open1
from module2 import open as open2

1.10 保存并执行程序

在unix下,如果希望直接 ./aaa.py执行,只需要将

#!/usr/bin/env python

注:如果一个系统中装了两个python,这个就不好用了。(主要是不知道使用哪个python运行)。这个时候就需要使用

#!/usr/bin/python  需要写完整路径了

1.11 字符串

a) str和repr

str: 他会吧值转换成合理形式的字符串,以便于用户理解

repr:它会创建一个字符串,以合法的Python表达式的形式来表示值。但是repr的返回值还是一个str

注意:在旧版本中反引号跟repr的功能是一样的。

>>> print repr("Hello,world!")
   'Hello world!'
>>> print str("Hello,world!")
   Hello,world!
>>> print repr(10000L)
   10000L
>>> print str(10000L)
   10000

ps:按照我的理解是:str就是把值转换成人能看懂的形式。repr就是将值转换成python自己能看懂的形式。这个可能跟input和raw_input的区别差不多。

b)  长字符串

如果需要写很长很长的字符串,他需要跨多行,可以用三个引号代替普通的引号。单引号双引号都是可以的。

"""hidsbichkashdckash
vgfdgbdgssg
cdsafva"""

c) 原始字符串

原始字符串就是对反斜杠并不会特殊对待的字符串。他的本质上还是str类型的(这与Unicode字符串不一样)

注:当原始字符串中引号的时候需要特别的注意:

i) r’Let’s go’ 这个是不合法的。因为编译器不知道什么时候结束字符串。  可以用r”Let’s go”解决此问题

ii)r’Let\’s go’ 这个是合法的,但是其并不是我们想要的。

>>> print r'Let\'s go'
  Let\'s go

iii) r’This is illegal\’ 这个也是不合法的。(斜杠不能放在最后)

>>> print r'This is illegal\'
   File "<stdin>", line 1
     print r'This is illegal\'
     ^
   SyntaxError: EOL while scanning string literal

>>> print r'This is illegal\\'
  This is illegal\\
(上述结果是可以通过编译的,但是显然不是我们想要的结果)

>>> print r'This is illegal''\\'
  This is illegal\\

d) Unicode字符串

特别注意,在python这个是一种数据类型。与str是不一样的。

在python内部字符串是以8位的ASCII码形式存储的,而Unicode字符串则是以16位Unicode存储的。

>>> u'Hello World!'
  u'Hello World!'

>>> print type(u'Hello World!')
  <type 'unicode'>

补充:

关于Python的基本数据类型:

None、布尔型(True、False)、整型、浮点型、string、List、tuple、Dict、set(集合(无序不重复))

第2章 列表和元组

2.2 通用序列操作(序列包括了列表、元组、字符串等)

索引、分片(步长(注意步长为负的时候))、序列相加、乘法、成员资格、长度(len)最小值(min)最大值(max)

索引:a[2]

分片:a[:1](注意:分片是前包后不包的)

>>> numbers = [1,2,3,4,5,6,7,8,9,10]
>>> numbers[3:6]
  [4, 5, 6]

注意:第一个索引是要提取的第一个元素的编号(即第一个索引是包含在分片当中的),而最后的索引则是分片之后剩余部分的第一个元素的编号(即不包含在分片当中)

>>> numbers[7:10]
  [8, 9, 10]

>>> numbers[7:100]
  [8, 9, 10]

注意:第二个索引,即使元素不存在也是可以的。

>>> numbers[-3:-1]
  [8, 9]

>>> numbers[-3:]
  [8,9,10]

注意上面两段代码的不同:由于第二个索引是不包含在分片当中的,所以第一个代码并不能实现得到最后三个值。

>>> numbers[-3:0]
  []

注意:当步长为正的时候,第一个索引如果比第二个索引大(这里所说的是位置(负数的时候要看其具体指向的位置)),那么返回的就是一个空序列。

>>> numbers[8:3:-1]
  [9,8,7,6,5]

注意:当步长为负数的时候:其实就是一种倒序的实现了。(这个时候就要求第一个索引要大于第二个索引了,如果不是的话,那么就返回一个空序列)

序列相加:[1, 2, 3] + [4, 5, 6] 结果为[1, 2, 3, 4, 5, 6]

乘法:[‘yui’] * 4 结果为 [‘yui’, ‘yui’, ‘yui’, ‘yui’]

注意:如果要初始化一个长度为10的空序列

>>> a = [] * 10
>>> a
  []
这个显然不满足我们的要求

>>> a = [None] *10

成员资格:3 in [1, 2, 3] 结果为True

>>> subject = '$$$ Get rich now!!!$$$'
>>> '$$$' in subject
  True

注意:一般情况下,in都是检查一个元素是否在一个序列中。而上述的例子,其实是判断子串是否在字符串中。因为字符串的元素是一个字符。只有这一个特例(早期 版本并没有实现这个功能)

长度:len([1, 2, 3, 4, 5, 6]) 结果为 6

最小值: min([1, 2, 3, 4, 5, 6]) 结果为1

最大值:max([1, 2, 3, 4, 5, 6]) 结果为6

注:上边的例子都是用的list,同时元组也是可以实现上面的方法的。还有字符串也是可以实现上面的方法的。

2.3 列表:Python的“苦力”——可变的序列

a) list函数:list(“hello”)

list适用于所有类型的序列(列表、元组、字符串),而不只是字符串(同时字典也是可以的,用其键值作为list的值,但是其顺序是不一定的)

b)  基本的列表操作

元素赋值

>>> x = [1,1,1]
>>> x[1] = 2
>>> x
  [1,2,1]

注:不能为一个位置不存在的元素进行赋值。(这点跟字典是不同的)

删除元素(del)

>>> x = [1,2,3,4,5]
>>> del x[2]
>>> x
  [1,2,4,5]

分片赋值(赋给的值类型是序列)

注:分片赋值时:

>>> numbers= [1,5]
>>> numbers[1:1] = [2,3,4])
    [1,2,3,4,5]

我对于分片赋值的理解就是,用值替换分片得到的序列(在第一个索引的位置)其他的元素不变(当然位置要相应的移动)。所以上述例子就很好理解了,numbers[1:1]得到的是一个空序列,然后用[2,3,4]替换这个空序列在1这个位置。

因此

>>> numbers = [1,5]
>>> numbers[1:2] = [2,3,4]
  [1,2,3,4]

c) 列表的方法

append:用于在列表的末尾追加新的元素,注意返回值是None,但是其改变了原始列表

>>> lst = [1,2,3]
>>> lst.append(4)
>>> lst
  [1,2,3,4]

注意:添加的是元素

>>> lst = [1,2,3]
>>> lst.append([4,5])
>>> lst
  [1,2,3,[4,5]]

注:append也是可以用分片赋值实现的。但是注意append的是元素,而分片赋值的是list

count:统计某个元素在列表中出现的次数,返回值就是出现的次数

>>> x = [[1,2],1,1,[2,1,[1,2]]]
>>> x.count(1)
  2
>>> x.count([1,2])
  1

extend:可以在一个列表的末尾一次性追加另一个序列的多个值,返回值是None,但是其修改了原始列表

>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a.extend(b)
>>> a
  [1,2,3,4,5,6]

注意:这个跟序列相加的区别(序列相加并没有修改原始值,extend修改了原始的列表)

同时这个操作还可以用分片赋值实现

>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a[len(a):] = b
>>> a
  [1,2,3,4,5,6]

index:用于从列表中找出某个值第一个匹配项的索引位置,返回值就是一个索引位置,找不到则会抛出异常。

>>> x = [1,2,3,2,3,4,1,5]
>>> x.index(3)
  2

>>> x.index(6)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  ValueError: 6 is not in list

insert: 用于将元素插入到列表的相应位置,返回值也是None,但是其修改了原始列表

>>> numbers = [1,2,3,5,6,7]
>>> numbers.insert(3, 'four')
>>> numbers
  [1,2,3,'four',5,6,7]

当然上述过程也可以用分片赋值实现。

pop:会移除列表中的一个元素(默认为最后一个),并且返回该元素的值

>>> x = [1,2,3]
>>> x.pop()
  3
>>> x
  [1,2]

>>> x.pop(0)
  1
>>> x
  [2]

注意:pop方法跟del函数的区别:x.pop(2)      del x[2]

并且del没有返回值,而pop方法会返回删除的值

pop方法是唯一一个既能修改列表又返回值的列表方法

remove:用于移除列表中某个值的第一个匹配项(找不到相应的元素就会产生异常),返回值是None,但是其修改了原始列表

>>> x = ['to', 'be','or','not','to', 'be']
>>> x.remove('be')
>>> x
  ['to','or','not','to', 'be']

>>> x.remove('bee')
  Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
  ValueError: list.remove(x): x not in list

reverse与reversed:将列表中的元素反向存放

>>> x = [1,2,3]
>>> x.reverse()
>>> x
  [3,2,1]
>>> x = [1,2,3]
>>> reversed(x)
  <listreverseiterator object at 0x01C27690>
>>> print list(reversed(x))
  [3,2,1]

注意:reverse的返回值是None,但是其修改了原始列表。而reversed则返回了一个迭代器。

sort与sorted:用于将列表排序,其中sort返回值是None,但是修改了原始列表。而sorted返回排序后的列表。默认是以升序排列列表的。

>>> x = [4,6,2,1,7,9]
>>> x.sort()
>>> x
  [1,2,4,6,7,9]
>>> x = [4,6,2,1,7,9]
>>> sorted(x)
  [1,2,4,6,7,9]
>>> x
  [4,6,2,1,7,9]

注意:reversed与sorted的返回值是不一样的。reversed返回的是一个迭代器,而sorted返回的是一个列表(list)

注: append、extend、insert、pop、remove、reverse、sort都改变了原有列表,而不是返回一个新列表。

并且以上函数除了pop,其他都不返回值,或者说返回值是None。

pop返回的是被删除的元素的值

高级排序:

sort函数可以以一个比较函数作为参数。  numbers.sort(cmp)

sort函数可以自定义一种比较的方式,这种方式必须通过函数的形式传给sort函数,并且compare函数要满足当x<y时返回负数,如果x=y时返回0,如果x>y时返回正数。

>>> cmp(42,32)    # cmp函数是python的一个内建函数
  1
>>> cmp(99,100)
  -1
>>> cmp(10,10)
  0
>>> numbers = [5,2,9,7]
>>> numbers.sort(cmp)
>>> numbers
  [2,5,7,9]

此外sort方法还有两个可选的参数(key和reverse)

与cmp类似,key也是需要一个函数来确定比较的值。

>>> x = ['asdwqwe', 'adclon', 'awdc', 'opq', 'qwerty']
>>> x.sort(key=len)  # len是python的一个内建函数
>>> x
   ['opq', 'awdc', 'adclon', 'qwerty', 'asdwqwe']

reverse参数是用于说明是否要进行反向排序的

>>> x = [4,6,2,7,1,9]
>>> x.sort(reverse=True)
>>> x
  [9,7,6,4,2,1]

2.4 元组:不可变序列

只有一个值的元组需要表示时,需要在值后加一个”,”。(42,)

tuple函数:以一个序列作为参数并把它转换为元组。(经测试,字典也是可以的,用其键作为元组的值)

其实,对于元组他也是有count、index方法的,同时sorted方法也是可以的,但是sort方法就不行了。同理reverse方法也是不行的,但是reversed方法就可以了。

>>> a = (1,2,1,3,1,4,1,5)
>>> a.count(1)
  4
>>> a = (1,2,1,3,1,4,1,5)
>>> a.index(4)
  5
>>> a = (1,2,1,3,1,4,1,5)
>>> sorted(a)
  [1, 1, 1, 1, 2, 3, 4, 5]
>>> a.sort()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  AttributeError: 'tuple' object has no attribute 'sort'
>>> a = (1,2,1,3,1,4,1,5)
>>> tuple(reversed(a))
  (5, 1, 4, 1, 3, 1, 2, 1)
>>> type(reversed(a)) #注意reverser(a)的类型
  <type 'reversed'>
>>> a.reverse()
  Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
  AttributeError: 'tuple' object has no attribute 'reverse'

元组的意义:元组可以在映射(和集合的成员)中当做键使用。而且元组作为很多内建函数和方法的返回值存在。

第3章 使用字符串

3.1 基本字符串操作

索引、分片(步长(注意步长为负的时候))、序列相加、乘法、成员资格、长度(len)最小值(min)最大值(max)

但是字符串是不可变的,赋值操作是不合法的

索引

>>> a = "Hello World!"
>>> a[6]
  'W'

分片

>>> a = "Hello World!"
>>> a[4:]
  'o World!'

序列相加

>>> a = "Hello "
>>> b = "World!"
>>> a + b
'Hello World!'

乘法

>>> c = "go"
>>> d = c*5
>>> d
'gogogogogo'

成员资格(这个可以判断子串是否存在)

>>> f = 'Hello World!'
>>> 'l' in f
  True

长度

>>> f = 'Hello World!'
>>> len(f)
  12

最小值

>>> f = 'Hello World!'
>>> min(f)
  ' '

最大值

>>> f = 'Hello World!'
>>> max(f)
  'r'

3.2 字符串格式化

自己去看

3.4 字符串方法(注意字符串在python中是不可变的,所以下面的函数都是返回一个新的字符串,而原字符串没变)

其实对于字符串也是有count、index方法的,同时sorted方法也是可以的,但是sort方法就不行了。同理reverse方法也是不行的,但是reversed方法就可以了。

>>> f = "Hello World!"
>>> f.count('l')
  3
>>> f = "Hello World!"
>>> f.index('l')
  2
>>> f = "Hello World!"
>>> sorted(f)
  [' ', '!', 'H', 'W', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r']
>>> f.sort()
  Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
  AttributeError: 'str' object has no attribute 'sort'
>>> f = "Hello World!"
>>> str(reversed(f))
  '<reversed object at 0x01F076D0>'
>>> list(reversed(f))
  ['!', 'd', 'l', 'r', 'o', 'W', ' ', 'o', 'l', 'l', 'e', 'H']
>>> "".join(list(reversed(f)))
  '!dlroW olleH'

注意上述例子中str(reversed(f))并不能得到相对应的字符串,猜测原因是str并没有实现迭代器

find(返回值为-1时表示没找到)find的返回值是子串所在位置的最左端索引

函数原型:

string.find(sub[,start[,end]])

>>> f = "Hello World!"
>>> f.find('llo')
  2
>>> 'llo' in f
  True

如果不需要知道具体的位置,那么使用成员资格(in)也是可以的。

find:使用find函数的时候有一个坑,因为python把0、None、False、[]、()、{}、””作为逻辑假,所以find没找到子串时,返回-1,这时如果直接 if str.find(): print ‘find’ 就不能得到想要的结果。

正确的:

if str.find() == -1:
    print "find"

注:还有一些相近的方法:

string.rfind(sub[,start[,end]])  返回最后一个被找到子串的最左端索引

string.index(sub[,start[,end]])  返回值是子串所在位置的最左端索引,如果找不到的话,则抛出异常(这个是index跟find的区别)

string.rindex(sub[,start[,end]])  返回最后一个被找到子串的最左端索引,如果找不到的话,则抛出异常(这个是rindex跟rfind的区别)

string.count(sub[,start[,end]])   返回子串在字符串中的个数

>>> a = 'aaaaa'
>>> a.count('aa')
  2

string.startwith(sub[,start[,end]])  检查string是否以sub开始,可以使用给的索引start和end来定义匹配的范围

string.endswith(sub[,start[,end]])  检查string是否以sub结尾,可以使用给的索引start和end来定义匹配的范围

注意:index、find、成员资格、索引的区别

join(返回值是一个字符串)

注:需要连接的序列(list、tuple、str)的元素必须是字符串。(经测试,字典也是可以的)

>>> a = [1,2,3,4,5]
>>> sep = '+'
>>> sep.join(a)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: sequence item 0: expected string, int found
>>> a = ['1','2','3','4','5']
>>> sep = '+'
>>> sep.join(a)
  '1+2+3+4+5'
>>> a = "Hello World"
>>> sep = '\\'
>>> sep.join(a)
  'H\\e\\l\\l\\o\\ \\W\\o\\r\\l\\d'

lower(返回值是一个字符串)返回字符串的小写字母版

>>> f = 'Hello World!'
>>> f.lower()
'hello world!'

注:还有一些相近的方法:

string.islower()   判断所有的字符是否都是小写

string.capitalize()  返回首字母大写的字符串副本

string.swapcase()   返回一个副本,将所有基于实例的字符都交换大小写

string.title()  返回一个副本,将副本中的所有单词都以大写字母开头

string.istitle()

string.upper()

string.isupper()

replace(返回值是一个字符串)返回某字符串的所有匹配项均被替换之后得到的字符串

>>> 'This is a test'.replace('is', 'eez')
  'Theez eez a test'

注:还有一些相近的方法:

string.expandtabs([tabsize])  将tab替换成空格,tabsize为tab代表的空格数

注意这种情况:

>>> a = 'aaaaa'
>>> a.replace('aa', 'bb')
  'bbbba'

split:将字符串分割成序列

函数原型:

string.split([sp[,maxsplit]])

>>> '1+2+3+4+5'.split('+')
  ['1', '2', '3', '4', '5']

注:如果不提供任何分隔符,程序会把所有空格作为分隔符(空格、制表、换行等)

还有一些相近的方法:

string.rsplit([sp[,maxsplit]])  跟split一样,但是在使用maxsplit时是从右向左进行计数的

string.splitlines([keepends])   返回string中所有行的列表,可选择是否包含换行符()

strip(返回值是一个字符串)返回去除两侧(不包括内部)空格的字符串:

>>> '      Hello world!     '.strip()
  'Hello world!'
>>> '*** SPAM * for * everyone!!! ***'.strip(' *!')
  'SPAM * for * everyone'

注:还有一些相近的方法:

string.lstrip()

string.rstrip()

translate:返回字符串副本,其中所有字符都使用table进行了转换,可选择删除出现在deletechars中的所有字符

原型:

string.translate(table[,deletechars])

注: translate:

使用translate转换前,需要一张转换表。一般使用string.maketrans函数进行构建(长度为256的表)

>>> import string
>>> table = string.maketrans('cs', 'kz')   # 参数为两个等长的字符串,表示第一个字符串中的每个字符都用第二个字符串中相同位置的字符替换

下边就开始使用translate。一个必须参数就是转换表,还有一个可选的参数(用来指定需要删除的字符)

>>> 'this is an incredible test'.translate(table)
  'thiz iz an inkredible tezt'
>>> 'this is an incredible test'.translate(table, ' ')
  'thizizaninkredibletezt'

第4章 字典:当索引不好用时

4.2 创建和使用字典

a) 创建:

1. 字典最基本的初始化方法

 phonebook = {'Alice': '1234', 'Beth': '2190', 'Cevil': '2234'}

2. dict():通过其他映射(其他字典)或者键值对的序列建立字典

>>> items = [['name', 'Tom'],['age', 42]]
>>> dict(items)
  {'age': 42, 'name': 'Tom'}

>>> items = [('name', 'Tom'),('age', 42)]
>>> dict(items)
{'age': 42, 'name': 'Tom'}

3.同时dict函数还可以使用关键字参数来进行初始化

  dict(Alice = 1234, Beth= 2190, Cevil=2234)

b) 字典的基本操作:

索引(d[k])、赋值(d[k]=v)、删除项 (def  d[k])、成员资格(k in d)、长度(len)、max、min

索引(d[k]):

>>> a = {'age': 42, 'name': 'Tom'}
>>> a['name']
  'Tom'

赋值(d[k]=v):

>>> a = {'age': 42, 'name': 'Tom'}
>>> a['score'] = 99
>>> a
  {'age': 42, 'score': 99, 'name': 'Tom'}

删除项 (def  d[k]):

>>> a = {'age': 42, 'name': 'Tom'}
>>> del a['name']
>>> a
{'age': 42}

成员资格(k in d)(这里检查的是key(键))

>>> a = {'age': 42, 'name': 'Tom'}
>>> 'age' in a
  True

长度(len)

>>> a = {'age': 42, 'name': 'Tom'}
>>> len(a)
  2

max(这里也是计算的键)

>>> a = {'age': 42, 'name': 'Tom'}
>>> max(a)
  'name'

min(这里也是计算的键)

>>> a = {'age': 42, 'name': 'Tom'}
>>> min(a)
  'age'

注:

1)字典中的键可以是任意的不可变类型。(整型、浮点型、字符串、元组)

2)字典赋值是,即使原字典中并不存在该键,也是可以成功的,其会新建一个项。这点与列表不同(列表在不使用append方法的时候不能见值关联到列表范围之外的索引上)

3) 表达式k in d(d为字典)查找的是键,这点与列表也是不同的,列表查询的是值

c)字典的格式化字符串

>>> phone = {'cecil': '3411', 'ase': '2345'}
>>> "Cecil's phone number is %(cecil)s."  % phonebook

d)字典方法

1)clear(无返回值的(或者说是返回None),直接将原字典元素删除)

>>> a = {'age': 42, 'name': 'Tom'}
>>> print a.clear()
  None
>>> a
  {}

注意看下面两个代码的区别:

>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x = {}
>>> y
{'key': 'value'}

 

>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
  {'key': 'value'}
>>> x.clear()
>>> x
  {}
>>> y
  {}

2)copy

返回复制后的字典,浅复制(即字典中有“引用型”的数据时只是将“指针”给复制了,导致之后修改这个数据时会改变原始的值))

>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y['username'] = 'mlh'
>>> y['machines'].remove('bar')
>>> y
  {'username': 'mlh', 'machines': ['foo', 'baz']}
>>> x
  {'username': 'admin', 'machines': ['foo', 'baz']}

copy是浅复制。想要深复制需要调用copy模块的deepcopy函数完成操作。

>>> from copy import deepcopy
>>> d = {'name': ['aaaa', 'bbnb']}
>>> c = d.copy()
>>> dc = deepcopy(d)
>>> d['name'].append('Clive')
>>> c
  {'name': ['aaaa', 'bbnb', 'Clive']}
>>> dc
  {'name': ['aaaa', 'bbnb']}

注意:字典的copy这个方法对应于列表中的分片赋值。同时列表也是可以使用deepcopy进行复制的。

注意:赋值、copy、deepcopy的区别

>>> a = [1, 2, 3, 4, [5, 6]]
>>> b = a[:]
>>> a[4][1] = 7
>>> b
  [1, 2, 3, 4, [5, 7]]
>>> b[0] = 2
>>> b
  [2, 2, 3, 4, [5, 7]]
>>> a
  [1, 2, 3, 4, [5, 7]]
>>> a = [1, 2, 3, 4, [5, 6]]
>>> c = deepcopy(a)
>>> c
  [1, 2, 3, 4, [5, 6]]
>>> a[4][1] = 7
>>> c
  [1, 2, 3, 4, [5, 6]]
>>> a
  [1, 2, 3, 4, [5, 7]]

3)fromkeys:通过给定的键建立新的字典,每个键都对应 一个默认值None(注意返回值是一个字典,这个方法也可以用在字典的初始化上)

原型:aDict.fromkeys(seq[, val])

>>> {}.fromkeys(['name', 'age'])
   {'age': None, 'name': None}

>>> dict.fromkeys(['name', 'age'], '(unknow)')
   {'age': (unknow), 'name': (unknow)}

>>> {'name': 'yihuiwen', 'age': 25}.fromkeys(['score','male'])   #注意无论给的字典是什么,都返回一个新的字典
{'score': None, 'male': None}

注意:fromkeys并没有改变原始字典的值

>>> a = {'name': 'yihuiwen', 'age': 25}
>>> a.fromkeys(['score','male'])
  {'score': None, 'male': None}
>>> a
  {'age': 25, 'name': 'yihuiwen'}

4)get(用键值查找字典时,不存在该键值程序会出错,但是get不会出错(会返回None))

>>> d = {}
>>> print d['name']
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  KeyError: 'name'
>>> d = {}
>>> print d.get('name')
  None

get方法还可以自定义默认值,替换’None’

>>> d = {}
>>> print d.get('name', 'N/A')
N/A

注意:get、成员资格、has_key、setdefault的区别

5)has_key:相当于 k in d(d为字典)

注意:python3.0中不包含这个函数

>>> d = {}
>>> d.has_key('name')
  False
>>> d['name'] = 'Tom'
>>> d.has_key('name')
  True

6)items、iteritems

items将字典的所有项以列表的方式返回,列表中的每一项都表示为键值对的形式

iteritems跟items大致相同,就是返回值是一个迭代器

>>> d = {'age': 42, 'score': 99, 'name': 'Tom'}
>>> d.items()
  [('age', 42), ('score', 99), ('name', 'Tom')]

>>> d.iteritems()
  <dictionary-itemiterator object at 0x01CC7C30>
>>> print list(d.iteritems())
  [('age', 42), ('score', 99), ('name', 'Tom')]

7)keys、iterkeys

keys将字典的所有键值以列表的方式返回

iterkeys跟keys大致相同,就是返回值是一个迭代器

>>> d = {'age': 42, 'score': 99, 'name': 'Tom'}
>>> d.keys()
  ['age', 'score', 'name']

>>> d.iterkeys()
  <dictionary-keyiterator object at 0x01CE7990>
>>> list(d.iterkeys())
  ['age', 'score', 'name']

8)pop

pop用来获得对于给定键的值,并将这个键值对从字典中删除。(即有返回值,同时改变了原字典)

>>> d = {'x': 1, 'y': 2}
>>> d.pop('x')
  1
>>> d
  {'y': 2}
>>> d.pop()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: pop expected at least 1 arguments, got 0

9)popitem

popitem随机弹出字典中的项,返回值是键值对元组,同时改变了原字典的值(将键值对删除了)

>>> d = {'age': 42, 'score': 99, 'name': 'Tom'}
>>> d.popitem()
  ('age', 42)
>>> d
  {'score': 99, 'name': 'Tom'}

10)setdefault

与get类似,但是其增加了一个功能,就是如果给定的键值在字典中不存在,则会返回默认值并且相应的更新字典。

如果键值存在,那就返回了键值对应的值,不改变字典。默认值是可选的(默认值默认为None)。

>>> d = {}
>>> d.setdefault('name', 'Tom')
  'Tom'
>>> d
  {'name': 'Tom'}
>>> d.setdefault('name', 'Lucy')
  'Tom'
>>> d
{'name': 'Tom'}
>>> d = {}
>>> print d.setdefault('name')
  None
>>> d
  {'name': None}

11)update

update是利用一个字典项更新另一个字典。提供的字典中的项会添加到旧字典中,若有相同的键则会进行覆盖。

>>> d = {'age': 42, 'score': 99}
>>> x = {'name': 'Tom'}
>>> d.update(x)
>>> d
  {'age': 42, 'score': 99, 'name': 'Tom'}

12)values、itervalues

values将字典的所有值以列表的方式返回

itervalues跟values大致相同,就是返回值是一个迭代器

>>> d = {'age': 42, 'score': 99, 'name': 'Tom'}
>>> d.values()
  [42, 99, 'Tom']
>>> d.itervalues()
  <dictionary-valueiterator object at 0x01CE7990>
>>> print list(d.itervalues())
  [42, 99, 'Tom']

第5章 条件、循环和其他语句

5.1 print和import的更多信息

a) 使用逗号输出

在print函数中使用逗号将变量分隔开则在最后的结果中会在同一行打印出所有变量,变量中间用空格隔开。

>>> print 'Age:',42
  Age: 42

注意,这个时候的用法跟元组是不一样的。

>>> 1,2,3
  (1, 2, 3)
>>> print 1,2,3
  1 2 3
>>> print (1,2,3)
  (1, 2, 3)

如果在结尾处加上逗号,那么接下来的语句会与前一条语句在同一行打印

print 'Hello,',
print 'world!'

输出为Hello, world!

5.2 赋值魔法

a) 序列解包

= 的左右两边可以同时并行赋值,相同位置上的赋值给相同位置( 将多个值的序列解开,然后放到变量的序列中)

>>> x, y, z = 1, 2, 3
>>> print x,y,z
   1 2 3
>>> x, y = y, x
>>> print x,y,z
   2 1 3
>>> values = 1, 2, 3
>>> values
   (1,2,3)
>>> a, b ,c = values
>>> a
   1

这个功能最经常用到函数的返回值上,有利于函数返回两个以上的值。

注:等号两边的元素个数要一致,否则会报错

注意:序列解包跟收集参数逆过程的区别:(两个就是不同的概念,在序列解包的时候并不能使用收集参数的逆过程)

>>> a,b,*c = (1,2,3,4,5)
  File "<stdin>", line 1
    a,b,*c = (1,2,3,4,5)
        ^
SyntaxError: invalid syntax

b)  链式赋值

 x = y = 1

注意:上面的结果跟下面的结果不一定一样。

>>> x = y = somefunction()
>>> x = somefunction()
>>> y = somefunction()

c) 增量赋值

 x += 1
 。。。。

5.3 语句块:缩排的乐趣

就是缩进,python中一个语句块用:开始

5.4 条件和条件语句

a) 布尔值(注意在python中布尔值都是大写的True、False)

这些值在布尔表达式中为假: False(大写)   None  0   “”    ()   []   {}

其他一切都为真

注意:

>>> bool (bool("") == bool([]))
  True
>>> bool("" != [])
  True

b) 条件执行和if语句(注意:if、elif、else之后是不跟括号的。elif,在python中是没有else if的)

>>> num = input('Enter a number: ')
  Enter a number: 3
>>> if num > 0:
...     print 'The number is positive'
... elif num < 0:
...     print 'The number is negative'
... else:
...     print 'The number is zero'
...
  The number is positive

c) 比较运算符

在python中比较运算符和赋值运算符一样是可以连接的

>>> number <= 10 and number >= 1
等价于 
>>> 1<=number<= 10

d) 同一性运算符和相等运算符

is: 比较的是两个变量是不是同一个东西(即其地址也应该一样)

==: 比较两个变量的值是否一样

这个跟Java刚好相反

>>> x = y = [1,2,3]
>>> z = [1,2,3]
>>>
>>> x == y
  True
>>> x == z
  True
>>> x is y
  True
>>> x is z
  False

注意:判断不是同一个对象的时候:(is not)

>>> x is not y
  False

等价于

>>> not x is y
  False

e) in :成员资格运算符

x in y                 x是y容器的成员

x not in y          x不是y容器的成员

f) 字符串和序列比较

>>> 'alpha' < 'veta'
  True

注意:此时如果含有大小写就不一样了。可以用ord()函数查到一个字母的顺序值。

>>> ord('a')
  97
>>> ord('A')
  65

同时序列也是可以进行大小比较的。

>>> [1,2] < [2,1]
  True

如果序列中包含其他的序列元素也是可以比较的

>>> [2, [1,4]] < [2, [1,5]]
  True
>>> [2, [1,4]] < [2, 1]
  False
>>> [2, [1,4]] < [2, 1, 6]
  False
>>> [2, [1,4]] < [2, 2, 6]
  False

g) 布尔运算符(and、or、not

注意并不是&&、||、!

h) 短路逻辑和条件表达式

注意:在python中的布尔表达式只有在需要求值时才进行求值。

>>> name = raw_input('Please enter your name: ') or '<unknown>'
  Please enter your name: y
>>> name
  'y'
>>> name = raw_input('Please enter your name: ') or '<unknown>'
  Please enter your name:
>>> name
  '<unknown>'
>>> x = 9
>>> if x < 9 or bool(x=1):
...     print 'a'
...
  a
>>>
>>> x
  9

注意上述例子中,x的值并没有被改变

条件表达式(跟其他语言的三元表达式一样)

>>> x = 9
>>> y = x if x < 10 else 7
>>> y
  9

i)断言(assert)

要求某些条件必须为真。如果不为真,则程序中断,返回异常(可以用于程序调试中)

>>> age = 10
>>> assert 0 < age < 100

上边的代码运行正常

>>> age = -1
>>> assert 0 < age < 100

上边的代码会报错

assert之后还可以跟参数(如下)

 assert 0 < age < 100, 'The age must be realistic'

(断言后边可以添加字符串,用来解释断言)

5.5  循环

a) while循环

 >>> x = 1
>>> while x <= 10:
...     print x
...     x += 1
...
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10

b) for循环(注意在python中for语句只有这一种形式(即foreach的形式))

>>> for number in range(10):
...     print number
...
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9

c) 循环遍历字典元素(注意在python中对字典遍历,是对其键值的遍历)

>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> for key in d:
...     print key
...
  y
  x
  z

d) range 和 xrange

range 一次性生成整个序列,xrange一次只创建一个数,在需要迭代一个巨大的序列时xrange会更高效(主要是考虑内存损耗)

>>> type(range(10))
  <type 'list'>
>>> type(xrange(10))
  <type 'xrange'>

e) 并行迭代(zip)

zip可以将两个序列(字典和元组)压缩到一起,然后返回一个元组的列表

这样就可以在循环中并行迭代两个序列了。

for name, age in zip(names, ages):
     print name, age

注:zip可以处理不等长的序列,当最短的序列“用完”时就会停止

>>> type(zip(range(10), range(10, 0, -1)))
  <type 'list'>

e) 按索引循环(enumerate)

a = [1,2,3,4,1,2,3,4,5]
for index, num in enumerate(a):
    if num == 1:
        print a[index] = 4

enumerate这个函数可以在提供索引的地方迭代索引-值对(在迭代中使用的)

>>> type(enumerate(range(10)))
  <type 'enumerate'>

f) reversed、sorted(之前说过了就不赘述了)

reversed、sorted与列表的reverse、sort方法类似,但作用于任何序列或者可迭代对象上,不是原地修改,而是返回翻转或者排序后的版本

g) 跳出循环

break(结束循环)

>>> from math import sqrt
>>> for n in range(99, 0, -1):
...     root = sqrt(n)
...     if root == int(root):
...             print n
...             break
...
81

注意break只是跳出一层循环

>>> for x in range(6):
...     for y in range(10):
...             if y == 5:
...                     break
...             print (x,y)
...
  (0, 0)
  (0, 1)
  (0, 2)
  (0, 3)
  (0, 4)
  (1, 0)
  (1, 1)
  (1, 2)
  (1, 3)
  (1, 4)
  (2, 0)
  (2, 1)
  (2, 2)
  (2, 3)
  (2, 4)
...

continue:(跳过当前一轮循环,进入下一轮循环)

while True/break语句:

>>> while True:
...     word = raw_input('please enter a word: ')
...     if not word:
...             break
...     print 'The word was ' + word
...
please enter a word: a
  The word was a
please enter a word: b
  The word was b
please enter a word: c
  The word was c
please enter a word: d
  The word was d
please enter a word:
>>>

h)循环中的else子句

else子句仅在没有调用break时执行。

>>> from math import sqrt
>>> for n in range(99, 81, -1):
...     root = sqrt(n)
...     if root == int(root):
...             print n
...             break
... else:
...     print "Didn't find it!"
...
  Didn't find it!

注意:else只会判断平级的for中是否有break

>>> for x in range(6):
...     for y in range(10):
...             if y == 5:
...                     break
...             print x, y
... else:
...     print 'not'
...
  0 0
  0 1
  0 2
  0 3
  0 4
  1 0
  1 1
  1 2
...
  not

5.6  列表推导式——轻量级循环

 [x*x for x in range(10)]
>>> [x*x for x in range(10)]
  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

循环生成一个列表,并且在其中还可以使用if子句

>>> [x*x for x in range(10) if x % 3==0]
  [0, 9, 36, 81]

同时列表推导式还可以使用更多的for语句:

>>> [(x, y) for x in range(3) for y in range(3)]
  [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

注意:列表推导式跟循环生成器的区别

5.7  三人行

a) pass

不会执行任何东西,只是为了缩进时代码占位符使用

b) del

del删除的只是名称。不是值本身

>>> x = ['Hello', 'World']
>>> y = x
>>> del x
>>> print y
   ['Hello', 'World']

c) exec和eval

具体内容见  http://www.black-eleven.com/?p=80

第6章  抽象

6.3 创建函数

a) callable函数

>>> import math
>>> x = 1
>>> callable(x)
  False
>>> y = math.sqrt
>>> callable(y)
  True

内建函数callable可以用来判断函数是否可以调用

b) 函数的结构

def hello(name):
    return 'Hello, ' + name + '!'

>>> print hello('world')
  Hello, world!

a)文档化函数

这类字符串主要是出现在函数、模块、类的开头

如果在函数的开头写下字符串,他就会作为函数的一部分进行存储,这称为文档字符串。

def a():
    'hhhhhhh' # 文档字符串
    return 'hello'

查看文档字符串可以用 函数名.__doc__(即a.__doc__)进行查看。

文档字符串是作为一个变量存在的。

>>> def a():
...     'hhhhhhh' # 文档字符串
...     return 'hello'
...
>>> a.__doc__
  'hhhhhhh'
>>> a.__doc__()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: 'str' object is not callable

或者用help函数也可以显示出文档字符串

>>> help(a)
  Help on function a in module __main__:

  a()
      hhhhhhh

6.4  参数魔法

a) 对于“引用型的参数”(列表、字典之类),在函数内部是可以修改其具体的值的。

>>> def change(n):
...     n[0] = 'Mr. Gumby'
...
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> change(names)
>>> names
  ['Mr. Gumby', 'Mrs. Thing']

如果对于参数是不可变的(比如说数字,就没有办法在函数内部修改而修改外面的值)

想要实现这种功能可以将值放置在列表中

>>> def inc(x):
...     x[0] += 1
...
>>> foo = [10]
>>> inc(foo)
>>> foo
  [11]
>>>

b)关键字参数和默认值

之前的所使用的参数都叫做位置参数(即将参数按照定义的顺序一个一个往下排)。

关键字参数就是在调用的时候把参数名也写上

 hello(name='world', greeting='hello')

位置参数跟关键字参数可以同时使用,把位置参数放置在前面就可以了。

关键字参数可以在函数定义是就给出默认值:

def hello(name='world', greeting='hello'):
     print greeting, name

c)  收集参数

所谓收集参数就是收集其余位置的参数,将其放置在一个元组或字典当中。

位置参数对应着元组,使用的语法是(*a)

def print_params(*params):
    print params  # 返回值是一个元组

>>> print_params(1,2, 3)
   (1,2,3) #返回值是一个元组

关键字参数对应着字典,使用的语法是(**a)

def print_params(**params):
    print params # 返回值是一个字典

>>> print_params(x = 1,y = 2, z = 3)
    {'x': 1, 'y': 2,'z': 3}

此外两种收集参数还可以放在一起使用:

>>> def print_params_4(x, y, z=3, *pospar, **keypar): # 注意:*pospar跟**keypar这两个位置是不能错的,错误的话会抛出异常
...     print x, y, z
...     print pospar
...     print keypar
...
>>> print_params_4(1,2,3,5,6,7,foo=1,bar=2)
  1 2 3
  (5, 6, 7)
  {'foo': 1, 'bar': 2}
>>> print_params_4(1,2)
  1 2 3
  ()
  {}

d) 参数收集的逆过程

其实就是把元组(也可以是列表和字符串)或字典分开到参数中。

与收集参数相反的过程,就是在调用参数是添加*或**运算符,而不是在函数定义阶段使用。

>>> def add(x, y):
...     return x+y
...
>>> params = (1,2)
>>> add(*params)
  3

以上是位置参数收集的逆过程

>>> def hello_3(greeting = 'Hello', name = 'world'):
...     print '%s, %s!' % (greeting, name)
...
>>> params = {'name': 'yhw', 'greeting': 'Well met'}
>>> hello_3(**params)
Well met, yhw!

注: 位置参数对应的收集参数的逆过程跟关键字参数对应的收集参数的逆过程的位置是不能调换的,位置错误会抛出异常。

看看下列两个例子的区别:

>>> def with_stars(**kwds):
...     print kwds['name'], 'is', kwds['age'], 'years old'
...
>>> args = {'name': 'Mr. Gumby', 'age': 42}
>>> with_stars(**args)
  Mr. Gumby is 42 years old
>>> def without_stars(kwds):
...     print kwds['name'], 'is', kwds['age'], 'years old'
...
>>> args = {'name': 'Mr. Gumby', 'age': 42}
>>> without_stars(args)
  Mr. Gumby is 42 years old

6.5  作用域

在python中作用域可以看做是一个字典,(“不可见的字典”,可以用vars函数得到)。

>>> x = 1
>>> vars()
  {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'x': 1, '__doc__': None, '__package__': None}

那么在python中有几个这种字典呢。除了全局作用域外,每个函数的调用都会创建一个新的作用域

>>> def foo():
...     x = 42
...
>>> x = 1
>>> foo()
>>> x
  1
>>> def foo():
...     x = 21
...     print vars()
...
>>> x = 1
>>> foo()
  {'x': 21}

函数内部是可以访问全局变量的(但是这样做不是很好,会引发很多的错误)

注意:vars()、globals()、locals()、dir()

其中globals(),返回全局变量的字典;vars()跟locals()基本一样,返回局部变量的字典,dir则是返回局部变量的列表(参数名列表)

>>> def foo(x,y,z):
... m = 1
... k = 2
... print globals()
... print vars()
... print dir()
... print locals()
...
>>>
>>> foo(5,6,7)
  {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'x': 1, '__name__': '__main__', 'foo': <function foo at 0x01C19B30>, '__doc__': None}
  {'y': 6, 'x': 5, 'k': 2, 'z': 7, 'm': 1}
  ['k', 'm', 'x', 'y', 'z']
  {'y': 6, 'x': 5, 'k': 2, 'z': 7, 'm': 1}

重绑定全局变量:在函数内部,使用global就可以将变量声明为全局变量

>>> x = 1
>>> def change_global():
        global x
        x  = x + 1

嵌套作用域:

>>> def foo():
...     def bar():
...             print 'Hello World!'
...     bar()

>>> foo()
  Hello World!

一个函数位于另外一个函数的里面,外层函数返回里层函数。也就是说函数本身被返回了,但是并没有被调用。重要的是返回的函数还可以访问它的定义所在的作用域。换句话说,它带着它的环境(和相关的局部变量)。

>>> def multipiler(factor):
...     def multiplyByFactor(number):
...             return factor * number  # factor在外层被定义,在内层是可以使用的。 
...     return multiplyByFactor

>>> double = multipiler(2)
>>> double(5)
  10

每次调用外层函数,它内部的函数都会重新绑定,也就是factor变量每次都有一个新的值。

类似multiplyByFactor函数存储子封闭作用域的行为叫做闭包。

注:外部作用域的变量一般来说是不能被重新绑定的,但是在python3.0中,nonlocal关键字被引入。可以对外部作用域的变量进行赋值。

第7章  更加抽象

7.1  对象的魔力

a) 多态:所谓多态就是不用考虑形参的类型,传递参数都能得到正确的结果。由于python中的很多内建运算符和内建函数都有多态的性质,所以你写的代码使用这些运算符,就会与多态发生关联。事实上,唯一能够毁掉多态的就是使用函数显式的检查类型。应该尽量避免使用类型检查(type、isinstance、issubclass等函数)

b) 封装

c)继承

7.2  类和类型

a) 创建自己的类:

__metaclass__ = type #确定使用新式类
class Person:
    def setName(self, name):
        self.name = name

    def getName(self):
        return self.name

    def greet(self):
        print "Hello ,world! I'm %s. " % self.name

类的调用:

>>> foo = Person()
>>> bar = Person()
>>> foo.setName('lucy')
>>> bar.setName('Lily')
>>> foo.greet()
  Hello ,world! I'm lucy.
>>> bar.greet()
  Hello ,world! I'm Lily.
>>> Person.greet(foo)
  Hello ,world! I'm lucy.  # 还可以这么调用

注:python中是不需要用new去实例化类的。

对于self的理解:

self是方法中第一个参数(必须是self)。在方法被调用的时候,方法会将实例自动作为第一个参数传入方法中。所以命名为self。如果没有self的话,成员方法就没法访问他们要对其特性进行操作的对象本身了。

b)函数跟方法的区别:self参数就是方法跟函数的区别。方法将他们的第一个参数绑定到所属的实例上。因此你无需显式的提供该参数。

当然也可以将特性绑定到一个普通函数上,这样就不会有特殊的self参数了:

>>> class Class:
...     def method(self):
...             print 'I have a self!'
...
>>> def function():
...     print "I don't have a self!"
...
>>> instance = Class()
>>> instance.method()
  I have a self!
>>> instance.method = function
>>> instance.method()
  I don't have a self!

注意,self参数并不依赖于调用方法的方式,前面我们使用的是instance.method(实例.方法)的形式,可以随意使用其他变量引用同一个方法:

>>> class Bird:
...     song = 'Squaawk!'
...     def sing(self):
...             print self.song
...
>>> bird = Bird()
>>> bird.sing()
  Squaawk!
>>> birdsong = bird.sing
>>> birdsong()
  Squaawk!

尽管最后一个方法调用看起来与函数调用十分相似,但是变量birdsong引用绑定方法bird.sing上,也就意味着这还是会对self参数进行访问(也就是说,它仍旧绑定到类的相同实例上)。

c) python并不直接支持私有方式(变量跟函数)。外部是可以对实例中的变量进行修改的。

但是可以一些小技巧达到目的。

 class ssss:
    def __bbb(self):
        print "aaaa"
    def aaaa(self):
        print "bbbb"

上面的代码中,在类的外部是无法访问__bbb方法的,但是在类的内部还是能使用__bbb方法。

原因是在类的内部定义中,所有以双下划线开始的名字都被“翻译”成前面加上单下划线和类名的形式。

要调用上边的__bbb方法就需要:

 s._ssss__bbb()   # 其中s为ssss的实例

注:前面有下划线的名字都不会被带星号的import语句(from module import *)导入

d)  类的命名空间

在定义类时,所有位于class语句中的代码都在特殊的命名空间中执行——类命名空间。这个命名空间可由类内所有成员访问。

类的定义其实就是执行代码块。(在类的定义去并不只限定只能使用def语句)

>>> class C:
...     print 'Class C being defined...'
...
Class C being defined...
>>> class MemberCounter:
...     members = 0
...     def init(self):
...             MemberCounter.members += 1
...
>>> m1 = MemberCounter()
>>> m1.init()
>>>
>>> MemberCounter.members
  1
>>> m2 = MemberCounter()
>>> m2.init()
>>> MemberCounter.members
  2

上面的代码中,在类作用域内定义了一个可供所有成员(实例)访问的变量。

注:在类的内部,self.a与类名.a 的区别:

>>> class A():
...     a = 0
...     def changeSelfA(self):
...             self.a += 10
...     def changeClassA(self):
...             A.a -= 10
...
>>> ia1 = A() # 这个时候执行了类的代码块,此时只有一个类空间中的a,并没有所谓的self.a
>>> ia1.a     # 这个时候的ia1.a其实就是类的a
  0
>>> A.a       # 类的变量
  0
>>> ia1.changeSelfA()  # 这个时候运行了changeSelfA,那么就产生了self.a这个变量,同时这个变量是用的类中的A进行初始化的。同时这个变量由于作用域的关系屏蔽了类中的a变量
>>> ia1.a     # 此时是实例中的self.a
  10
>>> A.a       # 类中的a变量并没有被改变
  0
>>> ia1.changeClassA()  # 这里改变了类中的变量
>>> ia1.a     # 可以看到self.a并没有被改变,因为这个变量跟类中的a就是两个变量,只是名字一样而已,并且类中的a变量被屏蔽了
  10
>>> A.a       # 类中的a变量被改变了
  -10

e)指定超类、检查继承

 class  subclass(fatherclass):
    。。。。

对于继承:1. 我可以重写方法(重新定义之前的方法)   2. 还可以继承原有的方法(不需要去重新编写,就可以直接调用)

检查继承: 使用内建函数就可以

 issubclass(子类,父类)
>>> class classA:
...     a = 1
...
>>> class classB(classA):
...     b = 1
...
>>> issubclass(classB, classA)
True

想要知道一个类的父类,可以直接使用它的特殊特性__bases__

>>> classB.__bases__
  (<class __main__.classA at 0x01BF9490>,)

对于新式类还有__base__属性

>>> class classA(object):
...     a = 1
...
>>> class classB(classA):
...     b = 1
...
>>> classB.__base__
  <class '__main__.classA'>

检查一个对象是否是一个类的实例

 isinstance(实例,类)
>>> b = classB()
>>> isinstance(b, classB)
True

f)多重继承

 class subclass(fatherclass1, fatherclass2):
    。。。。

注:多个父类中有相同名字的方法时:先继承的类中的方法会重写后继承的类中的方法。也就是说子类会继承放在前面的方法,而不会继承后面类的方法

g)  接口和内省

对于接口的理解:

所谓的接口就是定义一些必须实现的方法和变量,然后便于其他的一些函数能够调用(其实还可以理解为就是对函数的限定(原因是其他的函数有用到了这个类中的这些方法,如果没有实现程序会出错))。所以接口就是对类的一种限定。

在python中,不用显式地指定对象必须包含哪些方法才能作为参数接收。例如,不用(像在java中一样)显式地编写接口,可以在使用对象的时候假定它可以实现你所要求的行为。如果它不能实现的话,程序就会失败。

一般来说只需要让对象实现当前的方法,但是还可以更灵活一些。除了调用方法然后期待一切顺利之外,还可以检查所需方法是否已经存在,如果不存在,就需要去做其他的事情(异常处理之类的)。

hasattr():判断对象中是否有相应的特性

>>> class classA():
...     aaa = 0
...     def bbb(self):
...             print 'hahaha'
...
>>> hasattr(classA, 'aaa')
  True
>>> hasattr(classA, 'bbb')
  True
>>> a = classA()
>>> hasattr(a, 'aaa')
  True
>>> hasattr(a, 'bbb')
  True

callable():判断特性是否能调用(在python3.0中已不再可用了)

>>> callable(classA.aaa)
  False
>>> callable(classA.bbb)
  True
>>> callable(a.aaa)
  False
>>> callable(a.bbb)
  True

可以用hasattr(x, ‘__call__’)代替callable(x)

>>> hasattr(classA.aaa, '__call__')
  False
>>> hasattr(classA.bbb, '__call__')
  True
>>> a = classA()
>>> hasattr(a.aaa, '__call__')
  False
>>> hasattr(a.bbb, '__call__')
  True

getattr() :用来得到特性的。对于类的特性,不能直接调用(当然是可以调用的特性)(原因是类方法第一个参数是self类的特性没有提供这个值)。对于实例的特性,可以直接调用的。

>>> getattr(classA, 'bbb')
  <unbound method classA.bbb>
>>> ccc = getattr(classA, 'bbb')
>>> ccc()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: unbound method bbb() must be called with classA instance as first argument (got nothing instead)
>>> ccc(classA())
  hahaha
>>> a = classA()
>>> ddd = getattr(a, 'bbb')
>>> ddd()
  hahaha

getattr函数允许提供默认值以便在特性不存在时使用。

>>> kkk = getattr(classA, 'ccc', None)

setattr():这个函数是相对于getattr的,可以用来设置对象的特性:

>>> setattr(classA, 'ccc', 'abc')
>>> classA.ccc
  'abc'
>>> a
  <__main__.classA instance at 0x01CC9058>
>>> a.ccc
  'abc'
>>> setattr(a, 'ccc', 'cba')
>>> classA.ccc
  'abc'
>>> a.ccc
  'cba'

__dict__ : 这个属性可以查看对象内所有存储的值,但是有些类没有这个属性。

>>> math.sin
  <built-in function sin>
>>> math.sin.__dict__
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  AttributeError: 'builtin_function_or_method' object has no attribute '__dict__'
>>> classA.__dict__
  {'__module__': '__main__', 'aaa': 0, 'bbb': <function bbb at 0x01C0C2B0>, '__doc__': None}
>>> a = classA()
>>> a.__dict__
  {}
>>> class classA():
...     a = 0
...     def pr(self):
...             self.a += 1
...             print self.a
...
>>> classA.__dict__
  {'a': 0, 'pr': <function pr at 0x01C0C1B0>, '__module__': '__main__', '__doc__': None}
>>> a = classA()
>>> a.__dict__
  {}
>>> a.pr()
  1
>>> a.__dict__
  {'a': 1}

上面这个例子可以佐证我对self.a与类名.a 的区别中的描述。

inspect模块(可以看到对象的组成)

第8章  异常

8.1 什么是异常

Python用异常对象磊表示异常情况。如果异常对象并没有被处理或捕捉,程序就会用回溯终止执行

>>> 1/0
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  ZeroDivisionError: integer division or modulo by zero

异常不仅仅可以回溯,而且还可以用很多种办法进行捕捉,使得程序可以捉住错误并对其进行处理,而不是让整个程序失效。

8.2  按自己的方式出错

a) raise语句

为了引发异常,可以使用一个类(应该是Exception的子类)或者这些类的实例参数调用raise语句。使用类时,程序会自动创建类的一个实例。

>>> raise Exception  # 使用一个类调用raise语句
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  Exception
>>> raise Exception('hyperdrive overload')   # 使用一个类的实例调用raise语句
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  Exception: hyperdrive overload

 

内建的异常类都可以在exceptions模块中找到。可以使用dir函数列出模块内容。

>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', ......

所有上述的异常都可以用在raise语句中。

>>> raise ArithmeticError
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  ArithmeticError

一些内建异常

类名 描述
Exception 所有异常类的基类
AttributeError 特性引用或赋值失败时引发
IOError 试图打开不存在文件(包括其他情况)时引发
IndexError 在使用序列中不存在的索引时引发
KeyError 在使用映射中不存在的键时引发
NameError 在找不到名字(变量)时引发
SyntaxError 在代码为错误形式时引发
TypeError 在内建操作或者函数应用于错误类型的对象时引发
ValueError 在内建操作或者函数应用于正确类型的对象,但是该对象使用不合适的值时引发
ZeroDivisionError 在除法或者模除操作的第二个参数为0时引发

b) 自定义异常类

每个异常都是一些类的实例,同时我们可以自定义自己的异常类(只要继承异常类就好了)

 class MyError(Exception):
    pass

定义自己的异常类有什么用呢???

感觉自己的异常类只能将其抛出,之后在调用函数的地方捕捉异常并进行异常处理。(下面是我能想到的用法了)

>>> class MyError(Exception):
...     pass
...
>>> def isNone(a):
...     if a==None:
...             raise MyError('a is None')
...
>>> isNone(1)
>>> isNone(0)
>>> isNone(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in isNone
__main__.MyError: a is None
>>> try:
...     isNone(None)
... except MyError,e:
...     print e
... except Exception,b:
...     print b
...
a is None

8.3  捕捉异常

异常最有意思的地方就是我们可以处理它们,使得程序不至于崩溃。

try/except语句

try:
    pass
except:
    pass

例子:

x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y

运行结果如下:

>>> python errorTry.py
  Enter the first number: 10
  Enter the second number: 0
  Traceback (most recent call last):
    File "errorTry.py", line 7, in <module>
      print x/y
  ZeroDivisionError: integer division or modulo by zero

为了进行一些错误处理可以这样写程序:

try:
    x = input('Enter the first number: ')
    y = input('Enter the second number: ')
    print x/y
except ZeroDivisionError:
    print 'The second number can not be zero!'

运行结果如下:

>>> python errorTry.py
  Enter the first number: 10
  Enter the second number: 0
  The second number can not be zero!

这样程序就不至于出错。

同时这个时候还可以使用if语句进行判断,但是if语句远没有异常处理来得简洁。因为如果要给程序中加入更多的除法,那么就需要更多的if语句,但是对于异常处理只需要一个。

要善于使用异常处理。

同时我们在捕捉到异常的时候,还可以接着将抛出异常 raise(类似于Java中的Throw)

8.4 不止一个except子句

如果在try中的代码会出现不止一处的异常,那么可以用多个except子句分别对不同的异常进行捕捉。具体语法如下:

try:
    pass
except ZeroDivisionError:
    pass
except TypeError:
    pass

例:

>>> try:
...     x = input('Enter the first number: ')
...     y = input('Enter the second number: ')
...     print x/y
... except ZeroDivisionError:
...     print 'The second number can not be zero!'
... except TypeError:
...     print 'That was not a number, was it?'
...
  Enter the first number: 10
  Enter the second number: 0
  The second number can not be zero!

8.5  用一个块捕捉两个异常

同样的,如果有两个或者以上的异常的处理过程是一样的,我们还可以将其合并,在一个块中捕捉两个或以上的异常。

语法如下:

try:
    pass
except (ZeroDivisionError, TypeError):
    pass

注: except子句中异常对象外面的圆括号很重要,忽略他们会得不到想要的结果。

8.6  捕捉对象

如果异常处理中需要访问异常对象本身,那么可以使用变量,这样python就会自动将异常对象保存在这个变量中的。

try:
    pass
except (ZeroDivisionError, TypeError),e:   # 在3.0之后,except (ZeroDivisionError, TypeError) as e:
    print e
    pass

8.7  真正的全捕捉

try:
    pass
except:
    pass

这样可以捕捉所有的错误。

但是这样做不提倡,因为这会隐藏了程序员没有考虑到的一些异常。同样还会捕捉用户的终止命令(ctrl+ C)以及用sys.exit函数终止程序的企图。

产生异常并不是一件坏事,有的时候忽略所有的异常,还不如让程序立刻崩溃。

此时用   except Exception,e:会更好

>>> try:
...     x = input('Enter the first number: ')
...     y = input('Enter the second number: ')
...     print x/y
... except Exception,e:
...     print e
...
  Enter the first number: 10
  Enter the second number: 0
  integer division or modulo by zero

8.8  万事大吉

在没有发生任何异常的情况下,想要执行一段代码,可以使用else子句进行。

try:
    pass
except:
    pass
else:
    print "aaa"  # 只有在没有发生异常的时候才会运行

例:

while True:
    try:
        x = input('Enter the first number: ')
        y = input('Enter the second number: ')
        value = x/y
        print 'x/y is',value
    except:
        print 'Invalid input. Please try again.'
    else:
        break

运行结果为:

Enter the first number: 10
Enter the second number: 0
Invalid input. Please try again.
Enter the first number: i
Invalid input. Please try again.
Enter the first number: 10
Enter the second number: 1
x/y is 10

8.9  最后。。。。

finally与java final的用法是一样的

finally子句用于关闭文件或者网络套接字时会非常有用。

>>> x = None
>>> try:
...     x = 1/0
... finally:
...     print 'cleaning up...'
...     del x
...
  cleaning up...
  Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
  ZeroDivisionError: integer division or modulo by zero

在一条语句中还可以组合使用try、except、finally、else(或者其中三个),但是一定要有try。

>>> try:
...     1/0
... except NameError:
...     print 'Unknown variable'
... else:
...     print 'That went well!'
... finally:
...     print 'Cleaning up.'
...
  Cleaning up.
  Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
  ZeroDivisionError: integer division or modulo by zero

8.10  异常和函数

异常跟函数可以很自然的一起工作。如果异常在函数内引发而不被处理,他就会传播至(浮到)函数调用的地方。如果在那里也没有处理异常,他就会继续传播,一直到达主程序(全局作用域)。如果那里没有异常处理程序,程序会带着栈跟踪中止。

>>> def faulty():
...     raise Exception('Something is wrong')
...
>>> def ignore_exception():
...     faulty()
...
>>> def handle_exception():
...     try:
...             faulty()
...     except:
...             print 'Exception handled'
...
>>> ignore_exception()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 2, in ignore_exception
    File "<stdin>", line 2, in faulty
  Exception: Something is wrong
>>> handle_exception()
  Exception handled

 

8.11 异常之道

有些时候,条件语句可以实现和异常处理同样的功能,但是条件语句在自然性和可读性上差些。但是也不是绝对的,还是需要具体情况具体分析。

>>> def getItem(myDict):  # if语句实现
...     if 'key1' in myDict:
...             print myDict['key1']
...
>>> def getItemErr(myDict):   # 用异常方式实现
...     try:
...             print myDict['key1']
...     except KeyError:
...             pass

可以看到if语句的实现比较直观,但是效率不高。而异常的方式效率就比if的高一点点了。

在查看对象是否存在特定特性时,try/except也很有用。

>>> try:
...     a = A()
...     a.a
... except AttributeError:
...     print 'not writeable'
... else:
...     print 'writeable'
...
  1
  writeable

以上所说的效率提高并不多(微乎其微)。但是在很多情况下,使用try/except语句比使用if/else会更自然一些(更Python化),应该养成异常的习惯。

第9章  魔法方法、属性和迭代器

魔法方法:在Python中,有一些特定的方法(一般以__开头),如果对象实现了这些方法中的某一个,那么这个方法会在特殊的情况下被python调用,而几乎用户没有调用他们的必要。

9.1  准备工作

>>> class NewStyle(object):
...     more_code_here

>>> class OldStyle:
...     more_code_here

新式类: 要将类定义为新式类,两种方法:

1) 把赋值语句__metaclass__=type放在你的模块的最开始(设置元类)

 class NewstyleClass:
    __metaclass__ = type
    .....

2) (直接或者间接)子类化内建类object(或者其他一些新式类)

在没有兼容之前版本的需要时,建议将所有类写为新式类,并且使用super函数这样的特性。

9.2  构造方法

当一个对象被创建的时候,会立即调用构造方法。

之前,我们要初始化类,还需要自己写一个init方法,并且还需要自己进行调用。

>>> class FooBar:
...     def init(self):
...             self.somevar = 42
...
>>> f = FooBar()
>>> f.init()
>>> f.somevar
  42

现在只是一些动作python内部帮你完成了

>>> class FooBar:
...     def __init__(self):
...             self.somevar = 42
...
>>> f = FooBar()
>>> f.somevar
  42

而且构造函数__init__函数还可以带参数的。我们给参数的值的时候就是在实例化类的时候。

>>> class FooBar:
...     def __init__(self, value=42):
...             self.somevar = value
...
>>> f = FooBar()
>>> f.somevar
  42
>>> d = FooBar(90)
>>> d.somevar
  90

注:在Python中还有一个魔法方法叫__del__,析构函数,他是在对象要被垃圾回收之前调用。但是具体调用的时间是不可知的,所以尽量避免使用。

a) 重写一般方法

如果一个方法在B类中的一个实例中被调用(或者一个属性被访问),但在B类中没有找到该方法,那么就会去它的超类A里面找。

>>> class A:
...     def hello(self):
...             print 'Hello, I am A.'
...
>>> class B(A):
...     pass
...
>>> a = A()
>>> b = B()
>>> a.hello()
  Hello, I am A.
>>> b.hello()
  Hello, I am A.

在子类中增加功能的最基本方式就是增加方法。但是也可以重写一些超类的方法来自定义继承行为。

>>> class B(A):
...     def hello(self):
...             print 'Hello, I am B.'
...
>>> b = B()
>>> b.hello()
  Hello, I am B.

b)  子类调用父类构造方法:

构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。那么怎么调用超类的初始化代码呢?不调用超类的初始化代码会出现什么问题?

首先我们先说说不调用初始化代码会出现的问题:

>>> class Bird:
...     def __init__(self):
...             self.hungry = True
...     def eat(self):
...             if self.hungry:
...                     print 'Aaaah...'
...                     self.hungry = False
...             else:
...                     print 'No, thanks!'
...
>>> b = Bird()
>>> b.eat()
  Aaaah...
>>> b.eat()
  No, thanks!

上面定义了一个Bird类

>>> class SongBird(Bird):
...     def __init__(self):
...             self.sound  = 'Squawk!'
...     def sing(self):
...             print self.sound
...
>>> sb = SongBird()
>>> sb.sing()

上面的初始化中并没有调用超类的构造方法

>>> sb.eat()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 5, in eat
  AttributeError: SongBird instance has no attribute 'hungry'

显然,上边因为没有hungry变量而报错了,为什么会报错呢?首先超类的构造函数被子类的构造函数给屏蔽了,而子类中并没有调用超类的构造函数,所以导致hungry变量就没有被赋值,因此报错了。

因此在对子类进行初始化的时候,一定要注意对父类进行相应的初始化。要不有可能会丢失对一些值的赋值,导致在调用一些父类的方法时缺失一些值,导致程序出错。

但是在对父类进行初始化的时候还有有点复杂的,主要需要使用下面的两种方法进行:

1) 调用未绑定的超类构造方法:

class fatherclass:
    def __init__(selfd, b):
        self.b = b
    def a():
        pass

class subclass(fatherclass):
    def __init__(self, b):
        fatherclass.__init__(self, b)    # 这里就是调用未绑定的超类构造方法
    ……

注: 一般在调用一个实例的方法是,该方法的self参数会被自动绑定到实例上(这就是绑定方法)。

意思就是原本方法的第一个参数是python自动帮你绑定到实例上的,但是现在由于要调用超类的方法,而这时还没有实例产生呢,所以只能我们自动的将第一个参数绑定到超类的构造方法中。

2)使用super函数

这个方式只能用在新式类中。super函数是以当前的类和对象作为参数的。使用super方法的返回值调用的任何方法都是调用超类的方法,而不是当前类的方法。这个时候__init__方法就能以一个普通的方法被调用了。

class fatherclass:
    def __init__(selfd, b):
        self.b = b
    def a():
        pass

class subclass(fatherclass):
    def __init__(self, b):
        super(subclass, self).__init__(b)  # 在初始化的时候这里一定要注意super里边的是子类的类名,不是父类的类名。这样做的好处是,当更改了父类的类名的时候
                                             不需要更改子类中的初始化函数
    ………………

注:在python3.0之后还可以直接使用super().__init__()进行对父类的初始化。

注意:super(subclass, self)的参数是子类的类和对象实例。并不需要提供父类的参数。这一点很重要。

详细的内容还可以查看  python super()【转】

9.3  成员访问(本节主要是创建行为类似于序列或者映射的对象)

a) 基本的序列和映射的规则很简单,但如果要实现它们全部的功能就需要实现很多的魔法方法。

那么上面所说的规则是个什么鬼:

规则:用来描述管理某种形式的行为的规则。跟接口的概念类似。规则说明了应该实现何种方法和这些方法应该做什么。

注意:在其他的语言中对象可能被要求属于某一个类,或者被要求实现某个接口,但是Python的要求只是简单的要求它遵守几个给定的规则。例如:想要成为一个序列,只需要遵循序列的规则就好了。

b) 基本的序列和映射的规则

根据序列(映射)的规则,如果实现的序列(映射)是不可变的,那么需要实现两个魔法方法,如果是可变的,则需要实现4个魔法方法

__len__(self) :

这个方法需要实现的就是返回集合中所含项目的数量。如果 __len__返回0(并且没有实现重写该行为的__nonzero__),对象会被当做一个布尔值中的假值进行处理。

__getitem__(self, key)  : 这个方法需要实现的就是返回与所给键值对应的值。对于序列来说他的键值是一个整数(或者是一个负数),对于映射来说,其键值可以是任何的值。

__setitem__(self, key, value) : 这个方法应该按一定的方式存储和key相关的value。只能为可修改的对象定义__setitem__。

__delitem__(self, key): 这个方法在删除对象中的一部分是调用(即在使用del方法的时候被调用),必须同时删除键跟其所对应的值。只能为可修改的对象定义__delitem__。

注:对于上面方法有几个附加条件:

1)对于一个序列来说,如果键是负整数,那么要从末尾开始计数,换句话说就是x[-n] == x[len(x) – n]

2)如果键是不合适的类型,应该引发一个TypeError的异常(即这些异常应该是用户自己定义抛出的,这就是规则中定义的)

3)如果序列的索引是正确的类型,但是超出了范围,应该引发一个IndexError的异常(即这些异常应该是用户自己定义抛出的)

下面看一个例子,我们创建一个无穷序列:

>>> class ArithmeticSequence:
...     def checkIndex(self, key):
...             if not isinstance(key, (int, long)):
...                     raise TypeError
...             if key<0:
...                     raise IndexError
...     def __init__(self, start=0, step=1):
...             self.start = start
...             self.step = step
...             self.changed = {}
...     def __getitem__(self, key):
...             self.checkIndex(key)
...             try:
...                     return self.changed[key]
...             except KeyError:
...                     return self.start + key*self.step
...     def __setitem__(self, key, value):
...             self.checkIndex(key)
...             self.changed[key] = value

上面就是自己构造的一个无穷序列的例子,实现了__getitem__方法跟__setitem__方法,因此就可以对这个序列进行索引跟赋值的操作。

>>> s = ArithmeticSequence(1, 2)
>>> s[4]
  9
>>> s[4] = 2
>>> s[4]
  2
>>> s[5]
  11

但是并没有实现__delitem__跟__len__方法,所以调用它的len(s)跟del s[4]方法是不合法的。

>>> del s[4]
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  AttributeError: ArithmeticSequence instance has no attribute '__delitem__'
>>>
>>> len(s)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  AttributeError: ArithmeticSequence instance has no attribute '__len__'

同时他对索引的类型进行了限制(使用了checkIndex方法)。这样就实现了跟序列一样的对索引的要求了。(注意:checkIndex并不是一个魔法方法,还是需要自己在必要的时候进行调用的)

>>> s['four']
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 12, in __getitem__
    File "<stdin>", line 4, in checkIndex
  TypeError
>>> s[-24]
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 12, in __getitem__
    File "<stdin>", line 6, in checkIndex
  IndexError

注:在对序列的键值检查中,是会使用isinstance函数的。因为Python语言规定中明确指出序列的索引为整数,所以可以 使用isinstance函数破坏多态特性的。

c) 子类化列表,字典和字符串

如果要实现一个功能完整的序列(映射),不仅要实现上述的四个基本的方法,还有很多的方法需要实现,这是一项繁重的工作。那么最好的解决方法是什么呢?那就是继承。

在使用自己编写的序列或者映射时,可以继承python中的UserList、UserString、UserDict类。这样就不需要全部实现以上的魔法方法了,只需要重写你需要增加功能的方法就好了。

>>> class CounterList(list):
...     def __init__(self, *args):
...             super(CounterList, self).__init__(*args)
...             self.counter = 0
...     def __getitem__(self, index):
...             self.counter += 1
...             return super(CounterList, self).__getitem__(index)

上面的实现的list添加了对调用次数的统计。

>>> cl =  CounterList(range(10))
>>> cl
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> cl.reverse()
>>> cl
  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> del cl[3:6]
>>> cl
  [9, 8, 7, 3, 2, 1, 0]
>>> cl.counter
  0
>>> cl[4] + cl[2]
  9
>>> cl.counter
  2

可以看到上述的方法可以调用list的方法进行相应操作。

9.5  属性

通过访问器定义的特性被称为属性。那么为什么python要设计属性这个东西呢?

首先,如果没有属性这个东西的时候,我们想要访问一个并不是真实存在的特性(如下面的size)。

>>> class Rectangle:
...     def __init__(self):
...             self.width = 0
...             self.height = 0
...     def setSize(self, size):
...             self.width, self.height = size
...     def getSize(self):
...             return  self.width, self.height

这个时候就必须调用setSize和getSize方法了。但是如果有一天size变成一个真的特性,然后width变成一个需要用size计算的特性。这个时候所有使用到Rectangle的类的地方全部都要进行修改。

为了屏蔽这个问题,python设计了属性这个概念。对于所有的变量,无论是真实存在的还是不存在的(需要计算得到的)都可以使用r.size的形式调用。(这个设计还是很牛逼的)

a) property函数(property必须在新式类中使用)

 class Rectangle:
    __metaclass__ = type
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self, size):
        self.width, self.height = size
    def getSize(self):
        return self.width, self.height
    size = property(getSize, setSize)   # 对于属性其实就是多了一句这个

在这个类中property函数创建了一个属性,其中参数为访问器函数(先是取值,然后是赋值),这个属性为size,之后就可以使用这个属性了。

>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.size
   (10, 5)

>>> r.size = 150, 100
>>> r.width
   150

可以看出,size就不需要直接调用getSize和setSize了,这样这些函数就被封装起来了,类的外部不用知道内部的实现了。

property方法原型:       property(fget, fset, fdel, doc)

p.s

1) property函数可以用0、1、2、3或者4个参数来调用。

2) 没有参数: 产生的属性既不可读,也不可写

3) 1个参数(取值方法): 产生的属性是只读的

4) property函数的第三个参数是一个用于删除特性的方法(他不要参数的)

5) 第四个参数是一个文档字符串。

property的4个参数分别叫做fget、fset、fdel和doc。

如果想要实现一些特殊的属性,就可以使用关键字参数的方式来实现。

b)  静态方法和类成员方法

 class MyClass:
    __metaclass__ = type
    
    def smeth():
        print 'This is a static method'
    smeth = staticmethod(smeth)

    def cmeth(cls):
        print 'This is a class method of', cls
    cmeth = classmethod(cmeth)

静态方法和类成员方法都可以直接用类名调用。至于他们两个的区别详见:http://www.black-eleven.com/?p=210

在python2.4之后,引入了一个叫做装饰器的新语法(能对任何可调用的对象进行包装,既能够用于方法也能用于函数)

装饰器是使用@操作符,在方法(或函数)上方将装饰器列出,就可以制定一个或者多个的装饰器(多个装饰器在应用时的顺序与指定顺序相反)

 class MyClass:
    __metaclass__ = type
    
    @staticmethod
    def smeth():
        print 'This is a static method'

    @classmethod
    def cmeth(cls):
        print 'This is a class method of', cls

定义了方法后就可以直接用类名进行调用这些方法:

>>> MyClass.smeth()
   This is a static method

>>> MyClass.cmeth()
   This is a class method of <class '__main__.MyClass'>

>>> mc = MyClass()
>>> mc.smeth()
  This is a static method
>>> mc.cmeth()
  This is a class method of <class '__main__.MyClass'>

p.s. 两者的具体内容详见:http://www.black-eleven.com/?p=210

c) 旧式类实现属性

在Python中可是实现对所有特性访问的拦截,而这个拦截是通过几个魔法方法实现的。(实际上就是在访问所有特性的时候python会调用这几个魔法方法)。

通过对所有特性的访问拦截来实现了属性这个东西。

__getattribute__(self, name) : 当特性name被访问时自动被调用(只能在新式类中使用)

__getattr__(self, name): 当特性name被访问且对象没有相应的特性时被自动调用。

__setattr__(self, name, value):当试图给特性name赋值时会被自动调用

__delattr__(self, name): 当试图删除特性name时被自动调用。

尽管和使用property函数相比有点复杂(而且在某些方面效率更低),但是这些特殊方法是很强大的。因为可以对处理很多属性的方法进行再编码。

 class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0

    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.height = value
        else:
            self.__dict__[name] = value

    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.height
        else:
            raise AttributeError

注:

1) __setattr__方法在所涉及的特性不是size时也是会被调用的。所以这个方法就有两个方面的情况,如果name是size,那么就跟之前使用属性是一样的。如果name不是属性的时候,那么就要使用特殊的方法__dict__,该特殊的方法包含一个字典,字典里面是所有实例的属性。

那么,为什么要引入这个__dict__函数呢? 主要是为了避免__setattr__方法被再次调用(这样会使程序陷入死循环),__dict__方法被用来代替普通的特性的复制操作。

2)__getattr__方法只在普通的特性没有被找到的时候调用。对于上面的代码,如果给定的name不是size,这个特性不存在,方法就会引发一个AttributeError的异常。

9.6 迭代器

想要让对象像序列和字典一样的进行迭代,就需要实现该对象的__iter__方法。

对于__iter__ 方法会返回一个迭代器,所谓的迭代器就是具有next方法的对象。在调用next方法时,迭代器就会返回他的下一个值。如果next方法被调用,但是迭代器没有下一个值可以返回,那么就会引发一个StopIteration异常。

注:对于python3.0 迭代器对象应该实现__next__ 方法,而不是next。同时有了一个新的内建函数next可以用于访问这个方法。(换句话说,next(it)等同于it.next())

至于为什么要用迭代器呢?为啥不用list呢?原因是对于很多值的一些变量时,list是一次性将所有的值都加载到内存中,这样程序就不够优雅、高效了。而且list的长度必须有限的。

 class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1

    def next(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

    def __iter__(self):
        return self

注意: 正式的说法是,一个实现了__iter__方法的对象是可迭代的(即其可以在for语句中使用(或者之后说的使用类似list(fibs)的方法时),如果不实现这个方法,那么就会报“TypeError: iteration over non-sequence”的错误),一个实现了next方法的对象则是迭代器。

>>> fibs = Fibs()
>>> for f in fibs:
        if f > 1000:
            print f
            break
   1597
>>> fibs = Fibs()
>>> print fibs.next()
   1
>>> print fibs.next()
   1
>>> print fibs.next()
   2
>>> print fibs.next()
   3
>>> print fibs.next()
   5
>>> print fibs.next()
   8

此外,还可以从迭代器中得到序列

class TestIterator:
    value = 0
    def next(self):
        self.value += 1
        if self.a > 10:
            raise StopIteration
        return self.value

    def __iter__(self):
        return self
>>> ti = TestIterator()
>>> list(ti)
   [1,2,3,4,5,6,7,8,9,10]

9.7  生成器

生成器是一种用普通的函数语法定义的迭代器。

a)  创建和使用生成器

 def flatten(nested):
    for sublist in nested:
        for element in sublist:
            yield element

任何包含yield语句的函数成为生成器。生成器不是一次性产生所有的值,而是每次产生一个值,函数就会被冻结:即函数停止在那点等待被重新唤起,函数被重新唤起后就从停止的位置开始执行。

生成器可以使用for语句进行调用

>>> nested = [[1,2],[3,4],[5]]
>>> for num in flatten(nested):
        print num

   1
   2
   3
   4
   5

生成器也可以使用list方法生成序列

>>> list(flatten(nested))
   [1,2,3,4,5]

生成器还可以用next进行调用

>>> nested = [[1,2],[3,4],[5]]
>>> g = flatten(nested)
>>> g.next()
   1
>>> g.next()
   2
>>> g.next()
   3
>>> g.next()
   4

循环生成器

>>> g = ((i+2)**2 for i in range(2, 27))
>>> g.next()
   16

注意:循环生成器跟列表推导式区别就是循环生成器是用的(),而列表推导式是用[]。此外,循环生成器并没有一次生成所有的值,而是每调用一次next()函数生成一个值。但是列表推导式是一次性生成所有的值。因此在值的数量少的时候建议使用列表推导式,而在值的数量很多时,则应该使用循环生成器。

b) 递归生成器

生成器也是可以放到递归中使用的:

def flatten(nested):
    try:
        # 不要迭代类似字符串的对象:
        try:
            nested + ''
        except TypeError:
            pass
        else:
            raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested
>>> nested = ['foo', ['bar',['baz']]]
>>> list(flatten(nested))
   ['foo', 'bar', 'baz']

c)  通用生成器

当生成器被调用时,函数体中的代码并不会被执行,而是会返回一个迭代器。每次请求一个值时,就会执行函数体中的代码,直到遇到一个yield或return语句。

yield语句意味着应该生成一个值,

return语句意味着生成器要停止执行(不再生成任何东西,return语句只有在一个生成器中使用时才能进行无参数调用)

注: 生成器其实包括两个部分:生成器的函数和生成器的迭代器。

生成器的函数是用def语句定义的,包含yield的部分。

生成器的迭代器是这个函数返回的部分。即是 在生成器被调用时返回的东西(g = flatten(nested)  # 这里的g就是生成器的迭代器)

这两个实体经常被当做一个,合起来叫做生成器

>>> def simple_generator():
        yield 1
>>> simple_generator
   <function simple_generator at 153b44>   # 生成器的函数
>>> simple_generator()
   <generator object at 1510b0>   # 生成器的迭代器

d )  生成器方法(python2.5之后才有)

send: 该方法是用来给生成器提供值的,也就是说在生成器运行过程中,可以实时给他提供值,让其进行计算。

注意:

1) 外部作用域访问生成器的send方法,就像访问next方法一样,只不过send方法使用一个参数(需要发送的“消息”)

2)在内部则挂起生成器,yield现在作为表达式存在而不是语句使用,。当生成器重新运行的时候,yield方法返回一个值,也就是外部通过send方法发送的值。如果next方法被使用,那么yield方法返回None。

使用send方法(不是next)只有在生成器挂起之后才有意义(也就是说在yield函数第一次被执行之后。如果使用会报错TypeError)。如果在此之前需要给生成器更多的信息,那么只需要使用生成器函数的参数就好了。

如果非要在想对刚刚启动的生成器使用send方法,那么可以将None作为参数进行调用。

def repeater(value):
    while True:
        print "value is ", value
        new = (yield value)  # 将send传递进来的值赋值给new(注意:此时value变量名只能用value)
        print "new is ",new
        if new is not None:
            value = new
            print "value is", value
>>> r = repeater(12)
>>> r.next()   # 第一次运行到yield的位置就停了,此时如果使用send会报错
   value is 12
   12
>>> r.send("ssss")
   new is ssss 
   value is ssss 
   value is ssss 
   'ssss'
>>> r = repeater(12)
>>> r.send(None)   # 非要在第一次使用send方法时,需要使用None
   value is 12
   12
>>> r.send("aa")
   new is aa 
   value is aa 
   value is aa 
   'aa'

注: yield表达式周围的括号的使用: 虽然并未严格的要求,但是在使用返回值的时候,安全起见还是要添加扩号,将其闭合起来。

throw:用于在生成器内引发一个异常(异常发生在yield表达式中)

 def repeater(value):
    while True:
        value = (yield value)
>>> r = repeater(12)
>>> r.send(None)
   12
>>> r.send("sasss")
   'sasss'
>>> r.throw(Exception)  # 抛出了一个异常
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "<stdin>", line 3, in repeater
   Exception

close:用于停止生成器

close方法会在yield运行处引发一个GeneratorExit异常,所以可以将yield放在try/finally语句中。但随后必需将其引发(可能在清理之后)、或者引发另一个异常或者直接返回。

 def repeater(value):
    while True:
        try:
            value = (yield value)
        except GeneratorExit,e:
            print e
            return  # 这行就是“但随后必需将其引发、或者引发另一个异常或者直接返回。”的实现也可以是 raise GeneratorExit 或者 raise Exception
                    # 如果没有这一句话,程序会报错

注: 试着在生成器的close方法被调用后再通过生成器生成一个值则会导致RuntimeError异常。

f )  生成器的替代方案

生成器在旧版本的Python中是不可用的。但是可以用以下的替代方案模拟生成器:

>>> def flatten(nested):
...     result = []
...     try:
...             try:
...                     nested+''
...             except TypeError:
...                     pass
...             else:
...                     raise TypeError
...             for sublist in nested:
...                     for element in flatten(sublist):
...                             result.append(element)
...     except TypeError:
...             result.append(nested)
...     return result

第10章  自带电池

10.1 模块

Python的标准安装中还包括了一组模块,称为标准库。

a)  模块是程序

 # hello.py
print "Hello, world!"

假设上述代码保存在C:\\python中为了使用上述的模块,就需要将上述模块的路径包含在解析器会寻找的路径中。

>>> import sys
>>> sys.path.append('c:\\python')

上述代码是将hello.py的目录添加到python的执行路径中去。

如果是在Unix系统中,不能只是简单地将字符串‘~/python’添加到sys.path中,必须使用完整路径。或者使用sys.path.expanduser(‘~/python’)。

如下所见,模块中的代码会在import的时候执行。并且会输出结果。

>>> import hello
   Hello, world! #注意此时会执行hello.py中的语句

但是上述代码在第二次调用的时候就不会运行了。

因为导入模块并不意味着在导入时执行某些操作。它们的主要用于定义变量函数或者类。此外,因为只需要定义这些东西一次,导入模块多次和导入一次的效果一样。

b)   模块用于定义

模块跟类一样是有其作用域的。因此可以在模块中定义函数,类等。

# hello2.py
def hello():
    print 'Hello world!'
>>> import hello2
>>> hello2.hello()  # 在调用时,模块名.函数名()
   Hello world!

代码模块化的好处当然就是可以代码重用了。

c)  在模块中增加测试代码

# hello3.py
def hello():
    print 'Hello world!'

# A test:
hello()

上面的代码如果将它作为模块导入的话,就会出现下面的情况

>>> import hello3
   Hello world!
>>> hello3.hello()
   Hello world!

就可以看到hello被执行了两次

为了避免上述问题:

# hello4.py
def hello():
    print 'Hello world!'

def test():
    hello()

if __name__ == "__main__": test()

d)  让你的模块可用

为了让python能够找到你的模块,你有两种解决方案解决这个问题:

1) 将模块放置在正确的位置

只需要将模块放置到sys.path中的目录中就好,这样所有机器上的python程序都能调用这个模块了

这样做不适用于几种情况:

  • 不希望将自己的模块填满python解析器目录
  • 没有在Python解析器目录中存储文件的权限
  • 想将模块放到其他地方

2) 告诉编译器去哪里找

>>> import sys
>>> sys.path.append('c:\\python')

上述是第一种方法,此外还可以PYTHONPATH环境变量中包含模块所在的目录

PYTHONPATH是一个操作系统的参数。

注:模块的命名:

包含模块代码的文件的名字要和模块名一样,再加上.py扩展名。在windows中,也可以使用.pyw作为扩展名。

e) 包(包就是存储模块的目录)

为了组织好模块,可以将它们分组为包。包基本上是另外一类模块。当模块存储在文件中时,包就是模块所在的目录。

为了让python将其作为包对待,它必须包含一个命名为__init__.py的文件(模块)。__init__.py中的内容就直接用包名调用就好了。

# constants/__init__.py

PI = 3.14
import constants

print constants.PI

为了将模块放置在包内,直接把模块放在包目录中即可。

调用时带上模块名就好

import drawing   #  调用此句是__init__模块中的内容是可用的,而colors、和shapes中的内容不可用
import drawing.colors
from drawing import shapes

在第一条语句drawing中__init__模块的内容是可用的,但是drawing和colors模块则不可用。

在第二条语句之后,color模块可用了,但只能通过全名 drawing.colors来使用。

在执行第三条语句之后,shapes模块可用,可以通过短名(也就是仅使用shapes)来使用。

注意:这些语句只是例子,执行顺序并不是必须的。例如,不用在导入包的模块之前导入包本身,第二条语句可以独立使用,第三条语句也一样可以独立使用。我们还可以在包之间进行嵌套。

10.2 探究模块

a) 模块中有什么

dir函数: 该函数会将对象中的所有特性(以及模块的所有函数、类、变量等)列出。

>>> import copy
>>> dir(copy)
  ['Error', 'PyStringMap', '_EmptyClass', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 
   '_copy_dispatch', '_copy_immutable', '_copy_inst', '_copy_with_constructor', '_copy_with_copy_method', '_deepcopy_atomic', 
   '_deepcopy_dict', '_deepcopy_dispatch', '_deepcopy_inst', '_deepcopy_list', '_deepcopy_method', '_deepcopy_tuple', 
   '_keep_alive', '_reconstruct', '_test', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']

一些名字以下划线开始的特性,暗示他们并不是为在模块外部使用而准备的。所以:

>>> [n for n in dir(copy) if not n.startswith('_')]
  ['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']

注:dir(copy)和copy.__dict__的区别

>>> len([n for n in copy.__dict__.keys()]) == len(dir(copy))
  True

其实copy.__dict__就是将每一个特性以及特性的值都列出来了,而dir(copy)只列出了特性名。

__all__变量: 定义了模块的共有接口。即他告诉解释器:从模块导入所有名字代码什么含义。__all__变量是包内部被设置的。

>>> copy.__all__
  ['Error', 'copy', 'deepcopy']

其实all变量就是定了from copy import *   要导入的内容

如果__all__变量没有设定,用import * 语句默认将会导入模块中所有不以下划线开头的全局名称。

b) 用help获取帮助

其实help、__doc__都是引用的文档字符串的。跟函数一些模块也可以有文档字符串(写在模块开头),类也一样(写在类开头)。

help函数:可以提供日常所需的信息。

>>> help(copy.copy)
  Help on function copy in module copy:

  copy(x)
      Shallow copy operation on arbitrary Python objects.

      See the module's __doc__ string for more info.

__doc__: 该字符串是文档字符串

>>> copy.copy.__doc__
  "Shallow copy operation on arbitrary Python objects.\n\n    See the module's __doc__ string for more info.\n
  "

help与直接检查文档字符串相比,他的好处是能得到更多的信息,例如函数签名。

c) 使用源代码

模块的源代码目录在哪呢?这时就需要用模块的__file__属性找到了。这样你就可以直接阅读源代码了。

>>> copy.__file__
  'D:\\Program Files\\Python\\lib\\copy.pyc'

10.3 标准库:一些最爱

sys

os

fileinput

集合、堆和双端队列

time

random

shelve

re

functools

difflib

hashlib

csv

timeit、profile和trace

datetime

itertools

logging

getopt和optparse

cmd

第11章  文件和流

11.1 打开文件

a) open函数用来打开文件

open(name, [, mode[, buffering]])

open使用一个文件名作为唯一的强制参数。然后返回一个文件对象。

>>> f = open(r'C:\text')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  IOError: [Errno 2] No such file or directory: 'C:\\text'

如果文件不存在会产生IOError的异常。

b) 文件模式

不显示的给出文件模式,那么就是默认为只读的模式。(即”r”)

模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

其中”+”参数可以用到其他的任何模式中,指明读和写都是允许的。

“b”参数改变文件处理方式。python默认是用文本文件处理的,使用”b”模式就是使用二进制进行处理。

文本处理方式跟二进制处理方式的不同:  主要是python在使用文本模式时会默认的将windows中的\r\n转化成\n进行处理。这样就有可能破坏二进制文件中的一些数据,导致错误。

c) 缓冲

open函数的第3个参数是控制文件的缓冲的。

为0代表: I/O无缓冲的(所有的读写操作都直接针对硬盘)

1代表:I/O有缓冲的(意味着Python使用内存来代替硬盘,让程序更快,只有在使用flush或者close时才会更新硬盘上的数据)

大于1的数字代表:缓冲区的大小(单位字节)

-1 代表:使用默认的缓冲区大小

注: file函数也是可以打开文件的。

11.2 基本的文件方法

这里所说的文件是指广义的文件(即包括了真实的文件对象和类文件对象(流))(类文件的概念)

a)  读和写

写: f.write(string)

>>> f = open('somefile.txt', 'w')
>>> f.write('Hello,')
>>> f.write("World!")
>>> f.close()

读:f.read()

>>> f = open('somefile.txt', 'r')
>>> f.read(4)  # 读取4个字符数
   'Hell'
>>> f.read()   # 不给字符数代表读取剩下的文件
   'o,World!'

b)  管式输出

得到管道流

在使用管道时,Python会从sys.stdin中读取数据,并把结果写入他的sys.stdout中

# somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print 'WordCount:', wordcount

这样python就能处理管道中的内容了

λ cat somefile.txt | python somescript.py | sort

c)  随机访问

seek(offset[, whence]):这个方法吧当前位置(进行读和写的位置)移动到由offset和whence定义的位置。offset表示一个字节(字符)数,表示偏移量。whence默认为0,表示偏移量从文件开头算起 ,1为表示相对于当前位置进行移动,此时offset可以是负数。2表示相对于文件结尾的移动。

>>> f = open(r'c:\text\somefile.txt', 'w')
>>> f.write('01234567890123456789')
>>> f.seek(5)
>>> f.write('Hello,World!')
>>> f.close()
>>> f = open(r'c:\text\somefile.txt')
>>> f.read()
   '01234Hello,World!89'

tell方法返回当前文件的位置(返回值是一个长整数)

>>> f = open(r'c:\text\somefile.txt')
>>> f.read(3)
   '012'
>>> f.read(2)
   '34'
>>> f.tell()
   5L

d)  读写行

file.readline: 读取单独一行,有一个可选参数,确定可以读取的字符的最大值

file.readlines: 读取文件中的所有行并将其作为列表返回

file.writelines:与readlines相反:传给他一个字符串的列表,他会把所有字符串写入文件。程序不会添加新行,需要自己添加。

注: 没有writeline方法,因为能使用write。

e)  关闭文件

尽管文件会在退出程序后(也可能在退出程序前)自动关闭,但是自己关闭程序可以避免在某些操作系统或设置中进行无用的修改,也会避免用完系统中所打开文件的配额。

写入过的文件总是应该关闭的,因为Python可能会缓存写入的数据(此时可以将数据人为的强制写入硬盘中(flush)。但是最好还是直接关闭文件)。如果程序崩溃了,数据可能就丢失了。

# Open your file here
try:
    # Write data to your file
finally:
    file.close()

with语句

 with open("somefile.txt") as somfile:
    do_something(something)

with语句可以打开文件并将其赋值到变量上(somefile)。之后就可以对文件对象进行相应的操作,文件在语句结束后会被自动关闭,即使是由于异常的结束。

上下文管理器:

with语句实际上是很通用的结构,允许使用所谓的上下文管理器。上下文管理器是一种支持__enter__和__exit__这两个方法的对象。

__enter__方法不带参数,它在进入with语句的时候被调用,返回值绑定到在as关键字之后的变量。

__exit__方法带有三个参数:异常类型、异常对象、异常回溯。在离开方法时这个函数被调用。如果__exit__返回false,那么所有的异常不会被处理。

文件可以被用作上下文管理器是因为文件实现了上述两个魔法方法。它们的__enter__方法返回文件对象本身,__exit__方法关闭文件。

11.3 对文件内容进行迭代

a) 按字节处理

f = open(filename)
while True:
    char = f.read(1)
    if not char: breaf
    process(char)
f.close()

b) 按行处理

f = open(filename)
while True:
    line = f.readline()
    if not line: break
    process(line)
f.close()

c) 读取所有内容

f = open(filename)
for char in f.read():
    process(char)
f.close()
f = open(filename)
for line in f.readlines():
    process(line)
f.close()

d) 使用fileinput实现懒惰行迭代

需要对一个非常大的数据进行迭代操作时,readlines会占用太多的内存。可以 使用while和readline一行一行读取,。

当然还可以使用fileinput进行懒惰迭代。(说他懒惰是因为他只是读取实际需要的文件部分)

import fileinput
for line in fileinput.input(filename):
    process(line)

注: 在旧式代码中可以用xreadlines实现懒惰行迭代。

e)文件迭代器

Python2.2之后,文件对象也是可以迭代的。而且这个迭代是类似于懒惰行迭代的,他会将文件的每一行分步加载到内存中。不至于占用过多的内存。

f = open(filename)
for line in f:
    process(line)
f.close()

由于Python解释器会自动的关闭文件,因此上边的代码可以简化:

for line in open(filename):
    process(line)

注意sys.stdin也是可以迭代的

import sys
for line in sys.stdin:
    process(line)

还可以对文件迭代器执行和普通迭代器相同的操作。例如:list、序列解包等

>>> lines = list(open(filename))
>>> lines
   ['First line\n', 'Second line \n', 'Third line\n']

>>> first, second, third = open(filename)

每天进步一点点之tcpdump【转】

1、抓取回环网口的包:tcpdump -i lo

2、防止包截断:tcpdump -s0

3、以数字显示主机及端口:tcpdump -n

第一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0是一个网络地址,port 23 指明端口号是23。如果没有指定类型,缺省的类型是host.
第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,这些关键字指明了传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 , dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是src or dst关键字。
第三种是协议的关键字,主要包括fddi,ip,arp,rarp,tcp,udp等类型。Fddi指明是在FDDI(分布式光纤数据接口网络)上的特定 的网络协议,实际上它是”ether”的别名,fddi和ether具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和 分析。其他的几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有协议的信息包。

除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less,greater,还有三种逻辑运算,取非运算是 ‘not ‘ ‘! ‘, 与运算是’and’,’&&;或运算 是’or’ ,’||’;这些关键字可以组合起来构成强大的组合条件来满足人们的需要,下面举几个例子来说明。
普通情况下,直接启动tcpdump将监视第一个网络界面上所有流过的数据包。
# tcpdump
tcpdump: listening on fxp0
11:58:47.873028 202.102.245.40.netbios-ns > 202.102.245.127.netbios-ns: udp 50
11:58:47.974331 0:10:7b:8:3a:56 > 1:80:c2:0:0:0 802.1d ui/C len=43
0000 0000 0080 0000 1007 cf08 0900 0000
0e80 0000 902b 4695 0980 8701 0014 0002
000f 0000 902b 4695 0008 00
11:58:48.373134 0:0:e8:5b:6d:85 > Broadcast sap e0 ui/C len=97
ffff 0060 0004 ffff ffff ffff ffff ffff
0452 ffff ffff 0000 e85b 6d85 4008 0002
0640 4d41 5354 4552 5f57 4542 0000 0000
0000 00
使用-i参数指定tcpdump监听的网络界面,这在计算机具有多个网络界面时非常有用,
使用-c参数指定要监听的数据包数量,
使用-w参数指定将监听到的数据包写入文件中保存
A想要截获所有210.27.48.1 的主机收到的和发出的所有的数据包:
#tcpdump host 210.27.48.1
B想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令:(在命令行中适用 括号时,一定要
#tcpdump host 210.27.48.1 and / (210.27.48.2 or 210.27.48.3 /)
C如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
D如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
#tcpdump tcp port 23 host 210.27.48.1
E 对本机的udp 123 端口进行监视 123 为ntp的服务端口
# tcpdump udp port 123

F 系统将只对名为hostname的主机的通信数据包进行监视。主机名可以是本地主机,也可以是网络上的任何一台计算机。下面的命令可以读取主机hostname发送的所有数据:
#tcpdump -i eth0 src host hostname

G 下面的命令可以监视所有送到主机hostname的数据包:
#tcpdump -i eth0 dst host hostname
H  我们还可以监视通过指定网关的数据包:
#tcpdump -i eth0 gateway Gatewayname
I 如果你还想监视编址到指定端口的TCP或UDP数据包,那么执行以下命令:
#tcpdump -i eth0 host hostname and port 80
J 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包
,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
K 想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令
:(在命令行中适用 括号时,一定要
#tcpdump host 210.27.48.1 and / (210.27.48.2 or 210.27.48.3 /)
L 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
M 如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
#tcpdump tcp port 23 host 210.27.48.1
第三种是协议的关键字,主要包括fddi,ip ,arp,rarp,tcp,udp等类型
除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less,
greater,还有三种逻辑运算,取非运算是 ‘not ‘ ‘! ‘, 与运算是’and’,’&&’;或运算 是’o
r’ ,’||’;
第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,
如果我们只需要列出送到80端口的数据包,用dst port;如果我们只希望看到返回80端口的数据包,用src port。
#tcpdump –i eth0 host hostname and dst port 80  目的端口是80
或者
#tcpdump –i eth0 host hostname and src port 80  源端口是80  一般是提供http的服务的主机
如果条件很多的话  要在条件之前加and 或 or 或 not
#tcpdump -i eth0 host ! 211.161.223.70 and ! 211.161.223.71 and dst port 80
如果在ethernet 使用混杂模式 系统的日志将会记录
May  7 20:03:46 localhost kernel: eth0: Promiscuous mode enabled.
May  7 20:03:46 localhost kernel: device eth0 entered promiscuous mode
May  7 20:03:57 localhost kernel: device eth0 left promiscuous mode
tcpdump对截获的数据并没有进行彻底解码,数据包内的大部分内容是使用十六进制的形式直接打印输出的。显然这不利于分析网络故障,通常的解决办法是先使用带-w参数的tcpdump 截获数据并保存到文件中,然后再使用其他程序进行解码分析。当然也应该定义过滤规则,以避免捕获的数据包填满整个硬盘。

# tcpdump   -i eth1 src  host 211.167.237.199
00:02:03.096713 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 2010208:2010352(144) ack 33377 win 8576
00:02:03.096951 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 2010352:2010496(144) ack 33377 win 8576
00:02:03.100928 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 2010496:2010640(144) ack 33377 win 8576
00:02:03.101165 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 2010640:2010784(144) ack 33377 win 8576
00:02:03.102554 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 2010784:2010928(144) ack 33425 win 8576

表明在00:02:03点的时候,211.167.237.199通过ssh源端口连接到221.216.165.189的1467端口

#tcpdump -i eth1 src host 211.167.237.199 and dst port 1467
00:09:27.603075 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 180400:180544(144) ack 2833 win 8576
00:09:27.605631 IP 211.167.237.199.ssh > 221.216.165.189.1467: P 180544:180688(144) ack 2881 win 8576

截获所有由eth0进入、源地址(src)为192.168.0.5的主机(host),并且(and)目标(dst)端口(port)为80的数据包
观看网卡传送、接收数据包的状态
$ netstat  -i
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500  0  14639   0      0      0    5705    119    0     0   BMRU

Iface:  网卡
RX-OK RX-ERR RX-DRP RX-OVR : 网卡正确接收数据包的数量以及发生错误、流失、碰撞的总数
TX-OK TX-ERR TX-DRP TX-OVR : 网卡正确发送数据包的数量以及发生错误、流失、碰撞的总数

[root@linux ~]# tcpdump [-nn] [-i 介面] [-w 儲存檔名] [-c 次數] [-Ae]        [-qX] [-r 檔案] [所欲擷取的資料內容]參數:-nn:直接以 IP 及 port number 顯示,而非主機名與服務名稱-i :後面接要『監聽』的網路介面,例如 eth0, lo, ppp0 等等的介面;-w :如果你要將監聽所得的封包資料儲存下來,用這個參數就對了!後面接檔名-c :監聽的封包數,如果沒有這個參數, tcpdump 會持續不斷的監聽,     直到使用者輸入 [ctrl]-c 為止。-A :封包的內容以 ASCII 顯示,通常用來捉取 WWW 的網頁封包資料。-e :使用資料連接層 (OSI 第二層) 的 MAC 封包資料來顯示;-q :僅列出較為簡短的封包資訊,每一行的內容比較精簡-X :可以列出十六進位 (hex) 以及 ASCII 的封包內容,對於監聽封包內容很有用-r :從後面接的檔案將封包資料讀出來。那個『檔案』是已經存在的檔案,     並且這個『檔案』是由 -w 所製作出來的。所欲擷取的資料內容:我們可以專門針對某些通訊協定或者是 IP 來源進行封包擷取,     那就可以簡化輸出的結果,並取得最有用的資訊。常見的表示方法有:     'host foo', 'host 127.0.0.1' :針對單部主機來進行封包擷取     'net 192.168' :針對某個網域來進行封包的擷取;     'src host 127.0.0.1' 'dst net 192.168':同時加上來源(src)或目標(dst)限制     'tcp port 21':還可以針對通訊協定偵測,如 tcp, udp, arp, ether 等     還可以利用 and 與 or 來進行封包資料的整合顯示呢!範例一:以 IP 與 port number 捉下 eth0 這個網路卡上的封包,持續 3 秒[root@linux ~]# tcpdump -i eth0 -nntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes01:33:40.41 IP 192.168.1.100.22 > 192.168.1.11.1190: P 116:232(116) ack 1 win 964801:33:40.41 IP 192.168.1.100.22 > 192.168.1.11.1190: P 232:364(132) ack 1 win 9648<  ctrl-c span>6680 packets captured              < span>14250 packets received by filter   < span>7512 packets dropped by kernel     < span>

如果你是第一次看 tcpdump 的 man page 時,肯定一個頭兩個大,因為 tcpdump 幾乎都是分析封包的表頭資料,使用者如果沒有簡易的網路封包基礎,要看懂粉難吶! 所以,至少您得要回到網路基礎裡面去將 TCP 封包的表頭資料理解理解才好啊! ^_^!至於那個範例一所產生的輸出範例中,我們可以約略區分為數個欄位, 我們以範例一當中那個特殊字體行來說明一下:

  • 01:33:40.41:這個是此封包被擷取的時間,『時:分:秒』的單位;
  • IP:透過的通訊協定是 IP ;
  • 192.168.1.100.22 > :傳送端是 192.168.1.100 這個 IP,而傳送的 port number 為 22,您必須要瞭解的是,那個大於 (>) 的符號指的是封包的傳輸方向喔!
  • 192.168.1.11.1190:接收端的 IP 是 192.168.1.11, 且該主機開啟 port 1190 來接收;
  • P 116:232(116):這個封包帶有 PUSH 的資料傳輸標誌, 且傳輸的資料為整體資料的 116~232 byte,所以這個封包帶有 116 bytes 的資料量;
  • ack 1 win 9648:ACK與 Window size 的相關資料。

最簡單的說法,就是該封包是由 192.168.1.100 傳到 192.168.1.11,透過的 port 是由 22 到 1190 , 且帶有 116 bytes 的資料量,使用的是 PUSH 的旗標,而不是 SYN 之類的主動連線標誌。 呵呵!不容易看的懂吧!所以說,上頭才講請務必到 TCP 表頭資料的部分去瞧一瞧的啊!

再來,一個網路狀態很忙的主機上面,你想要取得某部主機對你連線的封包資料而已時, 使用 tcpdump 配合管線命令與正規表示法也可以,不過,畢竟不好捉取! 我們可以透過 tcpdump 的表示法功能,就能夠輕易的將所需要的資料獨立的取出來。 在上面的範例一當中,我們僅針對 eth0 做監聽,所以整個 eth0 介面上面的資料都會被顯示到螢幕上, 不好分析啊!那麼我們可以簡化嗎?例如只取出 port 21 的連線封包,可以這樣做:

[root@linux ~]# tcpdump -i eth0 -nn port 21tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes01:54:37.96 IP 192.168.1.11.1240 > 192.168.1.100.21: . ack 1 win 6553501:54:37.96 IP 192.168.1.100.21 > 192.168.1.11.1240: P 1:21(20) ack 1 win 584001:54:38.12 IP 192.168.1.11.1240 > 192.168.1.100.21: . ack 21 win 6551501:54:42.79 IP 192.168.1.11.1240 > 192.168.1.100.21: P 1:17(16) ack 21 win 6551501:54:42.79 IP 192.168.1.100.21 > 192.168.1.11.1240: . ack 17 win 584001:54:42.79 IP 192.168.1.100.21 > 192.168.1.11.1240: P 21:55(34) ack 17 win 5840

瞧!這樣就僅提出 port 21 的資訊而已,且仔細看的話,你會發現封包的傳遞都是雙向的, client 端發出『要求』而 server 端則予以『回應』,所以,當然是有去有回啊! 而我們也就可以經過這個封包的流向來瞭解到封包運作的過程。 舉例來說:

  1. 我們先在一個終端機視窗輸入『 tcpdump -i lo -nn 』 的監聽,
  2. 再另開一個終端機視窗來對本機 (127.0.0.1) 登入『ssh localhost』

那麼輸出的結果會是如何?

[root@linux ~]# tcpdump -i lo -nn 1 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode 2 listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes 3 11:02:54.253777 IP 127.0.0.1.32936 > 127.0.0.1.22: S 933696132:933696132(0)    win 32767  4 11:02:54.253831 IP 127.0.0.1.22 > 127.0.0.1.32936: S 920046702:920046702(0)    ack 933696133 win 32767  5 11:02:54.253871 IP 127.0.0.1.32936 > 127.0.0.1.22: . ack 1 win 8192  6 11:02:54.272124 IP 127.0.0.1.22 > 127.0.0.1.32936: P 1:23(22) ack 1 win 8192     7 11:02:54.272375 IP 127.0.0.1.32936 > 127.0.0.1.22: . ack 23 win 8192 

上表顯示的頭兩行是 tcpdump 的基本說明,然後:

  • 第 3 行顯示的是『來自 client 端,帶有 SYN 主動連線的封包』,
  • 第 4 行顯示的是『來自 server 端,除了回應 client 端之外(ACK),還帶有 SYN 主動連線的標誌;
  • 第 5 行則顯示 client 端回應 server 確定連線建立 (ACK)
  • 第 6 行以後則開始進入資料傳輸的步驟。

從第 3-5 行的流程來看,熟不熟悉啊?沒錯!那就是 三向交握 的基礎流程啦!夠有趣吧! 不過 tcpdump 之所以被稱為駭客軟體之一可不止上頭介紹的功能吶! 上面介紹的功能可以用來作為我們主機的封包連線與傳輸的流程分析, 這將有助於我們瞭解到封包的運作,同時瞭解到主機的防火牆設定規則是否有需要修訂的地方。

更神奇的使用要來啦!如果我們使用 tcpdump 在 router 上面監聽『明碼』的傳輸資料時, 例如 FTP 傳輸協定,你覺得會發生什麼問題呢? 我們先在主機端下達『 tcpdump -i lo port 21 -nn -X 』然後再以 ftp 登入本機,並輸入帳號與密碼, 結果你就可以發現如下的狀況:

[root@linux ~]# tcpdump -i lo -nn -X 'port 21'    0x0000:  4500 0048 2a28 4000 4006 1286 7f00 0001  E..H*(@.@.......    0x0010:  7f00 0001 0015 80ab 8355 2149 835c d825  .........U!I./.%    0x0020:  8018 2000 fe3c 0000 0101 080a 0e2e 0b67  .....< g    x  ee b        aspan style='font-size:13px;font-style:normal;font-weight:normal;font-family:monospace;color:rgb(17, 17, 17);'  >vsFTPd.    0x0040:  322e 302e 3129 0d0a                      2.0.1)..    0x0000:  4510 0041 d34b 4000 4006 6959 7f00 0001  E..A.K@.@.iY....    0x0010:  7f00 0001 80ab 0015 835c d825 8355 215d  ........./.%.U!]    0x0020:  8018 2000 fe35 0000 0101 080a 0e2e 1b37  .....5.........7    0x0030:  0e2e 0b67 5553 4552 2064 6d74 7361 690d  ...gUSER.dmtsai.    0x0040:  0a                                       .    0x0000:  4510 004a d34f 4000 4006 694c 7f00 0001  E..J.O@.@.iL....    0x0010:  7f00 0001 80ab 0015 835c d832 8355 217f  ........./.2.U!.    0x0020:  8018 2000 fe3e 0000 0101 080a 0e2e 3227  .....>........2'    0x0030:  0e2e 1b38 5041 5353 206d 7970 6173 7377  ...8PASS.mypassw    0x0040:  6f72 6469 7379 6f75 0d0a                 ordisyou..</span>

上面的輸出結果已經被簡化過了,你必須要自行在你的輸出結果當中搜尋相關的字串才行。 從上面輸出結果的特殊字體中,我們可以發現『該 FTP 軟體使用的是 vsftpd ,並且使用者輸入 dmtsai 這個帳號名稱,且密碼是 mypasswordisyou』 嘿嘿!你說可不可怕啊!如果使用的是明碼的方式來傳輸你的網路資料? 所以我們才常常在講啊,網路是很不安全低!

另外你得瞭解,為了讓網路介面可以讓 tcpdump 監聽,所以執行 tcpdump 時網路介面會啟動在 『錯亂模式 (promiscuous)』,所以你會在 /var/log/messages 裡面看到很多的警告訊息, 通知你說你的網路卡被設定成為錯亂模式!別擔心,那是正常的。 至於更多的應用,請參考 man tcpdump 囉!

例題:如何使用 tcpdump 監聽 (1)來自 eth0 介面卡且 (2)通訊協定為 port 22 ,(3)目標來源為 192.168.1.100 的封包資料?

答:

    tcpdump -i eth0 -nn ‘port 22 and src host 192.168.1.100’

 

安装第三方库出现 Python version 2.7 required, which was not found in the registry【转】

安装第三方库出现 Python version 2.7 required, which was not found in the registry

建立一个文件 register.py 内容如下. 然后执行该脚本.

import sys

from _winreg import *

# tweak as necessary
version = sys.version[:3]
installpath = sys.prefix

regpath = "SOFTWARE\\Python\\Pythoncore\\%s\\" % (version)
installkey = "InstallPath"
pythonkey = "PythonPath"
pythonpath = "%s;%s\\Lib\\;%s\\DLLs\\" % (installpath, installpath, installpath)


def RegisterPy():
    try:
        reg = OpenKey(HKEY_CURRENT_USER, regpath)
    except EnvironmentError as e:
        try:
            reg = CreateKey(HKEY_CURRENT_USER, regpath)
            SetValue(reg, installkey, REG_SZ, installpath)
            SetValue(reg, pythonkey, REG_SZ, pythonpath)
            CloseKey(reg)
        except:
            print "*** Unable to register!"
            return
        print "--- Python", version, "is now registered!"
        return
    if (QueryValue(reg, installkey) == installpath and
        QueryValue(reg, pythonkey) == pythonpath):
        CloseKey(reg)
        print "=== Python", version, "is already registered!"
        return
    CloseKey(reg)
    print "*** Unable to register!"
    print "*** You probably have another Python installation!"

if __name__ == "__main__":
    RegisterPy()