python2.2 New Style class

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值