[初学python]新类(new-style class)

类(class)也是对象
在python之中,万物皆对象。类也是对象。“类的类”就被称为元类(即类是元类的实例)。正如类的实例的行为取决于类,元类的实例(类)的行为也取决于元类。

new-style classes的由来
new-style classes是python在2.2版本所加入的一个重要特性。所有的内建类型都是new-style classes。引入new-style classes,就是为了逐步弥合python中内建类型和classic classes之间的鸿沟。至此,内建类型终于可以和classic classes一样,在定义类时作为基类被继承。但在完全统一之前,他们之间仍然存在巨大差别。其根本原因是他们各自的元类不同:classic classes的元类是types.ClassType,而new-style classes的元类是type。
换句话说,我们可以做出这样的定义:元类为types.ClassType的类是classic classes;元类为type的类是new-style classes。
另外,根据后面的结论,我们还可以预先给出new-style classes的另一种定义:object和所有直接或间接以它为基类的类是new-style classes。这里的object是一个new-style class的内建类型, 它作为所有new-style classes的
一个公共基类(详见下文)。

new-style classes的创建
python的类对象,直接由class语句生成。
那么,class语句是怎样确定它所要生成的类对象的元类的呢?这个问题的答案,可以从class语句的工作原理中得出:
解释器通过执行class语句,首先获得三方面的信息:类名,类的基类,类的属性和方法。
其中类的基类被放入一个tuple中,类的属性和方法被放在一个dict中。
接着,解释器就可以通过这些信息来确定元类了:
 .如果类属性中有“__metaclass__”,那么它所绑定的对象就是元类
 .否则,如果类定义了基类,那么就使用基类的元类(只要基类中有new-style classes,那么元类就为type )
 .否则,如果类所在的模块中有"__metaclass__"这样一个名字,它所绑定的对象就是元类
 .否则,类的元类就被默认为types.ClassType
确定了元类,解释器就把类名、类的基类(tuple)、类的方法和属性(dict)作为初始化参数传给元类,以生成该元类的一个实例(即是类)。
最后,解释器将类名与生成的类对象绑定在一起。
所有其它的内建类型(即非object),就是通过继承一个公共的new-style基类object,而成为new-style classes的。
由上面的叙述,我们可以得出创建一个new-style class的方法,共三种:
1.继承一个内建类型
>>> class MyDict(dict):
 pass
>>> type(MyDict)
<type 'type'>
2.设定类的__metaclass__属性
>>> class MyNewStyle1:
 __metaclass__ = type
>>> type(MyNewStyle)
<type 'type'>
3.设定一个模块级的__metaclass__
>>> __metaclass__ = type
>>> class MyNewStyle2:
 pass
>>> type(MyNewStyle2)
<type 'type'>
还有一点值得注意,不管用以上哪种方式创建的new-style classes都最终会直接或间接地以object做为基类(这里就体现了元类对类的控制)。
也就是说,即使如方法2,3那样没有直接写出继承语法,object也会成为基类:
>>> MyNewStyle1.__bases__
(<type 'object'>,)
>>> MyNewStyle2.__bases__
(<type 'object'>,)
当然,如果上面提出的创建new-style classes的条件都不满足,就会创建一个原来的classic class
>>> del __metaclass__ #消除前面引入的模块级的影响
>>> class MyClassic:
 pass
>>> type(MyClassic)
<type 'classobj'>
>>> MyClassic.__bases__
()

new-style classes的使用
new-style classes提供了一些新的特殊方法,让我们可以更好地控制类的行为。
下面,我们讲几个常用的:
1.__new__:
  __init__用来控制类的实例的初始化,而__new__控制初始化之前的行为---也就是实例的生成。
  因此__new__的第一个参数是类(cls),而不是self。__new__的任务是返回一个所传入参数类(cls)的实例。
  但,真正能够生成一个new-style class的实例的,只有object.__new__。因此我们定义的__new__,在需
  要生成实例时,就应该调用它。
  利用__new__的这个特性,我们可以很容易实现Singleton Pattern,让我们的类只会产生一个唯一的对象
>>> class Singleton(object):
 __instance = None
 def __new__(cls, *args, **kwd):
  if Singleton.__instance is None:
   Singleton.__instance = object.__new__(cls, *args, **kwd)
  return Singleton.__instance
>>> class MyClass(Singleton):
 pass

>>> a = MyClass()
>>> b = MyClass()
>>> a is b
True
>>> a is b is Singleton._Singleton__instance
True 
  通过继承,就可以使我自定义的类的行为符合Singleton Pattern的要求
 
2.__getattribute__:
  __getattribute__比__getattr__对使用“.语法”(如obj.a)进行对象的属性和方法的读取操作提供了更强大的控制。
  任何obj.a,都会被直接转化为obj.__getattribute__(a),包括__dict__。因此,在__getattribute__的实现,我们
  不能直接用使用__dict__(那会造成循环调用),而应该的把真正的读取操作交给object.__getattribute__。
>>> class MyClass(object):
 def __getattribute__(self, name):
  print "accessing", name
  return object.__getattribute__(self, name)

 
>>> a = MyClass()
>>> a.__dict__
accessing __dict__
{}

3.__get__,__set__,__delete__:
  重载这三个方法,可以实现对类属性的读写控制
class RevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """

    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val

    def __set__(self, obj, val):
        print 'Updating' , self.name
        self.val = val

>>> class MyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5

>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5
  可以用内建函数property,简化上述步骤
>>> class MyClass(object):
 def __init__(self):
  self.__x = 1
 def getx(self):
  print "Retrieving x"
  return self.__x
 def setx(self, value):
  print "Updating x"
  self.__x = value
 def delx(self):
  print "Deleting x"
  del self.__x
 x = property(getx, setx, delx, "I'm the x property")
 
>>> m = MyClass()
>>> m.x
Retrieving x
1
>>> m.x = 2
Updating x
>>> del m.x
Deleting x


关于新类的更多特性和使用方法,请参考
Unifying types and classes in Python 2.2
How-To Guide for Descriptors

后记
我也是一名python初学者,把自已学习中的困惑与收获记录下来,与大家交流,希望能得到大家的意见。


 

 

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭