Python


239 浏览 1 year, 11 months

1.1 变量

版权声明: 转载请注明出处 http://www.codingsoho.com/

变量

概述

变量是什么? 在数学里,我们用下面的格式来表示变量

3y = 2x + 1 + 2y
x = 7

3y - 2y = 2x + 1
x = 7

y = 2x + 1
x = 7

y = 2*7 + 1
x = 7

y = 15
x = 7

在C/C++里,表示方法如下:

int a = 1;
...
a = 3;

在数学和C++里,变量可以认为是计算过程中间值得一个存储位置。

<font color='red'>在python中呢?</font>

下面是代码的比较:

C++:

#include <array>
#include <iostream>
using namespace std;

int main() {
  cout<<" a   b \n";
  array<int,1> a = {1};
  cout<<"["<<a[0]<<"]\n";
  a[0] = 3;
  cout<<"["<<a[0]<<"]\n";
  array<int,1> b = a;
  cout<<"["<<a[0]<<"] ["<<b[0]<<"]\n";
  a[0] = 4;
  cout<<"["<<a[0]<<"] ["<<b[0]<<"]\n";
  b[0] = 5;
  cout<<"["<<a[0]<<"] ["<<b[0]<<"]\n";
  return 0;
}
//>

python

#!/usr/local/bin/python2.7

print ' a   b '
a = [1]
print a
a[0] = 3
print a
b = a
print a, b
a[0] = 4
print a, b
b[0] = 5
print a, b

C++的在线编译运行可以访问网址 http://www.compileonline.com/compile_cpp11_online.php

输出结果如下:

C++

 a   b
[1]
[3]
[3] [3]
[4] [3]
[4] [5]

python

 a   b
[1]
[3]
[3] [3]
[4] [4]
[5] [5]

为什么python的结果会不一样呢?

其他语言的赋值过程如下图

每一个变量都是一个<font color='red'>容器(存储空间)</font>,向一个变量赋值相当于把一个值放到这个盒子里。 例如int a=1;, a盒子里会包含整数1
向这个变量赋另外一个值时,会替换盒子里的内容,执行a=2;, 盒子里的内容变成了2
将一个变量赋值给另外一个变量时,实际上时把当前变量值得拷贝放到新的盒子里,int b=a;, 变量b是一个新的盒子,里面放的是变量a的内容的拷贝

Python里的情况就有点不同,变量更像是一个<font color='red'>名字,绑定到某个对象</font>

例如a=1;,这儿整数1是一个对象,a是绑定到这个对象的标签;
如果给a重新分配对象,实际只是将这个标签移到另外的对象; a=2, 标签a从对象1绑定移到对象2;原来的对象1将失去标签a,这个对象可能还是存在的,但是我们已经不能通过a来访问了。

通常一个对象如果没有引用或者标签,它将会被从内存里删除。

如果我们将一个标签名赋值给另外一个标签,实际上是将新的标签同时绑定到这个对象上。 b=a, b和a将会绑定到同一个对象上去。

所以,在python里,变量只是一个名字或者ID,它不是一个存储的盒子。

变量赋值

Python中变量在使用前需要赋值,变量赋值不需要事先声明类型

Python使用号类给变量赋值:左边是变量名,右边是值

a=1b=0.5, c='abc',d=(1,2,3),e=[1,2,3],f={1,2,3}

Python支持多变量赋值

a=b=c=1, a,b,c = 1,1.1,'aaa'

对于变量键盘输入赋值,使用input()函数:

>>> a = input("please input your number:")
please input your number:12
>>> a
'12'
>>>
多态 polymorphic

多态的意思是说变量可以分配不同类型的值,因为python里变量只是一个绑定到实际值的名字而已,所以它天生就是多态的。

引用 References

看下面一段C++代码

int a = 1;
int &b = a;
b = 2;

它对应的过程图解如下

C++里,变量可以是引用,它是位置的别名。一旦绑定之后就不能再改变,重新分配也不会改变引用。所以上例b=2不会改变b。

python里,变量是值得名字,所以所有变量都是引用。一个变量不能是另外一个引用/变量的引用。分配会改变引用本身。如下图

可以看到,在C++或者其他语言里,变量是一个存储位置,你会向这个位置写值(例如,整型,对象或者指针);引用是一个存储位置的别名,当你分配给一个非引用的变量时,你会向这个存储位置拷贝一个值,如果你分配给是引用,将会拷贝到其他的某个位置。

注意:你无法改变引用本身,一旦绑定,所有向它的赋值不会改变引用本身,只会修改它引用的内容。

在python里,变量只是个名字。值是在其他某个地方,变量关联到它。多个名字可以引用同一个值. python里将这种绑定到其他值的方式叫做引用,尽管这个跟C++里的引用不太一样。分配一个变量就是简单的将它引用到那个值。存储位置的模型不适用于python,程序员不需要处理值的存储位置。

变量标识符

在Python 里,标识符由字母、数字、下划线组成,区分大小写,但不能以数字开头。

另外,一些变量标识符有特殊的意义:

  • 以下划线_开头的标识符是有特殊意义的。以单下划线开头_foo 的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用from xxx import * 而导入;
  • 以双下划线__开头的__foo 代表类的私有成员;以双下划线开头和结尾的__foo__代表Python 里特殊方法专用的标识,如__init__() 代表类的构造函数。
  • Python保留标识符有特殊的意义,不能用作变量定义
import keyword; keyword.kwlist # 可以列出所有保留关键字
作用域解析 scope resolution LEGB
  • L : Local 函数内分配的一个名字(def或lambda),没有全局声明
  • E : Enclosing function locals 外部嵌套函数的名字空间
  • G : Global 定义在module文件的顶部
  • B : Built-In 内置模块的名字

在python里,函数定义会引入一个新的作用域,当调用嵌套函数和lambda时,会引入更多的作用域。嵌套函数能够访问本地作用域,以及在封闭函数 内的内容。

例子:

内部作用域之外的变量是只可读的,如果尝试去写这个变量的话,只是在内部作用域创建一个新的变量,外部的那个变量保持不变。

对于嵌套函数来说,也是相同的规则

global_var = [1,2]

def func():
    global_var = [4,5]
    nonlocal local1 = 4
    def embedded_func():
        # Won't affect func's local1.
        # Create new local variable
        # (also called local1).
        local1 = 5
        print(local1) # Prints 5
    print(local1)    # Prints 4
    # return embedded_func

如果我就是想在函数作用域内部修改全局变量的绑定,该怎么做呢?通过"global"关键字

global_var = "ab"
def change_global():
    global global_var
    global_var = "bb"

上面函数中,先声明global_var是一个全局变量,然后就可以重新绑定该变量了,不管你的嵌套有多深,它都不会创建一个新的本地变量。

如果想改变外部嵌套函数内的变量可以用nonlocal,nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量。

def func():
    local1 = 4
    def embedded_func():
        nonlocal local1
        local1 += 1
        return local1
    return embedded_func

val = func()
print(val())
print(val())
print(val())

>>> val = func()
>>> print(val())
5
>>> print(val())
6
>>> print(val())
7

声明local1为nonlocal变量后,函数外的变量也被修改了

global和nonlocal的差别

  • 第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。
  • 第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误(见第一)。

练习

已知a = [1, 2, 3]和b = [1, 2, 4],那么id(a[1])==id(b[1])的执行结果 ()

答案:True, python中对于小整数对象有一个小整数对象池,范围在[-5,257)之间。

对于在这个范围内的征数,不会新建对象,直接从小整数对象池中取就行。

a = [1, 2, 3]
b = [1, 2, 4]
id(a[1])==id(b[1])
True