zoukankan      html  css  js  c++  java
  • Python的基础--对象 转

     

     
    对象(Objects)是python中数据的抽象,python中所有的数据均可以用对象或者是对象之间的关系来表示。每个对象均有标识符(identity)、类型(type)、值(value)
    1. 标识符。对象一旦创建,那么它的标识符就不会改变,可以把标识符看作对象在内存中的地址。is 操作可以用来比较两个对象的标识符,函数id()用来返回对象标识符(python中返回对象在内存中的地址)。
    2. 类型。对象的类型也是不可变的,对象的类型决定了该对象支持的操作,另外也决定了该对象可能的值。type()函数返回一个对象的类型。
    3. 值。一些对象的值可以改变,我们叫它可变对象,字典和列表均属于可变对象;值不可改变的对象我们叫它不可变对象,数字、字符串、元组均属于不可变对象。

    在python中,不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传地址。

    可变对象与不可变对象

    python在heap中分配的对象分成两类:可变对象和不可变对象。所谓可变对象指的是,对象的内容可变,而不可变对象是指对象的内容不可变。

    不可变(immutable)对象:int、字符串(string)、float、数值型(number)、元组(tuple)。

    可变(mutable)对象:字典型(dictionary)、列表型(list)。

    一、不可变对象

    由于Python中的变量存放的是对象引用,所以对于不可变对象而言,尽管对象本身不可变,但变量的对象引用是可变的。

    i = 73  
    i += 2 
    


    (点小图查看大图)
    点击图片看大图

    从上面图示得知,对象73和对象75并没有变化,变化的只是创建了一个新的对象,改变了变量的对象的引用。看看下面的代码,更能体现这一点:

    #因为258是int对象,是不可变对象的。所以下面3个id的值都是一样的,最后一句的结果也是为True  
    #有点奇怪的是为什么在IDLE,和在脚本执行的结果不太一样。所以下面的代码请在脚本中执行。  
      
    print(id(258))  
    a = 258  
    print(id(a))  
    b = 258  
    print(id(b))  
    print(a is b) 
    

    总结一下,不可变对象的优缺点。

    优点是:这样可以减少重复的值对内存空间的占用。

    缺点是:我要修改这个变量绑定的值,如果内存中没用存在该值的内存块,那么必须重新开辟一块内存,把新地址与变量名绑定。而不是修改变量原来指向的内存块的值,这回给执行效率带来一定的降低。

    二、可变对象

    其对象的内容是可以变化的。当对象的内容发生变化时,变量的对象引用是不会变化的。如下面的例子:

    m=[5,9]  
    m+=[6] 
    


    (点小图查看大图)
    点击图片看大图

    函数参数


     

    Python函数参数对于可变对象,函数内对参数的改变会影响到原始对象对于不可变对象,函数内对参数的改变不会影响到原始参数。原因在于:

    1、可变对象,参数改变的是可变对象,其内容可以被修改。

    2、不可变对象,改变的是函数内变量的指向对象。

    有关可变对象和不可变对象的介绍,简单的介绍到这里。


    对象的回收机制

    python不像C那样需要显式地回收对象占用的空间,python内核中有垃圾回收机制,当一个对象不可达时,就会交由垃圾回收机制处理。

    一些对象引用了一些外部资源,例如打开的文件或者窗口。通常我们认为当这些对象被垃圾回收机制回收时,它占用的外部资源即被释放。但是,垃圾回收机制并不一定会回收这些对象,因此这些对象提供了显式的方法(通常是_close()_)用来释放外部资源。程序中最好使用显式的方法来释放外部资源,一般可以使用 _try...finally_方便地释放。


    对象的类型

    对象的类型几乎影响了该对象的所有功能,在某种程度上,对象的标识符也受其类型的影响。

    >>> sum = 15
    >>> sum_add = 12 + 3
    >>> sum is sum_add
    True
    >>> sum = 15000000
    >>> sum_add = 10000000 + 5000000
    >>> sum is sum_add
    False
    >>> sum_add == sum
    True
    

    对于不可变对象(这里是int),当我们需要一个新的对象(sum_add = 12 + 3)时,python可能会返回已经存在的某个类型和值都一致的对象(sum)的引用。当然,这里只是可能会返回已经存在的对象,要看python的具体实现。同样是创建新的对象sum_add = 10000000 + 5000000,python并没有把值和类型都一样的sum返回给sum_add。

    >>> value = []
    >>> value_1 = []
    >>> value is value_1
    False
    >>> value == value
    True
    >>> value = value_1 = []
    >>> value is value_1
    True
    

    对于可变对象,当我们需要新的对象时,python一定会为我们新建一个。注意,这里value = value_1 = []将会创建一个空的列表对象,然后同时返回给value和value_1。


    扑朔迷离的不可变对象

    首先看下面的代码:看我怎么改变不可变对象的值

    >>> mutability = [1, 2, 3, 4]
    >>> immutability = (0, mutability, 5)
    >>> immutability
    (0, [1, 2, 3, 4], 5)
    
    #查看可变对象与不可变对象的标识符(内存地址)
    >>> id(mutability)
    28356200
    >>> id(immutability)
    28048640
    
    #对可变对象与不可变对象都做一些改变
    >>> mutability[2] = "see here!"
    >>> immutability
    (0, [1, 2, 'see here!', 4], 5)
    
    #查看改变后的可变对象与不可变对象的标识符(内存地址)
    >>> id(mutability)
    28356200
    >>> id(immutability)
    28048640
    

    这里元组immutability中一个元素为可变对象列表mutability,当我们改变mutability的值时,号称不可变对象的元组的值似乎发生了变化。这又是为什么呢?

    回答这个问题前,先总结下上面这段代码发生了什么:不可变对象A包含了一个对可变对象B的引用,可变对象B的值发生改变时,不可变对象A的值似乎会发生改变。

    那么,为什么我们仍认为A是不可变对象呢?因为A仍然包含对象B,而B的标识符并没有发生变化,也就是说A的所有元素的标识符并没有发生变化。

    看内存的布局是怎样的:


    (点小图查看大图)
    点击图片看大图

    可以看出a是一个list列表类型的对象,它是一个可变对象,所以修改它的值不会创建新的对象。但是b是一个不可变对象,只要修改它的值,就会重新创建对象,注意,这里并没有修改它的值,因为它的所有元素的标识符并没有变化。

    再看下面的例子:

    >>> a = 2
    >>> b = (1, a, 3)
    >>> b
    (1, 2, 3)
    >>> id(a)
    23112444
    >>> id(b)
    27983104
    
    >>> a += 10
    >>> b
    (1, 2, 3)
    >>> id(b[1])
    23112444
    >>> id(b)
    27983104
    
    >>> id(a)
    23112324
    

    在执行a+=10之前的内存布局为:


    (点小图查看大图)
    点击图片看大图

    执行完a+=10的指令之后的内存布局为:


    (点小图查看大图)
    点击图片看大图

    这里仅仅是原来的int(2)这个对象的引用数目减1.

    在来一个狠的,在上面代码的基础上再来一个:

    >>> b
    (1, 2, 3)
    
    >>> b[1] = 13
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    
    >>> b[1]
    2
    

    通过元组类型的对象b对其中的一个int类型对象赋值,这样的话,因为int是不可变对象,所以会重新申请新的内存,那么同样会引起元组b中的xxx引用变量的改变,那么这样就真正的改变了元组b对象的某个元素的标识符。导致真正的改变了不可变元组对象b,这时就会报错。


    修改上面的一些bug:

    1. 首先id(a)函数是查看引用变量a所指向的对象的标识符,也就是内存地址。并不是引用变量a的内存地址。

    2. 不管是元组还是列表,它们的元素存储的都是某个真实对象的引用,并不存储这个对象,因为python中一切皆对象。

    先看一个内存结构,然后在看代码:


    (点小图查看大图)
    点击图片看大图

    >>> a = (1,2,3)
    >>> b = [1,2,3]
    >>> id(a)
    19840616
    >>> id(b)
    28577384
    >>> id(a[0])
    20032264
    >>> id(b[0])
    20032264
    
  • 相关阅读:
    poj 3666 Making the Grade
    poj 3186 Treats for the Cows (区间dp)
    hdu 1074 Doing Homework(状压)
    CodeForces 489C Given Length and Sum of Digits...
    CodeForces 163A Substring and Subsequence
    CodeForces 366C Dima and Salad
    CodeForces 180C Letter
    CodeForces
    hdu 2859 Phalanx
    socket接收大数据流
  • 原文地址:https://www.cnblogs.com/zengkefu/p/5506727.html
Copyright © 2011-2022 走看看