美文网首页Python
python参考手册——第三章

python参考手册——第三章

作者: 转身丶即天涯 | 来源:发表于2017-12-13 01:31 被阅读22次

    类型与对象

    要知道,Python中所有数据都是对象来构建的。
    每个对象都有一个身份,一个类型,和一个值。
    例如,a = 42,使用值42创建一个整数对象,对象的身份可以看做指向它在内存中所处位置的指针,而a就是引用这个具体位置的名称。
    在实例被创建后,它的身份和类型就不可改变。
    如果对象的值是可以修改的,则称之为可变对象,反之,称之为不可变对象。
    如果某个对象包含对其他对象的引用,则将其称之为容器或者集合。

    对象的身份和类型

    内置函数id()可以返回一个对象的身份,返回值为整数。这个整数对应该对象在内存中的位置。
    is运算符用于比较两个对象的身份,type()函数返回一个对象的类型。
    if type(b) is list:
    print("b是一个列表")

    还有一个内置函数isinstance(object, type),同样可以检测对象的类型
    if isinstance(b, list):
    print("b是一个列表")
    值得一提的是,isinstance()函数能够实现继承,所以是检查对象类型的首选方式。
    不要经常检查对象的类型,它会检查每一层继承关系,包括这个对象所属类的父类,这会严重影响程序的效率。

    引用计数与垃圾收集

    所有对象都有引用计数,无论是给一个对象分配一个新的名称,还是将其放入一个容器(list,dict,tuple),该对象的引用计数就会增加。
    书上有几个例子比较有特点:


    image.png

    例子中,创建了一个值为37的对象,a引用了这个对象,然后b同样也引用了这个对象,那么这个对象的引用计数就会+1.
    然后把b放到列表中的时候,值为37这个对象的引用计数又+1。
    自始至终只有一个值为37的对象,所有其他操作都是创建了对该对象的新引用。

    当使用del语句或者引用超出作用域的时候,引用计数会减少。


    image.png

    使用sys.getrefcount(对象)函数可以获取对象的当前引用计数。


    image.png
    image.png

    这里发生了意外,在这个只有4行的代码中,3789789789这个对象的引用计数应该为2啊,为什么会是5呢?
    当然不是函数算错了,而是对于不可变的数据(比如数字,字符串),解释器会主动在程序的不同部分共享对象,以便节约内存。原来是其他地方也引用了3789789789,我们仅仅引用了2次,所以引用计数加了2。

    当一个对象的引用计数归零时,垃圾回收机制会回收该对象的内存。但是有些情况,很多已经不再使用的对象可能存在循环依赖关系,然后就导致了内存泄漏。


    image.png

    在执行了前4行代码时,ab的引用计数分别是2,然后又分别执行一次del操作,所以引用计数没有归零,对象也没有被销毁,从而导致了内存泄漏。
    为了解决这个问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环引用,然后删除他们。
    gc模块中的函数可以准确调整和控制该算法的行为(13章会讲解)。

    引用与复制

    a = b
    这句话,a会引用b,也就是对b创建一个新的引用。
    有两种情况,b是可变类型(list,dict,tuple),那么a和b就引用的是同一对象。比如:


    image.png
    image.png

    另外一种情况,b是不可变类型(数字,字符串),这样的赋值其实是创建了一个b的副本。

    深复制与浅复制

    对于像字典,列表这样的可变对象,可以使用两种赋值操作:深复制和浅复制。
    浅复制将创建一个新对象,但它是对原始对象包含的项的引用。
    深复制也是对原始对象的引用,但是深复制是粒子性的,对list中的可变对象也会创建新的引用。
    比如说:
    import copy
    a = [1, 2, 3, [4, 5, 6]]
    b = copy.deepcopy(a)
    c = copy.copy(a)
    b对a进行了深复制,对于a中每一项和每一对象的子对象都进行了拷贝。
    c对a进行了浅复制,b只对a的每一项元素进行了拷贝,就是1,2,3,[4,5,6]。
    那么最大的区别在哪呢?当我们修改a[3]这个嵌套list时,c中会跟着改变,因为c中复制的是对[4,5,6]的引用,而[4,5,6]这是一个可变对象,它的改变不会改变引用。而b中不会有任何改动。

    常见的浅复制有,copy.copy()函数和切片[:]。
    深复制有:copy.deepcopy()

    表示数据的内置类型

    None:
    如果一个函数没有return,那么就返回None。
    None也经常作为可选参数的默认值,以便让调用者检测是否是为参数传递了实际的值。
    None没有任何属性,在布尔值中返回False。

    数字类型:
    所有数字类型都是不可变的。
    如果想精确控制float类型,可以使用numpy扩展库。
    说一下复数,平时用到的地方很少。复数由两个浮点数表示。
    实部和虚部分别用z.real,z.imag表示。还有,用z.conjugate()来计算共轭复数。
    对于浮点数,如果想测试一个浮点数是否是整数,y.is_integar()。

    序列类型

    索引为非负整数的有序对象集合, string, dict, tuple
    string,tuple不可变,dict可变。所有的序列都支持迭代。
    all()函数,检查所有项是否都为True
    any()函数,检查任意项是否为Ture

    关于方法的类型

    有三种常见的方法类型:实例方法,静态方法,类方法。
    实例方法:就是操作特定类的实例方法,实例被作为第一个参数(self)传递给方法。
    类方法:把类本身当做一个对象进行操作。第一个参数把类对象传递给方法
    静态方法:就是打包在类中的函数,它不能使用实例或者类作为第一个参数。

    绑定方法:由实例调用的函数的方法,比如
    f = Foo()
    meth = f.instance_method
    非绑定方法:由类调用的函数的方法,但是调用时候要显示的提供对象,比如
    umeth = Foo.instance_method
    umeth(f, arg)

    定义一个class,当创建一个实例时,会先调用类的init()函数,用来初始化新创建的实例。
    如果一个实例定义了一个特殊方法call(),它就能够模拟函数的行为。
    如果call()是为了某个实例定义的,那么x(args)等同于x.call(args)。

    一个实例的创建过程:
    x = class.new(A, args)
    is isinstance(x, A):
    x.init(args)

    相关文章

      网友评论

        本文标题:python参考手册——第三章

        本文链接:https://www.haomeiwen.com/subject/wvlwixtx.html