new-style class是指从type元类或者type元类的子类创建的类对象,而传统的类(classic class)则相反
1.builtin-type现在是类了
和老版本的python不一样,int,long,float,complex,list,tuple,dict等builtin-type现在都是类了,而且他们的父类都是
object,并根据type类产生(元类是type)
这些类的属性是不可变化的,他们的__dict__属性是只读的,并且只有New Style自定义类支持赋值操作,但是这些自定义类的
__dict__属性依然是不变的,举例如下:
list.append = list.append
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: can't set attributes of built-in/extension type 'list'
list.answer = 42
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: can't set attributes of built-in/extension type 'list'
list.__dict__['answer'] = 42
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object does not support item assignment
class L(list):
pass
L.append = list.append #ok
L.__dict__['append'] = list.append
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: object does not support item assignment
2.可以子类化builtin-type
在python老版本中当你需要继承比如dict等内置类型的时候,需要使用如下代码:
import UserDict
class defaultdict(UserDict.UserDict):
pass
现在你可以直接从内置类型dict继承了,这是因为现在的dict已经是类了
class defaultdict(dict):
def __init__(self, default=None):
dict.__init__(self)
self.default = default
def __getitem__(self, key):
#key in dict 在python2.2中被引入
if key in self:
return dict.__getitem__(self,key)
else:
return self.default;
3.使用__slots__属性限制实例属性
class Person:
def __init__(self,name):
self.name = name
person = Person("foo")
person.age = 25 #可以为实例person赋其他属性,但有些情况下这不是你想要的
上面的这种情况可以使用__slots__属性完成
class Person(object): #需要要继承object
__slots__=['name']
def __init__(self,name):
self.name = name
person = Person("foo")
person.age = 25
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
AttributeError: 'Person' object has no attribute 'age'
使用__slots__的一些注意事项:
不能使用Person.__slots__=...定义__slots__
__slots__目前可以从父类继承下来(python2.4.1)
拥有__slots__定义的类实例没有__dict__属性,但是类的子类却有,除非子类也定义了__slots__
用__slots__=[]使类实例没有实例变量
4.__getattr__和__getattribute__钩子方法
__getstr__方法在实例没有查找到属性的时候调用,而__getattribute__方法在实例查找任何属性的时候调用
特别要注意的是__getattribute__方法容易引起无限循环,甚至使用__dict__的时候,也会调用__getattribute__方法:
class A(object):
def __getattribute__(self,key):
print "accessing %r.%s" %(self,key)
#return self.__dict__[key] 无限循环
return object.__getattribute__(self, key)
5.静态方法(staticmethod)
在以前的python版本中想要实现静态方法的功能需要一些技巧,如下:
import os
class Command:
class _StaticCommand:
def __call__(self,command):
os.system(command)
#run实际上是一个内部class对象的实例,该实例是可调用的,这种方法的关键在于定义一个可调用的类字段
run = _StaticCommand()
if __name__ == '__main__':
Command.run("dir /w") #windows
Command.run("ls -l") #unix linux
python2.2中提供了staticmethod类,可以方便的实现静态方法
import os
#不一定要继承object也可以使用staticmethod,但作为python趋势,在以后的类设计中尽量使用object
class Command(object):
def run(command):
os.system(command)
#使用staticmethod将一个function包装
run = staticmethod(run)
6.类方法(classmethod)
类方法将第一个参数认为是类对象,这在Java/C++中没有等价物
class A:
#注意:使用cls代替self,因为第一个参数不是实例而是类对象
def foo(cls,x):
print "classmethod" ,cls,x
foo = classmethod(foo)
A.foo(1),A().foo(1)
7. 读取/设置方法(java的getter/setter)
面向对象的封装通常要求使用读取/设置方法访问对象数据代替直接的属性访问,这样做有什么好处呢?通过使用方法获取对象属性
可以使有机会做一些额外的操作,比如返回缓存过的对象属性等,从而提高程序效率 或者 一些额外的数据检查(比较一下person.age = -1
和person.setAge(-1),很容易的可以给setAge方法加上逻辑判断)
#必须继承object
class Person(object):
def __init__(self):
self.__age = 0;
def getAge(self):
return self.__age
def setAge(self,age):
if 0 < age <150 :
self.__age = age
else:
raise ValueError,"invalid age"
#property(fget=None, fset=None, fdel=None, doc=None)
age = property(getAge,setAge)
person = Person()
person.age = 26 #调用了person.setAge
print person.age #调用了person.getAge
8.方法查找顺序(Method Resolution Order,mro)
当多重继承出现后,就出现了一个方法查找顺序的问题
class A:
^ ^ def save(self): ...
/ /
/ /
/ /
/ /
class B class C:
^ ^ def save(self): ...
/ /
/ /
/ /
/ /
class D
在classic class中,查找顺序是深度优先的,所以上图的查找顺序是D,B,A,C;假设我们调用D类实例的save方法,
很显然这种查找顺序是有问题的,因为他会忽略了C的save方法(找到A的save方法后终止)
而在new style class中,查找顺序是D,B,C,A,可以使用D.__mro__看查找顺序
9.合作方法和super
现看一个例子:
class A:
def m(self):print "save A's data"
class B(A):
def m(self):A.m(self);print "save B's data"
上面的例子中,B的m方法重载了A的m方法,这在面向对象的设计中是常有的,
在B的m实现中,我们显式的调用了A的m方法以便A保存自己的状态
当在多重继承的情况下这种机制会出现问题,比如
class A(object):
def m(self):print "save A's data"
class B(A):
def m(self):A.m(self);print "save B's data"
class C(A):
def m(self):A.m(self);print "save C's data";
class D(B,C):
def m(self):B.m(self);C.m(self);print "save D's data"
当使用D().m()调用的时候,很显然A的m方法会被调用两次!
可能有些人会想,可以把A,B,C的print语句抽取出方法,假设叫_m,在D的m中调用B._m(self),C._m(self),A.m(self)..
但是这样的结构使子类必须很清楚的知道父类如何干一件事情,其耦合度是很大的,一旦父类改变自己的方法实现,很有可能破坏
原本可以工作的D
针对这个问题,python使用super call解决
class A(object):
def m(self):print "save A's data"
class B(A):
def m(self):print "save B's data";super(B,self).m()
class C(A):
def m(self):print "save C's data";super(C,self).m()
class D(B,C):
def m(self):print "save D's data";super(D,self).m()
此时使用D().m()调用后发现打印:
save D's data
save B's data
save C's data
save A's data
分析:super是通过MRO顺序调用的,D.__mro__=(D,B,C,A,object)
首先,调用D的m方法:打印save D's data,然后super(D,self).m()找到了B的m方法,打印save B's data,
在B的super(B,self).m()中,由于传递的self是D的实例,所以还是根据D的同一个mro顺序查找,此时找到C,同理打印
save C's data,C的super(C,self).m()传递的self依然是D的实例,此时找到A,调用A的m方法后终止
10.元类(Metaclass)
元类是产生类的类,元类产生类对象,类对象产生实例对象
当执行一个class定义语句的时候,python解释器首先寻找相关的元类M,然后调用M(name,base,dict)来产生一个新的类
对象,比如你可以使用如下语句产生一个新的类对象
#type是所有元类的祖先
Foo = type("Foo",(object,),{"getName":lambda self:"foo-name"})
使用Foo().getName()调用方法
这里M(name,base,dict)三个参数的意义是:
name:类名
base:产生的类对象的超类,空元祖表示没有超类
dict:类对象的类属性和方法
那么python解释器如何寻找M元类的呢?
a.通过dict[__metaclass__]
b.如果定义了超类,使用超类的元类
c.使用全局变量__metaclass__
d.使用classic classes的元类(types模块的ClassType),此时产生一个classic classes
另外需要注意的是:当定义的类有多个父类时,假如这些父类是new style classes和classic classes的混合物,那么python
会使用第一个new style classes的元类
元类编程的例子:用于自动给类加上get/set方法
class autoprop(type):
def __init__(cls,name,base,dict):
super(autoprop,cls).__init__(name,base,dict)
props = [] #保存哪些属性需要使用get/set
for name in dict:
if name.startswith("_get_") or name.startswith("_set_"):
#比如类中定义了一个方法叫做_get_name,那么把name保存起来,表示对name使用get/set
props.append(name[5:])
for name in props:
fget = getattr(cls,"_get_%s" %name,None) #取到类定义中的_get_xxx方法对象
fset = getattr(cls,"_set_%s" %name,None) #取到类定义中的_set_xxx方法对象
setattr(cls,name,property(fget,fset)) #给name设置get/set
class InvertedX:
__metaclass__ = autoprop #使用元类
def _get_x(self):
return -self.__x
def _set_x(self,x):
self.__x = -x
a = InvertedX()
a.x = 12
print a.x #12
print a._InvertedX__x #-12