Python 变量申明 python

变量的申明

通常情况下,我们申明一个变量

a=1
b='abc'
c=1000000000000000000000L
d=2.3

上面我们分别申明了值为整数、字符串、长整数、浮点数的四个变量。作为解释性语言,我们可以享受这种带来的便利,想想我们的java吧,有int,float,long当然,上面的长整型,在java中怎么办?

BigDecimal big1 = new BigDecimal("123456789012345678901234567890");
BigDecimal big2 = new BigDecimal("123456789012345678901234567890");
BigDecimal sum = big1.multiply(big2);
System.out.println(sum.toPlainString());

而在python中,就可以很简单的处理

123456789012345678901234567890L*123456789012345678901234567890L

在什么变量的时候需要注意几点

  • 标识符的第一个字符必须是字母表中的字母(大写或小写)或者一个下划线(’_ ‘)。
  • 标识符名称的其他部分可以由字母(大写或小写)、下划线(’_‘)或数字(0-9)组成。
  • 标识符名称是对大小写敏感的。例如,mynamemyName不是一个标识符。注意前者中的小写n和后者中的大写N。
  • 有效 标识符名称的例子有i、__my_name、name_23a1b2_c3
  • 无效 标识符名称的例子有2things、this is spaced outmy-name

再来看看python中的批量命名

a, b, c = (1, 2, 3)

这里申明的a,b,c分别指向了元组[或列表]对应的元素。这叫将序列解包绑定到单独的参数上如:

name,age,address=['robin',10,('aaa',222,'ccc')]
name,age,(city,zcode,road)=['robin',10,('aaa',222,'ccc')]

需要注意变量个数和序列的个数保持一致,否则会抛出ValueError.以下两个申明方式都有问题

a,b,c=[1,2,3,4]
a,b,c=[1,2]

这种解包不仅适用于元组,列表。对strings,files,iterators等都可以

a,b,c,d,e='hello'

有时对序列中的多个元素只需要关注特定的几个元素,其他忽略:

_,age,_=['robin',10,('aaa',222,'ccc')]

考虑序列中多个参数,不想一一对应,而采取批量命名的方式

a,b,*c=[1,2,3,4,5] #c=[3,4,5]
a,*b,c=[1,2,3,4,5] #b=[2,3,4]

变量的作用域

先来看两个例子

if True:
    d = 10
print d #这里输出10

这里先在if块内申明了变量d,在块外面使用,这里正确输出。而这在java中是不能这样处理的,出了if块后申明就失效了

def fun1(a):
    print a
    a = 10
    print a
     
a = 100
fun1(a)
print a


def fun2():
    global b
    print b
    b = 10
    print b
    
b = 100
fun2()
print b

这里包含了两个函数,其中一个变量用了global,在函数内改变后即全局的改变,而未有任何修饰的a则只是局部的改变。

这里有几点需要注意

  • python能够改变变量作用域的代码段是def、class、lamda
  • if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的
  • 变量搜索路径是:本地变量->全局变量

看下面的函数

def fun4():
    global a1
    print a1
    a1 = 100
    print a1
 
a1=1000
fun4()
print a1

在函数体内,第一次print的时候并没有赋值,将会搜索本地变量,即外部定义的a1=1000而赋值后则遵循global左右的范围

传值还是引用

需要明确一点,在python中没有传值一说,所有变量都是传的引用。

def method1():
    a = 1;
    b = a
    b = 2;
    print a,b

这里输出的1, 2而不是2, 2。来看看具体的过程

  • a=1将a的地址指向内存中1的地址,
  • b=a将b的地址指向于a一样的内存引用,
  • b=2将b的地址指向新的内存地址2的引用,但a的地址引用并没有改变

没问题了么?那么我们再来看看下面这个函数

def method2():
    a = [1]
    b = a
    b[0] = 2
    print a[0],b[0]

那么这个将输出什么,既然都这么问了如果再回答1, 2肯定对不起观众了,是的输出是2, 2?!。前面不是说了都是传引用,这是什么情况,且慢且慢

可变参数与不可变参数

前面的问题涉及到python中的可变参数和不可变参数,而在上面的例子中恰好a,b为列表属于可变参数。在python中列表(list)、字典(dictionary)为可变参数,而元组(tuple),字符串,整形为不可变参数。实际上上面可以理解为整个列表指向了一个地址,而b[0]恰恰也操作了这个地址内的元素

再来看看下面的函数

def method3(b = []):
    b.append(0)
    return b

这里将列表b作为参数传给函数,我们在运行函数几次后来看看列表b的值

print method3()
print method3()
print method3()

你回答对了吗?可惜真相面前了无遁形

[0]
[0, 0]
[0, 0, 0]

说好的引用呢,每次我传递进去的参数可都是空[]啊?都是坑啊或者我们这样调用

print method3(b=[])
print method3(b=[])
print method3(b=[])

你又答对了吗?

默认入参

是不是又一次感受到了自己的无力,其实认真观察,你会发现上面的例子入参是这样b = [],这是python函数的默认传值,如果在调用的使用没有传值那么将会使用这个默认值,比如上面的函数可以这样调用method3()而如果函数这样申明

def method3(b):
    b.append(0)
    return b

那么在调用时需要指定的参数

def method3(b):
    b.append(0)
    return b

print method3(b=[])
print method3(b=[])
print method3(b=[])

那么这样输出”正常”了吧?再回到上面的问题,需要了解一个函数dir(),这可以理解为我们java的内省,通过他我们可以观察到具体的申明,我们来看看上面这个method3()做了什么

print dir(method3)

这里输出了一个列表,包含的各种属性

['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

我们需要关注的是__defaults__,让我们更详细看下调用的过程

method3()
print method3.__defaults__
method3()
print method3.__defaults__
method3()
print method3.__defaults__

而这就是__defaults__

([0],)
([0, 0],)
([0, 0, 0],)

为了对比,我们来看看另外一个函数

def method4(c, b = [], a = 1): 
    b.append(0)
    a+=1
    return b,a
  
method4(c=1)
print method4.__defaults__  
method4(c=2)
print method4.__defaults__  
method4(c=3)
print method4.__defaults__  

我们可以看看其中__defaults__的输出,这样我们可以体会可变和不可变的区别、以及默认参数与非默认参数的区别了。其实python在处理函数默认传参时如果申明了默认参数如b=[]那么会体现在__defaults__

以上并不是特意选取了一些奇淫技巧,而是需要深刻理解的,因为上面的场景都是很常见的



blog comments powered by Disqus