编写高质量代码读书笔记-内部机制

原创
2016/10/12 14:41
阅读数 53

1、理解built-in objects

    python中一切皆对象,自python2.2以后为了弥补内建类型和古典类型之间的鸿沟引入了新式类。在新式类中objec是所有内建类型的基类,用户定义的类可以继承自object也可以继承自内建类型

    

 

2、__init__()不是构造方法:

    __init__()方法并不是真正的初始化方法,其做的工作是在类创建了实例以后进行变量的初始化。__new__()方法才是真正的实例化方法。这两个方法都是object类中的默认方法,继承自object的新式类,如果不覆盖这俩方法,会默认调用objcet中的这俩方法,这俩方法的定义如下:

    object.__new__(cls, [,args...])

    object.__init__(self, [,args...])

    这俩方法之间有如下不同点:

    1、__new__是类方法,而__init__是实例方法

    2、__new__()方法以一般需要返回类对象,当返回类的对象的时候会调用__init__()进行初始化,如果没有返回对象,则__init__()方法不会调用

    3、当需要控制实例创建的时候可以使用__new__()方法,而控制实例初始化的时候使用__init__()

    4、一般情况下不需要覆盖__new__()方法,当子类继承自不可变类型的时候,往往需要覆盖

    5、当同时需要覆盖__new__() __init__()方法的时候两个方法的参数必须爆出一致,否则会抛出异常

 

    什么情况下需要覆盖__new__():

    1、当类继承自不可变类型,切默认__new__()不能满足需求的时候

    2、用工厂模式或者单例模式进行元类编程的时候   

    作为用来初始化的__init__()方法,再多继承的情况下,子类__init__()方法如果不显示的调用父类__init__(),则父类的__init__()不会被调用

知识点:

继承自object的新式类才有__new__

__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行

1

2

3

4

5

6

7

8

9

10

11

12

class A(object):

    pass

 

class B(A):

    def __init__(self):

        print "init"

    def __new__(cls,*args, **kwargs):

        print "new %s"%cls

        return object.__new__(A, *args, **kwargs)

 

b=B()

print type(b)

 输出:

new <class '__main__.B'>
<class '__main__.A'>

 

 

 

3、理解名字查找机制

    python的变量其实都是名字,所有这些名称都会存在一个表里(命名空间)

    可以通过locals()函数来查看当前的环境变量,可以使用globals()来查询全局变量

    python中所有的变量名都是在赋值的时候生成的,而在任何变量名的创建爱你、查找或者改变都会在命名空间中机型。

    python在2.2以后作用于分为四种:

    1)、局部作用域:一般来说函数每次创建的时候都会创建一个信息的本地作用域,拥有新的命名空间。在改函数内部的变量,如果没有申明gloable,这都为局部变量,即仅在该函数中可见

    2)、全局作用域:定义在python模块中的变量名拥有去哪聚作用域(仅限于单个文件)

    3)、嵌套作用域:一般在多重函数嵌套的情况下才会考虑到。注意gloable申明,在嵌套作用域下没用

    4)、内置作用域:他是通过__builtin__的模块来是实现的

 

    在查找的时候按照本层-》局部作用域-》嵌套作用与-》全局作用域-》内置作用域一次查找即:

 

 

3、多继承的方法寻找机制:

    在python中多继承的方法寻找方式古典类和新式类不同:

    古典类采用从左到右深度优先

    新式类采用的是c3 mro算法,可以通过新式类的__mro__属性来查看查看其搜索顺序

 

4、理解描述符机制:

    除了不同的局部变量、全部变量中查找名字,还有一个相似的场景不可不察,那就是查找对象的属性。在python中一切皆对象,类是对象,类的实例也是对象。

    每一个对象(类实例等都有)都有一个__dict__属性,其中包含的是他的所有的属性。

    我们可以动态的给实例属性和类属性,但是对于内置类型,python不允许对其属性进行动态的更改。

    ??????????????????????????????????????????????????????????????????????/

    待后续

 

5、__getattr__ __getattribute__区别:

    

    这两个方法均可用做实例属性的获取和拦截(仅对实例对象,非类对象)。

    __getattr__适用于未定义的属性即该属性在实例中以及对应的类的基类及祖先类中都不存在,而__getattribute__对于所有属性的访问都会被调用

 

    当访问一个不存在的实例属性的时候就会抛出AttributeError异常,这个异常通常是由内部方法__getattribute__抛出的,因为__getatttribute__是被无条件调用的,只要涉及到实例属性的访问就会调用到它,要么返回实际值,要么抛出异常。

    什么时候会调用__getattr__方法?

    属性不在实例的__dict__中;属性不在其基类以及祖先类的__dict__中;触发AttributeError异常(不仅仅是__getattribute__抛出的,在property中定义的get()方法也会抛出该异常)

    当两个方法同时被定义的时候,要么在__getattribute__中显式调用,要么__getattribute__抛出AttributeError异常,否则__getattr__永远不会被调用。

    __getattribute__和__getattr__都是Object类中默认的方法,当药覆盖这俩方法的时候要注意以下几点:

    1)避免无穷递归

    2)访问未定义的属性:如果访问不存在的属性,调用到__getattr__,如果不在__getattr__中抛出异常或者返回一个值,__getattr__会自动放回None

 

    另外还有两点要注意:

    1)、覆盖了__getattribute__方法以后,任何属性的访问都会调用用户定义的__getattribute__方法,性能上会有损耗,比默认方法慢

    2)、覆盖__getattr__方法如果能动态的处理事先未定义的属性,可以更好的实现数据的隐藏。因为通常dir()只会显示正常的属性和方法,因此不会将该属性作为可用属性

 

    我们知道当property也能控制属性的访问,如果一个类中一个实例属性定义了property, 同时这个类又定义了__getattribute__ __getattr__,这时对实例属性进行访问,访问顺序是什么那?

    最先访问的是__getattribute__(),由于该属相是一个property对象,并不存在于__dict__中,因此此时并不会返回,此时会搜索到property中定义的get()方法。最后会根据情况访问__getattr。这几个方法的优先级是__getattributer__最高,property其次,__getattr__最后。

 

 

 

6、使用更为安全的property

    property是用来实现属性可管理的built-in数据类型,其实质是一种特殊的数据描述符(数据描述符:如果一个对象同时定义了__get__()、__set__()方法,则称为数据描述符,如果仅定义了__get__()方法,则被称为非数据描述符)。它和普通描述符的区别在于:普通描述符提供的是一种较为低级的控制属性访问的机制,而property是它的高级应用,它以标注库的形式提供描述符的还是实现。其签名形式为:

    property(fget=None, fset=None, fdel=None, doc=None)->property attribute

 

    property常见的使用形式有以下两种:

    1、

    class Some_Class(object):

        def __init__(self):

            self._somevalue = 0

        def get_value(self):

            print 'calling get method to return value'

            return self._somevalue

        def set_value(self, value):

            print 'calling set method to reutn value'

            self._somevalue = value

        def del_attr(self):

            print 'calling delete method to delete value'

            del self._somevaule

        x = property(get_value, set_value, del_attr, "I am the 'x' property.")S

    

    2、

    class Some_Clasee(object):

        _x = None

        def __init__(self):

            self._x = NOne

        @property

        def x(self):  

            print "calling get method to return value" 

            return self._x

        @x.setter   对应a.attr = xxxx

        def x(self, value):

            print "calling set method to set value"

            self._x = value

        @x.deleter      对应del

        def x(self):

            print "calling delete method to deletevvalue"

            del self._x

 

    property优势:

    1)、代码更简洁,可读性更强

    2)、更好的管理访问属性。property能将对属性的访问直接转化为对对应的get set等祥相关函数的调用

    3)、代码可维护性好

    4)、控制访问权限,提高数据安全

    值得注意的是,property并不能达到属性只读的效果。

 

    __new__??????????????????????后续搞清楚

 

    property的本质并不是函数而是一个类,可以被继承,因此用户可以根据自己的需要定义property       

 

7、掌握metaclass

    

    metaclass即元类:

    1)、元类是关于类的类,是类的模板

    2)、元类是用来控制入户创建类的,正如类似创建对象的模板一样

    3)、元类的实例为类,正如类的实例为对象

 

    python中一切皆对象,类也是对象,可以再运行的时候动态的创建。当使用关键词class时,python解释器在执行的核实后就会创建一个对象

 

    class test(object):

        pass

    type(test)        <type 'type'>

    tyep(test())      <class '__main__.test' >

 

    既然类是对象,那一定有东西可以控制部他的生成,使用type我们发现,他的类型是'type'。我们知道type还可以这样使用:

    type(类名, 父类的元组(针对继承情况,可以为空), 包含属性的字典(名称和值))如:

    A = type('A', (object,), {'value': 2})

 

    上例中type通过接受类的描述作参数返回一个对象,这个对象可以被继承,属性能被访问,它实际是一个类,其创建又type控制,由typy创建的对象的__classe__属性为type。type实际上是一个内建元类,用来直接指导类的生成。当然除了使用内建元类进行生成,用户也可以通过继承type来自定义元类。

 

    在创建类时,我们可以使用 元类(name, bases, dict)来创建,也可以通过指定类的内置属性__metaclass__来指定创建其的元类。

    当类的__metaclass__设置了以后,所有继承自该类的子类都要使用说设置的元类来指导类的生成。

    新式类中,当一个类未设置__metaclass__属性的时候,它将使用默认的type元类来生成。而该属性被设置时查找规则如下:

    1)、如果存在dict['__metaclasss__'],则使用对应的值来构建类;否则使用其父类dict['__metaclasss__']中指定的元类来构建,当父类中也不存在metaclass情况下使用默认的元类type。

    2)、对于古典类,条件一不满足的情况下,如果存在全局变量__metaclass__,则使用该变量对应的元类来构建类,否则和使用types.ClassType来进行创建

 

    需要注意的是,元类中所定义的方法为其创建类的类方法,并不属于该类的对象

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部