变量:数字 和 字符
(动态语言程序,变量只是为他们赋值而使用,不需要\使用任何声明或类型定义)
fotmat() 用于构造字符串
age=20
name='Swaroop'
print('{0} was {1} years old when he wrote his book'.format(name,age))
print(name + ' is ' + str(age) + ' years old ')
Swaroop was 20 years old when he wrote his book
Swaroop is 20 years old
动态VS静态
在Python中,等号=
是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:
a = 123 # a是整数
print a
a = 'imooc' # a变为字符串
print a
这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。
静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下(// 表示注释):
int a = 123; // a是整数类型变量
a = "mooc"; // 错误:不能把字符串赋给整型变量
和静态语言相比,动态语言更灵活,就是这个原因
变量无类型,对象有类型
Python中的变量是没有类型的,但Python其实是区分类型的:Python的所有变量其实都是指向内存中的对象的一个指针,都是值的引用,而其类型是跟着对象走的。总结来说:在Python中,类型是属于对象的,而不是变量, 变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针
可变对象 & 不可变对象
在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。
变量在计算机内存中的表示也非常重要。当我们写:a = 'ABC'
时,Python解释器干了两件事情:
1. 在内存中创建了一个'ABC'
的字符串;
2. 在内存中创建了一个名为a
的变量,并把它指向'ABC'
。
也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据,例如下面的代码:
a = 'ABC'
b = a
a = 'XYZ'
print b
最后一行打印出变量b的内容到底是'ABC'呢还是'XYZ'?如果从数学意义上理解,就会错误地得出b和a相同,也应该是'XYZ',但实际上b的值是'ABC',让我们一行一行地执行代码,就可以看到到底发生了什么事:
执行a = 'ABC'
,解释器创建了字符串 'ABC'和变量 a,并把a指向 'ABC':
执行b = a
,解释器创建了变量 b,并把b指向 a 指向的字符串'ABC':
执行a = 'XYZ'
,解释器创建了字符串'XYZ',并把a的指向改为'XYZ',但b并没有更改:
所以,最后打印变量b
的结果自然是'ABC'
了
a=[1,2,3,4]
b=a
a=[4,3,2,1]
print(a,b)
[4, 3, 2, 1] [1, 2, 3, 4]
不可变对象,该对象所指向的内存中的值不能被改变
可变对象,该对象所指向的内存中的值可以被改变
(区别是该对象指向的内存中的值改变后,是产生新的对象,还是在内存中的保持位置不变)
不可变对象的例子
先说明一点is
就是判断两个对象的id是否相同, 而 ==
判断的则是内容是否相同。
a=2
b=2
c=a+1
print(id(a),id(b),id(c),id(2)) #a,b相等,c不等a,b
print(a is b) #true
1817272512 1817272512 1817272544 1817272512
True
由于是不可变对象,变量对应内存的值不允许被改变。当变量要改变时,实际上是把原来的值复制一份后再改变,开辟一个新的地址,astr再指向这个新的地址(所以前后astr的id不一样),原来astr对应的值因为不再有对象指向它,就会被垃圾回收。这对于int和float类型也是一样的。
add=(1,2,3)
aee=(1,2,3)
print(id(add),id(aee),id((1,2,3))) #id不等
add+=()
print(add,id(add)) #id改变,值虽不变
1542843460200 1542825189520 1542842864552
(1, 2, 3) 1542842864624
如果是可变对象add = aee
,它们指向同一地址(id相同)是肯定的。但不是同一对象的不同引用,因为如果是的话,aee的改变会引起add的改变,再tuple中并不是这样。所以tuple是不可变对象,但又和str和数值类型稍微有点区别。平常说的tuple不可变更多时候是指里面存放的值不能被改变(有些特殊情况,如tuple里面存放了list,可改变list里的元素。但实际上这个tuple并没有被改变)。
可变对象的例子
lis = [1, 2, 3]
lis2 = [1, 2, 3]
# 虽然它们的内容一样,但是它们指向的是不同的内存地址
print(lis is lis2) #false
print(id(lis), id(lis2), id([1, 2, 3])) # 三个id都不同
alist = [1, 2, 3]
# alist实际上是对对象的引用,blist = alist即引用的传递,现在两个引用都指向了同一个对象(地址)
blist = alist
print(id(alist), id(blist)) # id一样
print(alist is blist) #true
# 所以其中一个变化,会影响到另外一个
blist.append(4)
print(alist) # 改变blist, alist也变成了[1 ,2 ,3 4]
print(id(alist), id(blist)) # id一样,和上面值没有改变时候的id也一样
1542843444808 1542843444808
True
[1, 2, 3, 4]
1542843444808 1542843444808
blist = alist
这一句。alist
实际上是对对象的引用,blist = alist
即引用的传递,现在两个引用都指向了同一个对象(地址)。所以其中一个变化,会影响到另外一个。(可变对象为引用传递,不可变对象为值传递)
但是如果是拷贝,就仅仅是将内容拷贝过去,传递的并是不引用。这在想使用列表的值又不想修改原列表的时候特别有用,一般使用copy()函数
blist = alist[:] # or alist.copy()
print(alist is blist) # False
blist.append(4)
print(alist) # 还是[1,2 ,3]没有变化
False
[1, 2, 3, 4]
作为函数参数
作为函数参数,也是一样的,可变类型传递的是引用,不可变类型传递的是内容
test_list1= [1, 2, 3, 4]
test_list2=test_list1.copy()
test_list3=test_list2[:]
test_str = 'HAHA'
def change(alist):
alist.append(5)
def not_change(astr):
astr.lower()
print(id(test_list1),id(test_list2),id(test_list3),id(test_str))
change(test_list1)
change(test_list3)
not_change(test_str)
print(test_list2) #不变,如果不想改变原来列表的值,参数可以传入列变的拷贝。alsit[:]
print(test_list3) #改变
print(test_list1) # 改变了原来的值
print(test_str) # 没有变
print(id(test_list1),id(test_list2),id(test_list3),id(test_str))
2879171545608 2879171545672 2879171543752 2879171172312
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
HAHA
2879171545608 2879171545672 2879171543752 2879171172312
有趣的例子
a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
print(a1 is a2)
a2 = a2 + [4] # 这个等式中,右边的a2还是和a1的id一样的,一旦赋值成功,a2就指向新的对象
print(id(a1), id(a2),a1 is a2) # 不等,a2的id变化了
print(a1,id(a1)) # [1, 2, 3]没有变
a2 = a2 + [4] # 这个等式中,右边的a2还是和a1的id一样的,一旦赋值成功,a2就指向新的对象
print(id(a1), id(a2),a1 is a2) # 不等,a2的id变化了
print(a1,id(a1)) # [1, 2, 3]没有变
2879171590792 2879171590792
True
2879171590792 2879171590920 False
[1, 2, 3] 2879171590792
在看看
a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2),a1 is a2)
a2 += [4] # 相当于调用了a2.extend([4]),原地改变并没有新的对象产生
print(id(1), id(a2),a1 is a2) # 相等,a2的id没有变化
print(a1)
2879171545928 2879171545928 True
1919377568 2879171545928 True
[1, 2, 3, 4]
不同的地方在于a2 += [4]
,这句相当于调用了a2.extend([4])
相当于原地改变,并没有新的对象产生
解释:
不可变对象 “+=,a+=1","=+,a=a+1" 就是使用_add_()方法
_add_() 会返回一个新的对象,原对象不修改
可变对象(多有一种魔法方法) ”+=,a+=[1]" 就是使用_iadd_()方法 直接在原对象上修改
_iadd_()方法 直接在原对象上修改
”=+,a=a+[1] 就是使用_add_()方法
_add_()方法 会返回一个新的对象,原对象不修改
转义序列
#转义序列
print('what\'s your name' )
#换行
print('first line \n second line')
#行尾的单个反斜线指示字符串在下一行中继续, 但不添加换行符
print('first line.\
second line')
#原始字符串,如果需要指定一些不处理诸如转义序列之类的特殊处理的字符串
#则需要通过前缀或字符串指定raw字符串。例如:rR
print(r'newlines are indicated by \n v')
what's your name
first line
second line
first line. second line
newlines are indicated by \n v