查看原文
其他

数据治理 | 带你学Python之控制结构与函数

数据seminar 数据Seminar 2022-12-31


我们将在数据治理板块中推出一系列原创推文,帮助读者搭建一个完整的社科研究数据治理软硬件体系。该板块将涉及以下几个模块:

  1. 计算机基础知识

(1)社科研究软硬件体系搭建——虚拟化技术概述与实践

  1. 编程基础

(1)数据治理 | 带你学Python之 环境搭建与基础数据类型介绍篇

(2)本期内容:数据治理 | 带你学Python之控制结构与函数

  1. 数据采集
  2. 数据存储

(1)安装篇 数据治理 | 遇到海量数据stata卡死怎么办?这一数据处理利器要掌握

(2)管理篇 数据治理 | 多人协同处理数据担心不安全?学会这一招,轻松管理你的数据团队

(3)数据导入 数据治理 | “把大象装进冰箱的第二步”:海量微观数据如何“塞进”数据库?

  1. 数据清洗
  2. 数据实验室搭建

Part1前言

这里是 Python 编程基础系列内容,该系列共包含三部分内容(本文属第二部分内容),上一部分内容讲解了使用 Python 的第一步,即搭建编程环境和熟悉数据类型。经过第一部分的学习,相信你对编程有了一点重新的认识。本文将会承接上一部分的内容,重点介绍程序控制结构和函数两节内容

Part2Python程序的控制结构

1赋值语句

把赋值符号“ = ”右边的表达式计算后的结果赋值给左边的变量的语句被称为赋值语句,可以读作:把【表达式】赋值给【变量】。例如:

>>> name = '张三' # 把字符串'张三'赋值给变量 name
>>> age = 18   # 把整数 18 赋值给变量 age
>>> result = 8.5 ** 3  # 把表达式 8.5 ** 3 的运算结果赋值给变量 result
>>> value_0 = [(1,), 'b''c'].pop(0)
>>> value_1 = ['a''b''c'][:2]

上述的赋值语句分别给变量赋予了不同类型的值,这些赋值语句也可以使用增量赋值的形式,其形式为:

<变量> += <表达式>

这里的运算符 “+” 也可以是其他计算运算符(-,*,%,//,%,** 等),等价于:

<变量> = <变量> + <表达式>

Python定义的对象是有类型的,但是变量是没有类型的,赋值语句使某个变量得到了表达式的值,但它只是引用了该对象的值。对于同一个变量,第一次赋值和第二次赋值可以是不同的类型。例如:

>>> value = 10
>>> value
10
>>> value = 'The second'
>>> value
'The second'
>>> value == 10
False

为了方便使用,赋值语句还有以下特殊的赋值形式:

>>> x = y = z = 10          # 同时把 10 赋值给 x,y,z 三个变量。
>>> y
10
>>> name,age = "张三",18    # 序列赋值语句,一次性为多个变量赋值。
>>> x,y = y,x               # 变量x 与 变量y 的值互换。

2顺序结构

顺序结构是所有程序的默认结构,也是最方便人们理解的一种控制结构。以顺序结构编写的程序按照程序语句的先后顺序依次执行,顺序结构有一个入口和一个出口,如下图所示。

3分支结构

Python 有三种分支结构,单分支结构,二分支结构,多分支结构。分别用 ifif - else 和  if - elif - else 语句描述解决问题。

单分支结构

单分支结构的语句多使用 if 语句,其语法格式为:

if <判断条件>:
    <语句块>

其中,< 判断条件> 可以是任意的数字,字符,关系或逻辑表达式。< 判断条件> 以 True 表示真,以 False 表示假。 <语句块> 是否执行依赖于 <判断条件>。不管语句块的内容是否执行,都会转到与 if y语句同级的下一条语句,如下图所示。

下面通过一个例题来深入了解一下单分支结构的 Python 程序。

例1】编写程序,从键盘输入一个人的年龄,若他 / 她的年龄超过 18 ,则他 / 她是成年人。

>>> age = int(input('请输入您的年龄:'))
请输入您的年龄:20
>>> if age >= 18:
        print('您已成年。')
        
您已成年。

二分支结构

二分支结构(if - else)的语法格式为:

  if <判断条件>:
      <语句块 1>
  else:
      <语句块 2>

if 条件得到满足后,执行<语句块 1>,而 <语句块 2> 则是 不满足if 条件才执行的部分。判断条件的真与假分别形成了两条不同的执行路径。如下图所示。

例2】编写程序,从键盘输入一个人的年龄,若他 / 她的年龄超过 18 ,则他 / 她是成年人,若低于 18,则是未成年人。

>>> age = int(input('请输入您的年龄:'))  # 从键盘上输入一个整数作为变量age的值
请输入您的年龄:17
>>> if age >= 18:
        print('您已成年。')
else:
        print('您还未成年。')
        
您还未成年。

二分支结构还有一种更简洁的表达方式,该方式只适用于 <语句块 1> 和 <语句块 2>只包含一条表达式的情况,其语法格式为:

<表达式 1> if <判断条件> else <表达式 2>

例如:

>>> age = int(input('请输入您的年龄:'))  # 从键盘上输入一个整数作为变量age的值
请输入您的年龄:17
>>> print('您已成年。'if age >= 18 else print('您还未成年。')
您还未成年。

多分支结构

多分支结构使用关键字 if - elif - else 的组合,对多个条件进行判断,并根据不同的条件的真与假执行相应的分支。多分支的语法格式为:

if <条件1>:
    <语句块1>
elif <条件2>:
    <语句块2>
elif <条件3>:
    <语句块3>
……
else:
    <语句块n>

当所遇到的条件较多时就需要用到多分支结构,不管有多少分支,程序一旦执行其中一个分支,其他的分支就不再做判断以及执行。若有多个分支的判断条件都满足,系统仅执行第一个匹配的条件,所以多分支结构的条件顺序非常重要。其结构流程图如下图所示。

例3】某小学期末考试某科目,满分100分,根据分数将成绩划为五个等级:分数区间为 [0,60) 的为不及格,[60,70) 的为及格,[70,80) 的为中等,[80,90) 的为良好,[90,100) 的为优秀。编写程序,输入成绩,得到成绩等级。

>>> score = int(input('请输入学生分数:'))  # 从键盘输入一个整数作为变量score的值
请输入学生分数:79
>>> if score >= 90:
        print('成绩为优秀。')
elif score >= 80:
        print('成绩为良好。')
elif score >= 70:
        print('成绩为中等。')
elif score >= 60:
        print('成绩为及格。')
else:
        print('成绩为不及格。')
  
成绩为中等。

判断条件及其组合

分支结构中的判断条件可以使用任何能够产生 TrueFalse 的语句或者函数,形成判断条件的一般方式是采用关系操作符。如下表所示。

操作符含义
>大于
<小于
==等于
>=大于等于
<=小于等于
!=不等于

当形成判断条件的表达式是或者产生数字,字符串或者组合数据形式时,若数字为 0 ,则为 False,否则为 True,若字符串/组合数据形式为空时,为 False,否则为 True

4循环结构

while 循环

while 循环与 if 分支类似,在条件为真的情况下,执行一段代码。两者的区别是:if 语句判断为真后只会执行一遍对应的代码块,随后就会跳出 if 语句。而 while 循环的条件为真时,会一直重复执行对应的语句块直至循环条件变为假,这段被反复执行的语句块称为循环体。while 循环的语法格式为:

while <条件>:
    <语句块>

while 循环的流程图如下图所示。

例4】编写程序,计算并输出100以内(含100)所有正整数的和。

>>> n = 1
>>> result = 0
>>> while n <= 100:
        result += n
        n += 1
       
>>> result 
5050

while 循环在执行时,如果判断条件为 True,则执行循环体内的语句块,语句执行结束后返回;再次判断 while 条件是否满足要求,直到当条件为 False 时循环终止,执行与 while 同级的语句。

while循环还有另外一种扩展形式,其语法格式为:

while <条件>:
    <语句块1>
else:
    <语句块2>

当这种模式中的 while 循环结束之后,程序会执行else 语句的内容。例如:

>>> n = 1
>>> result = 0
>>> while n <= 100:
        result += n
        n += 1
else:
        print('程序执行完成!')
        print(result)

for 循环

for 循环是一种遍历循环,一般不会陷入死循环。其语法格式为:

for <循环变量> in <遍历结构>:
    <语句块>

for 循环的流程图如下图所示。

根据流程图可以了解到,从遍历结构中逐一获取全部元素,每一个元素都要执行语句块中的内容。for 循环的执行次数也就等于遍历结构中元素的个数。这里的遍历结构可以是字符串,range()函数,文件,组合数据类型等。其中 range() 函数的使用频率非常高,下面来介绍一下它。

range,在英文中有“范围”的意思,顾名思义,range() 函数是一个生成一个某范围内的组合数据。该函数有三个整数类型的参数:range(start,end,step),生成一个从参数 start 开始(包含本身,默认为 0),到 参数 end 结束(不包含本身),以 参数 step 为步长(默认为 1)的类似于列表的一个整数序列。虽然 range 函数有三个参数,但是输入一个参数或者两个参数也可,这样会自动省略掉其他的参数。下面逐一介绍。

当只输入一个参数 n 时,n 的取值应当大于 0 ,表示从 0 开始,到 n 结束的一个整数序列。例如:

>>> range(5)
range(0, 5)
>>> list(range(5))   # 为方便查看,将 range 序列转为列表
[0, 1, 2, 3, 4]

当输入两个参数 m,n 时,m 应当小于 n ,表示从 m 开始,到 n 结束的一个整数序列。例如:

>>> list(range(1,6))
[1, 2, 3, 4, 5]
>>> list(range(-2,3))
[-2, -1, 0, 1, 2]

当输入三个参数,m , n , p 时,相对比较麻烦,当 p 为大于零的整数时,m 应当小于 n ,表示从 m 开始,到 n 结束,以 p 为步长的一个以从小到大的顺序排列的整数序列;当 p 为小于零的整数时,m 应当大于 n,表示从 m 开始,到 n 结束,以 p 为步长的一个以从大到小的顺序排列整数序列。例如:

>>> list(range(1,7,2))
[1, 3, 5]
>>> list(range(-5,4,3))
[-5, -2, 1]
>>> list(range(5,0,-1))
[5, 4, 3, 2, 1]
>>> list(range(-2,-15,-3))
[-2, -5, -8, -11, -14]

在实际的编程中,for 循环还有另外一种扩展模式,使用方法如下:

for <循环变量> in <遍历结构>:
    <语句块1>
else:
    <语句块2>

这种结构的作用是当 for 循环全部执行完毕之后再执行 else 后的代码块。例如:

>>> for i in range(5):
        print(i)
else:
        print('代码运行结束,无意外。')
        
0
1
2
3
4
代码运行结束,无意外。

循环中的 break 和 continu

breakcontinue这两个关键字常用于循环结构之中。

  • break

    break 关键字用于终止循环语句,即使在循环中没有符合结束循环的条件产生,只要运行 break 关键字,就会强制跳出整个循环,不会再执行break 下面的任何语句。因此一个循环体中只能出现一个可执行的 break 语句。例如:

>>> for i in range(10):
        if i % 2 == 0:
            print(f'{i}是一个偶数')
        if i == 7:
            print('意外,终止循环!')
            break
        if i % 2 == 1:
            print(f'{i}是一个奇数')
else:
        print('完成')
        
0是一个偶数
1是一个奇数
2是一个偶数
3是一个奇数
4是一个偶数
5是一个奇数
6是一个偶数
意外,终止循环!

由于意外跳出循环,else 中的语句没有被执行。当一段代码中只出现多个循环嵌套使用时,break 语句只会跳出其所在的循环。

  • continue

    break 关键字不同,continue 用于中断本次正在执行的循环,直接进入下一次循环。在循环中一旦遇到 continue 关键字,其后的所有代码将不被执行,直接进入下一循环。例如我想要找到10 以内大于 6 的数字,可以这样实现。

>>> for i in range(10):
        if i <= 6:
            continue
        else:
            print(f'{i}是 10 以内大于 6 的数字')
                        
7是 10 以内大于 6 的数字
8是 10 以内大于 6 的数字
9是 10 以内大于 6 的数字

break 语句一样,当一段代码中只出现多个循环嵌套使用时,continue 语句只会中断其所在的循环。breakcontinue 不仅适用于 for 循环,在 while 循环中也同样适用。这里不再多赘述。

5异常处理

当我们因为一些不确定因素,不一定能够一次性完成一项任务;或者因为某些因素,代码容易出错,但又不影响其他部分执行;又或者我只想处理一个项目中可以处理的项目……这时就可以使用 Python 自带的异常处理语法来解决问题。

Python通常使用 try - except 组合来处理异常。使用语法如下。

try:
    <语句块1>
except <异常类型>:
    <异常处理语句块1>

省略 <异常类型> 时,默认为一切异常。

场景1:你需要获取一个列表中大于 0 的数字,并放入一个新列表。一般的做法如下:

>>> list_1 = [-3,43,0,-5,-56,4,65,'78',89,-2]  # 列表中含有一个字符串
>>> list_2 = []
>>> for i in list_1:
        if i > 0:
            list_2.append(i)
            
Traceback (most recent call last):
  File "<pyshell#45>", line 2, in <module>
    if i > 0:
TypeError: '>' not supported between instances of 'str' and 'int'
>>> list_2
[43, 4, 65]

这里会报错,提示你不支持 'str' 和 'int' 之间的实例,这样你就会不好处理,根据报错不难发现,list_1 中掺入了一个字符串 '78',由于它不是数字类型,无法与数字比较大小,不能够执行循环中的判断条件。所以 '78' 后面的符合条件的整数不能被获取。根据刚才的报错信息 "TypeError",可以这样编程以达到目的:

>>> list_1 = [-3,43,0,-5,-56,4,65,'78',89,-2]
>>> list_2 = []
>>> for i in list_1:
        try:
            if i > 0:
                list_2.append(i)
        except TypeError:
            # 表示遇到 TypeError 类型的错误时,执行下面的代码
            pass  # 不执行任何操作
            
>>> list_2
[43, 4, 65, 89]

这样做的话,既没有任何报错,又达到了目的。

try - except 还有其他高级用法,例如,try - except 支持多个 except 语句,这种方法可以精准的处理不同异常。语法格式如下:

try:
    <语句块1>
except <异常类型1>:
    <异常处理语句块1>
except <异常类型2>:
    <异常处理语句块2>
    ……
except <异常类型N>:
    <异常处理语句块N>

除此之外,try - except 还有一种用法,与 elsefinally 连用,使用结构如下:

try:
    <语句块1>
except <异常类型1>:
    <语句块2>
else:
    <语句块3>
finally:
    <语句块4>

其中,<语句块3> 只有当 <语句块1> 正常执行时才会执行,而 <语句块4> 则会在最后被无条件执行,可以把它当作是收尾部分。

try - except 的应用场景很多,更多的技巧需要编程者深入体会,探索。这里不再过多赘述。

Part3函数

函数是将具有独立功能的代码块组织成为一个整体,使之成为具有特殊功能的代码集。使用函数可以加强代码的复用性,降低编程的难度,使代码更简洁高效。

当你要针对一个具体需求,实现一个功能,这个功能需要反复使用,但是没有现成的模块可以使用,这时候就可以自己编写函数实现你想要实现的功能。下面介绍一下函数。

6函数的定义和调用

函数的定义

定义函数的语法格式如下:

def <函数名>(<形式参数列表>):
    <函数体>
    return <返回值组合>

函数的定义以 def (define (定义) 的简写)关键字开头,后接函数的名称,其命名规则与变量名命名规则相同;函数名称后面紧跟着一对圆括号(),括号之中可以定义参数,一个函数可以有零个,一个或多个参数,多个参数时,参数之间使用逗号分隔。<函数体>是每次调用函数时执行的代码;函数以 return <返回值组合> 结束,返回函数体得到一个或多个结果给调用方,函数可以没有 return 语句,<函数体> 执行结束后将控制权返还给调用方。例如:

>>> def run(a,b):  # a和b为形参
        # 定义一个函数,求两个数的积的整数部分
        result = int((a * b) // 1)
        return result

函数的调用

定义后的函数不能直接运行,需要经过调用才能运行,即函数的“先定义,后调用”原则。例如:

>>> def run(a,b):
        # 定义一个函数,求两个数的积的整数部分
        result = int((a * b) // 1)
        return result
        
>>> run(2.4,3.8)  调用函数run  # 2.4对应a,3.8对应b  为实参
9

函数的返回值

Python 函数使用 return 语句返回 “返回值”,所有的函数都有返回值,如果一个函数没有使用 return 语句,就会隐式地调用 return None 作为函数返回值。如果函数执行了 return 语句,会立刻返回,并结束调用,其后的所有代码都不会被执行。return 语句可以出现在函数的任意位置,也可以出现多次,但只有其中一个会被执行。可以将零个,一个或者多个函数的运算结果返回。当有多个返回值时,函数会使用一个元组来存放所有的返回值。例如:

>>> # 定义函数,计算并返回两个数的和,差和积
>>> def function_1(a,b):
        he = a + b
        cha = a - b
        ji = a * b
        return he,cha,ji
        
>>> result = function_1(4,5)
>>> result
(9, -1, 20)
>>> res_1,res_2,res_3 = function_1(4,5)
>>> print(res_1,res_2,res_3)
9 -1 20

7参数的传递形式

形参和实参

Python 函数有两种类型的参数,形式参数和实际参数,即形参和实参。在函数定义时的参数被称为形参,形参不是实际存在的变量,它实际存在的意义是接收调用函数时传入的实参,调用时将实参的值传递给形参,因此实参的个数与类型需与形参一一对应。函数调用时传入的参数叫实参,实参可以是,常量,变量,表达式,甚至是函数,但无论实参是什么类型,在调用时,实参都必须有实际的值。例如:

>>> def test(name:str,number:int):
        # 锁定了参数的类型
        # 此时的参数name和参数number都是形参
        print(f'{name} has {number} apples.')
        
>>> test('Lily',3)  # 传入的参数'Lily'和3都是实参
Lily has 3 apples.

在函数体内都是对形参进行操作,不能操作实参,即对实参作出修改。

位置参数

位置参数是函数最常用的传参方式,调用函数时实参的数量与顺序必须与形参完全一致。例如:

>>> def test(a,b,c):
        print(c,b,a)
        
>>> test(1,2,3)
3 2 1

默认值参数

在定义函数时给形参一个默认值,有默认值的参数被称为默认值参数,调用函数时,如果没有给默认值参数赋值,函数就会使用这个参数的默认值。例如:

>>> def test(a = 'Tom',b = 18):
        print(f'{a} is {b} years old.')
        
>>> test(b = 20)  # 只给形参b传入了实参,形参a将使用定义好的默认值参数作为实参
Tom is 20 years old.

关键字参数

关键字参数是指调用函数时,通过形参名传递参数值。此时,实参的顺序与形参可以不一致,只需对应参数名称即可。例如:

>>> def test(a,b,c):
        print(a,b,c)
        
>>> test(b = 2,c = 3,a = 1)
1 2 3

可变参数

在函数定义时,可以设计可变参数,通过在参数前使用 “ * ” 实现。可变参数在函数定义时表现为两种形式:*param**param,其中 *param 表示可以接收多个位置参数对应的实参组成一个元组; **param 表示可以接收多个关键字参数对应的实参名和值组成一个字典。下面是两种不同可变参数的使用实例。

>>> # 定义一个带有可变参数的函数
>>> def infomation(name,*others):
        print(name)
        for info in others:
            print('其他信息:',info)
            
>>> infomation('David')  # 可变参数为空时
David
>>> infomation('David','23岁')  # 一个可变参数
David
其他信息: 23岁
>>> infomation('David','23岁','男','未婚')  # 三个可变参数
David
其他信息: 23岁
其他信息: 男
其他信息: 未婚

>>> def infomation(name,age,**others):
        print('名字:',name + '\n','年龄:',age + '\n','其他:', others)
        
>>> data = {'婚姻状态':'已婚''身高':'2.34m''体重':'不详'}
>>> infomation('张伟','30',**data)
名字: 张伟
 年龄: 30
 其他: {'婚姻状态''已婚''身高''2.34m''体重''不详'}

组合参数

在实际编程中,经常会遇到位置参数,关键字参数,默认值参数和可变参数的几种参数组合使用,这时,参数的使用顺序必须是位置参数,默认值参数,可变参数,关键字参数。例如:

>>> def test(a,b,c=3,*m,**n):
        print('a = ',a, 'b = ',b, 'c = ',c, 'm = ',m, 'n = ',n)
        
>>> test(1,2)
a =  1 b =  2 c =  3 m =  () n =  {}
>>> test(1,2,5)
a =  1 b =  2 c =  5 m =  () n =  {}
>>> test(1,2,5,'X','Y')
a =  1 b =  2 c =  5 m =  ('X''Y') n =  {}
>>> test(1,2,5,'X','Y',x = 8,y = 9)
a =  1 b =  2 c =  5 m =  ('X''Y') n =  {'x': 8, 'y': 9}

变量作用域

变量的作用域是指在程序中能够对该变量进行操作的范围,根据作用域的不同,变量分为全局变量和局部变量。全局变量是指在函数之外定义的变量,在程序执行全过程有效。局部变量是指在函数内部使用的变量,仅在函数内部有效,当函数使用过后,变量从内存中释放。例如:

>>> a = 5  # a 是全局变量
>>> def test():
        b = a + 3  # b 是局部变量
        print(b)
        
>>> test()
4
>>> print(b)
Traceback (most recent call last):
  File "<pyshell#87>", line 1, in <module>
    print(b)
NameError: name 'b' is not defined

8匿名函数

匿名函数,乍一看似乎是没有名字的函数,其实不然,匿名函数并非是没有名字,而是将函数名作为函数的结果返回,所以函数名没有完全体现出来。Python 中使用关键字 lambda 创建匿名函数,其主体往往只是一个表达式,其语法格式如下:

<函数名> = lambda<形式参数> : <表达式>

匿名函数适用于函数主体代码量较少,不需要在其他位置服用的函数逻辑,可以省去函数定义过程以及函数命名,使代码更加简洁高效。但若用在代码量大,功能复杂的函数上,便会适得其反,使代码更加难以阅读,理解。下面是一个匿名函数实例。

>>> # 定义函数,求两个数的和
>>> # 1.普通函数
>>> def add(x,y):
        return x + y
>>> result_1 = add(1234,5678)
>>> result_1
6912

>>> # 2.匿名函数
>>> result_2 = lambda x,y : x + y
>>> result_2(1234,5678)
6912

Part4结束语

本文首先介绍了分支与循环的基本概念,其中有几个需要注意的地方:

  1. if 可以单独使用,但是 elifelse 必须和 if 同时搭配使用
  2. 写条件与循环时,合理利用 continue 或者 break 来避免复杂的嵌套,是十分重要的

然后,我们一起学习了Python函数的基本概念与用法,需要注意:

  1. Python函数的形式参数可以接受任意的数据类型,使用时请在函数开头加入数据类型的检查
  2. 和其他语言不同,Python函数的形式参数可以设定默认值
  3. 简单的功能可以用匿名函数实现,使代码更加简洁。

预告: 下一篇推文《带你学python之面向对象基础》将为大家介绍面向对象编程的基础知识,包括:类、对象、属性等多个重要概念





星标⭐我们不迷路!
想要文章及时到,文末“在看”少不了!

点击搜索你感兴趣的内容吧


往期推荐


统计计量 | 因果推理的匹配方法及其在Stata中的实现

统计计量 | 陆铭的13个实证研究锦囊(下)

统计计量 | 双重差分法(DID)安慰剂检验的做法:随机抽取500次?

统计计量 | 陆铭的13个实证研究锦囊(上)

统计计量 | DID最新进展汇总:命令、书单、论文、文章资源汇总

数据治理 | 带你学Python之 环境搭建与基础数据类型介绍篇

统计计量 | 梁捷:因果识别——挑战经典理论的实证研究工具






数据Seminar




这里是大数据、分析技术与学术研究的三叉路口


文 | 任正长


    欢迎扫描👇二维码添加关注    

点击下方“阅读全文”了解更多

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存