7. Python的内置函数

閱讀時間約 52 分鐘
raw-image

Hi,大家好。我是茶桁。

讲完了基础函数和高阶函数之后,我们这一节来研究下Python的内置函数,看看Python在安装完毕之后的解释器里,到底都预先给我们提供好了哪些可用的函数。

本节内容着重介绍一些常用函数,并且会做一些应用上的示例。当然,对于Python的内置函数,我们还可以查询官方文档,我这节参照的为3.10版本文档

range()函数

这几节课中,我们频繁使用并且着重介绍过这个函数,那我们就从它开始介绍吧。

一般我们需要遍历一个数值序列的时候,range()函数就会派上用场,它生成算数级数。

'''
range() 函数
功能: 能够生成一个置顶的数值序列
参数:
start: 开始的值,默认为0
stop: 结束的值
[, step]: 可选,步进值, 默认为1
返回值: 可迭代的对象,数字序列
'''
range(start, stop, [, step])

让我们来看一下:

res = range(10)
print(res, type(res))

-----------------------------
range(0, 10) <class 'range'>

可以看到这其实就是一个range的类,其实在我们Python中,任何数据都是一个对象而已。

来看案例:

# range函数的使用方式
res = range(11)
print(list(res))

-----------------------------
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

当我们的range内只写一个参数时,这个参数就是stop值,也就是从start的默认值0开始到输入的参数值(stop)之前为止,比如这段代码中,stop会结束到11之前,也就是10

我们在这段代码中,将range的内容转化成一个list并打印了出来。当然,我们也可以使用循环,依次去除range内的内容:

for i in res:
print(i, end=" ")

-----------------------------
0 1 2 3 4 5 6 7 8 9 10

记得上节课我们提到过,range()是不支持next()函数的,不过如果我们将其转成迭代器,就可以使用next()函数调用:

res = iter(range(11))
print(next(res))
print(next(res))
print(list(res))

-----------------------------
0
1
[2, 3, 4, 5, 6, 7, 8, 9, 10]

可以看到,使用iter转成迭代器之后,可以正常使用next()函数,并且我们再次查看res的内容,0,1已经被拿走,只将剩余内容转化为list打印了出来。

当我们在range中添加两个参数的时候,start就是第一个参数,第二个参数就是stop值。

# 添加两个参数
for i in range(5, 10):
print(i, end=" ")

-----------------------------
5 6 7 8 9

当我们输入三个参数的时候,第一个参数为start, 第二个参数为stop, 第三个参数就是[, step], 比如:

# 添加三个参数
for i in range(1, 10, 3):
print(i, end=" ")

-----------------------------
1 4 7

这段代码的含义就是从1开始, 以3为步进来提取数字,并打印出来,一直到10之前的数字为止。

如果不太理解步进值的可以执行数一遍就理解了,比如我们从1开始顺序往后数3个数,那就是2、3、4,数到了4, 再继续往后数3个数,就是5、6、7,数到了7。再继续往后就是8、9、10。但是,我们代码中的stop值为10,所以到9就结束了,也就是说,我们这段代码就只取出了1, 4, 7三个值。

三种参数值的情况我们都了解之后我们可以思考下,难道我们只能选择顺序取值吗?其实不然,我们还可以倒叙取值,聪明的小伙伴可能想到了,调换一下startstop值不就可以了嘛?我们从10开始取值,取到0为止:

for i in range(10, 0):
print(i, end=" ")

执行一下,哎,似乎什么都没打印出来。这又是为什么呢?是不是出BUG了?

其实,什么都没打印出来才是正确的,这是因为,虽然我们给了开始和结束值,但是我们遗忘了一个重要的参数,那就是步进值step,这个值默认可是1,从10开始+1来计数,无论如何也算不到0。所以,我们将步进值改成负数,也就是倒着数了:

for i in range(10, 0, -1):
print(i, end=" ")

-----------------------------
10 9 8 7 6 5 4 3 2 1

至此,我们可以得到结论,是否倒叙取值除了开始和结束值,更重要的是看step是正数还是负数。

res = range(-10,-20,-1) 
# [-10, -11, -12, -13, -14, -15, -16, -17, -18, -19]
res = range(-20,-10)
# [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11]
res = range(-10,10)
# [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

zip()函数

zip() 函数可以接受多个可迭代的对象,然后把每个可迭代对象中的第i隔元素组合在一起,形成一个新的迭代器。

'''
参数: *iterables, 任意个的可迭代对象
返回值: 返回一个元组的迭代器
'''
zip(*iterables)

让我们来直接看示例:

n1 = '1234'
n2 = ['a', 'b', 'c']
n3 = ['A', 'B', 'C', 'D']
# 调用zip函数,合成新的元组迭代器
res = zip(n1, n2, n3)
print(list(res))

-----------------------------
[('1', 'a', 'A'), ('2', 'b', 'B'), ('3', 'c', 'C')]

我知道你们看到这个执行结果会有很多疑问,先别着急,我们先看一下它是否包含迭代器的特性:

for i in res:
print(i)

当你执行这段代码的时候就会发现,似乎什么都没发生。

那到底是怎么回事?我们不用for,让我们再转换一次list之后看看里边有什么:

print(list(res))

-----------------------------
[]

列表居然是空的。是不是瞬间想到了什么?

没错,这个似乎就是迭代器的特性之一,当其中元素被使用之后,会删掉使用过的元素。而我们之前在执行print(list(res))的时候,已经将内部元素都转成list并展现过,所以现在res内的元素都被删掉了。

没事,让我们再重新来定义一次,也就是重新给res内填满元素然后直接for循环一次看看:

n1 = '1234'
n2 = ['a', 'b', 'c']
n3 = ['A', 'B', 'C', 'D']
# 调用zip函数,合成新的元组迭代器
res = zip(n1, n2, n3)

for i in res:
print(i)

-----------------------------
('1', 'a', 'A')
('2', 'b', 'B')
('3', 'c', 'C')

我们可以看到,每次打印i的时候都打印了一个元组,而这个元组就是一个新元素,比如第一行('1', 'a', 'A'), 这整个元组就是一个新元素。

让我们再用next试试(当然我又重新填满了res):

print(next(res))
print(next(res))

-----------------------------
('1', 'a', 'A')
('2', 'b', 'B')

next函数也能正常执行,那可以说明,zip确实组合成了一个新的迭代器。

现在我们返回来再看一遍代码中的n1,n2,n3, 分别是1234[‘a’, 'b', 'c']['A', 'B', 'C', 'D']。最后组成的迭代器对象为:[('1', 'a', 'A'), ('2', 'b', 'B'), ('3', 'c', 'C')]

通过分析可以看出来,zip的工作原理是先分别取可迭代对象的第一个元素组合成一个元组,然后再分别取第二个元素组合成一个元组,依次往后取...

可是n1, n3分别都是四个元素,为什么我们最后只组合成了三个元组?那是因为n2中只包含了三个元素,当在其中找不到第四个元素的时候,就会放弃组合。

来,让我们在看一个示例:

n1 = [1, 2, 3, 4]
n2 = [22, 33, 44, 55]
res = zip(n1, n2)
print(list(res))

-----------------------------
[(1, 22), (2, 33), (3, 44), (4, 55)]

大家看到最后的执行结果有没有觉得很眼熟?可能很多小伙伴一时间想不到,我们来调整一下:

[
(1, 22),
(2, 33),
(3, 44),
(4, 55)
]

记住这个数据结构,我们在后期做数据分析的时候, 当我们做矩阵运算的时候用的非常多。

不知道大家是否都学过高等数学里的线性代数、微积分,包括概率统计。这些在我们之后做数据分析,数据挖掘,包括机器学习、人工智能这些科学运算里面,非常重要的一些数学功底。

不太记得了也没关系,这些我后面将会专门拿几节出来给大家补一下这方面。

让我们继续,zip还有一种应用方式,当其与*运算符结合使用的时候,可以用来拆解列表:

# zip 与 * 运算符相结合使用
x = [1, 2, 3]
y = [4, 5, 6]

print(zip(x, y))
print(*zip(x, y))

-----------------------------
<zip object at 0x107b8d200>
(1, 4) (2, 5) (3, 6)

可以看到,zip是一个迭代器,*zip这生成了组合好的多个元组数据。

比如:

x1 = [1, 2, 3]
y1 = [4, 5, 6]

x2, y2 = zip(*zip(x, y))
print(x2, y2)

-----------------------------
(1, 2, 3) (4, 5, 6)

这样,我们就将两个列表转换成了两个元组。当然,其实我们这样操作还不如直接使用tuple函数来的方便快捷一点。

那下面,我们就看看都有哪些数据类型转换相关的内置函数。

数据类型转换相关的内置函数

这些函数的功能非常简单和单一,属于拿来就用的函数,我们就仅列出来,不多做介绍了。

  • int() 将其它类型数据转为整型
  • float()转为浮点类型
  • bool()转为布尔类型
  • complex()转为复数
  • str()转为字符串类型
  • list 转为列表类型
  • tuple转为元组类型
  • dict 转为字典类型
  • set 转为集合类型

变量相关函数

  • id()获取当前数据的ID标识
  • type()获取当前数据的类型字符串
  • print()数据的打印
  • input()获取输入的数据
  • isinstance()检测是否为指定的数据类型

数学相关函数

abs()获取一个数的绝对值

print(abs(-99.99))

-----------------------------
99.99

sum()求和 从 start 开始自左向右对 iterable 中的项求和并返回总计值

print(sum([1,2,3]))

-----------------------------
6

max() 获取最大值

print(max([1,2,3]))
print(max(99,12,45))

-----------------------------
3
99

min() 获取最小值

print(min([2,1,6,-9]))
print(min(6,7,1,0,-2))

-----------------------------
-9
-2

pow(x, y)幂运算 返回 x 的 y 次幂

print(pow(2,3))

-----------------------------
8

round(x, n) 对x四舍五入,小数点保留n位

print(round(3.1415926))
print(round(3.1415926,2))

-----------------------------
3
3.14

 

round这个函数不是绝对意义上的四舍五入,在取整这个问题是是奇进偶退:

print(round(3.5))
print(round(4.5))

-----------------------------
4
4

进制函数及字符集

bin() 将数值类型转为二进制

print(bin(123)) 

-----------------------------
0b1111011

int() 将二进制转化为整型

print(int(0b1111011))

-----------------------------
123

oct() 转为八进制数

print(oct(123))

-----------------------------
0o173

hex() 转为十六进制数

print(hex(123))

-----------------------------
0x7b

ASCII及字符集

ASCII,全称为美国信息互换标准代码。是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。它一共有128个支付,最后更新是1986年。

我们要知道的是,ASCII 编码是美国人给自己设计的,他们并没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语和日语,我大中华几万个汉字更是不可能被重视。计算机也是美国人发明的,起初使用的就是 ASCII 码,只能显示英文字符。各个国家为了让本国公民也能正常使用计算机,开始效仿 ASCII 开发自己的字符编码,例如 ISO/IEC 8859(欧洲字符集)、shift_Jis(日语字符集)、GBK(中文字符集)等。

从65开始到90为止,是大写字母(A ~ Z), 97到122是小写字母(a ~ z),48到57是0 ~ 9。

而我们经常使用的是GB2312-80, GBK和GBK18030以及Unicode字符集。

GB2312-80 是 1980 年制定的中国汉字编码国家标准。共收录 7445 个字符,其中汉字 6763 个。

GBK 于1995年制定 收录了 21003 个汉字。GBK向下与 GB 2312 编码兼容,

GBK18030 2001年的1月正式强制执行,是我国制订的以汉字为主并包含多种我国少数民族文字(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)的超大型中文编码字符集强制性标准,其中收入汉字70000余。

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。

  • 它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
  • UTF-8 以字节为单位对Unicode进行编码。

我们现在写代码的时候基本遵循UTF-8编码为主。

有的时候,我们是需要将字符转为ASCII, 也有对应的方法:

print(ord('a'))

-----------------------------
a

将ASCII转为字符也一样:

print(chr(65))

-----------------------------
A

高阶函数

和上一节课不同,我们现在要讲的高阶函数,是Python解释器里的内置高阶函数。

sorted()

很多时候,我们在处理数据的时候都需要对数据进行排序。不管是以序号,名称还是日期的方式。sorted()就是我们最常用的排序函数:

sorted(iterable, [reverse, key])
‘’‘
运行原理:把可迭代数据里面的元素,一个一个的取出来,放到key这个函数中进行处理,并按照函数中return的结果进行排序,返回一个新的列表
功能:排序
参数:
iterable:可迭代的数据 (容器类型数据,range数据序列,迭代器)
reverse:可选,是否反转,默认为False,不反转, True反转
key:可选, 函数,可以是自定义函数,也可以是内置函数
返回值:排序后的结果
’‘’

我们来看几个示例,首先我们先来看看默认的排序方式:从小到大:

arr = [3,7,1,-9,20,10]
res = sorted(arr)
print(res)

-----------------------------
[-9, 1, 3, 7, 10, 20]

当然,既然我们能从小到大来进行排序,那就可以用从大到小的方式:

arr = [3,7,1,-9,20,10]
print(sorted(arr,reverse=True))

-----------------------------
[20, 10, 7, 3, 1, -9]

现在我们得到了从小到大排序,也得到了从大到小排序。然后我们再来作妖:能不能按照所有数字的绝对值大虾哦进行排序呢?哎,还记得我们刚讲过的数学相关的函数里有一个求绝对值的函数嘛?既然sorted()这个函数里的参数key可以接收函数,那让我们结合在一起试试看:

arr = [3,7,1,-9,20,10]
res = sorted(arr,key=abs)
print(res)

-----------------------------
[1, 3, 7, -9, 10, 20]

果然,我们得到了想要的结果。来分析下内部到底做了什么:

[3,7,1,-9,20,10] # 原始列表
3 7 1 9 20 10 # 求绝对值
1 3 7 9 10 20 # 给绝对值进行排序
1 3 7 -9 10 20 # 转换成原本的值

那现在,我再多尝试一下,我试试看自己定义一个函数:

def func(num):
return num % 2

函数定义好了,让我们尝试使用自定义函数对数据进行排序:

arr = [3,2,4,6,5,7,9]
print(sorted(arr, key = func))

-----------------------------
[2, 4, 6, 3, 5, 7, 9]

看似起结果了。那到底函数内干了些什么呢?让我们在其中多打印一点东西出来,看个明白:

def func(num):
print(num, num % 2, end=" ")
print()
return num % 2

arr = [3,2,4,6,5,7,9]
print(sorted(arr, key = func))

-----------------------------
3 1
2 0
4 0
6 0
5 1
7 1
9 1
[2, 4, 6, 3, 5, 7, 9]

这样我们就很清晰的看到了对原数字和取余结果,在对取余进行排序之后,再在取余的结果上进行默认的从小到大进行排序,就得到了最后的结果[2, 4, 6, 3, 5, 7, 9]

不过,这种功能大多数时候我们基本是临时用一下,特意写一个方法似乎有点多余。还记得咱们之前讲的匿名函数吧?让我们用匿名函数优化一下:

# 用匿名函数优化
arr = [3,2,4,6,5,7,9]
res = sorted(arr, key=lambda num:num%2)
print(res)

-----------------------------
[2, 4, 6, 3, 5, 7, 9]

正是我们所要的结果。

从这就能看出来,高阶函数sorted()key因为能接收自定义函数,所以给了我们很大的可玩空间。小伙伴们还能想到哪些方法,快去做做实验。

map()

这个函数在对传入的可迭代数据中的每一个元素进行处理,然后返回一个新的迭代器:

map(func, *iterables)
'''
功能: 对传入的可迭代数据中的每个元素放入到函数中进行处理,返回一个新的迭代器
参数:
func 函数 自定义函数|内置函数
iterables:可迭代的数据
返回值:迭代器
'''

让我们先来看一个普通的函数:

# 把一个字符串数字的列表转为整型列表
items = ['1', '2', '3', '4']
new_list = []
for i in items:
new_list.append(int(i))

print(new_list)

-----------------------------
[1, 2, 3, 4]

我们将一个内部元素均为字符串的列表,转成了一个整型列表。

不过这个函数看起来似乎还是有些麻烦,让我们再用map试试看:

items = ['1', '2', '3', '4']
res = map(int, items)
print(list(res))

-----------------------------
[1, 2, 3, 4]

看,是不是简便多啦?当然,最后map最后生成的是迭代器而并不是列表,我们还是需要转化一下数据类型。

这段代码中map的处理方式其实非常简单,它将items里的每一个元素用int方法转换成整型,转换完之后形成一个新的迭代器,然后返回。

再让我们看一个示例感受一下, 这次我们将两段对比写在一起:

# 普通方法进行实现
items = [1, 2, 3, 4]
res = []
for i in items:
x = i ** 2
res.append(x)
print(res)

# 使用map函数处理
items = [1, 2, 3, 4]
def func(x):
return x ** 2
res = map(func, items)
print(res, list(res))

-----------------------------
[1, 4, 9, 16]
<map object at 0x107ea5030> [1, 4, 9, 16]

我们看到执行结果完全一样,不过使用map()的方式更简洁,逻辑也更清晰。我们要知道,Python本身是自带幂次方方法的。即便是我们自己来实现,其实我们还可以把代码写的更简洁:

items = [1, 2, 3, 4]
res = map(lambda x:x**2, items)
print(res,list(res))

-----------------------------
<map object at 0x107c98610> [1, 4, 9, 16]

基于map的应用,我们来个小作业吧:

我们现在有这样一个列表:['a','b','c','d']

要求将其转换成:[65,66,67,68]

最后,再给大家留个课后作业, 我给大家两个函数及其功能介绍,大家自己去尝试看看,然后自己琢磨下其用法。

reduce()

reduce(func, iterable)

功能:每一次从 iterable 拿出两个元素,放入到func函数中进行处理,得出一个计算结果,然后把这个计算结果和iterable中的第三个元素,放入到func函数中继续运算,得出的结果和之后的第四个元素,加入到func函数中进行处理,以此类推,直到最后的元素都参与了运算

filter()

filter(func, iterable)

功能:过滤数据,把 iterable 中的每个元素拿到 func 函数中进行处理,如果函数返回True则保留这个数据,返回False则丢弃这个数据。

这两个函数在处理数据上作用都非常大。

好了,下课。大家有问题记得留言。

Hi,大家好。我是茶桁。

讲完了基础函数和高阶函数之后,我们这一节来研究下Python的内置函数,看看Python在安装完毕之后的解释器里,到底都预先给我们提供好了哪些可用的函数。

本节内容着重介绍一些常用函数,并且会做一些应用上的示例。当然,对于Python的内置函数,我们还可以查询官方文档,我这节参照的为3.10版本文档

range()函数

这几节课中,我们频繁使用并且着重介绍过这个函数,那我们就从它开始介绍吧。

一般我们需要遍历一个数值序列的时候,range()函数就会派上用场,它生成算数级数。

'''
range() 函数
功能: 能够生成一个置顶的数值序列
参数:
start: 开始的值,默认为0
stop: 结束的值
[, step]: 可选,步进值, 默认为1
返回值: 可迭代的对象,数字序列
'''
range(start, stop, [, step])

让我们来看一下:

res = range(10)
print(res, type(res))

-----------------------------
range(0, 10) <class 'range'>

可以看到这其实就是一个range的类,其实在我们Python中,任何数据都是一个对象而已。

来看案例:

# range函数的使用方式
res = range(11)
print(list(res))

-----------------------------
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

当我们的range内只写一个参数时,这个参数就是stop值,也就是从start的默认值0开始到输入的参数值(stop)之前为止,比如这段代码中,stop会结束到11之前,也就是10

我们在这段代码中,将range的内容转化成一个list并打印了出来。当然,我们也可以使用循环,依次去除range内的内容:

for i in res:
print(i, end=" ")

-----------------------------
0 1 2 3 4 5 6 7 8 9 10

记得上节课我们提到过,range()是不支持next()函数的,不过如果我们将其转成迭代器,就可以使用next()函数调用:

res = iter(range(11))
print(next(res))
print(next(res))
print(list(res))

-----------------------------
0
1
[2, 3, 4, 5, 6, 7, 8, 9, 10]

可以看到,使用iter转成迭代器之后,可以正常使用next()函数,并且我们再次查看res的内容,0,1已经被拿走,只将剩余内容转化为list打印了出来。

当我们在range中添加两个参数的时候,start就是第一个参数,第二个参数就是stop值。

# 添加两个参数
for i in range(5, 10):
print(i, end=" ")

-----------------------------
5 6 7 8 9

当我们输入三个参数的时候,第一个参数为start, 第二个参数为stop, 第三个参数就是[, step], 比如:

# 添加三个参数
for i in range(1, 10, 3):
print(i, end=" ")

-----------------------------
1 4 7

这段代码的含义就是从1开始, 以3为步进来提取数字,并打印出来,一直到10之前的数字为止。

如果不太理解步进值的可以执行数一遍就理解了,比如我们从1开始顺序往后数3个数,那就是2、3、4,数到了4, 再继续往后数3个数,就是5、6、7,数到了7。再继续往后就是8、9、10。但是,我们代码中的stop值为10,所以到9就结束了,也就是说,我们这段代码就只取出了1, 4, 7三个值。

三种参数值的情况我们都了解之后我们可以思考下,难道我们只能选择顺序取值吗?其实不然,我们还可以倒叙取值,聪明的小伙伴可能想到了,调换一下startstop值不就可以了嘛?我们从10开始取值,取到0为止:

for i in range(10, 0):
print(i, end=" ")

执行一下,哎,似乎什么都没打印出来。这又是为什么呢?是不是出BUG了?

其实,什么都没打印出来才是正确的,这是因为,虽然我们给了开始和结束值,但是我们遗忘了一个重要的参数,那就是步进值step,这个值默认可是1,从10开始+1来计数,无论如何也算不到0。所以,我们将步进值改成负数,也就是倒着数了:

for i in range(10, 0, -1):
print(i, end=" ")

-----------------------------
10 9 8 7 6 5 4 3 2 1

至此,我们可以得到结论,是否倒叙取值除了开始和结束值,更重要的是看step是正数还是负数。

res = range(-10,-20,-1) 
# [-10, -11, -12, -13, -14, -15, -16, -17, -18, -19]
res = range(-20,-10)
# [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11]
res = range(-10,10)
# [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

zip()函数

zip() 函数可以接受多个可迭代的对象,然后把每个可迭代对象中的第i隔元素组合在一起,形成一个新的迭代器。

'''
参数: *iterables, 任意个的可迭代对象
返回值: 返回一个元组的迭代器
'''
zip(*iterables)

让我们来直接看示例:

n1 = '1234'
n2 = ['a', 'b', 'c']
n3 = ['A', 'B', 'C', 'D']
# 调用zip函数,合成新的元组迭代器
res = zip(n1, n2, n3)
print(list(res))

-----------------------------
[('1', 'a', 'A'), ('2', 'b', 'B'), ('3', 'c', 'C')]

我知道你们看到这个执行结果会有很多疑问,先别着急,我们先看一下它是否包含迭代器的特性:

for i in res:
print(i)

当你执行这段代码的时候就会发现,似乎什么都没发生。

那到底是怎么回事?我们不用for,让我们再转换一次list之后看看里边有什么:

print(list(res))

-----------------------------
[]

列表居然是空的。是不是瞬间想到了什么?

没错,这个似乎就是迭代器的特性之一,当其中元素被使用之后,会删掉使用过的元素。而我们之前在执行print(list(res))的时候,已经将内部元素都转成list并展现过,所以现在res内的元素都被删掉了。

没事,让我们再重新来定义一次,也就是重新给res内填满元素然后直接for循环一次看看:

n1 = '1234'
n2 = ['a', 'b', 'c']
n3 = ['A', 'B', 'C', 'D']
# 调用zip函数,合成新的元组迭代器
res = zip(n1, n2, n3)

for i in res:
print(i)

-----------------------------
('1', 'a', 'A')
('2', 'b', 'B')
('3', 'c', 'C')

我们可以看到,每次打印i的时候都打印了一个元组,而这个元组就是一个新元素,比如第一行('1', 'a', 'A'), 这整个元组就是一个新元素。

让我们再用next试试(当然我又重新填满了res):

print(next(res))
print(next(res))

-----------------------------
('1', 'a', 'A')
('2', 'b', 'B')

next函数也能正常执行,那可以说明,zip确实组合成了一个新的迭代器。

现在我们返回来再看一遍代码中的n1,n2,n3, 分别是1234[‘a’, 'b', 'c']['A', 'B', 'C', 'D']。最后组成的迭代器对象为:[('1', 'a', 'A'), ('2', 'b', 'B'), ('3', 'c', 'C')]

通过分析可以看出来,zip的工作原理是先分别取可迭代对象的第一个元素组合成一个元组,然后再分别取第二个元素组合成一个元组,依次往后取...

可是n1, n3分别都是四个元素,为什么我们最后只组合成了三个元组?那是因为n2中只包含了三个元素,当在其中找不到第四个元素的时候,就会放弃组合。

来,让我们在看一个示例:

n1 = [1, 2, 3, 4]
n2 = [22, 33, 44, 55]
res = zip(n1, n2)
print(list(res))

-----------------------------
[(1, 22), (2, 33), (3, 44), (4, 55)]

大家看到最后的执行结果有没有觉得很眼熟?可能很多小伙伴一时间想不到,我们来调整一下:

[
(1, 22),
(2, 33),
(3, 44),
(4, 55)
]

记住这个数据结构,我们在后期做数据分析的时候, 当我们做矩阵运算的时候用的非常多。

不知道大家是否都学过高等数学里的线性代数、微积分,包括概率统计。这些在我们之后做数据分析,数据挖掘,包括机器学习、人工智能这些科学运算里面,非常重要的一些数学功底。

不太记得了也没关系,这些我后面将会专门拿几节出来给大家补一下这方面。

让我们继续,zip还有一种应用方式,当其与*运算符结合使用的时候,可以用来拆解列表:

# zip 与 * 运算符相结合使用
x = [1, 2, 3]
y = [4, 5, 6]

print(zip(x, y))
print(*zip(x, y))

-----------------------------
<zip object at 0x107b8d200>
(1, 4) (2, 5) (3, 6)

可以看到,zip是一个迭代器,*zip这生成了组合好的多个元组数据。

比如:

x1 = [1, 2, 3]
y1 = [4, 5, 6]

x2, y2 = zip(*zip(x, y))
print(x2, y2)

-----------------------------
(1, 2, 3) (4, 5, 6)

这样,我们就将两个列表转换成了两个元组。当然,其实我们这样操作还不如直接使用tuple函数来的方便快捷一点。

那下面,我们就看看都有哪些数据类型转换相关的内置函数。

数据类型转换相关的内置函数

这些函数的功能非常简单和单一,属于拿来就用的函数,我们就仅列出来,不多做介绍了。

  • int() 将其它类型数据转为整型
  • float()转为浮点类型
  • bool()转为布尔类型
  • complex()转为复数
  • str()转为字符串类型
  • list 转为列表类型
  • tuple转为元组类型
  • dict 转为字典类型
  • set 转为集合类型

变量相关函数

  • id()获取当前数据的ID标识
  • type()获取当前数据的类型字符串
  • print()数据的打印
  • input()获取输入的数据
  • isinstance()检测是否为指定的数据类型

数学相关函数

abs()获取一个数的绝对值

print(abs(-99.99))

-----------------------------
99.99

sum()求和 从 start 开始自左向右对 iterable 中的项求和并返回总计值

print(sum([1,2,3]))

-----------------------------
6

max() 获取最大值

print(max([1,2,3]))
print(max(99,12,45))

-----------------------------
3
99

min() 获取最小值

print(min([2,1,6,-9]))
print(min(6,7,1,0,-2))

-----------------------------
-9
-2

pow(x, y)幂运算 返回 x 的 y 次幂

print(pow(2,3))

-----------------------------
8

round(x, n) 对x四舍五入,小数点保留n位

print(round(3.1415926))
print(round(3.1415926,2))

-----------------------------
3
3.14

 

round这个函数不是绝对意义上的四舍五入,在取整这个问题是是奇进偶退:

print(round(3.5))
print(round(4.5))

-----------------------------
4
4

进制函数及字符集

bin() 将数值类型转为二进制

print(bin(123)) 

-----------------------------
0b1111011

int() 将二进制转化为整型

print(int(0b1111011))

-----------------------------
123

oct() 转为八进制数

print(oct(123))

-----------------------------
0o173

hex() 转为十六进制数

print(hex(123))

-----------------------------
0x7b

ASCII及字符集

ASCII,全称为美国信息互换标准代码。是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。它一共有128个支付,最后更新是1986年。

我们要知道的是,ASCII 编码是美国人给自己设计的,他们并没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语和日语,我大中华几万个汉字更是不可能被重视。计算机也是美国人发明的,起初使用的就是 ASCII 码,只能显示英文字符。各个国家为了让本国公民也能正常使用计算机,开始效仿 ASCII 开发自己的字符编码,例如 ISO/IEC 8859(欧洲字符集)、shift_Jis(日语字符集)、GBK(中文字符集)等。

从65开始到90为止,是大写字母(A ~ Z), 97到122是小写字母(a ~ z),48到57是0 ~ 9。

而我们经常使用的是GB2312-80, GBK和GBK18030以及Unicode字符集。

GB2312-80 是 1980 年制定的中国汉字编码国家标准。共收录 7445 个字符,其中汉字 6763 个。

GBK 于1995年制定 收录了 21003 个汉字。GBK向下与 GB 2312 编码兼容,

GBK18030 2001年的1月正式强制执行,是我国制订的以汉字为主并包含多种我国少数民族文字(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)的超大型中文编码字符集强制性标准,其中收入汉字70000余。

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。

  • 它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
  • UTF-8 以字节为单位对Unicode进行编码。

我们现在写代码的时候基本遵循UTF-8编码为主。

有的时候,我们是需要将字符转为ASCII, 也有对应的方法:

print(ord('a'))

-----------------------------
a

将ASCII转为字符也一样:

print(chr(65))

-----------------------------
A

高阶函数

和上一节课不同,我们现在要讲的高阶函数,是Python解释器里的内置高阶函数。

sorted()

很多时候,我们在处理数据的时候都需要对数据进行排序。不管是以序号,名称还是日期的方式。sorted()就是我们最常用的排序函数:

sorted(iterable, [reverse, key])
‘’‘
运行原理:把可迭代数据里面的元素,一个一个的取出来,放到key这个函数中进行处理,并按照函数中return的结果进行排序,返回一个新的列表
功能:排序
参数:
iterable:可迭代的数据 (容器类型数据,range数据序列,迭代器)
reverse:可选,是否反转,默认为False,不反转, True反转
key:可选, 函数,可以是自定义函数,也可以是内置函数
返回值:排序后的结果
’‘’

我们来看几个示例,首先我们先来看看默认的排序方式:从小到大:

arr = [3,7,1,-9,20,10]
res = sorted(arr)
print(res)

-----------------------------
[-9, 1, 3, 7, 10, 20]

当然,既然我们能从小到大来进行排序,那就可以用从大到小的方式:

arr = [3,7,1,-9,20,10]
print(sorted(arr,reverse=True))

-----------------------------
[20, 10, 7, 3, 1, -9]

现在我们得到了从小到大排序,也得到了从大到小排序。然后我们再来作妖:能不能按照所有数字的绝对值大虾哦进行排序呢?哎,还记得我们刚讲过的数学相关的函数里有一个求绝对值的函数嘛?既然sorted()这个函数里的参数key可以接收函数,那让我们结合在一起试试看:

arr = [3,7,1,-9,20,10]
res = sorted(arr,key=abs)
print(res)

-----------------------------
[1, 3, 7, -9, 10, 20]

果然,我们得到了想要的结果。来分析下内部到底做了什么:

[3,7,1,-9,20,10] # 原始列表
3 7 1 9 20 10 # 求绝对值
1 3 7 9 10 20 # 给绝对值进行排序
1 3 7 -9 10 20 # 转换成原本的值

那现在,我再多尝试一下,我试试看自己定义一个函数:

def func(num):
return num % 2

函数定义好了,让我们尝试使用自定义函数对数据进行排序:

arr = [3,2,4,6,5,7,9]
print(sorted(arr, key = func))

-----------------------------
[2, 4, 6, 3, 5, 7, 9]

看似起结果了。那到底函数内干了些什么呢?让我们在其中多打印一点东西出来,看个明白:

def func(num):
print(num, num % 2, end=" ")
print()
return num % 2

arr = [3,2,4,6,5,7,9]
print(sorted(arr, key = func))

-----------------------------
3 1
2 0
4 0
6 0
5 1
7 1
9 1
[2, 4, 6, 3, 5, 7, 9]

这样我们就很清晰的看到了对原数字和取余结果,在对取余进行排序之后,再在取余的结果上进行默认的从小到大进行排序,就得到了最后的结果[2, 4, 6, 3, 5, 7, 9]

不过,这种功能大多数时候我们基本是临时用一下,特意写一个方法似乎有点多余。还记得咱们之前讲的匿名函数吧?让我们用匿名函数优化一下:

# 用匿名函数优化
arr = [3,2,4,6,5,7,9]
res = sorted(arr, key=lambda num:num%2)
print(res)

-----------------------------
[2, 4, 6, 3, 5, 7, 9]

正是我们所要的结果。

从这就能看出来,高阶函数sorted()key因为能接收自定义函数,所以给了我们很大的可玩空间。小伙伴们还能想到哪些方法,快去做做实验。

map()

这个函数在对传入的可迭代数据中的每一个元素进行处理,然后返回一个新的迭代器:

map(func, *iterables)
'''
功能: 对传入的可迭代数据中的每个元素放入到函数中进行处理,返回一个新的迭代器
参数:
func 函数 自定义函数|内置函数
iterables:可迭代的数据
返回值:迭代器
'''

让我们先来看一个普通的函数:

# 把一个字符串数字的列表转为整型列表
items = ['1', '2', '3', '4']
new_list = []
for i in items:
new_list.append(int(i))

print(new_list)

-----------------------------
[1, 2, 3, 4]

我们将一个内部元素均为字符串的列表,转成了一个整型列表。

不过这个函数看起来似乎还是有些麻烦,让我们再用map试试看:

items = ['1', '2', '3', '4']
res = map(int, items)
print(list(res))

-----------------------------
[1, 2, 3, 4]

看,是不是简便多啦?当然,最后map最后生成的是迭代器而并不是列表,我们还是需要转化一下数据类型。

这段代码中map的处理方式其实非常简单,它将items里的每一个元素用int方法转换成整型,转换完之后形成一个新的迭代器,然后返回。

再让我们看一个示例感受一下, 这次我们将两段对比写在一起:

# 普通方法进行实现
items = [1, 2, 3, 4]
res = []
for i in items:
x = i ** 2
res.append(x)
print(res)

# 使用map函数处理
items = [1, 2, 3, 4]
def func(x):
return x ** 2
res = map(func, items)
print(res, list(res))

-----------------------------
[1, 4, 9, 16]
<map object at 0x107ea5030> [1, 4, 9, 16]

我们看到执行结果完全一样,不过使用map()的方式更简洁,逻辑也更清晰。我们要知道,Python本身是自带幂次方方法的。即便是我们自己来实现,其实我们还可以把代码写的更简洁:

items = [1, 2, 3, 4]
res = map(lambda x:x**2, items)
print(res,list(res))

-----------------------------
<map object at 0x107c98610> [1, 4, 9, 16]

基于map的应用,我们来个小作业吧:

我们现在有这样一个列表:['a','b','c','d']

要求将其转换成:[65,66,67,68]

最后,再给大家留个课后作业, 我给大家两个函数及其功能介绍,大家自己去尝试看看,然后自己琢磨下其用法。

reduce(func, iterable)

功能:每一次从 iterable 拿出两个元素,放入到func函数中进行处理,得出一个计算结果,然后把这个计算结果和iterable中的第三个元素,放入到func函数中继续运算,得出的结果和之后的第四个元素,加入到func函数中进行处理,以此类推,直到最后的元素都参与了运算

filter(func, iterable)

功能:过滤数据,把 iterable 中的每个元素拿到 func 函数中进行处理,如果函数返回True则保留这个数据,返回False则丢弃这个数据。

这两个函数在处理数据上作用都非常大。

好了,下课。大家有问题记得留言。

avatar-img
9會員
62內容數
从基础开始,再到Python,然后是CV、BI、NLP等相关技术。从头到尾详细的教授一边人工智能。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
茶桁的沙龍 的其他內容
Hi,大家好。 我是茶桁。 本节课,我们来学习一下Python中的「高阶函数」。 让我们先来了解一下,什么是递归函数。 递归函数就是定义一个函数,然后在此函数内,自己调用自己。 既然是自己调用自己,那这个函数必须要有一个结束才行,否则会一直重复的调用下去,直到调用层数越来越多,最
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Hi,大家好。我是茶桁。 在前面几节课的基础之上,我们今天开始尝试在Python中控制流程。这中间,让我们来做一些实际的练习。 Python语句的分类 让我们先了解一下Python语句的分类。 在Python中,可分为单行代码和代码块/组, 顾名思义,单行代码就是一行的Python代码,而代
Python的系列课程是写给零基础的小白看的,如果基础比较好的小伙伴可以暂时先不关注,等待后续课程。 Hi, 大家好,我是茶桁。 之前的课程已经给大家讲解了Python相关特性和基本语法。那么这节课呢,我们尝试着从最简单的脚本来开始认识Python。 在开始这节课之前呢,我是默认大家已经安装好
千里之行始于足下。 大家好,我是茶桁,这里是我们《AI秘籍》的第一节,让我们先从Python来开始好好的打好基础。 第一堂课,我们先从最基础的Python特性开始,当然,还有一些基本语法。 上来就开始讲特性和语法,说明我们将会遗弃惯用的“环境搭建”等更基础的内容,那些内容网上已经很丰富了,一查
Hi,大家好。我是茶桁。 前两节我们学习了基本的Python特性和语法,并且认识了一些基本的Python脚本。今天,我们来学习一下Python的运算符,而我们选择的版本为Python3。 什么是运算符 为了能让我们的学习顺利进行下去,首先我们需要先弄明白:什么是运算符。 这里举一个简单的栗子
Hi,大家好。 我是茶桁。 本节课,我们来学习一下Python中的「高阶函数」。 让我们先来了解一下,什么是递归函数。 递归函数就是定义一个函数,然后在此函数内,自己调用自己。 既然是自己调用自己,那这个函数必须要有一个结束才行,否则会一直重复的调用下去,直到调用层数越来越多,最
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Hi,大家好。我是茶桁。 在前面几节课的基础之上,我们今天开始尝试在Python中控制流程。这中间,让我们来做一些实际的练习。 Python语句的分类 让我们先了解一下Python语句的分类。 在Python中,可分为单行代码和代码块/组, 顾名思义,单行代码就是一行的Python代码,而代
Python的系列课程是写给零基础的小白看的,如果基础比较好的小伙伴可以暂时先不关注,等待后续课程。 Hi, 大家好,我是茶桁。 之前的课程已经给大家讲解了Python相关特性和基本语法。那么这节课呢,我们尝试着从最简单的脚本来开始认识Python。 在开始这节课之前呢,我是默认大家已经安装好
千里之行始于足下。 大家好,我是茶桁,这里是我们《AI秘籍》的第一节,让我们先从Python来开始好好的打好基础。 第一堂课,我们先从最基础的Python特性开始,当然,还有一些基本语法。 上来就开始讲特性和语法,说明我们将会遗弃惯用的“环境搭建”等更基础的内容,那些内容网上已经很丰富了,一查
Hi,大家好。我是茶桁。 前两节我们学习了基本的Python特性和语法,并且认识了一些基本的Python脚本。今天,我们来学习一下Python的运算符,而我们选择的版本为Python3。 什么是运算符 为了能让我们的学习顺利进行下去,首先我们需要先弄明白:什么是运算符。 这里举一个简单的栗子
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
我們介紹了字串和列表的索引和切片操作。索引使用方括號[]來選擇字串或列表中的特定元素,並可以使用正向索引(從0開始)或反向索引(從最後一個元素為-1)來訪問元素。切片使用方括號[]和冒號:來選擇字串或列表中的一段子序列,指定起始位置和結束位置(不包含),並可以使用步長來控制間隔。
Thumbnail
我們可以利用工作表的append()方法,在工作表的列尾添加資料列。 利用迴圈的技巧,我們可以批次賦予區塊內所有儲存格相同的值。 我們也可以在指定的列(行)之前插入指定數量的空白列(行),從指定的列(行)開始向下(右)刪除指定數量的列(行)。
Thumbnail
當我們需要在程式中模擬或實作一些隨機發生的事件,例如模擬猜拳、丟骰子的結果,便需要程式可以根據開發者輸入的數值範圍、型態等,隨機產生一個結果,而Python的random()函數便是用於實現這個功能。
三分微光的新作品《充滿心機與謎團的成人戀愛啟蒙社》目前參加悅閱的小說比賽,有興趣的朋友,可以Google悅閱+成人戀愛啟蒙社一觀。 喜歡本篇故事嗎?那就收藏一下讓我知道^^ 三分微光目前出
Thumbnail
《你的孩子不是你的孩子》劇集激起社會漣漪之餘,也引發親子教養觀熱論。本文主張跳脫對立化的觀影視角,從結構究其背後寓意,觀察社會觀念及其外溢、加深的家長期待,如何共構成親子間窒礙的溝通及壓迫鏈結。打破人物妖魔化、教育神話迷思,結構受害者如你我終需倚賴對話與溝通理解,翻案性的代間觀念流動也才有望催致。
 https://www.youtube.com/watch?v=NpK2hRPaMZE   @影片出處:水管 Alpha M 受夠台灣GQ Magazine上面那些貴鬆鬆又沒有帥到哪去的模特兒業配文
Thumbnail
修咸頓在2005年排包尾降班,傷盡球迷的心。今季剩下4場比賽,只得29分,排18位。最大問題是,至今和了14場,是聯賽20隊之冠。如再一次於英超消失,要等多久才可回來?上一次,等了7年。唯一慶幸的是最後直路,會對無欲無求的中游球隊,而最後一場對曼城……
Thumbnail
過去10年,只得2人在頂峰比拼及受談論的時間,長得像一個世紀,無論足球先生獎項合併前後,2人都包辦了每屆殊榮,各領風騷5年。西班牙雙核恩尼斯達及沙維都未能奪得足球先生或金球獎。然而,綜觀91至2000年間,就出了12個有「名份」的球王。
Thumbnail
<p>剛問我這家餐廳什麼好吃的他。</p>
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
我們介紹了字串和列表的索引和切片操作。索引使用方括號[]來選擇字串或列表中的特定元素,並可以使用正向索引(從0開始)或反向索引(從最後一個元素為-1)來訪問元素。切片使用方括號[]和冒號:來選擇字串或列表中的一段子序列,指定起始位置和結束位置(不包含),並可以使用步長來控制間隔。
Thumbnail
我們可以利用工作表的append()方法,在工作表的列尾添加資料列。 利用迴圈的技巧,我們可以批次賦予區塊內所有儲存格相同的值。 我們也可以在指定的列(行)之前插入指定數量的空白列(行),從指定的列(行)開始向下(右)刪除指定數量的列(行)。
Thumbnail
當我們需要在程式中模擬或實作一些隨機發生的事件,例如模擬猜拳、丟骰子的結果,便需要程式可以根據開發者輸入的數值範圍、型態等,隨機產生一個結果,而Python的random()函數便是用於實現這個功能。
三分微光的新作品《充滿心機與謎團的成人戀愛啟蒙社》目前參加悅閱的小說比賽,有興趣的朋友,可以Google悅閱+成人戀愛啟蒙社一觀。 喜歡本篇故事嗎?那就收藏一下讓我知道^^ 三分微光目前出
Thumbnail
《你的孩子不是你的孩子》劇集激起社會漣漪之餘,也引發親子教養觀熱論。本文主張跳脫對立化的觀影視角,從結構究其背後寓意,觀察社會觀念及其外溢、加深的家長期待,如何共構成親子間窒礙的溝通及壓迫鏈結。打破人物妖魔化、教育神話迷思,結構受害者如你我終需倚賴對話與溝通理解,翻案性的代間觀念流動也才有望催致。
 https://www.youtube.com/watch?v=NpK2hRPaMZE   @影片出處:水管 Alpha M 受夠台灣GQ Magazine上面那些貴鬆鬆又沒有帥到哪去的模特兒業配文
Thumbnail
修咸頓在2005年排包尾降班,傷盡球迷的心。今季剩下4場比賽,只得29分,排18位。最大問題是,至今和了14場,是聯賽20隊之冠。如再一次於英超消失,要等多久才可回來?上一次,等了7年。唯一慶幸的是最後直路,會對無欲無求的中游球隊,而最後一場對曼城……
Thumbnail
過去10年,只得2人在頂峰比拼及受談論的時間,長得像一個世紀,無論足球先生獎項合併前後,2人都包辦了每屆殊榮,各領風騷5年。西班牙雙核恩尼斯達及沙維都未能奪得足球先生或金球獎。然而,綜觀91至2000年間,就出了12個有「名份」的球王。
Thumbnail
<p>剛問我這家餐廳什麼好吃的他。</p>