转载一个python基类,多态的文章
By:Roy.LiuLast updated:2012-02-24
很早以前见到过“python的类、基类、多态、取消基类的代码",现在很想找到那个例子,可惜,又找不到了!算了,把已经知道的收集一下,以后看到了再补充!
__bases__是一个元组(可能是None或独元), 包括其基类, 以基类列表中它们的排列次序出现
---------
http://www.ixpub.net/redirect.php?tid=641482&goto=lastpost
http://www.linuxforum.net/forum/printthread.php?Cat=&Board=python&main=108720&type=post
---------
def classic_lookup(cls, name):
"Look up name in cls and its base classes."
if cls.__dict__.has_key(name):
return cls.__dict__[name]
for base in cls.__bases__:
try:
return classic_lookup(base, name)
except AttributeError:
pass
raise AttributeError, name
-----------
http://www.rainsts.net/article.asp?id=386
定义
Python 的 Class 比较特别,和我们习惯的静态语言类型定义有很大区别。
1. 使用一个名为 __init__ 的方法来完成初始化。
2. 使用一个名为 __del__ 的方法来完成类似析购操作。
3. 所有的实例方法都拥有一个 self 参数来传递当前实例,类似于 this。
4. 可以使用 __class__ 来访问类型成员。
>>> class Class1:
def __init__(self):
print "initialize..."
def test(self):
print id(self)
>>> a = Class1()
initialize...
>>> a.test()
13860176
>>> id(a)
13860176
Class 有一些特殊的属性,便于我们获得一些额外的信息。
>>> class Class1(object):
"""Class1 Doc."""
def __init__(self):
self.i = 1234
>>> Class1.__doc__ # 类型帮助信息
'Class1 Doc.'
>>> Class1.__name__ # 类型名称
'Class1'
>>> Class1.__module__ # 类型所在模块
'__main__'
>>> Class1.__bases__ # 类型所继承的基类
(,)
>>> Class1.__dict__ # 类型字典,存储所有类型成员信息。
>>> Class1().__class__ # 类型
>>> Class1().__module__ # 实例类型所在模块
'__main__'
>>> Class1().__dict__ # 对象字典,存储所有实例成员信息。
{'i': 1234}
继承
Python 支持多继承,但有几点需要注意:
1. 基类 __init__ / __del__ 需显示调用。
2. 继承方法的调用和基类声明顺序有关。
>>> class Base1:
def __init__(self):
print "Base1"
def test(self):
print "Base1 test..."
>>> class Base2:
def __init__(self):
print "Base2"
def test(self):
print "Base2 test..."
>>> class Class1(Base2, Base1):
def __init__(self):
Base1.__init__(self)
Base2.__init__(self)
print "Class1"
>>> a = Class1()
Base1
Base2
Class1
>>> a.test()
Base2 test...
成员
Python Class 同样包含类型和实例两种成员。
>>> class Class1:
i = 123 # Class Field
def __init__(self):
self.i = 12345 # Instance Field
>>> print Class1.i
123
>>> print Class1().i
12345
-----------------------
有几个很 "特殊" 的 "规则" 需要注意。
(1) 我们可以通过实例引用访问类型成员。因此下面的例子中 self.i 实际指向 Class1.i,直到我们为实例新增了一个成员 i。
>>> class Class1:
i = 123
def __init__(self):
print self.i
print hex(id(self.i))
>>> hex(id(Class1.i)) # 显示 Class1.i
'0xab57a0'
>>> a = Class1() # 创建 Class1 实例,我们会发现 self.i 实际指向 Class1.i。
123
0xab57a0
>>> Class1.__dict__ # 显示 Class1 成员
{'i': 123, '__module__': '__main__', '__doc__': None, '__init__':}
>>> a.__dict__ # 显示实例成员
{}
>>> a.i = 123456789 # 为实例新增一个成员 i
>>> hex(id(a.i)) # 显示新增实例成员地址
'0xbbb674'
>>> a.__dict__ # 显示实例成员
{'i': 123456789}
(2) 调用类型内部方法,需要省略 self 参数。
>>> class Class1:
def __init__(self):
self.__test("Hello, World!")
def __test(self, s):
print s
>>> Class1()
Hello, World!
<__main__.Class1 instance at 0x00D37B48>
-----------------------
我们可以在成员名称前添加 "__" 使其成为私有成员。
>>> class Class1:
__i = 123
def __init__(self):
self.__x = 0
def __test(self):
print id(self)
>>> Class1.i
Traceback (most recent call last):
File "", line 1, in
Class1.i
AttributeError: class Class1 has no attribute 'i'
>>> Class1().__x
Traceback (most recent call last):
File "", line 1, in
Class1().__x
AttributeError: Class1 instance has no attribute '__x'
>>> Class1().test()
Traceback (most recent call last):
File "", line 1, in
Class1().test()
AttributeError: Class1 instance has no attribute 'test'
事实上这只是一种规则,并不是编译器上的限制。我们依然可以用特殊的语法来访问私有成员。
>>> Class1._Class1__i
123
>>> a = Class1()
>>> a._Class1__x
0
>>> a._Class1__test()
13860376
-----------------------
除了静态(类型)字段,我们还可以定义静态方法。
>>> class Class1:
@staticmethod
def test():
print "static method"
>>> Class1.test()
static method
-----------------------
从设计的角度,或许更希望用属性(property)来代替字段(field)。
>>> class Class1:
def __init__(self):
self.__i = 1234
def getI(self): return self.__i
def setI(self, value): self.__i = value
def delI(self): del self.__i
I = property(getI, setI, delI, "Property I")
>>> a = Class1()
>>> a.I
1234
>>> a.I = 123456
>>> a.I
123456
如果只是 readonly property,还可以用另外一种方式。
>>> class Class1:
def __init__(self):
self.__i = 1234
@property
def I(self):
return self.__i
>>> a = Class1()
>>> a.I
1234
-----------------------
用 __getitem__ 和 __setitem__ 可以实现 C# 索引器的功能。
>>> class Class1:
def __init__(self):
self.__x = ["a", "b", "c"]
def __getitem__(self, key):
return self.__x[key]
def __setitem__(self, key, value):
self.__x[key] = value
>>> a = Class1()
>>> a[1]
'b'
>>> a[1] = "xxxx"
>>> a[1]
'xxxx'
重载
Python 支持一些特殊方法和运算符重载。
>>> class Class1:
def __init__(self):
self.i = 0
def __str__(self):
return "id=%i" % id(self)
def __add__(self, other):
return self.i + other.i
>>> a = Class1()
>>> a.i = 10
>>> str(a)
'id=13876120'
>>> b = Class1()
>>> b.i = 20
>>> a + b
30
通过重载 "__eq__",我们可以改变 "==" 运算符的行为。
>>> class Class1:
pass
>>> a = Class1()
>>> b = Class1()
>>> a == b
False
>>> class Class1:
def __eq__(self, x):
return True
>>> a = Class1()
>>> b = Class1()
>>> a == b
True
Open Class
这是个有争议的话题。在 Python 中,我们随时可以给类型或对象添加新的成员。
1. 添加字段
>>> class Class1:
pass
>>> a = Class1()
>>> a.x = 10
>>> a.x
10
>>> dir(a)
['__doc__', '__module__', 'x']
>>> b = Class1()
>>> dir(b)
['__doc__', '__module__']
>>> del a.x
>>> dir(a)
['__doc__', '__module__']
2. 添加方法
>>> class Class1:
pass
>>> def test():
print "test"
>>> def hello(self):
print "hello ", id(self)
>>> a = Class1()
>>> dir(a)
['__doc__', '__module__']
>>> Class1.test = test
>>> dir(a)
['__doc__', '__module__', 'test']
>>> b = Class1()
>>> dir(b)
['__doc__', '__module__', 'test']
>>> a.hello = hello
>>> a.hello(a)
hello 13860416
>>> dir(a)
['__doc__', '__module__', 'hello', 'test']
>>> dir(b)
['__doc__', '__module__', 'test']
3. 改变现有方法
>>> class Class1:
def test(self):
print "a"
>>> def test(self):
print "b"
>>> Class1.test = test
>>> Class1().test()
b
另外,有几个内建函数方便我们在运行期进行操作。
>>> hasattr(a, "x")
False
>>> a.x = 10
>>> getattr(a, "x")
10
>>> setattr(a, "x", 1234)
>>> a.x
1234
Python Open Class 是如何实现的呢?我们看一下下面的代码。
>>> class Class1:
pass
>>> a = Class1()
>>> a.__dict__
{}
>>> a.x = 123
>>> a.__dict__
{'x': 123}
>>> a.x
123
>>> a.test = lambda i: i + 1
>>> a.test(1)
2
>>> a.__dict__
{'test': at 0x00D39DB0>, 'x': 123}
原来,Python Class 对象或类型通过内置成员 __dict__ 来存储成员信息。
我们还可以通过重载 __getattr__ 和 __setattr__ 来拦截对成员的访问,需要注意的是 __getattr__ 只有在访问不存在的成员时才会被调用。
>>> class Class1:
def __getattr__(self, name):
print "__getattr__"
return None
def __setattr__(self, name, value):
print "__setattr__"
self.__dict__[name] = value
>>> a = Class1()
>>> a.x
__getattr__
>>> a.x = 123
__setattr__
>>> a.x
123
如果类型继承自 object,我们可以使用 __getattribute__ 来拦截所有(包括不存在的成员)的获取操作。
注意在 __getattribute__ 中不要使用 "return self.__dict__[name]" 来返回结果,因为在访问 "self.__dict__" 时同样会被 __getattribute__ 拦截,从而造成无限递归形成死循环。
>>> class Class1(object):
def __getattribute__(self, name):
print "__getattribute__"
return object.__getattribute__(self, name)
>>> a = Class1()
>>> a.x
__getattribute__
Traceback (most recent call last):
File "", line 1, in
a.x
File "", line 4, in __getattribute__
return object.__getattribute__(self, name)
AttributeError: 'Class1' object has no attribute 'x'
>>> a.x = 123
>>> a.x
__getattribute__
123
---------------
__bases__是父类吧,指的是继承关系;
__class__才是一个对象的"类/类型",指的是类型/实例关系。
在06-1-10,shhgs 写道:
>
> mixin
>
> class A(object) :
> pass
>
> class B :
> def f(self) :
> print "B.f()"
>
> A.__bases__ += (B, ) #好像这个就是我要找的,可以把.__bases__这个元组?list里面的基类们随时删除、添加! 呵呵!功能超强啊!
>
> a = A()
> a.f()
-------------
开始学习python的时候,看了一些教程和资料,都觉得在面向对象编程这一方面讲得比较零散,自己也就总觉得不得要领。直到看到了Oreilly出的 Python in the Nutshell,英文版,特别是Charpter5: Object-Oriented Python,才开始明白一点点东西。这本书,对章节的编排非常合理,而且不光教你how还教你why,觉得受益匪浅。
看的过程中,自己陆续的记下一些东西,有对书的部分翻译,有自己的体会和测试代码。
翻译中,有少部分是直接翻译,大部分其实只是自己的意译,还添油加醋加上了自己的一些说明。毕竟,我的目的是为了把问题弄懂,不想去做那么多 hard translation反让人如坠迷雾。于是想汇成一篇,就有了这篇文章。文章的安排,基本上和Python in the Nutshell的Charpter5相同,但内容要短得多。
Wilson Sun
----------------------------------------------------------------------------------------------------------------
python是一种面向对象的编程语言。不过不象其它的面向对象的语言,python并不强迫你用面向对象的方法来写程序,它也允许你用面向过程的方式来写模块、函数等。
1.1 经典对象(classic class)
在2.1及其之前版本的python,只能用经典对象这种对象模型来编程。在2.2和2.3版本的python,经典对象也是默认的对象模型。
1.1.1 经典对象的一些特征:
l 你可以象调用函数一样的来调用一个对象。一旦调用,该对象的一个实例就被建立。
l 你可以随意地为对象内部的属性命名。
l 属性可以是数据类型,也可以是函数类型。
l 若属性(attribute)是函数类型,那么就把它看做对象的一个方法(method)。
l python为函数规定了一种特殊的命名方式,用前后两个下划线来包围函数名,例如:__methodName__。
l 对象可以继承。
1.1.2 对象声明
声明对象的语法:
class classname[(base-classes)]:
statement(s)
classname是函数名。
base-classes的值必须为对象,用逗号分隔,它相当于java中的超类(superclass)。
继承关系可以被传递,如果c1是c2的子类,c2是c3的子类,那么c1也是c3的子类。
内建函数issubclass(C1, C2)可以判断继承关系,若c1是c2的子类,那么函数返回true。由于任何类都被看作是自身的子类,所以若有类C,那么issubclass(C,C)返回true。
Def声明语法和class声明语法的区别:
Def声明时,冒号前面必须有括号,即使它本身没有任何参数;但class声明时,只有当class有其基类(base class)的时候,才需要在括号中写出其基类。
1.1.3 对象正文
1.1.3.1 对象内部的属性
在对象内部调用其属性,直接写其属性名称即可,如:
class C3:
x = 23
y = x + 22 # must use just x, not C3.x
但若在对象内部定义了方法,要在方法中调用对象中的其它属性,需要写属性的全名,如:
class C4:
x = 23
def amethod(self):
print C4.x # must use C4.x, not just x
当对象被声明的时候,其中的一些属性已经被隐式声明了。
__name__:类的名称
__base__:tuple对象,放置对象的所有基类
__dict__:dict对象,放置对象的所有属性
例如:对象C内部有属性S,那么C.S=x 等价于C._ _dict_ _['S']=x
1.1.3.2 对象内部的函数
对象内部的函数写法与普通的函数写法差不多,不过函数的第一个参数要写为self,如:
class C5:
def hello(self):
print "Hello"
1.1.3.3 私有变量
私有变量的声明,只需在变量名前面加两个下划线,如类C的内部有私有变量user,应声明为:__user。
事实上,当python编译的时候,会把__user改为_C__user(即_ClassName__VariableName的格式)。
无论是否在对象内部,以一个下划线开头的变量,都被看作私有变量。
1.1.4 实例
回顾一下前面的“经典对象的一些特征”中的第一点:“你可以象调用函数一样的来调用一个对象”。创建实例的时候,就是如此创建:
anInstance = C5( )
1.1.4.1 _ _init_ _
如果一个对象的内部,有或继承有_ _init_ _方法,那么当这个对象被调用(用java上的词可以叫实例化)时,_ _init_ _方法会自动地被调用。
_ _init_ _方法不能有返回值,如果一定需要跳出或返回,也只能返回None。
例如:
class C4:
def __init__(self):
return 'sss'
a = C4();
python会报错:
Traceback (most recent call last):
File "", line 1, in -toplevel-
a = C4();
TypeError: __init__() should return None
_ _init_ _方法的主要目的,是为了在创建对象实例的时候,对对象的属性赋值。这么做可以增加程序的可读性。如果对象内部没有_ _init_ _,那么你调用对象的时候就不能带上任何的参数。
1.1.4.2 实例中的属性
用点(.)来访问实例中的属性。
即使一个对象已经被实例化,仍可以个实例增加任意的属性,并对其赋值。
class C7: pass
z = C7( )
z.x = 23
实例被创建以后,该实例会被自动加上两个属性:
_ _class_ _:实例所属的对象
_ _dict_ _:实例的所有属性(实例自身的属性和其所属对象的属性)
如:
class C6:
def _ _init_ _(self,n):
self.x = n
a = C6(234)
a.y=213213
a.__dict__
#执行结果:{'y': 213213, 'x': 234}
1.1.4.3 工厂函数
遥想一下,设计模式中被用得最多得工厂模式(Factory),它被用于创建对象的实例。在python当中,最直接的用来实现工厂模式的方式,似乎是用_ _init_ _来返回不同的实例,但是,unfortunately,_ _init_ _最多也只能返回None。所以要实现工厂模式,最佳的方式就是专门写一个函数,用来返回不同的实例,这类函数,可以称之为工厂函数(Factory Function)。
如下例,appropriateCase就是一个工厂函数。
class SpecialCase:
def amethod(self): print "special"
class NormalCase:
def amethod(self): print "normal"
def appropriateCase(isnormal=1):
if isnormal: return NormalCase( )
else: return SpecialCase( )
aninstance = appropriateCase(isnormal=0)
aninstance.amethod( )
1.1.5 属性引用
假设x是对象C的实例,当引用x.name的时候,是如何来查找它的值呢?用最简单的话来概括,就是,由小到大由近到远,依次查找name的值。
再说得具体一些,是按下面的方式查找:
l 若x.name是x._ _dict_ _,中的key,那么返回x._ _dict_ _['name'] (查找自身)
l 否则,查找C._ _dict_ _中的key,是则返回C._ _dict_ _['name'] (查找所属对象)
l 否则,查找C的基类,在C._ _bases_ _中继续按上面的两步查找(查找所属对象的基类)
l 否则,抛出异常:AttributeError
1.1.6 方法的绑定与非绑定(Bound and Unbound)
上面讲了属性的引用,方法的绑定与非绑定实际上涉及到的是方法引用的问题。方法实际上使用函数来实现。当方法被引用时,并非直接返回其对应的函数,而是将这个函数,载入到了bound或者unbound方法上。
bound和unbound的区别在于:bound将特定的函数,与特定的实例相关联;而unbound则相反。
若没有同名属性,直接用函数名(函数名后不带括号),可以观察到其绑定状态。
假设有对象C和实例x:
class C:
a = 1
def g(self): print "method g in class C"
x = C()
print x.g
#执行结果:>
print C.g
#执行结果:
上面的执行结果表明:x.g被绑定到了C.g()函数上,所以执行x.g()会有结果返回;而C.g没有被绑定,所以执行C.g()没有结果。
1.1.6.1 细说非绑定
若处于非绑定状态,当一个函数被引用的时候,实际返回的是unbound方法,该方法内部载有该函数。Unbound方法有三个只读属性
im_class:被引用函数所在的对象
im_func:被引用的函数
im_self:总是为None
非绑定的方法也可以被调用,需要把im_class对象的实例名做为第一个参数,那么就会返回im_func的函数了。
例如上面的C.g()没有结果,但可以执行C.g(x),x为C的实例,就可以得到C.g()函数的正确执行结果了。
1.1.6.2 细说绑定
当执行x.g()时,返回的是bound 方法。bound方法和unbound方法类似,也有三个只读属性:im_class,im_func,im_self,但不同之处在于:im_self的值为x。
1.1.7 继承与重载
从前面说到的“属性引用”的查找方法,不难看出python继承的实现方式。若x为C的实例,当调用x.g()的时候,python先看x._ _dict_ _中有没有g,没有就查找C._ _dict_ _,若再没有就查找C._ _base_ _有没有g。C._ _base_ _中放置了C的基类,就实现了对象的继承。用这样的机制,也同样实现了重载。
1.1.7.1 超类代理
在子类中,有可能要用到超类的属性,那么就要使用到超类代理机制,实际上是用unbound方式调用超类的函数。例如:
class Base:
def greet(self, name): print "Welcome ", name
class Sub(Base):
def greet(self, name):
print "Well Met and",
Base.greet(self, name)
x = Sub( )
x.greet('Alex')
超类代理常用于_ _init_ _方法中,因为在python中,子类的_ _init_ _方法中并不会自动地调用其超类中的_ _init_ _方法,所以需要利用这种超类代理机制手动调用一下。
__bases__是一个元组(可能是None或独元), 包括其基类, 以基类列表中它们的排列次序出现
---------
http://www.ixpub.net/redirect.php?tid=641482&goto=lastpost
http://www.linuxforum.net/forum/printthread.php?Cat=&Board=python&main=108720&type=post
---------
def classic_lookup(cls, name):
"Look up name in cls and its base classes."
if cls.__dict__.has_key(name):
return cls.__dict__[name]
for base in cls.__bases__:
try:
return classic_lookup(base, name)
except AttributeError:
pass
raise AttributeError, name
-----------
http://www.rainsts.net/article.asp?id=386
定义
Python 的 Class 比较特别,和我们习惯的静态语言类型定义有很大区别。
1. 使用一个名为 __init__ 的方法来完成初始化。
2. 使用一个名为 __del__ 的方法来完成类似析购操作。
3. 所有的实例方法都拥有一个 self 参数来传递当前实例,类似于 this。
4. 可以使用 __class__ 来访问类型成员。
>>> class Class1:
def __init__(self):
print "initialize..."
def test(self):
print id(self)
>>> a = Class1()
initialize...
>>> a.test()
13860176
>>> id(a)
13860176
Class 有一些特殊的属性,便于我们获得一些额外的信息。
>>> class Class1(object):
"""Class1 Doc."""
def __init__(self):
self.i = 1234
>>> Class1.__doc__ # 类型帮助信息
'Class1 Doc.'
>>> Class1.__name__ # 类型名称
'Class1'
>>> Class1.__module__ # 类型所在模块
'__main__'
>>> Class1.__bases__ # 类型所继承的基类
(
>>> Class1.__dict__ # 类型字典,存储所有类型成员信息。
>>> Class1().__class__ # 类型
>>> Class1().__module__ # 实例类型所在模块
'__main__'
>>> Class1().__dict__ # 对象字典,存储所有实例成员信息。
{'i': 1234}
继承
Python 支持多继承,但有几点需要注意:
1. 基类 __init__ / __del__ 需显示调用。
2. 继承方法的调用和基类声明顺序有关。
>>> class Base1:
def __init__(self):
print "Base1"
def test(self):
print "Base1 test..."
>>> class Base2:
def __init__(self):
print "Base2"
def test(self):
print "Base2 test..."
>>> class Class1(Base2, Base1):
def __init__(self):
Base1.__init__(self)
Base2.__init__(self)
print "Class1"
>>> a = Class1()
Base1
Base2
Class1
>>> a.test()
Base2 test...
成员
Python Class 同样包含类型和实例两种成员。
>>> class Class1:
i = 123 # Class Field
def __init__(self):
self.i = 12345 # Instance Field
>>> print Class1.i
123
>>> print Class1().i
12345
-----------------------
有几个很 "特殊" 的 "规则" 需要注意。
(1) 我们可以通过实例引用访问类型成员。因此下面的例子中 self.i 实际指向 Class1.i,直到我们为实例新增了一个成员 i。
>>> class Class1:
i = 123
def __init__(self):
print self.i
print hex(id(self.i))
>>> hex(id(Class1.i)) # 显示 Class1.i
'0xab57a0'
>>> a = Class1() # 创建 Class1 实例,我们会发现 self.i 实际指向 Class1.i。
123
0xab57a0
>>> Class1.__dict__ # 显示 Class1 成员
{'i': 123, '__module__': '__main__', '__doc__': None, '__init__':
>>> a.__dict__ # 显示实例成员
{}
>>> a.i = 123456789 # 为实例新增一个成员 i
>>> hex(id(a.i)) # 显示新增实例成员地址
'0xbbb674'
>>> a.__dict__ # 显示实例成员
{'i': 123456789}
(2) 调用类型内部方法,需要省略 self 参数。
>>> class Class1:
def __init__(self):
self.__test("Hello, World!")
def __test(self, s):
print s
>>> Class1()
Hello, World!
<__main__.Class1 instance at 0x00D37B48>
-----------------------
我们可以在成员名称前添加 "__" 使其成为私有成员。
>>> class Class1:
__i = 123
def __init__(self):
self.__x = 0
def __test(self):
print id(self)
>>> Class1.i
Traceback (most recent call last):
File "
Class1.i
AttributeError: class Class1 has no attribute 'i'
>>> Class1().__x
Traceback (most recent call last):
File "
Class1().__x
AttributeError: Class1 instance has no attribute '__x'
>>> Class1().test()
Traceback (most recent call last):
File "
Class1().test()
AttributeError: Class1 instance has no attribute 'test'
事实上这只是一种规则,并不是编译器上的限制。我们依然可以用特殊的语法来访问私有成员。
>>> Class1._Class1__i
123
>>> a = Class1()
>>> a._Class1__x
0
>>> a._Class1__test()
13860376
-----------------------
除了静态(类型)字段,我们还可以定义静态方法。
>>> class Class1:
@staticmethod
def test():
print "static method"
>>> Class1.test()
static method
-----------------------
从设计的角度,或许更希望用属性(property)来代替字段(field)。
>>> class Class1:
def __init__(self):
self.__i = 1234
def getI(self): return self.__i
def setI(self, value): self.__i = value
def delI(self): del self.__i
I = property(getI, setI, delI, "Property I")
>>> a = Class1()
>>> a.I
1234
>>> a.I = 123456
>>> a.I
123456
如果只是 readonly property,还可以用另外一种方式。
>>> class Class1:
def __init__(self):
self.__i = 1234
@property
def I(self):
return self.__i
>>> a = Class1()
>>> a.I
1234
-----------------------
用 __getitem__ 和 __setitem__ 可以实现 C# 索引器的功能。
>>> class Class1:
def __init__(self):
self.__x = ["a", "b", "c"]
def __getitem__(self, key):
return self.__x[key]
def __setitem__(self, key, value):
self.__x[key] = value
>>> a = Class1()
>>> a[1]
'b'
>>> a[1] = "xxxx"
>>> a[1]
'xxxx'
重载
Python 支持一些特殊方法和运算符重载。
>>> class Class1:
def __init__(self):
self.i = 0
def __str__(self):
return "id=%i" % id(self)
def __add__(self, other):
return self.i + other.i
>>> a = Class1()
>>> a.i = 10
>>> str(a)
'id=13876120'
>>> b = Class1()
>>> b.i = 20
>>> a + b
30
通过重载 "__eq__",我们可以改变 "==" 运算符的行为。
>>> class Class1:
pass
>>> a = Class1()
>>> b = Class1()
>>> a == b
False
>>> class Class1:
def __eq__(self, x):
return True
>>> a = Class1()
>>> b = Class1()
>>> a == b
True
Open Class
这是个有争议的话题。在 Python 中,我们随时可以给类型或对象添加新的成员。
1. 添加字段
>>> class Class1:
pass
>>> a = Class1()
>>> a.x = 10
>>> a.x
10
>>> dir(a)
['__doc__', '__module__', 'x']
>>> b = Class1()
>>> dir(b)
['__doc__', '__module__']
>>> del a.x
>>> dir(a)
['__doc__', '__module__']
2. 添加方法
>>> class Class1:
pass
>>> def test():
print "test"
>>> def hello(self):
print "hello ", id(self)
>>> a = Class1()
>>> dir(a)
['__doc__', '__module__']
>>> Class1.test = test
>>> dir(a)
['__doc__', '__module__', 'test']
>>> b = Class1()
>>> dir(b)
['__doc__', '__module__', 'test']
>>> a.hello = hello
>>> a.hello(a)
hello 13860416
>>> dir(a)
['__doc__', '__module__', 'hello', 'test']
>>> dir(b)
['__doc__', '__module__', 'test']
3. 改变现有方法
>>> class Class1:
def test(self):
print "a"
>>> def test(self):
print "b"
>>> Class1.test = test
>>> Class1().test()
b
另外,有几个内建函数方便我们在运行期进行操作。
>>> hasattr(a, "x")
False
>>> a.x = 10
>>> getattr(a, "x")
10
>>> setattr(a, "x", 1234)
>>> a.x
1234
Python Open Class 是如何实现的呢?我们看一下下面的代码。
>>> class Class1:
pass
>>> a = Class1()
>>> a.__dict__
{}
>>> a.x = 123
>>> a.__dict__
{'x': 123}
>>> a.x
123
>>> a.test = lambda i: i + 1
>>> a.test(1)
2
>>> a.__dict__
{'test':
原来,Python Class 对象或类型通过内置成员 __dict__ 来存储成员信息。
我们还可以通过重载 __getattr__ 和 __setattr__ 来拦截对成员的访问,需要注意的是 __getattr__ 只有在访问不存在的成员时才会被调用。
>>> class Class1:
def __getattr__(self, name):
print "__getattr__"
return None
def __setattr__(self, name, value):
print "__setattr__"
self.__dict__[name] = value
>>> a = Class1()
>>> a.x
__getattr__
>>> a.x = 123
__setattr__
>>> a.x
123
如果类型继承自 object,我们可以使用 __getattribute__ 来拦截所有(包括不存在的成员)的获取操作。
注意在 __getattribute__ 中不要使用 "return self.__dict__[name]" 来返回结果,因为在访问 "self.__dict__" 时同样会被 __getattribute__ 拦截,从而造成无限递归形成死循环。
>>> class Class1(object):
def __getattribute__(self, name):
print "__getattribute__"
return object.__getattribute__(self, name)
>>> a = Class1()
>>> a.x
__getattribute__
Traceback (most recent call last):
File "
a.x
File "
return object.__getattribute__(self, name)
AttributeError: 'Class1' object has no attribute 'x'
>>> a.x = 123
>>> a.x
__getattribute__
123
---------------
__bases__是父类吧,指的是继承关系;
__class__才是一个对象的"类/类型",指的是类型/实例关系。
在06-1-10,shhgs
>
> mixin
>
> class A(object) :
> pass
>
> class B :
> def f(self) :
> print "B.f()"
>
> A.__bases__ += (B, ) #好像这个就是我要找的,可以把.__bases__这个元组?list里面的基类们随时删除、添加! 呵呵!功能超强啊!
>
> a = A()
> a.f()
-------------
开始学习python的时候,看了一些教程和资料,都觉得在面向对象编程这一方面讲得比较零散,自己也就总觉得不得要领。直到看到了Oreilly出的 Python in the Nutshell,英文版,特别是Charpter5: Object-Oriented Python,才开始明白一点点东西。这本书,对章节的编排非常合理,而且不光教你how还教你why,觉得受益匪浅。
看的过程中,自己陆续的记下一些东西,有对书的部分翻译,有自己的体会和测试代码。
翻译中,有少部分是直接翻译,大部分其实只是自己的意译,还添油加醋加上了自己的一些说明。毕竟,我的目的是为了把问题弄懂,不想去做那么多 hard translation反让人如坠迷雾。于是想汇成一篇,就有了这篇文章。文章的安排,基本上和Python in the Nutshell的Charpter5相同,但内容要短得多。
Wilson Sun
----------------------------------------------------------------------------------------------------------------
python是一种面向对象的编程语言。不过不象其它的面向对象的语言,python并不强迫你用面向对象的方法来写程序,它也允许你用面向过程的方式来写模块、函数等。
1.1 经典对象(classic class)
在2.1及其之前版本的python,只能用经典对象这种对象模型来编程。在2.2和2.3版本的python,经典对象也是默认的对象模型。
1.1.1 经典对象的一些特征:
l 你可以象调用函数一样的来调用一个对象。一旦调用,该对象的一个实例就被建立。
l 你可以随意地为对象内部的属性命名。
l 属性可以是数据类型,也可以是函数类型。
l 若属性(attribute)是函数类型,那么就把它看做对象的一个方法(method)。
l python为函数规定了一种特殊的命名方式,用前后两个下划线来包围函数名,例如:__methodName__。
l 对象可以继承。
1.1.2 对象声明
声明对象的语法:
class classname[(base-classes)]:
statement(s)
classname是函数名。
base-classes的值必须为对象,用逗号分隔,它相当于java中的超类(superclass)。
继承关系可以被传递,如果c1是c2的子类,c2是c3的子类,那么c1也是c3的子类。
内建函数issubclass(C1, C2)可以判断继承关系,若c1是c2的子类,那么函数返回true。由于任何类都被看作是自身的子类,所以若有类C,那么issubclass(C,C)返回true。
Def声明语法和class声明语法的区别:
Def声明时,冒号前面必须有括号,即使它本身没有任何参数;但class声明时,只有当class有其基类(base class)的时候,才需要在括号中写出其基类。
1.1.3 对象正文
1.1.3.1 对象内部的属性
在对象内部调用其属性,直接写其属性名称即可,如:
class C3:
x = 23
y = x + 22 # must use just x, not C3.x
但若在对象内部定义了方法,要在方法中调用对象中的其它属性,需要写属性的全名,如:
class C4:
x = 23
def amethod(self):
print C4.x # must use C4.x, not just x
当对象被声明的时候,其中的一些属性已经被隐式声明了。
__name__:类的名称
__base__:tuple对象,放置对象的所有基类
__dict__:dict对象,放置对象的所有属性
例如:对象C内部有属性S,那么C.S=x 等价于C._ _dict_ _['S']=x
1.1.3.2 对象内部的函数
对象内部的函数写法与普通的函数写法差不多,不过函数的第一个参数要写为self,如:
class C5:
def hello(self):
print "Hello"
1.1.3.3 私有变量
私有变量的声明,只需在变量名前面加两个下划线,如类C的内部有私有变量user,应声明为:__user。
事实上,当python编译的时候,会把__user改为_C__user(即_ClassName__VariableName的格式)。
无论是否在对象内部,以一个下划线开头的变量,都被看作私有变量。
1.1.4 实例
回顾一下前面的“经典对象的一些特征”中的第一点:“你可以象调用函数一样的来调用一个对象”。创建实例的时候,就是如此创建:
anInstance = C5( )
1.1.4.1 _ _init_ _
如果一个对象的内部,有或继承有_ _init_ _方法,那么当这个对象被调用(用java上的词可以叫实例化)时,_ _init_ _方法会自动地被调用。
_ _init_ _方法不能有返回值,如果一定需要跳出或返回,也只能返回None。
例如:
class C4:
def __init__(self):
return 'sss'
a = C4();
python会报错:
Traceback (most recent call last):
File "
a = C4();
TypeError: __init__() should return None
_ _init_ _方法的主要目的,是为了在创建对象实例的时候,对对象的属性赋值。这么做可以增加程序的可读性。如果对象内部没有_ _init_ _,那么你调用对象的时候就不能带上任何的参数。
1.1.4.2 实例中的属性
用点(.)来访问实例中的属性。
即使一个对象已经被实例化,仍可以个实例增加任意的属性,并对其赋值。
class C7: pass
z = C7( )
z.x = 23
实例被创建以后,该实例会被自动加上两个属性:
_ _class_ _:实例所属的对象
_ _dict_ _:实例的所有属性(实例自身的属性和其所属对象的属性)
如:
class C6:
def _ _init_ _(self,n):
self.x = n
a = C6(234)
a.y=213213
a.__dict__
#执行结果:{'y': 213213, 'x': 234}
1.1.4.3 工厂函数
遥想一下,设计模式中被用得最多得工厂模式(Factory),它被用于创建对象的实例。在python当中,最直接的用来实现工厂模式的方式,似乎是用_ _init_ _来返回不同的实例,但是,unfortunately,_ _init_ _最多也只能返回None。所以要实现工厂模式,最佳的方式就是专门写一个函数,用来返回不同的实例,这类函数,可以称之为工厂函数(Factory Function)。
如下例,appropriateCase就是一个工厂函数。
class SpecialCase:
def amethod(self): print "special"
class NormalCase:
def amethod(self): print "normal"
def appropriateCase(isnormal=1):
if isnormal: return NormalCase( )
else: return SpecialCase( )
aninstance = appropriateCase(isnormal=0)
aninstance.amethod( )
1.1.5 属性引用
假设x是对象C的实例,当引用x.name的时候,是如何来查找它的值呢?用最简单的话来概括,就是,由小到大由近到远,依次查找name的值。
再说得具体一些,是按下面的方式查找:
l 若x.name是x._ _dict_ _,中的key,那么返回x._ _dict_ _['name'] (查找自身)
l 否则,查找C._ _dict_ _中的key,是则返回C._ _dict_ _['name'] (查找所属对象)
l 否则,查找C的基类,在C._ _bases_ _中继续按上面的两步查找(查找所属对象的基类)
l 否则,抛出异常:AttributeError
1.1.6 方法的绑定与非绑定(Bound and Unbound)
上面讲了属性的引用,方法的绑定与非绑定实际上涉及到的是方法引用的问题。方法实际上使用函数来实现。当方法被引用时,并非直接返回其对应的函数,而是将这个函数,载入到了bound或者unbound方法上。
bound和unbound的区别在于:bound将特定的函数,与特定的实例相关联;而unbound则相反。
若没有同名属性,直接用函数名(函数名后不带括号),可以观察到其绑定状态。
假设有对象C和实例x:
class C:
a = 1
def g(self): print "method g in class C"
x = C()
print x.g
#执行结果:
print C.g
#执行结果:
上面的执行结果表明:x.g被绑定到了C.g()函数上,所以执行x.g()会有结果返回;而C.g没有被绑定,所以执行C.g()没有结果。
1.1.6.1 细说非绑定
若处于非绑定状态,当一个函数被引用的时候,实际返回的是unbound方法,该方法内部载有该函数。Unbound方法有三个只读属性
im_class:被引用函数所在的对象
im_func:被引用的函数
im_self:总是为None
非绑定的方法也可以被调用,需要把im_class对象的实例名做为第一个参数,那么就会返回im_func的函数了。
例如上面的C.g()没有结果,但可以执行C.g(x),x为C的实例,就可以得到C.g()函数的正确执行结果了。
1.1.6.2 细说绑定
当执行x.g()时,返回的是bound 方法。bound方法和unbound方法类似,也有三个只读属性:im_class,im_func,im_self,但不同之处在于:im_self的值为x。
1.1.7 继承与重载
从前面说到的“属性引用”的查找方法,不难看出python继承的实现方式。若x为C的实例,当调用x.g()的时候,python先看x._ _dict_ _中有没有g,没有就查找C._ _dict_ _,若再没有就查找C._ _base_ _有没有g。C._ _base_ _中放置了C的基类,就实现了对象的继承。用这样的机制,也同样实现了重载。
1.1.7.1 超类代理
在子类中,有可能要用到超类的属性,那么就要使用到超类代理机制,实际上是用unbound方式调用超类的函数。例如:
class Base:
def greet(self, name): print "Welcome ", name
class Sub(Base):
def greet(self, name):
print "Well Met and",
Base.greet(self, name)
x = Sub( )
x.greet('Alex')
超类代理常用于_ _init_ _方法中,因为在python中,子类的_ _init_ _方法中并不会自动地调用其超类中的_ _init_ _方法,所以需要利用这种超类代理机制手动调用一下。
From:一号门
Previous:django 模板中无法实现计算,收集的方案。
COMMENTS