保姆级入门系列阿ken教你学 Python ——函数
Posted 请叫我阿ken
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了保姆级入门系列阿ken教你学 Python ——函数相关的知识,希望对你有一定的参考价值。
你好,我是阿ken
我又双叒叕来了!
蓝色、灰色为了解内容,其中蓝色多为引用举例。红色为重点记忆。
目录
快速通道:
🐮阿ken:咳咳从此刻开始,调整心情,开始上课!
5.1 函数概述
🐮阿ken:函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码段,通过函数的名称表示和调用。函数也可以看作是一段有名字的子程序,可以在需要的地方使用函数名调用执行。在学习本文内容之前,其实我们已经接触过一些函数,比如输出信息到命令行窗口的 print() 函数、接收键盘输入信息的 input() 函数等。
函数是一种功能抽象,它可以完成特定的功能,与黑箱模型的原理一样。黑箱模型是指所建立的模型只考虑输入与输出,而与过程机理无关。现实生活中,应用黑箱原理的实物有很多,比如洗衣机,对于使用者来说,大家只需要了解洗衣机的使用方法,将洗衣粉、水放入,最终得到洗干净的衣服,这个过程是完全封闭的。对于函数外界不需要了解其内部的实现原理,只需要了解函数的输入输出方式即可使用,换言之,调用函数时以不同的参数作为输入,执行函数后以函数的返回值作为输出。
函数大体可以划分为两类,一类是系统内置函数,它们由 Python内置函数库提供,例如我们在前面章节中学习的 print()、input()、type()、int() 等函数;另一类是用户根据需求定义的具有特定功能的一段代码。自定义函数像一个具有某种特殊功能的容器 —— 将多条语句组成一个有名称的代码段,以实现具体的功能。
使用函数的好处主要体现在以下几方面:
(1) 将程序分解成更小的块(模块化)。
(2) 降低理解难度,提高程序质量。
(3) 减小程序体积,提高代码可重用性。
(4) 降低了软件开发和维护的成本。
5.2 函数的基础知识
🐮阿ken:函数的使用可以分为函数的定义和函数的用两部分,它只需要定义一次,便可
以无限次地被重复使用。
5.2.1 函数的定义
Python 使用 def 关键字定义函数,基本语法格式如下:
def 函数名([参数列表]):
['''文档字符串''']
函数体
[return语句]
上述语法的介绍如下:
(1) 关键字def:标志着函数的开始。
(2) 函数名:函数的唯一标识,其命:名方式遵循标识符的命名规则。
(3) 参数列表:可以有零个、一个或多个参数,多个参数之间使用逗号分隔。根据参数的有无,函数分为带参函数和无参函数。
(4) 冒号:用于标记函数体的开始。
(5) 文档字符串:用于描述函数的功能,可以省略。
(6) 函数体:函数每次调用时执行的代码,由一行或多行Python语句构成。
(7) return语句:标志着函数的结束,用于将函数中的数据返回给函数调用者。若函数需要返回值,则使用 return语句返回,否则 return语句可以省略,函数在函数体顺序执行完毕后结束。
定义函数时,函数参数列表中的参数是形式参数,简称为“形参”,形参用来接收调用该函数时传入函数的参数。注意,形参只会在函数被调用的时候才分配内存空间,一旦调用结束就会即刻释放,因此,形参只在函数内部有效。
定义一个求绝对值的函数,示例如下:
def my_absolute(x):
if x >= 0:
return x
else:
return -x
以上定义的 my absolute() 函数接收参数x,使用 if-else 语句区分 x的正负,若 x为正数,它的绝对值就是它本身,直接返回 x;否则返回它的相反数。
5.2.2 函数的调用
函数定义好之后不会立即执行,直到被程序调用时才会生效。调用函数的方式非常简单,一般形式如下:
函数名(参数列表)
以上形式的参数列表为会被传递给函数的形参、在函数执行过程中会使用的参数,这些参数是实际参数,简称为“实参”。实参可以是常量、变量、表达式、函数等。
调用上述中定义好的 my_absolute()函数,代码如下:
my_absolute(-10.0)
以上代码中的-10.0是实参,它将被传递给函数定义中的形参x。注意,函数在使用前必须已经被定义,否则解释器会报错。
程序执行时若遇到函数调用,会经历以下流程:
(1) 程序在函数调用处暂停执行。
(2) 为函数传入实参。
(3) 执行函数体中的语句。
(4) 程序接收函数的返回值(可选)并继续执行。
定义和调用函数 my_absolute() 的完整代码如下:
def my absolute(x):
if x >= 0:
print(x)
else:
print(-x)
my_absolute(-10.0)
print("---程序结束---")
对以上程序进行分析: Python 解释器读取第 1 ~ 5 行代码时判定此处定义了一个函数,它先将函数名和函数体存储在内存空间中,但不执行;解释器执行第 6行代码,由于此处调了 my_absolute() 函数,程序首先暂停执行,将该函数的实参 -10.0 传递给形参x (x=-10.0),然后执行函数体内部的语句,函数体执行结束之后重新回到第 6行,最后执行第7行的打印语句。
5.3 函数的参数传递
🐮阿ken:函数的参数传递是指将实参传递给形参的过程,Python 中的函数支持以多种方式传递参数,包括位置传递、关键字传递、默认值传递、包裹传递、解包裹传递以及混合传递。本节将针对函数不同的传参方式进行讲解。
5.3.1 参数的位置传递
调用函数时,默认按照位置顺序将对应的实参传递给形参,即将第 1个实参分配给第 1个形参,第 2个实参分配给第 2个形参,以此类推。
假设有个用于判断三角形是否为直角三角形的 is_triangle() 函数, 该函数的定义
具体如下:
def is_triangle(a, b, c):
if a*a + b*b == c*c or a*a + c*c == b*b
or b * b + c * a:
print("是直角三角形")
else:
print("不是直角三角形")
由以上定义可知,is_triangle() 函数需要接收 3个表示三角形各边边长 (大于0)的整型参数。调用 is_triangle()函数, 传入 3个整数,代码如下:
is_triangle(1, 2, 3)
以上代码中的第 1 个实参 "1 " 会被赋给第 1个形参a,第 2个实参 " 2 " 会被赋给第 2个形参b,第 3个实参 3会被赋给第 3个形参c。
通过位置传递方式传参时实参的个数必须与形参的个数保持一致,否则程序会出现异常。
5.3.2 参数的关键字传递
🐮阿ken:如果函数中形参的数目过多,开发者很难记住每个参数的作用,这时可以使用关键字方式传递参数。关键字传递通过 ” 形参变量名 = 实参 " 的形式将形参与实参关联,根据形参的名称进行参数传递、它允许实参和形参的顺序不一致。
例如,有一个构建URL格式序符中的函数 makeup_url(),该函数有两个参数:protocal和 address,分别用于接收协议头和主机地址。它的定义如下所示:
def makeup_url(protocal, address):
print("URL = {}: //{}".format(protocal, address))
通过关键字方式传参时,可以使用如下两种形式:
makeup_url(protocal='http', address='www.baidu.com')
makeup_url(address='www.baidu.com',protocal='http')
这时,我们无须再关心定义函数时参数的顺序,直接在传参时指定对应的名称即可。
5.3.3 参数的默认值传递
函数在定义时可以给每个参数指定默认值,基本形式为:函数名(参数 = 默认值),这样在调用时既可以给带有默认值的参数重新赋值,也可以省略相应的实参,使用参数的默认值。
例如,fun(a=1,b=2,c=3)函数中分别为3个参数a、b、c设置了默认值1、2、3,使用fn(a=7,b=8)调用函数,此时 a和 b的值7和8将覆盖默认值1和2,但是参数 c保持不变,仍然使用默认值3。默认值传递方式并不要求实参与形参的数量相等。
定义 makeup_url() 函数时为参数 protocal 设置默认值,如下所示:
def makeup_url(address, protocal="http"):
print("URL = {}: //{}" .format(protocal, address))
注意,若带有默认值的参数与必选参数同时存在,则带有默认值的参数必须位于必选参数的后面。
调用 makeup_url() 函数可以使用如下两种形式:
makeup_url(address='www.itcast.cn')
makeup_url(protocal="https",address='www.baidu.com')
使用第 1种形式调用函数时,因为没有传值给protocal参数,所以默认会使用该参数的默认值“http";使用第 2种形式调用函数时,因为同时传值给 protocal和 address参数、所以 address参数的新值会替换该参数的默认值。
5.3.4 包裹传递
若定义函数时不确定需要传递多少个参数,可以使用包裹传递。包裹传递的关键在于定义函数时,在相应的参数前添加 “*" 或 ”**":若在某个参数名称的前面加 “*",可以元组形式为该参数传入一组值;若在某个参数名称前加 “**”,可以关键字传递形式为该参数传入一组值。
例如,定义以 “*” 包裹形参 args 的函数test():
def test(*args):
print(args)
调用以上定义的 test()函数时可以传入多个参数,比如传入 5个参数:
test(1,2,3,4,5)
(1,2,3,4,5)
由以上运行结果可知,test()的参数 args接收了一个包含 5个元素的元组。
例如,定义带有 “**” 包裹形参 kwargs 的函数 test():
def test(**kwargs):
print(kwargs)
调用 test()函数时能够以关键字传递的方式传递多个参数,例如:
test(a=1, b=2,c=3,d=4,e=5)
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e':5}
由以上运行结果可知,test()的参数 args接收了一个包含 5个键值对的字典。
5.3.5 解包裹传递
在调用函数时,若函数接收的实参为元组或字典类型,可以使用 “*” 和 “**” 对函数参数解包裹,将实参拆分为多个值,并按照位置传递方式或关键字传递方式将值赋给各个形参。
(1) 元组解包裹
下面来看一个对元组解包裹的示例,代码如下:
def func(a, b, c):
print(a, b, c)
args = (1, 2, 3)
func(*args)
以上代码先定义了需要接收 3个参数的 func() 函数,然后调用 func()函数并向该函数传入了一个包含 3个元素的元组 args,由于元组 args的前面添加了 “*”,Python 对 kwargs 进行解包裹操作,将 args元组中的 3 个元素拆分为 3个值,并分别按顺序赋值给形参a、b、c。
(2) 字典解包裹
下面来看一下对字典解包裹的示例,代码如下:
kwargs = {'a':1, 'b': 2,'c':3}
func(**kwargs)
以上代码调用了 func() 函数, 并向该函数中传入了一个包含 3 个键值对的字典 kwargs,由于字典 kwargs 的前面添加了 "**",Python 对 kwargs 进行解包裹操作,将字典 kwargs 中的 3 个键值对拆分为 3 个值,并分别按参数名称赋值给形参a、b、c。
5.3.6 混合传递
前面介绍了函数参数的若干种传递方式,这些方式在调用函数时可以混合使用,但是在使用的过程中要注意前后的顺序。混合使用的基本原则如下:
(1) 先按照参数的位置传递。
(2) 再按照参数的关键字传递。
(3) 最后按包裹的形式传递。
例如,定义一个函数,该函数包含必选参数、默认参数、可变参数和关键字参数:
def func(a,b,c=0, *args, **kw):
print ('a =',a,'b =', b, 'c =', c, 'args =', args, 'kw =',kw)
在调用 func()函数时,Python 解释器按照混合使用的原则传递参数。调用函数的示例如下:
func(1,2) # 按位置传递方式将1、2赋值给a、b,c采用默认值0
a=1 b=2 c=0 args = () kw = {}
func(1, 2, c=3) # 按位置传递方式将1、2赋值给a、b,将3赋值给c
a=1 b=2 c=3 args = () kw = {}
func(1, 2, 3, 'a', 'b')
a=1 b=2 c=3 args = ('a', 'b') kw = {}
func(1, 2, 3, 'a', 'b', x=99)
a=1 b=2 c=3 args = ('a', 'b') kw = {'x': 99}
调用 func()函数时传入一个元组和字典,可以通过解包裹的形式传递参数。例如:
args = (1, 2, 3, 4)
kw = {'x': 99}
func(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw= {'x': 99}
使用混合传递时有两点需要注意:
(1) 若定义函数时参数有默认值,则带有默认值的参数必须跟在必选参数的后面。
(2) 若调用函数时需要混合使用位置传递和关键字传递,则必选参数要出现在关键字参数之前。
5.4 函数的返回值
函数中的 return 语句是可选项,可以出现在函数体的任何位置,它的作用是结束当前函数,将程序返回到函数被调用的位置继续执行,同时将函数中的数据返回给主程序。
编写含有自定义函数 is_capital()的程序,实现判断键盘输入的字符串是否以大写字母开头的功能,代码如下:
def is capital(words):
if ord("A")<=ord(words[0])<=ord("z"):
return '首字母是大写的'
else:
return '首字母不是大写的'
result = is_capital("othon") # 将函数返回的结果交给变量
print(result)
执行程序,程序输出的结果:
首字母是大写的
游戏项目通过坐标控制角色位置,角色坐标由 x和 y两个值决定,这要求与位置相关的函数能够同时返回 x和 y两个值。函数可以返回两个值吗? 答案是肯定的,不仅如此,Python 函数中的 return也可以返回多个值。当函数使用 return语句返回多个值时,这些值将以元组形式保存。
例如,定义一个控制游戏角色移动的函数 move(),使用 return语句返回反映角色当前位置的 nx 和 ny,代码如下:
02 control game role. py
import math
def move(x, y, step, angle=0)
nx=x+ step math cos(angle
y =y- step math sin (angle)
return nx, ny
#返回多个值
result move (100, 100, 60, math. pi/6)
#实际上返回的是一个元组
print(result)
以上程序定义了 move()函数、使用变量 result 接收了 move()函数返回的计算结果并将结果打印,打印信息如下:
(151.96152422706632, 70.0)
由以上结果可知,函数返回的其实是一个包含两个元素的元组。
5.5 变量作用域
Python 变量并不是在哪个位置都可以访问的,具体的访问权限取决于变量定义的位置,其所处的有效范围视为变量的作用域。根据作用域的不同,变量可以划分为局部变量和全局变量。
5.5.1 局部变量
在函数内部定义的变量称为局部变量,局部变量只能在定义它的函数内部使用。例如,定义一个包含局部变量 count 的函数 test(),在函数的内部和外部分别访问变量 count,代码如下:
def test():
count = 0 # 局部变量
print(count) # 函数内部访问局部变量
test()
print(count) # 函数外部访问局部变量
执行程序,程序执行的结果如下:
0
Traceback (most recent call last):
File "C:/Users/admin/PycharmProjects/测试/func.py", line 6, in <module>
print(count)
NameError: name 'count' is not defined
以上程序在打印 count的值之后又打印了错误信息 "name 'count' is not defined“,由此可知,函数中定义的变量在函数内部可使用,但无法在函数外部使用。
局部变量的作用域仅限于定义它的代码段内,在同一个作用域内,不允许出现同名的变量。
5.5.2 全局变量
全局变量是指在函数之外定义的变量,它在程序的整个运行周期内都占用存储单元。默认情况下,函数的内部只能获取全局变量,而不能修改全局变量的值。例如,将前面定义的 test()函数进行调整,如下所示:
count = 10 # 全局变量
def test():
count = 11 # 实际上定义了局部变量, 局部变量与全局变量重名
print(count)
test()
print(count)
以上代码中首先在 test() 函数外定义了一个全局变量 count,其次在该函数的内部尝试为 count 重新赋值,然后在函数的内部访问了变量 count,最后在执行完函数后访问变量 count。
执行程序,程序执行的结果如下:
11
10
从以上结果可知,程序在函数 test() 内部访问的变量 count为 1,函数外部访问的变量为10。也就是说,函数的内部并没有修改全局变量的值,而是定义了一个与全局变量同名的局部变量。
在函数内部若要修改全局变量的值,需要提前使用保留字 global进行声明,语法格式如下:
global 全局变量
对以上定义的 test() 函数再次进行调整,在该函数中对全局变量 count进行修改,具体代码如下所示:
count = 10 # 全局变量
def test():
global count # 声明 count为全局变量
count += 10 # 函数内修改 count变量
print(count)
test()
print(count)
以上代码首先定义了变量 count并赋值为10,其次在 test() 函数内部使用 global保留字声明 count为全局变量,然后重新给 count变量赋值并将其输出,最后在函数执行完以后再次输出。
执行程序,程序执行的结果如下:
20
20
观察执行结果,程序在函数内部和外部获得的变量 count的值均为20。由此可知,在函数内部使用关键字 global对全局变量进行声明后,函数中对全局变量进行的修改在整个程序中都有效。
多学一招:LEGB法则
Python 中的作用域大致可以分为以下 4种
(1) L(local):局部作用域。
(2) E(enclosing):嵌套作用域。
(3) G(global):全局作用域。
(4) B(built-in):内置作用域。
基于 LEGB 法则,搜索变量名的优先级是:局部作用域>嵌套作用域>全局作用域>内置作用域。当函数中使用了未确定的变量名时,Python 会按照优先级依次搜索 4个作用域,以此来确定该变量名的意义。首先搜索局部作用域 (L),其次是上一层函数的嵌套作用域 (E),然后是全局作用域 (G),最后是内置作用域(B)。按照 LEGB原则查找变量,在某个区域内若找到变量,则停止继续查找;若一直没有找到变量,则直接引发 Name Error 异常。
5.6 函数的特殊形式
🐮阿ken:除了前面介绍的普通函数之外,Python 还有两种具有特殊形式的函数:匿名函数和递归函数。
5.6.1 匿名函数
匿名函数是一类无须定义标识符的函数,它与普通函数一样可以在程序的任何位置使用,但是在定义时被严格限定为单一表达式。Python 中使用 lambda关键字定义匿名函数,它的语法格式如下:
lambda <形式参数列表>: <表达式>
与普通函数相比,匿名函数的体积更小,功能更单一,它只是一个为简单任务服务的对象。它们的主要区别如下:
(1) 普通函数在定义时有名称,而匿名函数没有名称。
(2) 普通函数的函数体中包含有多条语句,而匿名函数的函数体只能是一个表达式。
(3) 普通函数可以实现比较复杂的功能,而匿名函数可实现的功能比较简单。
(4) 普通函数能被其他程序使用,而匿名函数不能被其他程序使用。
定义好的匿名函数不能直接使用,最好使用一个变量保存它,以便后期可以随时使用这个函数。例如,定义一个计算数值平方的匿名函数,并赋值给一个变量:
temp = lambda x : pow(x, 2) # 定义匿名函数, 它返回的函数对象赋值给变量 temp
此时,变量 temp可以作为匿名函数的临时名称来调用函数,示例如下:
temp(10)
100
5.6.2 递归函数
递归是指函数对自身的调用,它可以分为以下两个阶段:
(1) 递推:递归本次的执行都基于上一次的运算结果。
(2) 回溯:遇到终止条件时,则沿着递推往回一级一级地把值返回来。
递归函数通常用于解决结构相似的问题,其基本的实现思路是将一个复杂的问题转化成若干个子问题,子问题的形式和结构与原问题相似,求出子问题的解之后根据递归关系可以获得原问题的解。递归有以下两个基本要素:
(1) 基例:子问题的最小规模,用于确定递归何时终止,也称为递归出口。
(2) 递归模式:将复杂问题分解成若干子问题的基础结构,也称为递归体。
递归函数的一般形式如下:
def 函数名称 (参数列表):
if 基例:
return 基例结果
else:
return 递归体
由于每次调用函数都会占用计算机的一部分内存,若递归函数未提供基例,函数执行后会返回 " 超过最大递归深度 " 的错误信息。
递归最经典的应用就是阶乘,例如,求n的阶乘,数学中使用函数 fact(n)表示:
fact(n)=n!=1*2*3*…*(n-1)*n=fact(n-1)*n
在程序中定义 fact()函数实现阶乘计算,可以写成如下形式:
def fact(n):
if n == 1: # 基例
return 1
else:
return fact(n-1)*n # 递归体
fact(n) 是一个递归函数,当n大于1时,fact() 函数以 n-1作为参数重复调用自身直到 n为1时调用结束,开始通过回溯得出每层函数调用的结果,最后返回计算结果。
[回头补一个递归过程表]
斐波那契数列也是递归的一个经典案例。斐波那契数列又称黄金分割数列,这个数列从第 3项开始,它的每一项都等于前两项的和。在数学上,斐波纳契数列以递推的方式定义,具体如下所示:
F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2) (n>=3, n∈N*)
根据以上定义,斐波那契数列的前 9项依次为:1、1、2、3、5、8、13、21、34。
斐波那契数列由数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为 " 兔子数列 "。兔子繁殖的故事是这样的,一般兔子在出生两个月后就有繁殖能力,对兔子每个月能生出一对小兔子来,如果所有的兔子都不死,那么一年以后一共有多少对兔子?
第1个月, 兔子没有繁殖能力,此时兔子的总数量为1对。
第2个月, 免子拥有了繁殖能力,生下一对小免子,此时兔子的总数量为2对。
第3个月, 兔子又生下一对小兔子,而小兔子没有繁殖能力,此时兔子的总数量为3对。
依此类推,可以得知,经历 0或 1个月份后,兔子的总数量均为1,之后每经历一个月份,兔子的总数量为前两个月份的数量和。例如,经过 3个月时兔子的总数量为1+2=3,经过 4个月时兔子的总数量为 2+3=5,经历 5个月时兔子的总数量为 3+5=8。
使用代码实现计算兔子数列的函数,具体如下所示:
def rabbit(month):
if month <= 1:
return 1
else:
return rabbit(month-1) + rabbit(month-2)
以上代码定义了一个递归函数 rabbit(),该函数接收一个代表经历的月份的参数 month,并在代码段中使用 if-else 语句区分了 1月份和其他月份的不同,若是经过了一个月,则返回总数量为1,代表着递归函数的出口,若是经过了N (大于1)个月,则会重复调用 rabbit()函数,返回 rabbit(month-2) 与 rabbit(month-1) 的和。
在解释器中定义 rabbit()函数,并使用以下语句调用该函数,可计算出经过一年以后,兔子的总数量为:
rabbit(12)
233
5.7 时间处理模块 —— datetime
🐮阿ken:Python 提供了专门操作日期与时间的 datetime模块,该模块提供了很多处理日期与时间的方法,使用这些方法可以从系统中获得时间,并以用户选择的格式进行输出。
datetime 模块以格林尼治时间为基础,将每天用 3600×24 秒精准定义。 datetime 模块中定义了两个常量:datetime.MINYEAR 和 datetime.MAXYEAR,这两个常量分别表示最小年份 (1)和最大年份 (9999) ;datetime 模块还定义了 6个核心的类:
datetime 模块的核心类:
类名 | 说明 |
date | 表示具体日期,精确到天 |
time | 表示具体的时间,可精确到微秒 |
datetime | 表示具体的日期时间,可以理解为 date和 time |
timedelta | 表示具体的时间差 |
tzinfo | 表示日期与时间的时区 |
timezone | tzinfo 抽象基类,表示与 UTC的固定偏移量 |
上表中的前 3个类最为常见,接下来分别对这 3个类中的常用方法进行介绍:
1. date类
date 类表示理想化日历中的日期,由年、月和日组成,比如 1998年1月1日。最简单的创建日期的方式是使用 date类的构造方法,该函数的语法格式如下:
class date(year, month, day)
以上函数中每个参数都只能是整型,它们的含义如下:
(1) year:指定的年份,MINYEAR ≤ year ≤ MAXYEAR。
(2) month:指定的月份,1 ≤ month ≤ 12。
(3) day:指定的日期,1 ≤ day ≤ 给定月份和年份中的天数。
例如,创建一个表示 2019年1月4日的日期对象,代码如下所示:
from datetime import date
date(2019, 1, 4)
date time.date(2019, 1, 4)
2. time 类
time 类是 datetime 模块中用于处理时间的类,表示一天中的(本地)时间,由时、分、秒以及微秒组成,比如 12点 0分 0秒。通过 time类的构造方法可以创建一个时间对象,该函数的语法格式如下:
datetime.time(hour=0, minute=0, second=0, microsecond=0)
以上函数中每个参数的含义如下:
(1) hour:指定的小时,0 ≤ hour < 24。
(2) minute:指定的分钟数,0 ≤ minute < 60。
(3) second:指定的秒数,0 ≤ second < 60。
(4) microsecond:指定的微秒数,0 ≤ microsecond < 1 000 000。
例如,创建一个表示 12时 10分 30秒的时间对象,代码如下所示:
from datetime import time
time(12, 10, 30)
datetime.time(12, 10, 30)
3. datetime 类
datetime 类可以视为 date类与 time类的结合体,它可以同时表示日期和时间,例如 1970年1月1日0时0分0秒。创建 datetime对象的常见方法有以下4个:
(1) datetime():datetime 类的构造方法,用于构造一个指定日期和时间的 datetime对象,可精确到微秒。
(2) today():获取一个表示本地当前日期和时间的 datetime对象。
(3) now():获取一个表示当前时区日期和时间的 datetime对象。
(4) utcnow():获取当前日期和时间对应的 UTC (世界标准时间)对象。
通过 datetime()方法可以直接构造一个日期时间对象,该方法中参数的含义与 date() 和time() 方法中参数的含义相同,此处不再赘述。例如,创建一个表示 2018年 6月 1日 12点 12分 30秒 50微秒的对象,如下所示:
from datetime import datetime
datetime(2018, 6, 1, 12, 12, 30, 50)
datetime.datetime(2018, 6, 1, 12, 12, 30, 50)
通过 today()方法获取本地当前的日期与时间,时间会精确到微秒,如下所示:
datetime.today()
datetime.datetime(2019, 1, 4, 14, 33, 8, 248797)
通过 now()方法可以获取指定时区的日期和时间,时间同样会精确到微秒。若不指定时区,返回本地的日期与时间,作用等同于 today()方法。例如,获取本地当前的日期与时间,如下所示:
datetime.now()
datetime.datetime(2019, 1, 4, 14, 39, 45, 780534)
通过 utcnow() 方法可以获取当前日期和时间对应的 UTC时间 (世界标准时间),时间仍然会精确到微秒。例如,今天的日期是 2019年 1月 4日,所处东八区的具体时间是 14时 45分,当前所对应的世界标准时间为:
datetime.utcnow()
datetime.datetime(2019, 1, 4, 6, 45, 9, 505050)
创建好 datetime对象以后,可以使用对象的属性和方法进一步控制时间的输出格式。
datetime类的常用属性
属性 | 说明 |
year | 返回日期包含的年份 |
month | 返回日期包含的月份 |
day | 返回日期包含的日 |
hour | 返回日期包含的小时 |
minute | 返回日期包含的分钟 |
second | 返回日期包含的秒钟 |
microsecond | 返回日期包含的微秒 |
此外,datetime 类中还提供了常用的格式化日期字符串的 strftime() 方法,可以使用任何通用的格式输出时间。
strftime() 方法控制符
格式控制符 | 说明 |
%Y | 四位数的年份表示,取值范围为0001 ~ 9999 |
%m | 月份(01 ~ 12) |
%d | 月内中的一天 |
%B | 本地完整的月份名称,比如 January |
%b | 本地简化的月份名称,比如 Jan |
%a | 本地简化的周日期 |
%A | 本地完整周日期 |
%H | 24小时制小时数(0 ~ 23) |
%I | 12小时制小时数(01 ~ 12) |
%p | 本地 A.M. 或 P.M. 等价符 |
%M | 分钟数(00 ~ 59) |
%S | 秒(00 ~ 59) |
例如,创建一个 datetime对象,以形如 " 时-分-秒 年-月-日 " 的格式进行输出,代码如下:
date_time = datetime.now()
date_time
datetime.datetime(2019, 1, 4, 17, 15, 58, 255314)
date_time, strftime("%H-%M-%S %Y-%m-%d") # 返回格式化日期
'17-15-58 2019-01-04'
多学一招:格林尼治时间
我们平时所使用的时间,是以太阳在天空中的方位作标准来计量的。每当太阳转
到夭球子午线的时刻,就是当地正午12时。由于地球自转,地球上不同地点看到太
阳通过天球子午线的时刻是不一样的。例如,当英国伦敦是中午12点时,北京正值
晚上7时45分,上海则是晚上8时06分。
为了使用方便,人们把全球划分成24个时区,毎个时区跨经度为15度。英国原
格林尼治天文台所在的时区叫作零时区,包括西经7.5度到东经7.5度范围内的地区,
在这个时区里的居民都采用原格林尼治天文台的时间。零时区以东第一个时区,叫作
东一区,从东丝75度到225度,是用东经15度的时间作标准的。再往东顺次是东
区、东三区……直到东十二区。每跨过一个时区,时间正好相差1小时。同样地,
零时区以西顺次划分为西一区、西二区……一直到西十二区(西十二区就是东十二
区)世界各地都包括在这24个时区里,每个时区的时间是统一的,称为区时。
中国位于格林尼治东面,使用的是东经120度的标准时间,属于东八区。我们日
常所说的“北京时间**点”就是东八区的标准时间。
5.8 代码抽象与模块化设计
函数的特点主要体现在两个方面:代码抽象和模块化设计,关于它们的介绍分别如下:
1. 代码抽象
程序由一系列的代码组成,若代码无序且无组织,不仅不利于开发人员的阅读与理解,后期也很难开发与维护。为了形成易于理解的结构,避免编写出面条式代码 (非结构化和难以维护的源代码),需要对代码进行抽象。通常采用函数和对象两种抽象方式抽象代码。
函数将一段代码封装起来并对其命名后供其他程序调用。函数的优点有很多,最直接的优点就是实现代码复用,函数定义之后可以在程序中多次被调用,从而避免重复编写具有相同功能的代码。
对象是程序的一种高级抽象方式,它将一段代码组织成更高级别的类。类是一组具有相同属性和方法的对象集合,描述了属于该对象的所有性质。对象存在于现实世界中,比如大学生、汽车、空调等,对象包括描述特征的属性和描述行为的方法。例如,大学生是—个对象,姓名、年龄等是属性,跑步、学习、思考等是方法。
函数和对象分别是面向过程编程思想和面向对象编程思想的核心。面向过程是种以过程描述为中心的编程方式,它要求开发人员列出解决问题所需要的步骤,然后用函数将这些步骤逐个实现,使用时依次建立调用函数的语句即可;面向对象编程是一种组织程序的新型思维方式,这种思维方式会将数据和操作封装到一起,组成一个相互依存、不可分离的整体 ——对象。
面向对象程序设计的焦点不再是过程,而是对象及对象间的关系。此种思想提取同一类型事物的共性构造出类,在类中设置这一类事物的共同属性,为类定义与外界发生关系的接口 ——方法。
面向过程与面向对象是两种不同的编程方式,它们的抽象级别有所不同,所有能通过面向对象编程实现的功能都可以采用面向过程完成,两者在解决问题上并不存在优劣之分,具体采用哪种方式取决于开发要求。一般在编写大规模程序时建议采用面向对
象的编程方式。
Python 语言同时支持面向对象和面向过程两种编程方式,本系列文采用面向过程的方
式编写程序,但 Python3 内部代码全部采用面向对象方式实现,为降低读者的理解难
度,阿ken 在讲解和使用 Python模块时会涉及面向对象 (调用类的函数创建对象、调
用对象的方法操作对象)
2. 模块化设计
模块化设计是指通过函数或对象的封装功能将程序划分成主程序、子程序、子程
序与子程序间关系的表达,它体现的是分而治之的思想。
针对复杂问题的求解所采用的模块划分通常是从功能的角度进行的,划分后的模块要具备 " 相对独立、功能单一 " 的特征。也就是说,一个好的模块必须具有高度的独立性和较强的功能。在实际应用中,通常会用如下两个指标从不同的角度对模块的划分情况加以度量。
(1) 内聚度:是对模块内各元素之间相互依赖性大小的度量。内聚度越大,模块内各元素之间联系越紧密,其功能越强;反之,低内聚模块内各元素的关系较为松散。
(2) 耦合度:是对模块之间相互依赖程度的度量。耦合度越低,模块的相对独立性越大;耦合性越高,一个模块受其他模块的影响越大。
模块划分时应当尽可能降低不同模块间的关联,提升单一模块自身的功能性,做
到 " 高内聚、低耦合 "。
采用模块结构设计程序的好处在于:整个程序结构清晰,易于分别编写与调试,便于维护与调用,并利于程序功能的进一步扩充与完善。
5.9 本文总结
本章首先介绍了函数的概念、定义和调用,其次介绍了函数参数传递的几种方式,然后介绍了变量作用域和两个具有特殊形式的函数:匿名函数和递归函数,之后介绍了日期时间处理模块 datetime,最后介绍了代码重用与模块化设计的思想。通过对本文的学习,读者应能够理解函数式编程的优越性,可以按照需求灵活定义函数。
后来你常常会遇到更优秀的人
可能也会跟我一样经常感觉自己是个 loser
但后来想起来Kobe的一句话:如果一定有人会赢,那个人为什么不能是我呢?
而今故人已逝,随着年纪增长,我也渐渐懂了这句话背后的艰辛和努力
也常常用这句话勉励尚在低谷阶段的自己
不管怎样
我一定要赢
Peace
快速通道:
以上是关于保姆级入门系列阿ken教你学 Python ——函数的主要内容,如果未能解决你的问题,请参考以下文章
保姆级入门系列阿ken的 Python学习笔记数字类型和字符串
保姆级|建议收藏阿ken带你学Java入门及进阶——运算符循环语句,文末有彩蛋鸭✨✨✨
保姆级|建议收藏阿ken带你学Java入门及进阶——运算符循环语句,文末有彩蛋鸭✨✨✨