转贴自:http://www.xwy2.com/article.asp?id=119
原作者:Loong Tsui
定义
Python 的 Class 比较特别,和我们习惯的静态语言类型定义有很大区别。
1. 使用一个名为 __init__ 的方法来完成初始化。
2. 使用一个名为 __del__ 的方法来完成类似析购操作。
3. 所有的实例方法都拥有一个 self 参数来传递当前实例,类似于 this。
4. 可以使用 __class__ 来访问类型成员。
2 . def __init__ (self):
3 . print " initialize.... "
4 . def Foo(self):
5 . print id(self)
6 .
7 .
8 . >>> a = MyClass()
9 . initialize....
10 . >>> a.Foo()
11 . 14412576
12 . >>> id(a)
13 . 14412576
Class 有一些特殊的属性,便于我们获得一些额外的信息。
2 . >>> class MyClass(object):
3 . """ This is MyClass's Docoment """
4 . def __init__ (self):
5 . self.i = 1234
6 .
7 .
8 . >>> MyClass. __doc__ # 类型帮助信息
9 . " This is MyClass's Docoment "" This is MyClass's Docoment "
10 . >>> MyClass. __name__ # 类型名称
11 . ' MyClass '
12 . >>> MyClass. __module__ # 类型所在模块
13 . ' __main__ '
14 . >>> MyClass. __bases__ # 类型所继承的基类(Python 支持多继承)
15 . ( < type ' object ' > ,)
16 . >>> MyClass. __dict__ # 类型字典,存储所有类型成员信息
17 . < dictproxy object at 0x00DC1AD0 >
18 . >>> # 以下是实例拥有的属性
19 . >>> MyClass(). __class__ # 实例的类型
20 . < class ' __main__.MyClass ' >
21 . >>> MyClass(). __module__ # 实例类型所在模块
22 . ' __main__ '
23 . >>> MyClass(). __dict__ # 对象字典,存储所有实例成员信息
24 . { ' i ' : 1234 }
25 . >>>
继承
Python 支持多继承,但有几点需要注意:
1. 基类 __init__ / __del__ 需显示调用。
2. 继承方法的调用和基类声明顺序有关。
1 . >>> class Base1:
2 . def __init__ (self):
3 . print " Base1 "
4 . def test(self):
5 . print " Base1 test... "
6 .
7 .
8 . >>> class Base2:
9 . def __init__ (self):
10 . print " Base2 "
11 . def test(self):
12 . print " Base2 test... "
13 .
14 .
15 . >>> class MyClass(Base2,Base1):
16 . def __init__ (self):
17 . Base1. __init__ (self)
18 . Base2. __init__ (self)
19 . print " MyClass "
20 .
21 .
22 . >>> a = MyClass()
23 . Base1
24 . Base2
25 . MyClass
26 . >>> a.test()
27 . Base2 test...
28 . >>> # 下面把 Base1 放在前面
29 . >>> class MyClass(Base1,Base2):
30 . def __init__ (self):
31 . Base1. __init__ (self)
32 . Base2. __init__ (self)
33 . print " MyClass "
34 .
35 .
36 . >>> a = MyClass()
37 . Base1
38 . Base2
39 . MyClass
40 . >>> a.test()
41 . Base1 test...
42 . >>>
成员
Python Class 同样包含类型和实例两种成员。
1 . >>> class Class1:
2 . i = 123 # 类成员
3 . def __init__ (self):
4 . self.i = 100 # 实例成员
5 .
6 .
7 . >>> print Class1.i
8 . 123
9 . >>> print Class1().i
10 . 100
11 . >>>
有几个很 "特殊" 的 "规则" 需要注意。
(1) 我们可以通过实例引用访问类型成员。因此下面的例子中 self.i 实际指向 Class1.i,直到我们为实例新增了一个成员 i。
1 . >>> class Class1:
2 . i = 123
3 . def __init__ (self):
4 . print self.i
5 . print hex(id(self.i))
6 .
7 .
8 . >>> hex(id(Class1.i)) # 显示 Class1.i 的地址
9 . ' 0xab5860 '
10 . >>> a = Class1() # 创建 Class1 实例,我们会发现 self.i 实际指向 Class1.i
11 . 123
12 . 0xab5860
13 . >>> Class1. __dict__ # 显示 Class1 成员
14 . { ' i ' : 123 , ' __module__ ' : ' __main__ ' , ' __doc__ ' : None, ' __init__ ' : < function __init__ at 0x012911B0 > }
15 . >>> a. __dict__ # 显示实例成员
16 . {}
17 . >>> a.i = 100 # 为实例新增加一个成员i
18 . >>> hex(id(a.i)) # 显示新成员i的地址
19 . ' 0xab5974 '
20 . >>> a. __dict__ # 显示实例成员
21 . { ' i ' : 100 }
22 . >>>
(2) 调用类型内部方法,需要省略 self 参数。
1 . >>> class Class1:
2 . __i = 123
3 . def __init__ (self):
4 . self. __x = 0
5 . def __test (self):
6 . print id(self)
7 .
8 .
9 . >>> Class1.i
10 .
11 . Traceback (most recent call last):
12 . File " <pyshell#203> " , line 1 , in < module >
13 . Class1.i
14 . AttributeError: class Class1 has no attribute ' i '
15 . >>> Class1(). __x
16 .
17 . Traceback (most recent call last):
18 . File " <pyshell#204> " , line 1 , in < module >
19 . Class1(). __x
20 . AttributeError: Class1 instance has no attribute ' __x '
21 . >>> Class1(). __test ()
22 .
23 . Traceback (most recent call last):
24 . File " <pyshell#205> " , line 1 , in < module >
25 . Class1(). __test ()
26 . AttributeError: Class1 instance has no attribute ' __test '
27 . >>>
事实上这只是一种规则,并不是编译器上的限制。我们依然可以用特殊的语法来访问私有成员。
2 . 123
3 . >>> a = Class1()
4 . >>> a._Class1__x
5 . 0
6 . >>> a._Class1__test()
7 . 14432256
8 . >>>
除了静态(类型)字段,我们还可以定义静态方法。
1 . >>> class Class1:
2 . @staticmethod
3 . def test():
4 . print " In Static method... "
5 .
6 .
7 . >>> Class1.test()
8 . In Static method...
9 . >>>
从设计的角度,或许更希望用属性(property)来代替字段(field)。
1 . >>> class Class1:
2 . def __init__ (self):
3 . self. __i = 1234
4 . def getI(self): return self. __i
5 . def setI(self, value): self. __i = value
6 . def delI(self): del self. __i
7 . I = property(getI, setI, delI, " Property I " )
8 .
9 .
10 . >>> a = Class1()
11 . >>> a.I
12 . 1234
13 . >>> a.I = 1000
14 . >>> a.I
15 . 1000
16 . >>>
如果只是 readonly property,还可以用另外一种方式。
1 . >>> class Class1:
2 . def __init__ (self):
3 . self. __i = 1234
4 . @property
5 . def I(self):
6 . return self. __i
7 .
8 .
9 . >>> a = Class1()
10 . >>> a.I
11 . 1234
12 . >>>
用 __getitem__ 和 __setitem__ 可以实现 C# 索引器的功能。
1 . >>> class Class1:
2 . def __init__ (self):
3 . self. __x = [ " a " , " b " , " c " ]
4 . def __getitem__ (self, key):
5 . return self. __x [key]
6 . def __setitem__ (self, key, value):
7 . self. __x [key] = value
8 .
9 .
10 . >>> a = Class1()
11 . >>> a[ 1 ]
12 . ' b '
13 . >>> a[ 1 ] = " xxx "
14 . >>> a[ 1 ]
15 . ' xxx '
16 . >>>
重载
Python 支持一些特殊方法和运算符重载。
1 . >>> class Class1:
2 . def __init__ (self):
3 . self.i = 0
4 . def __str__ (self):
5 . return " id=%i " % (id(self))
6 . def __add__ (self, other):
7 . return self.i + other.i
8 .
9 .
10 . >>> a = Class1()
11 . >>> a.i = 10
12 . >>> str(a)
13 . ' id=19481664 '
14 . >>> b = Class1()
15 . >>> b.i = 20
16 . >>> a + b
17 . 30
18 . >>>
通过重载 "__eq__",我们可以改变 "==" 运算符的行为。
1 . >>> class Class1:
2 . pass
3 .
4 . >>> a = Class1()
5 . >>> b = Class1()
6 . >>> a == b
7 . False
8 . >>> # 实现操作符__eq__重载
9 . >>> class Class1:
10 . def __eq__ (self, other):
11 . return True
12 .
13 .
14 . >>> a = Class1()
15 . >>> b = Class1()
16 . >>> a == b
17 . True
18 . >>>
Open Class
这是个有争议的话题。在 Python 中,我们随时可以给类型或对象添加新的成员。
1. 添加字段
2 . pass
3 .
4 . >>> a = Class1()
5 . >>> a.x = 10
6 . >>> a.x
7 . 10
8 . >>> dir(a)
9 . [ ' __doc__ ' , ' __module__ ' , ' x ' ]
10 . >>> b = Class1()
11 . >>> dir(b)
12 . [ ' __doc__ ' , ' __module__ ' ]
13 . >>> del a.x
14 . >>> dir(a)
15 . [ ' __doc__ ' , ' __module__ ' ]
16 . >>> dir(Class1)
17 . [ ' __doc__ ' , ' __module__ ' ]
18 . >>> Class1.x = 100
19 . >>> Class1.x
20 . 100
21 . >>> dir(Class1)
22 . [ ' __doc__ ' , ' __module__ ' , ' x ' ]
23 . >>>
2. 添加方法
1 . >>> class Class1:
2 . pass
3 .
4 . >>> def test():
5 . print " test... "
6 .
7 .
8 . >>> def hello(self):
9 . print " hello " ,id(self)
10 .
11 .
12 . >>> Class1.test = test
13 . >>> a = Class1()
14 . >>> dir(a)
15 . [ ' __doc__ ' , ' __module__ ' , ' test ' ]
16 . >>> b = Class1()
17 . >>> a.hello = hello
18 . >>> dir(a)
19 . [ ' __doc__ ' , ' __module__ ' , ' hello ' , ' test ' ]
20 . >>> a.hello()
21 .
22 . Traceback (most recent call last):
23 . File " <pyshell#340> " , line 1 , in < module >
24 . a.hello()
25 . TypeError: hello() takes exactly 1 argument (0 given)
26 . >>> a.hello(a)
27 . hello 19474632
28 . >>> a.hello(b)
29 . hello 19481584
30 . >>> dir(b)
31 . [ ' __doc__ ' , ' __module__ ' , ' test ' ]
32 . >>> b.test()
33 .
34 . Traceback (most recent call last):
35 . File " <pyshell#344> " , line 1 , in < module >
36 . b.test()
37 . TypeError: test() takes no arguments ( 1 given)
38 . >>>
3. 改变现有方法
1 . >>> class Class1:
2 . def test(self):
3 . print " a "
4 .
5 .
6 . >>> def test(self):
7 . print " b "
8 .
9 .
10 . >>> Class1.test = test
11 . >>> Class1().test()
12 . b
13 . >>>
另外,有几个内建函数方便我们在运行期进行操作。
1 . >>> hasattr(a, " x " )
2 . False
3 . >>> a.x = 10
4 . >>> getattr(a, " x " )
5 . 10
6 . >>> setattr(a, " b " , 1234 )
7 . >>> a.b
8 . 1234
9 . >>>
Python Open Class 是如何实现的呢?我们看一下下面的代码。
1 . >>> class Class1:
2 . pass
3 .
4 . >>> a = Class1()
5 . >>> a. __dict__
6 . {}
7 . >>> a.x = 123
8 . >>> a. __dict__
9 . { ' x ' : 123 }
10 . >>> a.test = lambda i: i * 10
11 . >>> a.test( 1 )
12 . 10
13 . >>> a. __dict__
14 . { ' test ' : < function < lambda > at 0x012912F0 > , ' x ' : 123 }
15 . >>>
原来,Python Class 对象或类型通过内置成员 __dict__ 来存储成员信息。
我们还可以通过重载 __getattr__ 和 __setattr__ 来拦截对成员的访问,需要注意的是 __getattr__ 只有在访问不存在的成员时才会被调用。
1 . >>> class Class1:
2 . def __getattr__ (self, name):
3 . print " __getattr__ " , name
4 . return None
5 . def __setattr__ (self, name, value):
6 . print " __setattr__ " , name, value
7 . self. __dict__ [name] = value
8 .
9 .
10 . >>> a = Class1()
11 . >>> a.x
12 . __getattr__ x
13 . >>> a.x = 123
14 . __setattr__ x 123
15 . >>> a.x
16 . 123
17 . >>>
如果类型继承自 object,我们可以使用 __getattribute__ 来拦截所有(包括不存在的成员)的获取操作。
注意在 __getattribute__ 中不要使用 "return self.__dict__[name]" 来返回结果,因为在访问 "self.__dict__" 时同样会被 __getattribute__ 拦截,从而造成无限递归形成死循环。
1 . >>> class Class1(object):
2 . def __getattribute__ (self, name):
3 . print " __getattribute " , name
4 . return object. __getattribute__ (self, name)
5 .
6 .
7 . >>> a = Class1()
8 . >>> a.x
9 . __getattribute x
10 .
11 . Traceback (most recent call last):
12 . File " <pyshell#396> " , line 1 , in < module >
13 . a.x
14 . File " <pyshell#394> " , line 4 , in __getattribute__
15 . return object. __getattribute__ (self, name)
16 . AttributeError: ' Class1 ' object has no attribute ' x '
17 . >>> a.x = 123
18 . >>> a.x
19 . __getattribute x
20 . 123
21 . >>>