深入理解Python中变量赋值的问题


Posted in Python onJanuary 12, 2017

前言

在Python中变量名规则与其他大多数高级语言一样,都是受C语言影响的,另外变量名是大小写敏感的。
Python是动态类型语言,也就是说不需要预先声明变量类型,变量的类型和值在赋值那一刻被初始化,下面详细介绍了Python的变量赋值问题,一起来学习学习吧。

我们先看一下如下代码:

c = {}

def foo():
 f = dict(zip(list("abcd"), [1, 2 ,3 ,4]))
 c.update(f)

if __name__ == "__main__":
 a = b = d = c

 b['e'] = 5
 d['f'] = 6

 foo()

 print(a)
 print(b)
 print(c)
 print(d)

输出结果:

{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 6}
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 6}
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 6}
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 6}

如果你对以上输出结果不感到奇怪,那么就不必往下看了。实际上本文要讨论的内容非常简单,不要为此浪费您宝贵的时间。

Python 属于动态语言,程序的结构可以在运行的过程中随时改变,而且 python 还是弱类型的语言,所以如果你是从静态、强类型编程语言转过来的,理解起 Python 的赋值,刚开始可能会感觉有些代码有点莫名其妙。

可能你会以为上面代码的输出会是这样的:

{}
{'e': 5}
{}
{'f': 6}

你可能认为 a 没有被改变,因为没有看到哪里对它做了改变;b 和 d 的改变是和明显的;c 呢,因为是在函数内被改变的,你可能认为 c 会是一个局部变量,所以全局的 c 不会被改变。

实际上,这里的 a, b, c, d 同时指向了一块内存空间,这可内存空间保存的是一个字典对象。这有点像 c 语言的指针,a, b, c, d 四个指针指向同一个内存地址,也就是给这块内存其了 4 个笔名。所以,不管你改变谁,其他三个变量都会跟着变化。那为什么 c 在函数内部被改变,而且没有用 global 申明,但全局的 c 去被改变了呢?

我们再来看一个例子:

>>>a = {1:1, 2:2}
>>>b = a
>>>a[3] = 3
>>>b
{1: 1, 2: 2, 3: 3}
>>>a = 4
>>>b
{1: 1, 2: 2, 3: 3}
>>>a
4

当 b = a 时,a 与 b 指向同一个对象,所以在 a 中添加一个元素时,b 也发生变化。而当 a = 4 时, a 就已经不再指向字典对象了,而是指向一个新的 int 对象(python 中整数也是对象),这时只有 b 指向字典,所以 a 改变时 b 没有跟着变化。这是只是说明了什么时候赋值变量会发生质的改变,而以上的问题还没有被解决。

那么,我么再来看一个例子:

class TestObj(object):
 pass

x = TestObj()
x.x = 8
d = {"a": 1, "b": 2, "g": x}
xx = d.get("g", None)
xx.x = 10
print("x.x:%s" % x.x)
print("xx.x: %s" % xx.x)
print("d['g'].x: %s" % d['g'].x)

# Out:
# x.x:10
# xx.x: 10
# d['g'].x: 10

由以上的实例可以了解到,如果仅改变对象的属性(或者说成是改变结构),所有指向该对象的变量都会随之改变。但是如果一个变量重新指向了一个对象,那么其他指向该对象的变量不会随之变化。所以,最开始的例子中,c 虽然在函数内部被改变,但是 c 是全局的变量,我们只是在 c 所指向的内存中添加了一个值,而没有将 c 指向另外的变量。

需要注意的是,有人可能会认为上例中的最后一个输出应该是 d['g'].x: 8。 这样理解的原因可能是觉得已经把字典中 ‘g' 所对应的值取出来了,并重新命名为 xx,那么 xx 就与字典无关了。其实际并不是这样的,字典中的 key 所对应的 value 就像是一个指针指向了一片内存区域,访问字典中 key 时就是去该区域取值,如果将值取出来赋值给另外一个变量,例如 xx = d['g'] 或者 xx = d.get("g", None),这样只是让 xx 这个变量也指向了该区域,也就是说字典中的键 ‘g' 和 xx 对象指向了同一片内存空间,当我们只改变 xx 的属性时,字典也会发生变化。

下例更加直观的展示了这一点:

class TestObj(object):
 pass

x = TestObj()
x.x = 8
d = {"a": 1, "b": 2, "g": x}
print(d['g'].x)
xx = d["g"]
xx.x = 10
print(d['g'].x)
xx = 20
print(d['g'].x)

# Out:
# 8
# 10
# 10

这个知识点非常简单,但如果没有理解,可能无法看明白别人的代码。这一点有时候会给程序设计带来很大的便利,例如设计一个在整个程序中保存状态的上下文:

class Context(object):
 pass


def foo(context):
 context.a = 10
 context.b = 20
 x = 1

def hoo(context):
 context.c = 30
 context.d = 40
 x = 1

if __name__ == "__main__":
 context = Context()
 x = None
 foo(context)
 hoo(context)
 print(x)
 print(context.a)
 print(context.b)
 print(context.c)
 print(context.d)

# Out:
# None
# 10
# 20
# 30
# 40

示例中我们可以把需要保存的状态添加到 context 中,这样在整个程序的运行过程中这些状态能够被任何位置被使用。

在来一个终结的例子,执行外部代码:

outer_code.py

from __future__ import print_function

def initialize(context):
 g.a = 333
 g.b = 666
 context.x = 888

def handle_data(context, data):
 g.c = g.a + g.b + context.x + context.y
 a = np.array([1, 2, 3, 4, 5, 6])
 print("outer space: a is %s" % a)
 print("outer space: context is %s" % context)

main_exec.py

from __future__ import print_function

import sys
import imp
from pprint import pprint

class Context(object):
 pass

class PersistentState(object):
 pass


# Script starts from here

if __name__ == "__main__":
 outer_code_moudle = imp.new_module('outer_code')
 outer_code_moudle.__file__ = 'outer_code.py'
 sys.modules["outer_code"] = outer_code_moudle
 outer_code_scope = code_scope = outer_code_moudle.__dict__

 head_code = "import numpy as np\nfrom main_exec import PersistentState\ng=PersistentState()"
 exec(head_code, code_scope)
 origin_global_names = set(code_scope.keys())

 with open("outer_code.py", "rb") as f:
 outer_code = f.read()

 import __future__
 code_obj = compile(outer_code, "outer_code.py", "exec", flags=__future__.unicode_literals.compiler_flag)
 exec(code_obj, code_scope)
 # 去除掉内建名字空间的属性,仅保留外部代码中添加的属性
 outer_code_global_names = set(outer_code_scope.keys()) - origin_global_names

 outer_func_initialize = code_scope.get("initialize", None)
 outer_func_handle_data = code_scope.get("handle_data", None)

 context = Context()
 context.y = 999
 outer_func_initialize(context)
 outer_func_handle_data(context, None)

 g = outer_code_scope["g"]
 assert g.c == 2886
 print("g.c: %s" % g.c)
 print(dir(g))
 print(dir(context))
 pprint(outer_code_moudle.__dict__)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
python测试驱动开发实例
Oct 08 Python
python之Socket网络编程详解
Sep 29 Python
Python寻找两个有序数组的中位数实例详解
Dec 05 Python
Python提取频域特征知识点浅析
Mar 04 Python
Python实现的列表排序、反转操作示例
Mar 13 Python
Python3 itchat实现微信定时发送群消息的实例代码
Jul 12 Python
python爬取百度贴吧前1000页内容(requests库面向对象思想实现)
Aug 10 Python
用Pelican搭建一个极简静态博客系统过程解析
Aug 22 Python
Python3 Tkinkter + SQLite实现登录和注册界面
Nov 19 Python
Python 使用双重循环打印图形菱形操作
Aug 09 Python
15款Python编辑器的优缺点,别再问我“选什么编辑器”啦
Oct 19 Python
python各种excel写入方式的速度对比
Nov 10 Python
Python时间获取及转换知识汇总
Jan 11 #Python
json跨域调用python的方法详解
Jan 11 #Python
Python中遇到的小问题及解决方法汇总
Jan 11 #Python
python使用正则表达式匹配字符串开头并打印示例
Jan 11 #Python
SQLite3中文编码 Python的实现
Jan 11 #Python
利用python实现数据分析
Jan 11 #Python
使用python调用zxing库生成二维码图片详解
Jan 10 #Python
You might like
PHP中的类型提示(type hinting)功能介绍
2015/07/01 PHP
Laravel ORM 数据model操作教程
2019/10/21 PHP
php传值和传引用的区别点总结
2019/11/19 PHP
js split 的用法和定义 js split分割字符串成数组的实例代码
2012/05/13 Javascript
jQuery切换网页皮肤并保存到Cookie示例代码
2014/06/16 Javascript
JavaScript不使用prototype和new实现继承机制
2014/12/29 Javascript
JS日期格式化之javascript Date format
2015/10/01 Javascript
javascript实现网站加入收藏功能
2015/12/16 Javascript
深入解析nodejs HTTP服务
2017/07/25 NodeJs
详解如何写出一个利于扩展的vue路由配置
2019/05/16 Javascript
vue-i18n结合Element-ui的配置方法
2019/05/20 Javascript
react实现antd线上主题动态切换功能
2019/08/12 Javascript
React 实现车牌键盘的示例代码
2019/12/20 Javascript
[05:22]DOTA2 2015国际邀请赛中国区预选赛首日TOP10
2015/05/26 DOTA
[01:35]辉夜杯战队访谈宣传片—LGD
2015/12/25 DOTA
Pyhton中防止SQL注入的方法
2015/02/05 Python
Python中解析JSON并同时进行自定义编码处理实例
2015/02/08 Python
Python自动化运维之IP地址处理模块详解
2017/12/10 Python
Python cookbook(数据结构与算法)在字典中将键映射到多个值上的方法
2018/02/18 Python
解决pytorch多GPU训练保存的模型,在单GPU环境下加载出错问题
2020/06/23 Python
深入分析python 排序
2020/08/24 Python
python 常用日期处理-- datetime 模块的使用
2020/09/02 Python
python爬虫用request库处理cookie的实例讲解
2021/02/20 Python
Madewell美德威尔美国官网:美国休闲服饰品牌
2016/11/25 全球购物
美国旅游网站:Tours4Fun
2017/02/17 全球购物
Tom Dixon官网:英国照明及家具设计和制造公司
2019/03/01 全球购物
几道PHP的面试题
2012/05/19 面试题
swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
2013/03/30 面试题
2014年党的群众路线教育实践活动总结
2014/04/25 职场文书
教书育人演讲稿
2014/09/11 职场文书
学校查摆问题整改措施
2014/09/28 职场文书
工作简历自我评价
2015/03/11 职场文书
2015年房地产销售工作总结
2015/04/20 职场文书
小学德育工作总结2015
2015/05/12 职场文书
大学生村官工作心得体会
2016/01/23 职场文书
MySQL约束(创建表时的各种条件说明)
2022/06/21 MySQL