改善python程序-内部机制

理解built-in objects

Python中一切皆对象:字符是对象,列表是对象,内建类型也是对象;用户定义的类型是对象,object是对象,type也是对象。

1
2
3
4
5
6
7
8
class A:
pass
class B (object):
pass
class C(type):
pass
class D (diet):
pass

编号对象type<*>classbasesisinstance(*,object)isinstance(*,type)
[1]object〈type ‘type’〉〈type ‘type’〉()
[2]type〈type ‘type’〉〈type ‘type’〉〈type ‘type’〉
[3]a〈type ‘instance’〉main.A()
[4]b〈class ‘main.B’>〈class ‘main.B’>〈type ‘type’〉
[5]c〈class ‘main.C’>〈class ‘main.C’>〈type ‘type’〉
[6]d〈class ‘main.D’>〈class ‘main.D’>〈type ‘type’〉

新式类能够基于内建类型构建新的用户类型,支持property和描述符特性等。作为新式类的祖先,Object类中还定义了一些特殊方法,如:new(), init(), delattr(), getattribute(),setattr(), hash(), _repr(),str__()等


类的内部机制

init()不是构造方法

init()方法是在类的对象创建好之后进行变量的初始化。new()方法才会真正创建实例,是类的构造方法

  • object.new(els [,args…]):其中els代表类, args 为参数列表
  • object.init(self [,args…])::其中self代表实例对象, args为参数列表
  • new()方法是静态方法,而init()为实例方法。
  • new()方法返回类的对象,当返回类的对象时将会自动调用方法进行初始化,如果没有对象返回,则init()方法不会被调用。方法不需要显式返回,默认为None,否则会在运行时拋出TypeError。
  • 当需要控制实例创建的时候可使用new()方法,而控制实例初始化的时候使用init()方法。
  • 一般情况下不需要覆盖new()方法,但当子类继承自不可变类型,如str、int、 imicode或者tuple的时候,往往需要覆盖该方法。
  • 当需要覆盖new()和init()方法的时候这两个方法的参数必须保持一致,如果不一致将导致异常

特殊情况下需要覆盖new()方法

(1)当类继承(如str、int、unicode、tuple或者forzenset等)不可变类型且默认的new()方法不能满足需求的时候

1
2
3
4
def __new__(cls, *args):
if args and isinstance(args[0], basestring):
args = (args[0].split(), ) + args[1:]
return super (UserSet, els).__new__(clS, args)

(2)用来实现工厂模式或者单例模式或者进行元类编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Shape(object):
def __init__(object):pass
def draw(self): pass
class Triangle(Shape):
def __init__(self):
print " I am a triangle"
def draw(self):
print "I am drawing triangle"
class Rectangle(Shape):
def __init__(self):
print "I am a rectnagle"
def draw (self):
print "I am drawing triangle"
class ShapeFactory(object):
shapes = {'triangle': Triangle, 'rectangle': Rectangle, 'trapezoid': Trapezoid, 'diamond': Diamond}
def __init__(klass, name):
if name in ShapeFactory.shapes.keys():
print "creating a new shape %s" % name
return ShapeFactory.shapes[name]()
else:
print "creating a new shape %s" % name
return Shape()
ShapeFactory('rectangle').draw()

理解名字查找机制

1
2
3
4
5
>>> locals ()
{'a': 1, 'c1': 'china', 'b': 1, '_builtins_':<module.__builtin__(built-in)>}
>>> globals()
{'a': l,'c','china','b': 1, '_builtins_':<module.__builtin__(built-in)>, '__package__': None, '__name__': '__main__'}

Python中变量的作用域

  • 局部作用域:一般来说函数的每次调用都会创建一个新的本地作用域,拥有新的命名 空间。默认情况下函数内部任意的赋值操作(包括=语句、import语句、def语句、参数传递等)所定义的变量名,如果没用global语句,则都为局部变量。
  • 全局作用域:定义在Python模块文件中的变量名拥有全局作用域,全局仅限单个文件,其他文件中想使用这些变量名必须先导人文件对应的模块
  • 嵌套作用域:需要注意的是global语句仅针对全局变量,在嵌套作用域的情况下,如果想在嵌套的函数内修改外层函数中定义的变量,即使使用global进行申明也不能达到目的,其结果最终是在嵌套的函数所在的命名空间中创建了一个新的变量。
  • 内置作用域:它是通过一个标准库中名builtin的模块来实现的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def ex2 ():
    var = 'a'
    def inner ():
    global var
    var = 'b'
    print 'inside inner, var is ', var
    inner ()
    print 'inside outer function, var is ', var
    # 上述程序的输出如下:
    inside inner, var is b
    inside outer function, var is a

Python的名字査找机制:

  • 在最内层的范围内査找,一般而言,就是函数内部,即在locals()里面査找
  • 在模块内査找,即在global()里面查找
  • 在外层査找,即在内置模块中査找,也就是在builtin中査找
global声明其为全局变量
1
2
3
4
5
6
7
8
9
10
11
12
>>> a = 1
>>> def foo (x):
... global a
... a = a * x
... def bar ():
... global a
... b = a * 2
... a = b + 1
... print a
... return bar
>>> foo (1)
3

Python3引入的nonlocal关键字解决这方面的问题

1
2
3
4
5
6
7
8
9
10
>>> def foo (x):
... a = x
... def bar():
... nonlocal a
... b = a * 2
... a = b + 1
... print (a)
... return bar
>>> bar = foo (1)

self参数

Python语言本身的动态性决定了使用self能够带来一定便利。由于self在函数调用中是隐式传递的,因此当直接调用全局函数len()时传入的是point对象,而当在类中调用该方法时,传入的是类所对应的对象,使用self可以在调用的时候决定应该传入哪一个对象。

1
2
3
4
5
6
7
8
9
10
>>> def len(point):
... return math.sqrt(point.x ** 2 + point.y * * 2)
>>> class RTriangle (object):
... def __init__(self,right_angle_sideX,right_angle_sideY):
... self.right_angle_sideX = right_angle_sideX
... self.right_angle_sideY = right_angle_sideY
>>> RTriangle.len = len
>>> rt = RTriangle (3, 4)
>>> rt. len ()
5.0

Python属于一级对象语言,如果m是类A的一个方法,有好几种方式都可以引用该方法

1
2
3
4
5
6
7
>>> class A:
... def m(self,value):
... pass
>>> A.__diet__[ 'm']
<function m at OxOOD617BO>
>>> A.m.__fun__
<function m at 0x00D617B0>

理解MRO与多继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A():
def getvalue(self):
print "return value of A"
def show(self):
print "I can show the information of A"
class B(A):
def getvalue(self):
print "return value of B"
class C(A):
def getvalue(self):
print "return value of C"
def show(self):
print "I can show the information of C"
class D(B,C):pass
# 调用d.getvalue()和d.show()
古典类输出如下:
return value of B
I can show the information of A
新式类输出如下:
return value of B
I can show the information of C

古典类中,MRO搜索采用简单的自左至右的深度优先方法,即按照多继承申明的顺序形成继承树结构,自顶向下采用深度优先的搜索顺序,当找到所需要的属性或者方法的时候就停止搜索。当调用d.getvalue()的时候,其搜索顺序为D->B,所以调用的是B类中对应的方法。而d.show()的搜索顺序为D->B->A,因此最后调用的是A类中对应的方法。

新式类采用C3 MRO搜索方法

  • (1)C1,C2…CN表示类C1到CN的序列,其中序列头部元素(head)=Cl,序列尾部 (tail)定义为=C2..CN;
  • (2)C继承的基类自左向右分别表示为Bl,B2…BN;
  • (3)L[C]表示C的线性继承关系,其中L[object]=object
    算法具体过程如下L[C(B1 …BN)1 = C + merge(L[B1]…L[BN], B1 …BN)
    merge方法的计算规则如下:在L[B1]…L[BN],B1…BN中,取L[B1]的head,如果该元素不在L[B2]…L[BN], B1 …BN的尾部序列中,则添加该元素到C的线性继承序列中,同时将该元素从所有列表中删除该元素,否则取L[B2]的head。继续相同的判断,直到整个列表为空或者没有办法找到任何符合要求的头元素(此时 将引发一个异常)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    L(0)=0;
    L(A)=AO;
    L(B)=B+merge(L(A))=B+merge(AO)=B+A+merge(0,0)=B,A,0
    L(C)=C+merge(L(A))=C+merge(AO)=C+A+merge(0,0)=C,A,0
    L(D)=D+merge(L[B], L[C], BC)
    =D+merge(BAO,CAO,BC)
    =D+B+merge(AO,CAO,C) # (下一个计算取AO的头A,但A包含在CAO的尾部,因此不满足条件.跳到下一个元素CAO继续计算)
    =D+B+C+merge(AO,AO)
    =D+B+C+A+O
    =DBCAO

当D的实例d调用getvalue()和show()方法时按照D>B>C>A>O的顺序进行搜索,在第一个找该方法的位置停止搜素并调用该类对应的方法,因此getvalue() 会在B的类中找到对应的方法,而show会在C()类中找到对应的方法。
MRO的搜索顺序我们也可以在新式类中通过查看mro属性得到证实