Python基础之变量的相关知识总结

变量全都是引用

跟其他编程语言不同,Python的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面。

比如:

>>> a = [1, 2, 3]
>>> b = a
>>> a.append(4)
>>> b
[1, 2, 3, 4]
>>> b is a
True

a变量和b变量引用的是同一个列表[1, 2, 3]。b可以叫做a的别名。

比较来看:

>>> a = [1, 2, 3]
>>> c = [1, 2, 3]
>>> c == a
True
>>> c is a
False

c引用的是另外一个列表,虽然和a引用的列表的值相等,但是它们是不同的对象。

浅复制与深复制

浅复制是指只复制最外层容器,副本中的元素是源容器中元素的引用。如果所有元素都是不可变的,那么这样没有问题,还能节省内容。但是,如果有可变的元素,那么结果可能会出乎意料之外。构造方法或[:]做的都是浅复制。

示例:

>>> x1 = [3, [66, 55, 44], (7, 8, 9)]
# x2是x1的浅复制
>>> x2 = list(x1)

# 不可变元素没有影响
>>> x1.append(100)
>>> x1
[3, [66, 55, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 55, 44], (7, 8, 9)]  

# x1[1]是列表,可变元素会影响x2
# 因为它们引用的是同一个对象
>>> x1[1].remove(55)
>>> x1
[3, [66, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 44], (7, 8, 9)]  

# x2[1]也会反过来影响x1
>>> x2[1] += [33, 22]
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9)]

# 不可变元组也不会有影响
# +=运算符创建了一个新元组
>>> x2[2] += (10, 11)
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

深复制是指我们常规理解的复制,副本不共享内部对象的引用,是完全独立的一个副本。这可以借助copy.deepcopy来实现。

示例:

>>> a = [10, 20]
>>> b = [a, 30]
>>> a.append(b)
>>> a
[10, 20, [[...], 30]]
>>> from copy import deepcopy
>>> c = deepcopy(a)
>>> c
[10, 20, [[...], 30]]

即使是有循环引用也能正确复制。

注意copy.copy()是浅复制,copy.deepcopy()是深复制。

函数传参

Python唯一支持的参数传递模式是共享传参,也就是指函数的各个形式参数获得实参中各个引用的副本。因为Python的变量全都是引用。对于不可变对象来说没有问题,但是对于可变对象就不一样了。

示例:

>>> def f(a, b):
...     a += b
...     return a
... 

# 数字不变
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y
(1, 2)

# 列表变了
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b
([1, 2, 3, 4], [3, 4])

# 元组不变
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u
((10, 20), (30, 40))

由此可以得出一条警示:函数参数尽量不要使用可变参数,如果非用不可,应该考虑在函数内部进行复制。

示例:

class TwilightBus:
    """A bus model that makes passengers vanish"""

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

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

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

测试一下:

>>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
>>> bus = TwilightBus(basketball_team)
>>> bus.drop('Tina')
>>> bus.drop('Pat')
>>> basketball_team
['Sue', 'Maya', 'Diana']

TwilightBus下车的学生,竟然从basketball_team中消失了。这是因为self.passengers引用的是同一个列表对象。修改方法很简单,复制个副本:

 def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)  # 使用构造函数复制副本

del和垃圾回收

del语句删除的是引用,而不是对象。但是del可能会导致对象没有引用,进而被当做垃圾回收。

示例:

>>> import weakref
>>> s1 = {1, 2, 3}
# s2和s1引用同一个对象
>>> s2 = s1
>>> def bye():
...     print("Gone")
...
# 监控对象和调用回调
>>> ender = weakref.finalize(s1, bye)
>>> ender.alive
True
# 删除s1后还存在s2引用
>>> del s1
>>> ender.alive
True
# s2重新绑定导致{1, 2, 3}引用归零
>>> s2 = "spam"
Gone
# 对象被销毁了
>>> ender.alive
False

在CPython中,对象的引用数量归零后,对象会被立即销毁。如果除了循环引用之外没有其他引用,两个对象都会被销毁。

弱引用

某些情况下,可能需要保存对象的引用,但不留存对象本身。比如,有个类想要记录所有实例。这个需求可以使用弱引用实现。

比如上面示例中的weakref.finalize(s1, bye),finalize就持有{1, 2, 3}的弱引用,虽然有引用,但是不会影响对象被销毁。

其他使用弱引用的方式是WeakDictionary、WeakValueDictionary、WeakSet。

示例:

class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
...                 Cheese('Brie'), Cheese('Parmesan')]
...
>>> for cheese in catalog:
        # 用作缓存
        # key是cheese.kind
        # value是cheese的弱引用
...     stock[cheese.kind] = cheese
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']

# 删除catalog引用,stock弱引用不影响垃圾回收
# WeakValueDictionary的值引用的对象被销毁后,对应的键也会自动删除
>>> del catalog
>>> sorted(stock.keys())  # 还存在一个cheese临时变量的引用
['Parmesan']

# 删除cheese临时变量的引用,stock就完全清空了
>>> del cheese
>>> sorted(stock.keys())
[]

注意不是每个Python对象都可以作为弱引用的目标,比如基本的list和dict就不可以,但是它们的子类是可以的:

class MyList(list):
    pass
a_list = MyList(range(10))
weakref_to_a_list = weakref.ref(a_list)

小结

本文首先阐述了Python变量全部都是引用的这个事实,这意味着在Python中,简单的赋值是不创建副本的。如果要创建副本,可以选择浅复制和深复制,浅复制使用构造方法、[:]copy.copy(),深复制使用copy.deepcopy()。del删除的是引用,但是会导致对象没有引用而被当做垃圾回收。有时候需要保留引用而不保留对象(比如缓存),这叫做弱引用,weakref库提供了相应的实现。

参考资料:

《流畅的Python》

到此这篇关于Python基础之变量的相关知识总结的文章就介绍到这了,更多相关Python变量内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python 实现两个变量值进行交换的n种操作

    python 两个变量值交换 方法一: c = 0 c= a a= b b= c 方法二: a,b = b,a 这是python的基本方法 方法三:(只用两个变量实现) a = a+b b = a-b a = a-b python两个数值互换(浅析a,b=b,a原理) python交换两个值得方法非常简单,即a,b=b,a,一步操作就交换了两个值,那么这是为什么呢? 真相: Python的变量并不直接存储值,而只是引用一个内存地址,交换变量时,只是交换了引用的地址. 先看下面这段程序: impo

  • python子类如何继承父类的实例变量

    类型1:父类和子类的实例变量均不需要传递 class A(object): def __init__(self): self.name = "cui" def get_name(self): return self.name class B(A): def __init__(self): super(B,self).__init__() self.age = 12 b = B() 类型2:父类的实例变量不需要传递,子类的实例变量需要传递 class A(object): def __i

  • Python全局变量global关键字详解

    Python中的变量作用域 一般在函数体外定义的变量成为全局变量,在函数内部定义的变量称为局部变量. 全局变量所有作用域都可用,局部变量只能在本函数可用,变量的使用顺序是,局部变量 > 全局变量, 也就是说:优先使用局部变量 那么问题来了, 如果想在函数内使用全局变量,或改变全局变量的值, 应该怎么做呢? Python全局变量global关键字 为了解决函数内使用全局变量的问题,python增加了global关键字, 利用它的特性, 可以指定变量的作用域. Python全局变量global关键字

  • Python激活Anaconda环境变量的详细步骤

    简洁版 Windows10系统下,按Win+R键启动运行,输入cmd,进入命令窗口 输入conda info --envs,查看conda 环境变量的路径,base后边就是环境变量的路径. 接着输入conda activate +上述查到的环境变量路径,激活环境变量. 如果想停止激活Anaconda Python的环境变量,可以使用conda deactivate命令进行停止激活 详细版 第一,Windows10系统下,按Win+R键启动运行,输入cmd,进入命令窗口,输入conda init,

  • python定义变量类型

    目录 定义变量 什么是变量? 变量的定义? 标识符(命令规范) 命名原则 变量的类型 不同类型变量之间的计算 变量的输入 变量的格式化输出 变量赋值 删除变量 常量定义 定义变量 什么是变量? 在程序运行过程中,其值可以改变的量 变量的定义? 在 python 中,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建 等号(=)用来给变量赋值 =左边是一个变量名 =右边是存储在变量中的值 变量名 = 值 变量定义之后,后续就可以直接使用了 标识符(命令规范) 只能由数字.字母.下划线组成 不

  • Python基础之变量的相关知识总结

    变量全都是引用 跟其他编程语言不同,Python的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面. 比如: >>> a = [1, 2, 3] >>> b = a >>> a.append(4) >>> b [1, 2, 3, 4] >>> b is a True a变量和b变量引用的是同一个列表[1, 2, 3].b可以叫做a的别名. 比较来看: >>> a = [1, 2,

  • Python基础之循环语句相关知识总结

    目录 一.循环语句介绍 二.循环语句的分类 三.循环控制语句 四.while循环 五.break和continue 六.for循环 七.pass语句的使用 一.循环语句介绍  1.循环语句理解 循环语句允许我们执行一个语句或语句组多次,可以让我们的代码重复的去执行. 2.循环语句示意图 二.循环语句的分类 三.循环控制语句 四.while循环 while循环解释:判断语句的条件是否为真,如果为真,则执行代码,然后再次判断条件,直到条件为假为止,循环结束. 1.while死循环 # 死循环示例 w

  • 总结Python变量的相关知识

    一.变量的定义 程序中,数据都是临时存储在内存中,为了更快速的查找或使用这个数据,通常我们把这个数据在内存中存储之后,给整个数据定义一个名称,这个名称就是变量. 变量就是在存储数据的时候,当前数据所在的内存地址的一个名字而已. 二.Python变量说明 Python中使用变量,不需要声明,直接为变量赋值即可. 在Python中,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的数据. 这种变量本身类型不固定的语言称之为动态类型语言,与之对应的是静态类型语言.静态类型语言在

  • Python基础之变量基本用法与进阶详解

    本文实例讲述了Python基础之变量基本用法与进阶.分享给大家供大家参考,具体如下: 目标 变量的引用 可变和不可变类型 局部变量和全局变量 01. 变量的引用 变量 和 数据 都是保存在 内存 中的 在 Python 中 函数 的 参数传递 以及 返回值 都是靠 引用 传递的 1.1 引用的概念 在 Python 中 变量 和 数据 是分开存储的 数据 保存在内存中的一个位置 变量 中保存着数据在内存中的地址 变量 中 记录数据的地址,就叫做 引用 使用 id() 函数可以查看变量中保存数据所

  • 浅谈Python类的单继承相关知识

    一.类的继承 面向对象三要素之一,继承Inheritance 人类和猫类都继承自动物类. 个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性. 在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样就可以减少代码.多服用.子类可以定义自己的属性和方法 class Animal: def __init__(self,name): self._name = name def shout(self): print("{} shouts".format(self.__c

  • Python基础之元组与文件知识总结

    大纲 Python文件类型及汇总 一.元组 1 特征 1.任意对象的有序集合 2.通过下标访问 3.不可变 4.长度固定,任意类型,任意嵌套 >>> t = (1,2,3,4,5) >>> t[0] = 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not suppo

  • Java基础之反射技术相关知识总结

    一.反射概念 Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法.这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制.反射被视为动态语言的关键. 二.反射应用场景 1.几乎所有的框架都会用到反射 2.程序解耦合使用 3.代码更加的优雅 三.反射更多细节 1.Jdk中的位置: java.lang.reflect包下 2.获取字节码方式 //

  • Python入门学习之类的相关知识总结

    前言 Python是面向对象的程序设计(Object Oriented Programming). 面向对象的程序设计的一条基本原则是: 计算机程序由多个能够起到子程序作用的单元或者对象组合而成. 面向对象的程序设计的一个关键性概念是: 数据以及数据的操作(即函数方法)封装在一起. 这个概念对于接触C++的我来说已经很熟悉了.就是"类"Class. Python完全支持所有面向对象的基本功能,比如: "封装"."继承"."多态"

  • Java基础学习之运算符相关知识总结

    1.算术运算符 +表示加法运算符 -表示减法运算符 *表示乘法运算符 /表示除法运算符 %表示取模/取余运算符 package com.lagou.Day03; /** * 算术运算符 */ public class Demo01 { public static void main(String[] args) { //1.声明两个int类型的变量并初始化 //int ia = 6;ib = 2;//表示声明两个int类型的变量ia和ib,不推荐使用 int ia = 6; int ib = 2

  • python框架django项目部署相关知识详解

    这篇文章主要介绍了python框架django项目部署相关知识详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一:项目部署的框架 nginx和uWSGI在生产服务器上进行的部署 二:什么是nginx? nginx是一个web服务器. 什么是web服务器? web服务器则主要是让客户可以通过浏览器进行访问,处理HTML文件,css文件,js文件,图片等资源.web服务器一般要处理静态文件.对接服务器. 什么是静态文件? css,js,html

随机推荐