其中,前五种类型是不可变类型,后三种是可变类型,而不可变类型才能作为集合的元素或者字典的键。
在第三天还讲了:
今天还会继续讲语法部分:
涉及的语法比较多,像匿名函数,类,包等,这些我们知道怎么用即可。
推导式是python比较特殊的语法,其他编程语言比较少见,使用起来很就方便,能提升代码的可读性。
主要包含:
这三种推导式的语法几乎是一样的,放到一起去讲。其语法结构:
例如,我们如果要计算列表中每个元素的平方值,组成新的列表:
data = [1, 2, 3] # 使用循环的写法 new_data = [] for val in data: new_data.append(val**2) print(new_data)
data = [1, 2, 3] # 使用推导式的写法 # 如果是元组,则将下面语句的中括号改为小括号 new_data = [val**2 for val in data] print(new_data)
上面可以看到,对于某些循环语句,改用推导式,将是非常简洁的。事实上,大多数循环语句都能改成使用推导式的形式来实现。不过,注意不要走极端,并不是所有循环语句改成推导式都会提升可读性。
总结一下,列表推导式的语法如下:
[func(item) for item in data]
其中,func(item)
是指对item做某种运算,如上面例子的平方。
下面看一个复杂一点的推导式。
a = [1, 3, 6] b = [2, 1, 3] # 如果我们需要计算: # a的每个元素和b的每个元素的乘积 c = [ia*ib for ia, ib in zip(a, b)] print(c)
data = [4, 1, 2, 3, 5, 7, 8] # 如果我们需要计算: # data的偶数位上的数的平方 new_data = [val**2 for key, val in enumerate(data) if key % 2 == 1] print(new_data)
这是把zip或者enumerate函数和推导式结合在一起使用。
上面说的例子,对于元组和集合完全适用。如:
# 只需要把中括号改成大括号即可 new_data = {val**2 for key, val in enumerate(data) if key % 2 == 1} print(new_data)
字典也可以使用推导式的形式定义:
{key: val for key, val in zip(keys, values)}
# 姓名和成绩 names = ['张三', '李四', '王五'] scores = [66, 89, 59] # 使用字典推导式定义一个字典 # 将姓名和成绩关联起来 data = {name: score for name, score in zip(names, scores)} print(data)
# 将一个字典复制到另一个字典也可以这样写: data1 = {'张三': 66, '李四': 89, '王五': 59} data2 = {k: v for k, v in data1.items()} print(data2)
函数的调用方式:value = function_name(param)
,通过函数名去调用,前面我们已经用过不少了,如print, len等,都是函数。
常用的内置函数(前面有些已经涉及到了):
还有几个可以用于类型转换的函数:
这几个暂时没涉及到,后面可能需要用到的。
像for, in, and, or, not,is等,这些是语法的关键字。
python的常用的内置函数基本就在这里的,我们需要大概掌握它们的用法。如果一时不记得这些函数有哪些参数,我们也需要知道怎么找到说明,一种方式是直接通过搜索引擎查找,第二种当然是找人问,而在notebook中,还有一种常用的方式,如下:
# 函数名的后面,加一个问号,然后运行单元格 # 以range函数为例: range?
其输出结果:
Init signature: range(self, /, *args, **kwargs) Docstring: range(stop) -> range object range(start, stop[, step]) -> range object Return an object that produces a sequence of integers from start (inclusive) to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1. start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. These are exactly the valid indices for a list of 4 elements. When step is given, it specifies the increment (or decrement). Type: type Subclasses:
我们需要学会看这样的帮助文档。关于该函数的调用,关键的说明信息是:
range(stop) -> range object range(start, stop[, step]) -> range object
该函数可以接受的参数个数是3个:
前面两种调用形式前面已经涉及过,我们分别来看看:
data1 = range(10) data2 = range(5, 10) data3 = range(5, 10, 2) # 我们将这3个结果转成列表来观察结果 print(list(data1)) print(list(data2)) print(list(data3))
显然,step这个参数的作用是步长的意思。
对于字符串,元组,列表,集合和字典等,python都提供了很多内置的函数,而每个函数又有不同的参数,我们很难去记住它们。最重要的是,我们需要大概知道每种类型大概能实现什么功能,然后去哪里可以查到这些方法,及其使用的方式。这是我们学习编程最重要的技能之一。
例如,如果我们需要知道字符串有哪些内置函数则可以在cell中输入:str.
之后,然后按Tab键,就会出来该类型所有的方法列表:
str包含的函数很多很丰富,我们往下拉滚动条,就能看到我们之前用过的replace,接着我们可以继续查看该函数的用法:
输入:str.replace?
,然后运行单元格,结果如下:
Signature: str.replace(self, old, new, count=-1, /) Docstring: Return a copy with all occurrences of substring old replaced by new. count Maximum number of occurrences to replace. -1 (the default value) means replace all occurrences. If the optional argument count is given, only the first count occurrences are replaced. Type: method_descriptor
该函数的使用方式是str.replace(self, old, new, count=-1, /)
,作用是讲字符串中的old字符串替换成new字符串,说明中还有特别说明count参数的作用,还有返回值的说明。这里的self是类本身,这里暂时不用管,在类的部分会讲到。
我们也可以输入"变量名.
",然后按Tab键,这也会出现函数列表。
在前面我们使用如下:
s = 'This is a string string.' # 调用的方式跟上面说到的内置函数有点点不同 new_s = s.replace('string', '字符串') print(new_s)
# 这种方式也是完全一样的 s = 'This is a string.' s.replace?
对于list, set, dict等,查看帮助文档的方式都是类似的。
我们总结一下,这几种类型的常用方法:
这些常见的函数,大家可以都查一查其帮助文档,大致了解其用法。
这只是一部分,还有其他的函数,需要用的时候可以去了解其用法。
s = 'This is a string' # 按空格分拆字符串 ls = s.split(' ') print(ls) # 使用一个等号将列表拼接起来,组成一个新的字符串 '='.join(ls)
函数就是一种封装,把相关的功能组合在一起。例如,前面的打印九九乘法表,我们把它封装成一个函数:
# 定义一个函数:nine_nine_multi def nine_nine_multi(): for i in range(1, 10): # 注意缩进 for j in range(1, i+1): print("%d * %d = %d" % (i, j, i*j)) # return None # 函数调用 nine_nine_multi()
我们已经知道sum函数可以对列表进行求和,现在我们重新造一个轮子:
# 定义列表的求和函数 # list_sum是函数名 # ls是函数的参数 # 注意:理解缩进的层级 def list_sum(ls): ls_sum = 0 for val in ls: ls_sum += val # 返回语句 return ls_sum # 返回计算结果 a = [1,2,3,4,5] a_sum = list_sum(a) print(a_sum)
我们重点来理解一下这个函数执行机制,在执行到函数调用的语句时,执行状态如下图:
我们定义的函数名有点类似变量名,它指向了一个函数。
下一步就是执行函数调用:
这里我们需要注意: 外部定义的列表a和函数参数ls其实指向的是同一个列表。如果在函数里面,改变了ls参数的值,会导致外部的列表变量a的值也跟着改变。因为这里的参数是复杂类型,对于其他的类型,如集合,字典,类等都会产生这样的效果。但是对于简单类型则不会产生这样的副作用。
我们继续往前执行,就跟前面的循环类似了:
而执行到return语句时:
这个Return Value在返回之后,会赋值给a_sum变量。
注意:函数是不一定是需要又return语句的,默认会返回一个None。
作为一个练习,大家可以实现一个阶乘的函数,并观察其执行过程。
在python里,函数参数有几种类型:必选参数,默认参数,不定参数。
必选参数就是前面自定义函数时用到的,调用时,必须对应传递,否则就会报错。
我们知道,在调用函数时如果不指定某个参数,Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
# a是必选参数 # b就是带默认值的参数 def test(a, b=2): print(a, b) # 不传b参数,则b参数使用默认值 test(1) # 给b参数参数,通常这样:b=4 test(1, b=4)
带默认值的参数需要放在必选参数的后面,否则会报语法错误,如下:
我们查看一些函数的帮助文档,例如range,参数里会有*args
, **kwargs
这样的参数,通常我们自己实现函数的时候,尽量应该避免这种情况,不过,我们还是得理解这种语法,以便我们更好的理解某些函数的用法。
需要说明的是,args和kwargs都可以理解为参数名,使用其他的名字也是一样的,只是args和kwargs是习惯的命名。
对这两个参数大概可以这样理解:
range?
不定参数和必选参数或者是默认参数也是可以一起使用的,例如:
在上面的图,我们定义了一个求和函数,其实有两个变量的作用域:
def test(a): local_a = 20 # 定义一个函数内部的变量 print(a) print(global_a) # 这里会打印10 # 定义一个全局变量 global_a = 10 param = 15 test(param) # 在函数外部使用函数内部的变量会报错 print(local_a)
如果我们定义了多个函数,则每个函数都会有自己的内部作用域。
但是需要主要,如果我们在函数内部改变全局变量,会发生什么呢?请看:
def test(a): # 在函数内部改变重新定义之后,global_a这个变量已经变成了该函数的内部变量 # 在这里改变重新赋值并不会影响全局的global_a global_a = 5 print('In test: ', global_a) # output: 5 # 定义一个全局变量 global_a = 10 print('Global: ', global_a) # output: 10 test(20) # 外部的变量并不会改变 print('Global: ', global_a) # output: 10
在函数内部赋值之后,其实这两个变量已经完全是两个独立的变量了,虽然它们的名字是一样的。就像在这里有一个叫“张三”的人,而在另一个地方也有一个叫“张三”的人,它们两个同名,但却完全是独立的两个人。
但是如果我们在函数内部尝试操作全局的变量,例如四则运算,则会报错:
如果我们需要作为一个全局变量来使用,则需要使用global语句:
def test(a): global global_a # 声明该变量是全局变量 global_a += 5 print('In test: ', global_a) # 定义一个全局变量 global_a = 10 print('Global: ', global_a) test(20) print('Global: ', global_a)
这会输出:
Global: 10 In test: 15 Global: 15
这时,全局的变量就会跟着改变了。
注意:尽量不要使用全局的变量,尽量避免在函数内部的操作对外部造成影响,这会让程序的可读性变得很差。如果需要,通常可以作为函数的参数传入。
现在我们来看一个常用的函数:sorted
,这个函数可以用于元组和列表等的排序。先查看它的用法:
Signature: sorted(iterable, /, *, key=None, reverse=False) Docstring: Return a new list containing all items from the iterable in ascending order. A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order. Type: builtin_function_or_method
先看一下其简单用法:
a = [1, 3, 2] print(sorted(a)) # output: 1,2,3 print(sorted(a, reverse=False)) # output: 1,2,3 # 逆序排序 print(sorted(a, reverse=True)) # output: 3,2,1
[1, 2, 3] [1, 2, 3] [3, 2, 1]
对于一些复杂的列表或者元组,我们可能就需要用到sorted函数的key参数,该参数可以指定排序的值:
# 定义一个列表: 一个班级学生的考试成绩如下 scores = [ ['张三', 80], ['李四', 70], ['王五', 90] ] # 定义一个key参数的值 def score_sort(x): return x[1] # 指定按照下标1的值,即成绩进行排序 # 如果我们按成绩进行排名 # 这告诉我们,函数也是一个对象,可以作为其他函数的参数,也可以赋值给一个变量 res = sorted(scores, key=score_sort, reverse=True) print(res)
[['王五', 90], ['张三', 80], ['李四', 70]]
现在这样是可以实现功能的,但是这样很哆嗦,有没有更简洁优雅的方式呢?
显然是有的,就是匿名函数:
res = sorted(scores, key=lambda x: x[1], reverse=True) print(res)
lambda x: x[1]
: 这就是一个匿名函数:
这个匿名函数的效果和前面的score_sort函数的效果是一样的,可以对比一下。
红色椭圆圈住的地方就是匿名函数,它有自己的作用域,有自己的参数,有自己的返回值。
从图示我们可以看到,在实际运行的时候,其实是有点类似循环的,每次取列表的一个元素传入匿名函数的参数x,返回值就是x[1]。
对于做数据分析来说,类并不是那么重要,不过我们得掌握其基础的用法,因为很多场景下,我们会接触到类的对象。
在python中,一切皆对象,我们前面接触到的变量,函数等,都是对象,而所有对象都是某个类的实例化。对于数据分析师来说,我们对“类”秉持工具主义就好了,知道怎么去调用类和类的方法即可。
类方法的调用是需要使用点号进行调用的,在前面已经涉及到,例如:
# 定义一个字符串变量 # s就是一个字符串对象 s = 'string!' # 调用s对象的replace方法 s.replace('!', '.')
跟类相关的主要概念有几个,我们以房子来类比,以辅助理解:
我们不准备深入去讲解这些概念,但是我们需要有所了解。
前面讲到的东西只是python最核心的部分,但是python还有内容众多的包,几乎无所不包,这些包又分为python内置的包,还有浩如烟海的第三方包。
现在我们讲一个数学包:
# 在使用包的函数,需要先引入该包 import math # 这时包引用的语句 # PI的值 # 包的变量:pi是在math这个包中定义好的常量 print(math.pi) # 求平方根 # sqrt是math包中定义的函数 print(math.sqrt(2)) # 查看包函数的帮助文档 # 如果帮助信息不清晰,则可以使用搜索引擎进行搜索 math.sqrt?
包的引用语法还有以下的形式:
# 将numpy引入进来,并命名为np,这时在代码中就可以使用np进行调用 # numpy是一个数据分析常用的第三方包 import numpy as np # 从math包中单独引入sqrt这个函数 from math import sqrt
包引用语句通常放在代码行的最前面。
math这个包的内容很丰富,几乎囊括了中学数学里用到的很多函数,不可能也没必要一一讲解。
前面已经讲过列表字典等变量的复制问题,但是那都不是太彻底的。我们可以看一下下面这个例子:
如果我们观察b变量,发现其值也已经被修改了,因为这个copy只是浅层的copy,如果元素中包含有组合类型的话,就会出现这样的问题。
如果我们需要彻底的复制的话,这时需要用到一个“copy”的包:
from copy import deepcopy a = [1, 2, ["a", "b", "c"]] # deep copy a to b b = deepcopy(a) # change a[2][1] = 'abc' print(b)
[1, 2, ['a', 'b', 'c']]
显然,使用deepcopy之后,改变a的值并不会导致b的值也关联被改变,这时a和b已经是完全两个独立的变量了。
至此,python数据分析入门的语法部分算是学完了。
F[n]=F[n-1]+F[n-2](n>=3,F[1]=1,F[2]=1)
未完待续。。。
ps: 所有代码都在notebook运行。
本文分享自微信公众号 - 野生AI架构师(Moon-CV)
原文出处及转载信息见文内详细说明,如有侵权,请联系 [email protected] 删除。
原始发表时间: 2020-04-12
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。