系列解包赋值
a,b=1,2
#a=1
#b=2
a,b,c=1,2,3
#a=1,b=2,c=3
实现四舍五入
a=1.6
print(int(a+0.5))
print(round(a))
整数的缓存问题
python会将[-5,256]内的整数对象进行缓存,也就是说这个范围内的对象只会指向一个内存块
在pycharm里 会做一些优化把范围扩展到[-5,无穷]
a=10
b=10
print(id(a))
print(id(b))
a=-6
b=-5
print(id(a))
print(id(b))
'''
输出
140713838921824
140713838921824
2049070469808
140713838921344
'''
字符串
不支持通过索引的方式修改。
但是可以通过replace方法修改
replace(要替换的字符,要替换成的字符)
原理就是replace方法相当于重新创建了一个字符串,然后完成替换,并使原标识符引用这个新的字符串
使用三个双引号可以创建多行字符串,如
str = """ 这是一个
字
符
串
"""
如果像输出特殊字符,需要加\
print("\"")
print("\'")
print("\\")
输出
"
'
\
如果双引号前面有r说明后面字符串里的\只作为字符
print(r"abc\nwe")
切片
[起始点:终止点:步长]
举个例子 a='0123456'
则有 a[:]='0123456'
a[3:]='3456'
a[-2:]='56'
a[::2]='0246'
a[::-1]='6543210'
split将字符串按照分隔符分割,并将其存入列表
split(sep)sep为分隔符,默认为换行 空格 制表符
a='a b c d e f'
b=a.split()
print(b)
'''
输出
['a', 'b', 'c', 'd', 'e', 'f']
'''
join方法可以将列表里的字符组合起来
'分隔符'.join(要合并的列表)
a='a b c d e f'
b=a.split()
print(b)
c=''.join(b)
print(c)
'''
输出
['a', 'b', 'c', 'd', 'e', 'f']
abcdef
'''
+也可以连接字符串 但是如果要连接多个字符串,join效率更高
import time
time1= time.time()
s=''
for i in range(1000000):
s+='123'
time2=time.time()
print(time2-time1)
time1= time.time()
s=[]
for i in range(1000000):
s.append('123')
''.join(s)
time2=time.time()
print(time2-time1)
测试将'123'连接1000000次所需时间
+需要0.6163523197174072s
join需要0.11173439025878906s
显然是join更胜一筹
字符串驻留机制
仅包含符合标识符规则字符的标识符(仅包含下划线 字母 数字)python对于相同的字符串仅创建一个,若含有其他字符 如'#'就会创建多个
常用查找方法
len()查看字符串长度
startswith()是否以指定字符串开头
endswith()是否以指定字符串结尾
find()返回指定字符第一次出现的位置
rfind()返回指定字符最后一次出现的位置
count()返回指定字符串出现了几次
strip()去除首尾指定信息,默认是换行 空格
lstrip()去除左边指定信息,默认是换行 空格
rstrip()去除右边指定信息, 默认是换行 空格
title()产生新的字符串,每个单词首字母大写
upper()产生新的字符串,所有字符转大写
lower()产生新的字符串,所有字符转小写
swapcase()产生新的字符串,所有字母大小写转换
格式排版
a='SXT'
a.center(10,'*')
"***SXT****"
a.center(10)
" SXT "
a.ljust(10,'*')
"SXT*******"
a.rjust(10,'*')
"*******SXT"
可变字符串
包含模块 io
基本语法如下
import io
s = '123456'
sio = io.StringIO(s)
print(sio.getvalue())#输出字符串
sio.seek(5)#移动到索引为5的位置
sio.write('7')#用7替换索引为5的字符
print(sio.getvalue())
'''
123456
123457
'''
列表
语法:
变量名 = [a,b,c,d] a,b,c,d可代表 int double string char 等数据类型
列表的创建
1.基本语法创建
a=[1,2,3,4]
2.list方法创建
a=list(range(1,10))
注意list()可以接受元组 字符串 其他序列类型
3.推导式生成列表
a=[x*2 for x in range(5)]
a=[0,2,4,8]
a=[x*2 for x in range(100) if x%9==0]
a=[0,18,36,54,72,90,108,126,144,162,180,198]
访问列表:
利用下标方式访问,类似于数组,但是可以用负数下表,-1代表倒数第一个数据
修改某个元素:
直接通过下标,来修改值。
在列表尾部插入元素:在列表尾加元素,利用append()方法
例如 a=[1,2,3]
a.append(4)
就是把4插入到列表尾部
将两个列表相加 得到一个合并的列表,这个过程中创建了新的列表对象,不推荐这样使用。
可以使用extend()在原有对象基础上拓展,将b里的元素逐一加入到列表中。
extend和append的一个区别
a = [1,2]
b = [3,4]
a.append(b)
#a = [1,2,[3,4]]
a.extend(b)
#a = [1,2,[3,4],3,4]
在列表中插入元素:
利用insert(x(下标),数据)方法。将数据插入到x处,原本下标为x的元素往后挪
从列表中删除元素:
del a[x] 删除a列表中下标尾x的元素
del a 删除a列表
pop()方法,取走列表尾部元素
在括号里加上索引可以取走任意位置的元素如 pop(0)取走索引为0的元素
利用remove()方法删除给定值的元素,如a.remove(1)删除a列表里1
组织列表:
sort()方法对列表进行永久性排序
可以按字母顺序相反顺序排列 只需要使用 sort(revese =True)
sorted()对列表进行临时排序,即不会改变列表顺序,可以用在print里
reserve()反转列表
len()方法求列表长度
遍历整个列表
chars=[1,2,3,4]
for a in chars:
print(a)
含义是从chars中取元素赋给a,并输出a
如果想用不定义变量,进行类加,来输出索引值和列表元素,需要使用enumerate
a = [1,2,3,4]
for i,x in enumerate(a):
print(i,x)
创建数值列表
使用range()函数
for value in range(1,5)
print(value)
输出从1到4.
使用range()创建数字列表
numbers = list(range(1,6))
则numbers = [1,2,3,4,5]
range还可以指定步长
如for a in range(1,10,2)
print(a)
则将输出 2 4 6 8
min max sum 函数均可运用在列表里,使用时候是 min(列表名)
列表解析
列表解析可以让我们一行代码就可以创建一个特定的列表
如 a = [value**2 for value in range(1,11)]
于是 a=[1,4,9,16,25,36,49,64,81,100]
筛选大于0的元素
b=[i for i in a if i>0]
使用列表的一部分
切片
通过切片我们可以输出第几个到第几个之间的元素
如 a=[1,2,3,4]
print(a[0,2])
输出[1,2,3]
若不指定第起始索引则从表头开始,不指定终止索引则会一直输出到表尾
如果你想输出最后2个元素
可以这样 print(a[-2:])
切片也可以遍历
例如 for i in a[-2;]
print(i)
复制列表
a=[1,2,3,4,5]
b=a
这样是行不通的,只是b指向了a列表。
正确的方法应该是使用切片
b=a[:]把从头到尾的a的元素赋给b
多维列表
a = [[1,2,3],[4,5,6],[7,8,9]]
a[0][0] = 1
元组
元组跟列表类似也可以存储任意基本数据类型
但是元组用()包含元素,而且一旦初始化,元素不能被修改。但是可以包含可变对象, 这个可变对象是可以改变的,如list
可以连接
创建元组
1.语法创建
a = (1,2,3)
b = 1,2,3
c = (1,)#如果不加逗号程序会默认这个1为一个数字
2.通过tuple()函数创建
注意tuple()可以接受列表 字符串 其他序列类型
zip
zip(列表1,列表2,......)将多个列表对应位置元素组合成元组,并返回这个zip对象
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
d=zip(a,b,c)
print(d)
#输出
<zip object at 0x000001F0120C2D40>
d=list(zip(a,b,c))
print(d)
#输出
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
生成器推导式创建元组
与列表推导式雷系,只是生成器推导式使用小括号,列表推导式直接生成列表对象,生成器推导式生成的不是元组也不是列表,而是一个生成器对象,我们可以使用tuple()函数将其转换为元组
s = (x*2 for x in range(5))
print(s)
#输出
#<generator object <genexpr> at 0x000001E3EA7DFE40>
a = tuple(s)
print(a)
#输出
#(0, 2, 4, 6, 8)
b = tuple(s)
print(b)
#输出
#()
为什么会生成()呢?
是因为s是生成器对象,生成器对象是通过迭代的方式遍历,所以当使用过一次tuple()也就是说明已经遍历过一次了,这是迭代器会指向最后一个元素,所以如果再调用一次tuple()会生成一个空元组
这说明 需要重新访问生成器对象中的元素,都需要重新创建该生成器对象
元组总结
1.元组的核心特点是:不可变序列
2.元组的访问和处理速度比列表快
3.与整数和字符串一样,元组可以作为字典的键,列表则不可以,但是都可以作为值
集合
使用set()函数创建集合
add(x) 方法将x添加到集合里
remove()方法删除指定元素
clear()方法清空集合
设a,b为两个集合
a-b为差集
a&b为交集
a|b为并集
if语句
python里的if条件不需要用圆括号,而且不需要用{},只需要加冒号,没有else if 。
如, if a==b:
elif(a>b):
else:
使用 and 来替代&& or来替代||
判断列表a是否为空
if a:
判断b中元素是否在a中
a=[1,2,3,4]
b=[1,2]
a=[1,2,3,4]
b=[1,2]
for i in b:
if i in a:
print("yes")
else:
print("no")
字典
字典是一系列键-值对,如 a={'a':1,'b':2}
键必须使用不可变对象
创建字典
1.基本语法
a = {'a':1,'b':2}
b = dict(a='1',b='2')
c = dict([('a',1),('b',2)])
d = {}
print(a,b,c,d,sep='\n')
'''
输出
{'a': 1, 'b': 2}
{'a': '1', 'b': '2'}
{'a': 1, 'b': 2}
{}
'''
2.通过zip创建字典
k = {'a','b','c'}
v = {1,2,3}
d = dict(zip(k,v))
print(d)
'''
输出
k = {'a','b','c'}
v = {1,2,3}
d = dict(zip(k,v))
print(d)
'''
3.通过fromkeys创建值为空的字典
a = dict.fromkeys(['name','age','job'])
print(a)
'''
输出
{'name': None, 'age': None, 'job': None}
'''
访问字典中的值
如 print(a['a'])即可输出1
向空字典中添加值
a={}
a[1]='a'
a[2]='b'
则a=[1:'a',2:'b']
get()方法获得值,优点是指定键不存在的时候返回None,也可以给其定义默认返回值
a = {'a':'','b':2}
print(a.get('dwa'))
print(a.get('dwa','不存在该键'))
'''
输出
None
不存在该键
'''
修改字典中的值
通过键索引可以直接修改
删除键值对
使用del+ 字典名[键]来删除对应的键值对
清空字典
使用clear方法来清空字典
字典元素的弹出
字典名.pop("键名")
遍历字典中的键值对
b={'a':1,'b':2,'c':3}
for key in b:
print(key)
是输出b里的键
b={'a':1,'b':2,'c':3}
for key in b.items():
print(key)
输出为
('a', 1)
('b', 2)
('c', 3)
输出每个元素
b={'a':1,'b':2,'c':3}
for key,value in b.items():
print(key,value)
输出结果为
a 1
b 2
c 3
遍历字典中所有的键
b={'a':1,'b':2,'c':3}
for key in b.keys():
print(key)
输出结果为
a
b
c
遍历字典中的所有的值
b={'a':1,'b':2,'c':3}
for key in b.values():
print(key)
可以使用set来提取字典中不一样的项
b={'a':1,'b':2,'c':3,'d':3}
for key in set(b.values()):
print(key)
update()将新字典中所有键值对全部添加到旧字典对象,如果key重复,直接覆盖
a = {'a':'','b':2}
b = {'a':1,'c':3}
b.update(a)
print(b)
'''
输出
{'a': '', 'c': 3, 'b': 2}
'''
可以看出a中的'a':''将b中的'a':1覆盖了,就是说只要键相同就覆盖掉键值对
列表里套字典
如
a={'a':1,'b':2,'c':3}
b={'d':4,'e':5}
c=[a,b]
序列解包用于字典
b = {'a':1,'b':2,'c':3}
x,y,z = b
print(x,y,z)
x,y,z = b.values()
print(x,y,z)
x,y,z = b.items()
print(x,y,z)
'''
输出
a b c
1 2 3
('a', 1) ('b', 2) ('c', 3)
'''
字典存储列表
a={'a':[1,2,3],'b':[4,5,6]}
字典里存字典
a={'a':{'b':1},'b':{'c':2}}
字典的核心底层原理(重要)
字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做 bucket。每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用。所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。下面通过存储与获取数据的过程介绍字典的底层原理。
例如,我们将‘name’ = ‘张三’ 这个键值对存储到字典map中,假设数组长度为8,可以用3位二进制表示。
>>> map = {}
>>> map
{}
>>> map['name'] = '张三'
>>> bin(hash('name'))
'0b101011100000110111101000101010100010011010110010100101001000110'
2、用散列值的最右边 3 位数字作为偏移量,即“110”,十进制是数字 6。我们查看偏移量 6,对应的 bucket 是否为空。如果为空,则将键值对放进去。如果不为空,则依次取右移 3 位作为偏移量,即“000”,十进制是数字0,循环此过程,直到找到为空的 bucket 将键值对放进去。python 会根据散列表的拥挤程度扩容。“扩容”指的是:创造更大的数组,将原有内容拷贝到新数组中。接近 2/3 时,数组就会扩容。扩容后,偏移量的数字个数增加,如数组长度扩容到16时,可以用最右边4位数字作为偏移量。
获取数据的过程
1、计算name的散列值
2、用最右边 3 位数字作为偏移量,即“110”,十进制是数字6。查看偏移量 6,对应的 bucket 是否为空。如果为空,则返回 None。如果不为空,则将这个 bucket 的键对象计算对应散列值,和我们的散列值进行比较,如果相等,则将对应“值对象”返回;如果不相等,则再依次取其他几位数字,重新计算偏移量。循环此过程。
总结:
1.键必须可散列,如数字、元组、字符串;自定义对象需要满足支持hash、支持通过__eq__()方法检测相等性、若 a==b 为真,则 hash(a)==hash(b)也为真。
总结
字典在内存中开销巨大,典型的空间换时间;
键查询速度很快;
往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。
input函数
message=input()
print(message)
键盘输入,并将其输出,注意input会将输入的数据转化为字符串
如果输入的是数字,而且想得到数字,我们可以使用eval去除掉一对‘’来实现这个操作,也可以直接用int强制类型转换
message=input()
print(message)
print(int(message))
print(eval(message))
输入
123
输出
123
123
123
也可以将一部分输出的内容放在input里,使程序在你输入数据前,打印这部分内容
message=input("hello")
print(message)
while循环
举个简单的例子,打印3次hello world
count=1
while(count<=3):
print("hello world")
count+=1
注意 这里不能像C一样用++符号,因为python里没有自增自减运算符,python里的--是负负得正的意思,就是说假如a=1,--a,a还是等于1,在在这个过程中,a首先变成-1,然后又变成1
让用户决定什么时候退出
举个例子 当输入等于123的时候推出
message=''
while message !='123':
message=input()
print(message)
删除列表中的特定值
可以使用remove方法来删除,但是如果列表中出现多次,就需要用到循环了。
a=[1,1,2,2,1,3,6]
while 1 in a:
a.remove(1)
print(a)
函数
定义函数
def fun()://def关键字 定义一个函数,括号必不可少,括号后加冒号
print("hello")
fun()//执行函数
函数的传递
python中参数的传递都是引用传递
具体操作分为两类
1.可变对象,对可变对象的进行写操作,会直接作用于原对象本身
可变对象具体有:字典、列表、集合和自定义的对象
2.不可变对象进行写操作,会产生一个新的对象空间,并用新的值填充这块空间(起到按值传递的作用,但却不是值传递)
不可变对象有:字符串,数字,元组,function等
传不可变对象为参数的例子如下
def fun (n):
print(id(n))
n+=1000
print(id(n))
fun(10)
'''
输出结果
140706450131040
2596441953104
'''
很明显我们发现两个n的地址不一样了
浅拷贝和深拷贝
浅拷贝就是仅仅创建了一个拷贝了对象的引用。
深拷贝就是连子对象的内存一起全部拷贝一份,对子对象的修改不会改变原对象
浅拷贝内置函数copy
深拷贝内置函数deepcopy
import copy
a = [1,2,[5,6]]
print("浅拷贝======")
b = copy.copy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)#将7加入到第列表中第二个元素里,也就是[5,6]这个列表里
print("a:",a)
print("b:",b)
'''
输出结果为
浅拷贝======
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6]]
a: [1, 2, [5, 6, 7]]
b: [1, 2, [5, 6, 7], 30]
'''
我们可以看到,我们在改变b中那些属于a的元素的时候,a也会跟着改变,说明b引用了a的元素。
用C的角度来说,就相当于列表a其实就是一个指针数组,b指向了一个新的一个指针数组,然后把a的指针数组的值赋给b,b对指针数组里的指针指向的地方进行操作,就会影响到a,但是如果b对指针数组进行操作就不会影响到a
如果是深层拷贝
import copy
a = [1,2,[5,6]]
print("深拷贝======")
b = copy.deepcopy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)
print("a:",a)
print("b:",b)
'''
深拷贝======
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6]]
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6, 7], 30]
'''
这时候b与a再内存中的指向就变成这样了
用C来解释就是 b中指向了一个新的指针数组 ,其中的指针指向的地方跟a不同,但是指针指向地方存的值是相同的,所以修改b指向的这个指针数组里指针指向的位置也不会改变a。
传递不可变对象的时候用的是浅拷贝
传递一个元组时,当你试图修改元组中的元素时,他会报错
a = (1,2,[5,6])
def fun(m):
m[1]=2
fun(a)
'''
Traceback (most recent call last):
File "C:/Users/dell/PycharmProjects/untitled/main.py", line 4, in <module>
fun(a)
File "C:/Users/dell/PycharmProjects/untitled/main.py", line 3, in fun
m[1]=2
TypeError: 'tuple' object does not support item assignment
'''
但是可以修改[5,6],比如说在其中添加一个元素
a = (1,2,[5,6])
def fun(m):
print(id(m))
m[2].append(3)
print(id(m))
fun(a)
print(a)
'''
3013210054784
3013210054784
(1, 2, [5, 6, 3])
'''
可以发现,外面的a也跟着变了,不可变对象就是说不能通过引用去修改它的值,但是a[2]是引用了[5,6]这个列表的首地址,不可变对象只规定了不能修改它引用的值,但是没说不可以修改它引用的值 引用的东西(前提是这个东西不是元组,因为元组不可以修改),就是说你可以修改这个列表里的元素。
位置实参和关键字实参
顺序实参就是形参和实参要一一对应
关键字实参是调用函数时在括号里让对应的形参等于对应的实参
如
def fun(a,b):
print(a,b)
fun(b=1,a=2)
默认值
默认值就是给函数的形参提供了默认值,这样如果没有给函数提供形参,它会使用默认值来执行函数操作
def fun(a,b=0):
print(a,b)
fun(a=1)
使用默认值的时候,要先在函数参数表里列出无默认参数的形参,在列出有默认参数的形参,只有这样python才能正确的处理形参和实参
函数返回值
def fun(a,b=0):
print(a,b)
return 'ok'
f=fun(a=1)
print(f)
函数返回值不需要指明返回类型
可以将列表 字典传给函数,函数对其进行的修改是永久性的。
如果不想函数修改列表本身,可以创建一个副本过去,只需要将参数改为 列表名[:]即创建一个一模一样的列表传过去
python的函数可以返回多个返回值
def fun(a,b):
s = a//b
y = a%b
return s,y
shang,yv = fun(a,b)
函数可以接收任意数量的参数
def fun(*a):
print(a)
fun("hello")
fun("hello","world","wodetian")
输出结果为
('hello',)
('hello', 'world', 'wodetian')
这说明函数建立了一个元组来存储这些参数,于是可以用循环来打印这些参数
def fun(*a):
for i in a:
print(i)
fun("hello")
fun("hello","world","wodetian")
输出为
hello
hello
world
wodetian
结合位置参数使用任意数量实参
def fun(b,*a):
print(b)
for i in a:
print(i)
fun("hello")
函数会将第一个参数存在b里,剩余的存在a元组中
函数传递键值对
def fun(**x):
p={}
for k,v in x.items():
p[k]=v
return p
x=fun(a='1',b='2',c='3')
print(x)
输出为
{'a': '1', 'b': '2', 'c': '3'}
这两种参数即*x和**x被称为可变参数
强制命名参数
如果想在可变参数后可以添加参数,前提是参数是命名参数
def fun(*x,a):#直接这样写会报错
print(x)
x=fun(1,2,3,4)
print(x)
正确做法应该是这样
def fun(*x,a):
print(x)
fun(1,2,3,a=4)
将函数存储在模块里
将函数放在一个单独的.py文件里制作成模块,在其他py文件中可以通过import 模块名来载入模块,于是就可以使用该模块里的函数了,具体的原理就是,载入模块后,相当于程序在幕后将模块里的函数全部复制到使用import 的程序中。
如
#dwa.py
def fun(x):
return x
#make_fun.py
import dwa
x=dwa.fun(1)
print(x)
注意使用某个模块的函数时候,要在函数前注明所属模块并加上.作为分隔
还可以导入特定的函数,而不是将模块整体导入
from dwa import fun
x=fun(1)
print(x)
这样可以直接使用特定函数,不需要加.和模块名了。
可以使用逗号来一次性导入同一个模块的多个函数
如 form dwa import fun1,fun2,fun3
可以给导入的函数起别名
form dwa import fun as p
可以给导入的模块起别名
import dwa as makefun
可以导入模块中的所有函数(不需要使用.和模块名)
import dwa *
一般不要使用这种表示方法,可能会出现函数重名现象。
关于局部变量和全局变量效率的测试
局部变量的查询和访问速度比全局变量快,循环次数多的地方,通过将全局变量转换为局部变量可以提高运行速度
如我们在进行10^7次开方操作后,可以看到使用全局变量和局部变量的差距
import time
import math
def test1():
start = time.time()
for i in range(10000000):
math.sqrt(30)
end=time.time()
print("test1耗时为{0}".format(end-start))
def test2():
b = math.sqrt
start = time.time()
for i in range(10000000):
b(30)
end=time.time()
print("test2耗时为{0}".format(end-start))
test1()
test2()
'''
输出为
test1耗时为1.5555238723754883
test2耗时为1.099611520767212
'''
内部函数
在函数中定义一个函数,并使用它,这样做的话,在函数外无法使用在函数内定义的那个函数
举个例子
def fun():
def funny():
print('1')
funny()
funny()#这个会报错
fun()
具体的作用如下
1.数据隐藏
2.嵌套函数,避免在函数内部使用重复代码
举个例子,输出中文名字和英文名字的函数如下
def printChineseName(familyname,name):
print("{0} {1}".format(familyname,name))
def printEnglishName(familyname,name):
print("{0} {1}".format(name,familyname))
如何将他俩和一块呢,可以使用嵌套函数
def printName(isChineseName,familyname,name)
def flag()
print("{0} {1}".format(name, familyname))
if isChineseName:
flag(familyname,name)
else:
flag(name,familyname)
这样可以合在一起了,当然不用嵌套函数也可以实现,但是这样在函数里面,更整洁一点,因为这个flag可能只是printName使用,在外面和其他函数在一起,容易混淆。
nonlocal和global关键字
global声明是全局变量
nonlocal声明是来自外层的变量(来自外层函数)
a = 1
def fun():
b = 2;
def happy():
nonlocal b//必须要声明才能进行修改
print(b)
b =3
global a
a = 2
return b
3.闭包
LEGB规则
python在查找“名称”时,是按照LEGB规则查找的
Local->Enclosed->Global->Built in
Local 是函数或类的方法内部
Enclosed 指的是嵌套函数
Global 全局变量
Built in python自己的特殊名称
举个例子
#1.str = '123'
def fun():
#2.str = '456'
def funny():
#3.str = '789'
print(str)
如果1 2 3 均不注释 则输出789
如果将3注释 则输出456
如果将2也注释掉 则输出123
如果将1也注释掉 则输出<class str> 即python自带关键字