对象引用、变量

1.变量不是盒子,在python中变量不过是一种标注,类似于Java中的引用类型的变量。

a=[1,2,3]
b=a
b.append(4)
print(a)
print(b)
# [1, 2, 3, 4]
# [1, 2, 3, 4]

如上所示,可以清晰的看出,变量是一种标识,a b 指向同一块区域,所以修改b ,a也会随着改变。

每个变量都有标识、类型和值,对象一旦创建,它的标识一定不会改变,可以把标识理解为对象在内存中的地址。

is比较两个对象的标识;

id()返回对象标识在内存中的地址。

因此,在理解赋值语句时,要先看右边,对象在右边创建和获取,之后左边的变量才会绑定到对象上。

2==和is的区别

  • ==:比较两个对象的值(对象中保存的数据)
  • is:比较对象的标识。

charles={‘name‘: ‘Charles L. Dodgson‘, ‘born‘: 1832}
lews=charles    #变量赋值,其实相当于变量标识是相同,因为id返回值相同
print(lews is charles)  #True
print(id(lews),id(charles))  #2695836549352 2695836549352

lews[‘balance‘] = 950  #添加一个键值对,之后lews和alex的键和值完全相同
alex = {‘name‘: ‘Charles L. Dodgson‘, ‘born‘: 1832, ‘balance‘: 950} 

print(alex==charles)# True   比较两个对象,结果相等,这是因为 dict 类的 __eq__ 方法就是这 样实现的。
print(alex is charles) #False  这两个对象不相同,即为不同的标识
print(id(alex),id(charles))  #2221654098232 2221654098152

3.元组的相对不可变性:简单来讲,元组是不可变的,但在元组中存储了可变的对象时,元组相对可变。

  • 容器序列:存放的是他们所包含的任意类型的对象引用,而
  • 扁平序列:存放的是值而不是引用,换句话说扁平序列其实是一段连续的内存空间,但其中只能存放字符、字节和数值类型
  • 可变序列:list、bytearray、array.array、collections.deque和memoryview.
  • 不可变序列:tuple、str和bytes。

t1 = (1, 2, [30, 40]) #元组中存放相对可变序列
t1[-1].append(50) #元组相对可变
print(t1)  #(1, 2, [30, 40, 50])

4浅度复制和深度复制

4.1浅度复制:对于复制列表来说可以使用l2=list(l1)(将l1复制一份给l2)或可以通过l2=l1[:],来实现,但这种复制方式为浅复制,即只复制了容器的最外层,副本中保存的是原容器中元素的引用。

l1 = [3, [66, 55, 44], (7, 8, 9)]
l2=list(l1)    #浅拷贝 ,注意这种方式与l2=l1是不同的,
l1.append(100)  #给l1添加一个值,l2不会受到影响
print(l1)       #[3, [66, 55, 44], (7, 8, 9), 100]
print(l2)       #[3, [66, 55, 44], (7, 8, 9)]

l1[1].remove(55) #移除l1中的55,l2会受到影响,因为l1和l2指向同一个引用
print("l1",l1)    #l1 [3, [66, 44], (7, 8, 9)]
print("l2",l2)    #l2 [3, [66, 44], (7, 8, 9)]
l2[1]+=[33,22]
l2[2]+=(10,11)
print(l1)         #[3, [66, 44, 33, 22], (7, 8, 9), 100]
print(l2)         #[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
#对元组来说,+= 运算符创建一个新元组,然后重新绑定给变量 l1[2]。
# 这等同于 l1[2] = l1[2] + (10, 11)。现在,l1 和 l2 中最 后位置上的元组不是同一个对象。

如上图所示:l1经过浅度复制后为l2,l1[1]和l2[1]指向同一块区域,同样l1[2]和l2[2]指向同一块区域即(tuple)。

上述程序结束后各个引用的指向(流畅的Python)

4.2深度复制:副本不共享引用,各自独立

import copy

class Bus:
    def __init__(self,passengers=None):
        if passengers is None:
            self.passengers=[]
        else:
            self.passengers=list(passengers)

    def pick(self,name):
        self.passengers.append(name)

    def drop(self,name):
        self.passengers.remove(name)

bus1 = Bus([‘Alice‘, ‘Bill‘, ‘Claire‘, ‘David‘])
bus2=copy.copy(bus1)  #浅度复制
bus3=copy.deepcopy(bus1)  #深度复制

bus1.drop(‘Bill‘)       #bus1中移除Bill后,bus2中相应也移除了,bus3中还在
print(bus1.passengers)  #[‘Alice‘, ‘Claire‘, ‘David‘]
print(bus2.passengers)  #[‘Alice‘, ‘Claire‘, ‘David‘]
# #浅拷贝,bus2和bus1指向相同的列表对象
print(bus3.passengers)  #[‘Alice‘, ‘Bill‘, ‘Claire‘, ‘David‘]

5.函数的参数

python唯一支持参数传递模式是共享传参(call by sharing)。共享传参指函数的各个形式参数获得实名参数中的副本,也就是说,函数中内部是形参是实参的别名(可以看成上面提到标记),简单说,就是传入函数的形参改变可能会影响外部实参,这可能是我们不希望看到的。

def f(a,b):
    b.append(5)
    a+=b
    return a
a=[1,2]
b=[3,4]
f(a,b)
print(a)  #[1, 2, 3, 4, 5]
print(b)  #[3, 4, 5]
#a b改变了

5.1参数默认值的问题,

在python中可选参数可以有默认值,但要避免使用可变的对象作为参数的默认值。

import copy

class Bus:
    def __init__(self,passengers=[]):   #使用可变列表作为参数默认值
        self.passengers=passengers       #把self.passengers当做passengers的别名,当 passengers没有参数时又是默认列表别名

    #当在self.passengers上调用.append或.remove方法时,修改的其实时默认列表,是函数对象的一个属性。
    def pick(self,name):
        self.passengers.append(name)

    def drop(self,name):
        self.passengers.remove(name)

bus1=Bus([‘Alice‘, ‘Bill‘])
bus1.pick(‘Chariles‘)
print(bus1.passengers)  #[‘Alice‘, ‘Bill‘, ‘Chariles‘]

bus2=Bus()
bus2.pick(‘Helen‘)
print(bus2.passengers)  #[‘Helen‘]

bus3=Bus()
print(bus3.passengers)
#[‘Helen‘],这就是使用可变的列表作为默认参数的弊端,bus3中初始化没有传入任何值,却受到上一个实例化参数的影响
bus3.pick(‘Hel‘)
#这时bus2和bus3还会互相影响,bus1却正常,不会受影响
print(bus1.passengers)   #[‘Alice‘, ‘Bill‘, ‘Chariles‘]
print(bus2.passengers)  #[‘Helen‘, ‘Hel‘]
print(bus3.passengers)  #[‘Helen‘, ‘Hel‘]

问题原因:没有指定实例初始化乘客的Bus会共享一个乘客列表,默认值在函数定义时计算(通常认为是在程序加载时),因此默认值就成为了函数对象的属性,即如果默认值是可变对象。而且修改了其值,那么后续函数调用将受到影响。

解决办法,None判断:

import copy

class Bus:
    def __init__(self,passengers=None):
        if passengers is None:
            self.passengers=[]  #加入一个判断当传入参数为None时,创建一个新的空列表
        else:
            self.passengers=list(passengers)  #进行浅度复制,避免对外部传入参数的影响

    def pick(self,name):
        self.passengers.append(name)

    def drop(self,name):
        self.passengers.remove(name)

bus1=Bus([‘Alice‘, ‘Bill‘])
bus1.pick(‘Chariles‘)
print(bus1.passengers)  #[‘Alice‘, ‘Bill‘, ‘Chariles‘]

bus2=Bus()
bus2.pick(‘Helen‘)
print(bus2.passengers)  #[‘Helen‘]

bus3=Bus()
print(bus3.passengers)   #[] 没有受到上述问题影响

原文地址:https://www.cnblogs.com/2019zjp/p/11876963.html

时间: 2024-08-14 00:21:01

对象引用、变量的相关文章

【转载】Java对象 与 对象引用变量 的阐述

关于对象与引用之间的一些基本概念. 初学Java时,在很长一段时间里,总觉得基本概念很模糊.后来才知道,在许多Java书中,把对象和对象的引用混为一谈.可是,如果我分不清对象与对象引用, 那实在没法很好地理解下面的面向对象技术.把自己的一点认识写下来,或许能让初学Java的朋友们少走一点弯路. 为便于说明,我们先定义一个简单的类: 1 class Vehicle { 2 3 int passengers; 4 5 int fuelcap; 6 7 int mpg; 8 9 } 有了这个模板,就可

转:Java对象及对象引用变量

Java对象及其引用 关于对象与引用之间的一些基本概念. 初学Java时,在很长一段时间里,总觉得基本概念很模糊.后来才知道,在许多Java书中,把对象和对象的引用混为一谈.可是,如果我分不清对象与对象引用, 那实在没法很好地理解下面的面向对象技术.把自己的一点认识写下来,或许能让初学Java的朋友们少走一点弯路. 为便于说明,我们先定义一个简单的类: class Vehicle { int passengers; int fuelcap; int mpg; } 有了这个模板,就可以用它来创建对

Java对象及对象引用变量

Java对象及其引用 关于对象与引用之间的一些基本概念. 初学Java时,在很长一段时间里,总觉得基本概念很模糊.后来才知道,在许多Java书中,把对象和对象的引用混为一谈.可是,如果我分不清对象与对象引用, 那实在没法很好地理解下面的面向对象技术.把自己的一点认识写下来,或许能让初学Java的朋友们少走一点弯路. 为便于说明,我们先定义一个简单的类: class Vehicle { int passengers; int fuelcap; int mpg; } 有了这个模板,就可以用它来创建对

java中变量命名和引用变量的一个坑

这次有两个主题,第一个太简单啦,就是java中变量的命名规则,纯记忆性东西.第二个主题,就是讨论一下对象引用变量的一个注意点. 如果你对命名规则很熟啦,就直接看第二个内容吧.(上边的图稍微有点顺序紊乱啊) 一.java中变量的命名规则 大多数语言的命名规则是相似的,只有一些微小的差别. 变量的第一个位置,可以是字母,下划线(_),美元符($) (这个在c/c++,python中是不行的)(注意:不能是数字哦,一想就知道啦为什仫) 其他的位置可以是数字,字母,下划线 不能使用java中的关键字 j

java+内存分配及变量存储位置的区别

Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识.一般Java在内存分配时会涉及到以下区域: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中(new 出来的对象) ◆堆:存放用new产生的数据 ◆静态域:存放在对象中用static定义的静态成员 ◆常量池:存放常量 ◆非RAM存储:硬盘等永久

(转)Java静态变量,静态类,静态方法

静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                                        非静态对象     拥有属性:                是类共同拥有的                                             是类各对象独立拥有的内存分配:               内存空间上是固定的   

JAVA的静态变量、静态方法、静态类

JAVA的静态变量.静态方法.静态类 静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                                        非静态对象      拥有属性:                是类共同拥有的                                             是类各对象独立拥有的 内存分配:      

转载:JAVA的静态变量、静态方法、静态类

静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                                        非静态对象 拥有属性:                是类共同拥有的                                             是类各对象独立拥有的 内存分配:               内存空间上是固定的      

【转】JAVA的静态变量、静态方法、静态类

转自:http://blog.csdn.net/zhandoushi1982/article/details/8453522/ 静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                                        非静态对象     拥有属性:                是类共同拥有的                       

java是通过值传递,也就是通过拷贝传递——通过方法操作不同类型的变量加深理解

head first java里写到“java是通过值传递的,也就是通过拷贝传递”,由此得出结论,方法无法改变调用方传入的参数.该怎么理解呢? 看例子: public class Test1 { public static void main(String[] args) { int x = 7; System.out.println("传入方法之前:x="+x); addOne(x); System.out.println("传入方法之后:x="+x);}priv