同样的python代码通过python文件运行正常,但是直接在解释器里面逐行写的时候报语法错误,这是为什么呢?
我还没入门的,这个问题是在学习HeadFirstpython(中文版)这本书的108和112页的时候发现的,108页print(man)和print(other)这两行代...
我还没入门的,这个问题是在学习HeadFirst python(中文版) 这本书的108和112页的时候发现的,108页print(man)和print(other)这两行代码如果在IDLE里面接着输入报语法错误,但是如果将代码完整写入py文件再运行就正常。112页后面的try/except部分也是一样的情况。
展开
1个回答
展开全部
对于Python而言,存储好的脚本文件(Script file)和在Console中的交互式(interactive)命令,执行方式不同。对于脚本文件,解释器将其当作整个代码块执行,而对于交互性命令行中的每一条命令,解释器将其当作单独的代码块执行。而Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用(这句话不够严谨,后面会详谈)。所以在你给出的例子中,文件执行时(同一个代码块)会把a、b两个变量指向同一个对象;而在命令行执行时,a、b赋值语句分别被当作两个代码块执行,所以会得到两个不同的对象,因而is判断返回False。
# 如果你能理解上面一段,就不用看下面的废话了。
下面是详细的回答:
说真的,这简直是我最近在知乎遇到过的最好的问题!
这个问题远超我想象中的复杂。我本来以为我能用两分钟搞定这种每日一水的问题,结果我花了一个小时搜来搜去,读来读去,还跑去群里跟人讨论了一阵,都没能找到答案。
大概两个小时以后,我找到了相对正确的答案,把自己已经弄懂的部分强答一番,并邀请一些大神,希望能看到更为准确的回答。
这个问题的博大精深在于,能从中扯出许多小问题来,虽然这些东西很细枝末节,很trick,在日常编程中不怎么用的到,更不怎么需要额外关注,但是理解这些问题,对于我们理解Python的对象机制乃至内存处理机制有很大的帮助。
我从头开始说,大概会分以下几个部分来谈,每个部分其实都能展开很广,这次就把与问题相关的知识简单一提:
(虽然我觉得按照我寻找答案的过程讲,可能对认知更有帮助,但是理清头绪的话可能更好理解,之后会找时间为这个问题写篇文章好好记录一下)
Python中的数据类型——可变与不可变
Python中is比较与==比较的区别
Python中对小整数的缓存机制
Python程序的结构——代码块
Python的内存管理——新建对象时的操作
声明:以下所讲机制,与Python不同版本的具体实现有关(implement specific)可能不同。
Python中的数据类型
Python中的数据类型,这可能是大家入门Python的第一节课。很简单嘛,大家最常用的,int(包括long)、float、string、list、tuple、dict,加上bool和NoneType。
但是这里要重点说的,其实是可变类型和不可变类型。
不可变(immutable):Number(包括int、float),String,Tuple
可变(mutable):Dict,List,User-defined class
首先我们要记住一句话,一切皆对象。Python中把任何一种Type都当作对象来处理。其中有一些类型是不可变的,比如:
这个还是好理解的,在初始化赋值一个字符串后,我们没有办法直接修改它的值。但是数字呢?数字这种变来变去的又怎么理解。
可以看出,a的值虽然从10变成了11,但是a这个变量指向内存中的位置发生了变化,也就是说我们并没有对a指向的内存进行操作,而是对a进行了重新赋值。
再简单举一个可变的例子。
体会了可变与不可变的外在表现后,简单理解一下为什么不可变。
Python官方文档这样解释字符串不可变:
There are several advantages.
One is performance: knowing that a string is immutable means we can allocate space for it at creation time, and the storage requirements are fixed and unchanging. This is also one of the reasons for the distinction between tuples and lists.
Another advantage is that strings in Python are considered as “elemental” as numbers. No amount of activity will change the value 8 to anything else, and in Python, no amount of activity will change the string “eight” to anything else.
个人感觉,有性能上的考虑(比如对一些固定不变的元素给予固定的存储位置,整数这样操作比较方便,字符串的话涉及一些比较也会减少后续操作的时间),也有一些安全上的考虑(比如列表中的值会改变,元组不会)。这个我也不太精通,就不展开谈了。
Python中is比较与==比较的区别
前面已经提过一次,Python中一切皆对象。对象包含三个要素,id、type、value。
而Python中用于比较“相等”这一概念的操作符,is和==。
当两个变量指向了同一个对象时,is会返回True(即is比较的是两个变量的id);
当两个变量的值相同时,==会返回True(即==比较的是两个变量的value)。
示例(命令行交互模式下):
第一个和第三个示例是好理解的。
但是第二个就不那么好理解了,尤其是配合下面这个(假定我们已经知道命令行中的语句执行是单独执行两次不会相互影响,后面会具体解释):
为什么a、b分别赋值1000时is比较返回False,可以分别赋值100就会返回True?
Python中对小整数的缓存机制
Python官方文档中这么说:
The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-)简单来说就是,Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。
Python程序的结构——代码块&Python的内存管理——新建对象时的操作
终于要来到题主问题的部分了。
先来看最让我们困惑的,也就是题主给出的示例吧(接下来用float演示,int是同样的情况):
交互命令行下:
同样的还有:
(说好的小整数才有缓存呢(摔)!这跟你讲的不一样啊教练!)
这就很尴尬了对吧。
其实从结果论出发,我们很容易猜到结论,就像题主自己也猜了个差不多——缓存机制不同。毕竟is比较的就是对象的id,也就是对象在内存中的位置,也就是是不是同一个对象。
既然脚本文件的执行结果是True,那么,他俩就是同一个对象;既然命令行执行的结果是False,那么他俩就不是同一个对象。(这他喵的不是废话吗!)
所以我开始了漫长的找原理的过程……然而网上这方面提及的实在太少。尤其是大家的大部分讨论都是int的小整数缓存机制;就算讨论到了float,也不实际解决我们的问题。
其实我都快要放弃了,漫无目的地翻stackoverflow推荐的相关问题时终于找到了一个类似的情况,但是人家并不是比较的脚本文件和命令行执行,而是比较的函数体和赋值语句:
同样的代码,拆开就是False,放函数里就是True!是不是很像我们遇到的情况了。
根据提示我们从官方文档找到了这样的说法:
A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c‘ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.
A code block is executed in an execution frame. A frame contains some administrative information (used for debugging) and determines where and how execution continues after the code block’s execution has completed.
没错!跟我们猜的一样!这就是原理的出处了!
代码块作为一个执行单元,一个模块、一个函数体、一个类定义、一个脚本文件,都是一个代码块。
在交互式命令行中,每行代码单独视作一个代码块。
至此问题解决……了吗?视作一个代码块,就意味着要把相同value的赋值指向相同的对象吗?
在此重复一下'is' operator behaves unexpectedly with non-cached integers中提到的实验,并简单翻译结论。
通过compile()函数和dis模块的code_info()函数来检测我们执行的命令的信息。
示例:
可以看出,分别赋值a,b得到的value相等,id是不一样的。
把10.0 10.0 10.1分别赋值给a,b,c,可以看出结果中其实只保存了一个10.0,也就是a,b共用了这个数值。
也就是说,当命令行执行时,是以single的模式来compile代码(2. Built-in Functions)。它会在u_consts字典中记录对象常量。
The mode argument specifies what kind of code must be compiled; it can be 'exec' if source consists of a sequence of statements, 'eval' if it consists of a single expression, or 'single' if it consists of a single interactive statement (in the latter case, expression statements that evaluate to something other than None will be printed).而在同一代码块执行时,当增加新的常量,会先在字典中查询记录,所以相同赋值的变量会指向同一个对象而不是新建对象。
至此…问题大概是解决了。
# 如果你能理解上面一段,就不用看下面的废话了。
下面是详细的回答:
说真的,这简直是我最近在知乎遇到过的最好的问题!
这个问题远超我想象中的复杂。我本来以为我能用两分钟搞定这种每日一水的问题,结果我花了一个小时搜来搜去,读来读去,还跑去群里跟人讨论了一阵,都没能找到答案。
大概两个小时以后,我找到了相对正确的答案,把自己已经弄懂的部分强答一番,并邀请一些大神,希望能看到更为准确的回答。
这个问题的博大精深在于,能从中扯出许多小问题来,虽然这些东西很细枝末节,很trick,在日常编程中不怎么用的到,更不怎么需要额外关注,但是理解这些问题,对于我们理解Python的对象机制乃至内存处理机制有很大的帮助。
我从头开始说,大概会分以下几个部分来谈,每个部分其实都能展开很广,这次就把与问题相关的知识简单一提:
(虽然我觉得按照我寻找答案的过程讲,可能对认知更有帮助,但是理清头绪的话可能更好理解,之后会找时间为这个问题写篇文章好好记录一下)
Python中的数据类型——可变与不可变
Python中is比较与==比较的区别
Python中对小整数的缓存机制
Python程序的结构——代码块
Python的内存管理——新建对象时的操作
声明:以下所讲机制,与Python不同版本的具体实现有关(implement specific)可能不同。
Python中的数据类型
Python中的数据类型,这可能是大家入门Python的第一节课。很简单嘛,大家最常用的,int(包括long)、float、string、list、tuple、dict,加上bool和NoneType。
但是这里要重点说的,其实是可变类型和不可变类型。
不可变(immutable):Number(包括int、float),String,Tuple
可变(mutable):Dict,List,User-defined class
首先我们要记住一句话,一切皆对象。Python中把任何一种Type都当作对象来处理。其中有一些类型是不可变的,比如:
这个还是好理解的,在初始化赋值一个字符串后,我们没有办法直接修改它的值。但是数字呢?数字这种变来变去的又怎么理解。
可以看出,a的值虽然从10变成了11,但是a这个变量指向内存中的位置发生了变化,也就是说我们并没有对a指向的内存进行操作,而是对a进行了重新赋值。
再简单举一个可变的例子。
体会了可变与不可变的外在表现后,简单理解一下为什么不可变。
Python官方文档这样解释字符串不可变:
There are several advantages.
One is performance: knowing that a string is immutable means we can allocate space for it at creation time, and the storage requirements are fixed and unchanging. This is also one of the reasons for the distinction between tuples and lists.
Another advantage is that strings in Python are considered as “elemental” as numbers. No amount of activity will change the value 8 to anything else, and in Python, no amount of activity will change the string “eight” to anything else.
个人感觉,有性能上的考虑(比如对一些固定不变的元素给予固定的存储位置,整数这样操作比较方便,字符串的话涉及一些比较也会减少后续操作的时间),也有一些安全上的考虑(比如列表中的值会改变,元组不会)。这个我也不太精通,就不展开谈了。
Python中is比较与==比较的区别
前面已经提过一次,Python中一切皆对象。对象包含三个要素,id、type、value。
而Python中用于比较“相等”这一概念的操作符,is和==。
当两个变量指向了同一个对象时,is会返回True(即is比较的是两个变量的id);
当两个变量的值相同时,==会返回True(即==比较的是两个变量的value)。
示例(命令行交互模式下):
第一个和第三个示例是好理解的。
但是第二个就不那么好理解了,尤其是配合下面这个(假定我们已经知道命令行中的语句执行是单独执行两次不会相互影响,后面会具体解释):
为什么a、b分别赋值1000时is比较返回False,可以分别赋值100就会返回True?
Python中对小整数的缓存机制
Python官方文档中这么说:
The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-)简单来说就是,Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。
Python程序的结构——代码块&Python的内存管理——新建对象时的操作
终于要来到题主问题的部分了。
先来看最让我们困惑的,也就是题主给出的示例吧(接下来用float演示,int是同样的情况):
交互命令行下:
同样的还有:
(说好的小整数才有缓存呢(摔)!这跟你讲的不一样啊教练!)
这就很尴尬了对吧。
其实从结果论出发,我们很容易猜到结论,就像题主自己也猜了个差不多——缓存机制不同。毕竟is比较的就是对象的id,也就是对象在内存中的位置,也就是是不是同一个对象。
既然脚本文件的执行结果是True,那么,他俩就是同一个对象;既然命令行执行的结果是False,那么他俩就不是同一个对象。(这他喵的不是废话吗!)
所以我开始了漫长的找原理的过程……然而网上这方面提及的实在太少。尤其是大家的大部分讨论都是int的小整数缓存机制;就算讨论到了float,也不实际解决我们的问题。
其实我都快要放弃了,漫无目的地翻stackoverflow推荐的相关问题时终于找到了一个类似的情况,但是人家并不是比较的脚本文件和命令行执行,而是比较的函数体和赋值语句:
同样的代码,拆开就是False,放函数里就是True!是不是很像我们遇到的情况了。
根据提示我们从官方文档找到了这样的说法:
A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c‘ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.
A code block is executed in an execution frame. A frame contains some administrative information (used for debugging) and determines where and how execution continues after the code block’s execution has completed.
没错!跟我们猜的一样!这就是原理的出处了!
代码块作为一个执行单元,一个模块、一个函数体、一个类定义、一个脚本文件,都是一个代码块。
在交互式命令行中,每行代码单独视作一个代码块。
至此问题解决……了吗?视作一个代码块,就意味着要把相同value的赋值指向相同的对象吗?
在此重复一下'is' operator behaves unexpectedly with non-cached integers中提到的实验,并简单翻译结论。
通过compile()函数和dis模块的code_info()函数来检测我们执行的命令的信息。
示例:
可以看出,分别赋值a,b得到的value相等,id是不一样的。
把10.0 10.0 10.1分别赋值给a,b,c,可以看出结果中其实只保存了一个10.0,也就是a,b共用了这个数值。
也就是说,当命令行执行时,是以single的模式来compile代码(2. Built-in Functions)。它会在u_consts字典中记录对象常量。
The mode argument specifies what kind of code must be compiled; it can be 'exec' if source consists of a sequence of statements, 'eval' if it consists of a single expression, or 'single' if it consists of a single interactive statement (in the latter case, expression statements that evaluate to something other than None will be printed).而在同一代码块执行时,当增加新的常量,会先在字典中查询记录,所以相同赋值的变量会指向同一个对象而不是新建对象。
至此…问题大概是解决了。
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询