python super()【转】

转自http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html

 

一、问题的发现与提出

在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:

代码段1:

复制代码
class A:
def __init__(self):
print “enter A”
print “leave A”

class B(A):
def __init__(self):
print “enter B”
A.__init__(self)
print “leave B”

>>> b = B()
enter B
enter A
leave A
leave B
复制代码

即,使用非绑定的类方法(用类名来引用的方法),并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。
这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2,
代码段2:
class B(C): # A –> C
def __init__(self):
print “enter B”
C.__init__(self) # A –> C
print “leave B”
如果代码简单,这样的改动或许还可以接受。但如果代码量庞大,这样的修改可能是灾难性的。

因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:
super(type[, object-or-type])
Return the superclass of type. If the second argument is omitted the super object
returned is unbound. If the second argument is an object, isinstance(obj, type)
must be true. If the second argument is a type, issubclass(type2, type) must be
true. super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
New in version 2.2.
从说明来看,可以把类B改写如代码段3:
代码段3:
复制代码
class A(object): # A must be new-style class
def __init__(self):
print “enter A”
print “leave A”

class B(C): # A –> C
def __init__(self):
print “enter B”
super(B, self).__init__()
print “leave B”
复制代码
尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。

在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。
有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:
代码段4:

复制代码
class A(object):
def __init__(self):
print “enter A”
print “leave A”

class B(object):
def __init__(self):
print “enter B”
print “leave B”

class C(A):
def __init__(self):
print “enter C”
super(C, self).__init__()
print “leave C”

class D(A):
def __init__(self):
print “enter D”
super(D, self).__init__()
print “leave D”
class E(B, C):
def __init__(self):
print “enter E”
B.__init__(self)
C.__init__(self)
print “leave E”

class F(E, D):
def __init__(self):
print “enter F”
E.__init__(self)
D.__init__(self)
print “leave F”
复制代码
f = F() result:

复制代码
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
复制代码
明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:

object
| \
| A
| / |
B C D
\ / |
E |
\ |
F
按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!
也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:
F E B C D A object
从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。 ???
我们把代码段4改写为:
代码段9:

复制代码
class A(object):
def __init__(self):
print “enter A”
super(A, self).__init__() # new
print “leave A”

class B(object):
def __init__(self):
print “enter B”
super(B, self).__init__() # new
print “leave B”

class C(A):
def __init__(self):
print “enter C”
super(C, self).__init__()
print “leave C”

class D(A):
def __init__(self):
print “enter D”
super(D, self).__init__()
print “leave D”
class E(B, C):
def __init__(self):
print “enter E”
super(E, self).__init__() # change
print “leave E”

class F(E, D):
def __init__(self):
print “enter F”
super(F, self).__init__() # change
print “leave F”
复制代码
f = F() result:

复制代码
enter F
enter E
enter B
enter C
enter D
enter A
leave A
leave D
leave C
leave B
leave E
leave F
复制代码
明显地,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。

再看类结构:

复制代码
object
/ \
/ A
| / \
B-1 C-2 D-2
\ / /
E-1 /
\ /
F
复制代码
E-1,D-2是F的父类,其中表示E类在前,即F(E,D)。

所以初始化顺序可以从类结构图来看出 : F->E->B –>C –> D –> A

由于C,D有同一个父类,因此会先初始化D再是A。

三、延续的讨论
我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不能发现mro的顺序正好是该有向图的一个拓扑排序序列。
从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。
但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。
四、小结
1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象;
2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);
5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。

python实践设计模式(二)Builder,Singleton,Prototype【转】

在上次学习的基础上,本次继续把创建型模式的其他3种模式学习总结一下。

4.Builder模式

个人理解,如果说工厂模式旨在选择创建哪一类的实例,而Builder模式的重点是封装一个实例的复杂创建过程。它可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。也就是说,建造的步骤可以稳定不变,但是每一步的内部表象可以灵活变化。

UML图如下:

Builder:为创建Product对象的各个部件指定抽象接口,python中为父类。
ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口,也就是返回产品类的方法。
Director:构造一个使用Builer接口的对象,该对象中定义了建造对象的步骤顺序。
Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的具体装配方法,包含定义组成部件的类,以及将这些部件装配成最终产品的接口。

一个比较贴切的例子:

要建一座房子,可是我不知道怎么盖,于是我需要找建筑队的工人他们会,还得找个设计师,他知道怎么设计,我还要确保建筑队的工人听设计师的领导,而设计师本身不干活,只下命令,这里砌一堵墙,这里砌一扇门,这样建筑队的工人开始建设,最后,我可以向建筑队的工人要房子了。在这个过程中,设计师是什么也没有,除了他在脑子里的设计和命令,所以要房子也是跟建筑队的工人要。在这个例子中Director是设计师,Builder代表建筑队工人会的建筑技能,ConcreteBuilder工人建筑技能的具体操作,Product就是我要盖的房子。

下面代码的例子,建筑队的工人有砌墙,装窗户,装门的技能以及交房的安排,设计师决定了建设房屋的安排和步骤,现在我要通过2个建筑队的民工,建2所房子,实例代码如下:

Builder

 注:因为python没有private类型的成员,不过我们可以用命名为__name的变量代替,例如上例中的__Room,为什么这样可以呢,我们用builder1=ConcreteBuilder1()
print dir(builder1)

打印语句看出如下图,__name实例化后变为__ConcreteBuilder1__name,就是避免外界对__name属性的修改,从而达到了封闭性。

 5. Singleton模式

Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点,UML如下。

单例模式虽然不复杂,我一直认为这个模式是最简单的,当我想用python实现的时候确犯难了,这篇文章也足足用了2星期才写出来,期间各种查资料(省略1000个字),下面就来说说实现方法。

先说以前比较熟悉的像C#这样的语言,一般的实现方法是:

1.有一个私有的无参构造函数,这可以防止其他类实例化它。
2.单例类被定义为sealed,目的是单例类也不被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了Singleton模式“唯一实例”的初衷,所以为了保险起见可以把该类定义成不允许派生,但没有要求一定要这样定义。
3.一个静态的变量用来保存单实例的引用。
4.一个公有的静态方法用来获取单实例的引用,如果实例为null 即创建一个。

上面是我熟悉的Singleton模式的创建方法,但是对于python,既没有static类型,也没有私有方法和sealed修饰的类,如何实现呢?

  • 关于私有方法和属性,我前面已经提到可以用__name形式为名称定义方法名和属性名来解决
  • 利用isinstance()或issubclass()

本人力推isinstance()和issubclass()2个方法,正是由于python提供这两个方法才能完成设计模式的开发。

isinstance(object, classinfo)如果object是CLASSINFO的一个实例或是子类,或者如果CLASSINFO和object的类型是对象,或是该类型的对象的子类,返回true。

issubclass(class, classinfo)如果class是CLASSINFO的一个子类返回true。

下面是利用 isinstance实现的Singleton模式

Singleton1

上面代码的执行结果如下:

从运行结果可以看出,我们可以控制同一个子类的生成同一个对象实例,但是如果Singleton类被继承(不论是子类之间还是,子类的子类)不能控制生成一个实例。这个问题后面再探讨。

  • 利用__new__

提到__new__就不能不说__init__,先说说关于__new__和__init__的不同与用法:

object.__new__(cls[, …]):调用创建cls类的一个新的实例。是静态方法不用声明。返回一个新对象的实例

object.__init__(self[, …]):当实例创建的时候调用。没有返回值。

__new__在__init__这个之前被调用:

如果__new__返回一个cls的实例,那么新的实例的__init__方法就会被调用,且self是这个新的实例。如果是自定义重写__new__,没有调用__init__的话__init__就不起作用了。

如果__new__不返回一个cls的实例,那么新的实例的__init__方法就不会被调用。

示例代码如下:

Singleton2

如上代码,我们重写了__new__方法,没有用到__init__,即使需要用到我们也需要显式的调用,否则__init__不会起作用,这段代码返回的结果与第一种方法类似如下,也没有解决多继承多对象的问题。

  • 利用元类__metaclass__

利用元类编写单例其实原理和重写__new__是一样的,都是在对象创建的时候进行拦截。上面Singleton2中注释的代码就是利用__metaclass__,可以用注释部分的声明代替之前的,Singleton类在声明是继承了type,对于type她其实是Python在背后用来创建所有类的元类。

class MyClass1(object):
__metaclass__ = Singleton

在声明MyClass1时用到了以上的方式,原理是这样的,MyClass1中有__metaclass__这个属性吗?如果有,Python会在内存中通过__metaclass__创建一个名字为MyClass1的类对象。如果Python没有找到__metaclass__,它会继续在object(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。这里当程序发现MyClass1中有__metaclass__,所以用Singleton类代替元类type创建这个类。

其中还用到了__call__

object.__call__(self[, args…]):当把一个实例当作方法来调用的时候,形如instance(arg1,args2,…),那么实际上调用的就是 instance.__call__(arg1,arg2,…),实际上__call__模拟了()调用,作用在实例上,因此__init__作用完了,才调用__call__

关于元类的具体解析请参考http://blog.jobbole.com/21351/

  • 利用pythonDecoratorLibrary——Singleton

python提供了丰富的装饰者库,其中就有现成的Singleton,官方参考链接http://wiki.python.org/moin/PythonDecoratorLibrary#Singleton

我改写了一个较简单的版本,如下:

singleton3

 总结:利用上面多种方法实现后,能实现对于一个类只有一个对象,但是不能避免的事类有继承,有多个子类就可以生成多个子类的对象。其实在python中要实现单例模式并不需要借用类的概念(java和C#需要类是因为所有代码需要写在类中),而是可以借助模块来实现,python的模块本身就是唯一的单例的,其中属性和方法直接写为全局的变量和方法即可。

  6.Prototype模式

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

 

原型模式与工厂模式一样都生成一个对象,区别就是工厂模式是创建新的对象,而原型模式是克隆一个已经存在的对象,所以在对象初始化操作比较复杂的情况下,很实用,它能大大降低耗时,提高性能,因为“不用重新初始化对象,而是动态地获得对象运行时的状态”。

先来看看,原型模式的UML

图中各部分意思如下:

客户(Client)角色:客户类提出创建对象的请求,让一个原型克隆自身从而创建一个新的对象。
抽象原型(Prototype)角色:此角色给出所有的具体原型类所需的接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。

对于python实现原型模式有现成的copy模块可用。

copy.copy(x)浅拷贝
copy.deepcopy(x) 深拷贝

浅拷贝和深拷贝之间的区别仅适用于复合对象(包含其他对象也就是子对象,如list类或实例对象):

浅拷贝——构建一个新的对象然后插入到原来的引用上。只拷贝父对象,不会拷贝对象的内部的子对象。

深拷贝——构造一个新的对象以递归的形式,然后插入复制到它原来的对象上。拷贝对象及其子对象

 

Prototype

 

上面代码运行结果如下:

从结果可以看出,当b是a的浅拷贝,那么b中的实例对象WorkExperience只会复制了a中的引用,当不论是a,b哪一个修改都会改变a和b的WorkExperience实例。

c是a的深拷贝,创建了新的WorkExperience实例,所以c只会改变自己的WorkExperience

 

到这里6中创建型的模式已经学习完,下面接着学习Structural Patterns。

未完待续……

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。