Excel Python:飞速搞定数据分析与处理

在这里插入图片描述

第一部分 Python 入门

第三章 Python 入门

本章首先会介绍 Python 的基本数据类型,比如整型和字符串。然后会介绍 Python 的核心概念——索引和切片,使你可以访问一个序列的指定元素。接下来会讲到列表和字典等数据结构,它们可以保存多个对象。之后会介绍 Python 中的控制流:if 语句、for 循环和 while 循环。紧接着是函数和模块的相关知识,它们可以用来组织和架构你的代码。最后会展示应该如何正确格式化 Python 代码

3.1 数据类型

最常用的数据类型有整型浮点型布尔值字符串。要理解什么是数据类型,需要先解释一下什么是对象。

3.1.1 对象

在 Python 中,一切皆对象(object)。数字、字符串、函数,以及我们会在本章中见到的其他所有东西,它们都是对象。通过提供一系列变量和函数,对象可以让复杂的东西简单化。先来看看变量和函数。

1、变量

在 Python 中,变量(variable)是通过等号给对象赋予的一个名字。 Python 中,可以通过给变量赋值一个新的对象来改变变量的类型。这种行为被称作动态类型

In [2]: a = 3
 print(a)
 a = "three"
 print(a)
 
3
three

Python 是区分大小写的,因此 a 和 A 是不同的变量。变量名必须遵守下列规则:

​ • 必须以字母或下划线开头;

​ • 只能由字母、数字和下划线组成。

2、函数

要调用一个函数,需要在函数名后跟上一对圆括号,并在圆括号中提供参数,和数学记法几乎一模一样:

function_name(argument1, argument2, ...)

3、属性和方法

谈到对象时,变量被称作属性(attribute)1 ,函数被称作方法(method)。你可以通过属性访问对象的数据,而方法可以用来执行某种操作。你可以通过点号访问属性和方法,比如 myobject.attribute 和 myobject.method()。

如果你在写一个赛车游戏,那么很可能需要表示车的对象。car 对象应该有一个 speed 属性,这样你就可以通过 car.speed 来获取车辆的当前速度。或许还可以通过调用加速方法 car.acc.accelerate(10) 来让车辆加速,即让车速增加到每小时 10 英里。

对象的类型及其行为是由类(class)定义的,因此在前面的例子中,你可能需要编写一个 Car 类。从 Car 类构造 car 对象的过程叫作实例化(instantiation)。要实例化一个对象,需要像调用函数那样去调用类:car = Car()。

3.1.2 数值类型

int 和 float 分别表示整数(integer)和浮点数(floating-point number)。通过内置的 type 函数可以获得指定对象的类型:

In [3]: type(4)
Out[3]: int
In [4]: type(4.4)
Out[4]: float

强制让一个数字成为 float 类型而不是 int 类型,可以在后面加一个小数点,或者使用 float 构造器:

In [5]: type(4.)
Out[5]: float
In [6]: float(4)
Out[6]: 4.0

对于整型也是一样的,int 构造器可以将一个 float 值转换为 int。如果小数部分不为零,那么转换时会直接舍去。

In [7]: int(4.9)
Out[7]: 4

Excel 单元格永远保存的是浮点数:从 Excel 单元格读取数字的时候,可能需要先把 float 转换为 int,然后才能把它传给一个需要整型参数的函数。原因是即使 Excel 显示的是整数,但在背后它总是以浮点数形式存储。

3.1.3 布尔值

Python 中,布尔类型只有 True 和 False 两种取值,Python 中的布尔运算符 and、or 和 not 全是小写形式。每个 Python 对象都可以被视作 True 或 False。大部分的对象会被视作 True,但 None、False、0 或空数据类型 [ 比如空字符串(下一节中会讲到字符串)] 会被视作 False。

None 是一个内置的常量,按照官方文档的说法,它代表“没有值”(the absence of a value)。如果一个函数没有显式地返回值,那么它实际上返回的就是 None。None 可以用来表示 Excel 中的空单元格。

3.1.4 字符串

Python 中的字符串既可以用双引号(")来表示,也可以用单引号(')来表示。唯一的要求是字符串的首尾必须是同一种引号。可以用 + 来拼接字符串,或者用 * 来重复字符串的内容。如果发现字符串的内容还是需要转义,可以用反斜杠来转义字符:

In [31]: print("Don't wait! " + 'Learn how to "speak" Python.')
Don't wait! Learn how to "speak" Python.
In [32]: print("It's easy to \"escape\" characters with a leading \\.")
It's easy to "escape" characters with a leading \.

当字符串中包含变量的值时,通常可以使用 f 字符串(f-string,格式化字符串字面量,formatted string literal 的缩写)来处理。

In [33]: # 注意Python如何在一行中为多个变量赋予多个值
 first_adjective, second_adjective = "free", "open source"
 f"Python is {first_adjective} and {second_adjective}."
Out[33]: 'Python is free and open source.'

转换大小写:

In [34]: "PYTHON".lower()
Out[34]: 'python'
In [35]: "python".upper()
Out[35]: 'PYTHON'

获取帮助:在 Jupyter 笔记本中,敲入对象后面的点之后按 Tab 键,比如 “python”.。画面上会出现一个下拉菜单,其中包含了这个对象提供的所有属性和方法。如果你的光标停在一个方法上,比如停在 “python”.upper() 的括号中,按下快捷键 Shift+Tab 就可以获得这个函数的描述信息。VS Code 会以提示的形式自动显示这些信息。如果你在 Anaconda Prompt 中运行 Python REPL,使用dir(“python”) 可以获得可用的属性,而使用 help(“python”,upper) 可以打印 upper 方法的描述信息。除此之外,也应该经常看一下 Python 的在线文档。如果你在找 pandas 之类的第三方包的文档,可以在 PyPI(Python 包索引)中搜索对应的包,在这里可以找到这些包的主页和文档的链接。

3.2 索引和切片

索引和切片让你可以访问一个序列的指定元素。

3.2.1 索引

Python 的索引从 0 开始,意思就是说序列的第一个元素通过 0 来引用。负索引从 -1 开始,你可以用负索引从序列末端引用元素。

在这里插入图片描述

索引的语法如下:

sequence[index]

访问字符串的指定元素:

In [36]: language = "PYTHON"
In [37]: language[0]
Out[37]: 'P'
In [38]: language[1]
Out[38]: 'Y'
In [39]: language[-1]
Out[39]: 'N'
In [40]: language[-2]
Out[40]: 'O'
3.2.2 切片

如果你想从一个序列中获取一个以上的元素,就要用到切片(slicing)语法:

sequence[start:stop:step]

Python 使用的是左闭右开区间,意思是切片区间包含 start,但不包含 stop。如果省略了 start 或者 stop,则切片会分别包含从头开始或者从末尾开始的所有元素。step 决定了切片的方向和步长。如果令步长为 2,那么切片就会从左到右每两个元素取一个值;如果令步长为 -3,则切片会从右到左每 3 个元素取一个值。默认步长为 1:

In [41]: language[:3] # 同language[0:3]
Out[41]: 'PYT'
In [42]: language[1:3]
Out[42]: 'YT'
In [43]: language[-3:] # 同language[-3:6]
Out[43]: 'HON'
In [44]: language[-3:-1]
Out[44]: 'HO'
In [45]: language[::2] # 每两个元素取一个
Out[45]: 'PTO'
In [46]: language[-1:-4:-1] # 负步长从右到左
Out[46]: 'NOH'

Python 也可以将多次索引和切片操作串联起来。如果你想获得最后 3 个字符中的第二个,可以像下面这样做:

In [47]: language[-3:][1]
Out[47]: 'O'

上述代码和 language[-2] 是等价的,连续索引并不会简单到哪儿去。但是在索引和切片列表的时候,连续索引会显得更有条理一些。

3.3 数据结构

Python 提供了强大的数据结构以便于处理对象集合。本节会介绍列表字典元组集合。虽然每种数据结构有各自的特点,但它们有一个共同特点,即都能存储多个对象

3.3.1 列表

列表(list)可以存储不同数据类型的多个对象。用途广泛,可以随时使用。创建列表的语法如下:

[element1, element2, ...]

实例,下面是两个列表,一个保存了一些 Excel 文件的名称,另一个保存了几个数字:

In [48]: file_names = ["one.xlsx", "two.xlsx", "three.xlsx"]
 		 numbers = [1, 2, 3]

和字符串一样,列表也可以用加号进行拼接。下面的代码还体现了列表的一个特性,那就是它可以保存不同类型的对象:

In [49]: file_names + numbers
Out[49]: ['one.xlsx', 'two.xlsx', 'three.xlsx', 1, 2, 3]

列表也是对象,也可以包含其他列表作为元素。我称之为嵌套列表(nested list):

In [50]: nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

把这种嵌套列表写成多行,你就会发现列表可以很好地表示矩阵和工作表单元格。注意,这些方括号会隐式地让代码跨行(参见“跨行”)。通过索引和切片,你可以获得想要的任何元素。

In [51]: cells = [[1, 2, 3],
 				  [4, 5, 6],
 				  [7, 8, 9]]
In [52]: cells[1] # 第二行
Out[52]: [4, 5, 6]
In [53]: cells[1][1:] # 第二行的第二列和第三列
Out[53]: [5, 6]

更改列表中的元素:

In [56]: users = ["Linda", "Brian"]
In [57]: users.append("Jennifer") # 最常用的操作是向列表末尾追加元素
		 users
Out[57]: ['Linda', 'Brian', 'Jennifer']
In [58]: users.insert(0, "Kim") # 在索引0处插入"Kim"
		 users
Out[58]: ['Kim', 'Linda', 'Brian', 'Jennifer']

要删除一个元素,可以使用 pop 或者 del。pop 是一个方法,而 del 是一种 Python 语句:

In [59]: users.pop() # 在默认情况下,移除并返回最后一个元素
Out[59]: 'Jennifer'
In [60]: users
Out[60]: ['Kim', 'Linda', 'Brian']
In [61]: del users[0] # del会移除指定索引处的元素

可以对列表进行以下操作:

In [62]: len(users) # 长度
Out[62]: 2
In [63]: "Linda" in users # 检查users是否包含"Linda"
Out[63]: True
In [64]: print(sorted(users)) # 返回新的排好序的列表
 		 print(users) # 原列表保持不变
		 ['Brian', 'Linda']
		 ['Linda', 'Brian']
In [65]: users.sort() # 对原列表进行排序
 		 users
Out[65]: ['Brian', 'Linda']

也可以把 len 和 in 用在字符串上:

In [66]: len("Python")
Out[66]: 6
In [67]: "free" in "Python is free and open source."
Out[67]: True

要访问列表中的元素,可以通过元素的**位置(索引)**来引用一个元素——但并非任何时候都能知道元素的位置。

3.3.2 字典

字典(dictionary)是键到值的映射。你会经常遇到键 – 值对。创建字典最简单的方法如下:

{key1: value1, key2: value2, ...}

和索引一样,键也被放在方括号中。下面的代码中,一对货币(键)映射到了汇率(值):

In [68]: exchange_rates = {"EURUSD": 1.1152,
						   "GBPUSD": 1.2454,
						   "AUDUSD": 0.6161}
In [69]: exchange_rates["EURUSD"] # 访问EURUSD的汇率
Out[69]: 1.1152

下面的代码展示了如何修改既存的值以及添加新的键 – 值对:

In [70]: exchange_rates["EURUSD"] = 1.2 # 修改已经存在的值
 		 exchange_rates
Out[70]: {'EURUSD': 1.2, 'GBPUSD': 1.2454, 'AUDUSD': 0.6161}
In [71]: exchange_rates["CADUSD"] = 0.714 # 添加新的键–值对
 		 exchange_rates
Out[71]: {'EURUSD': 1.2, 'GBPUSD': 1.2454, 'AUDUSD': 0.6161, 'CADUSD': 0.714}

合并两个或多个字典的最简单的办法是将字典解包(unpack)后再合并到一个新的字典中。在字典前加上两个星号就可以进行解包。如果第二个字典包含第一个字典中的键,那么第一个字典中对应的值会被覆盖。

In [72]: {**exchange_rates, **{"SGDUSD": 0.7004, "GBPUSD": 1.2222}}
Out[72]: {'EURUSD': 1.2,
 		  'GBPUSD': 1.2222,
 		  'AUDUSD': 0.6161,
		  'CADUSD': 0.714,
		  'SGDUSD': 0.7004}

Python 3.9 引入了管道符号 作为专门的字典合并运算符。上面的表达式可以简化成如下代码:

exchange_rates | {"SGDUSD": 0.7004, "GBPUSD": 1.2222}

get 方法可以在键不存在时返回一个默认值:

In [75]: # currencies[100]会引发异常
		 # 除了100,还可以尝试任何不存在的键
		 currencies.get(100, "N/A")
Out[75]: 'N/A'
3.3.3 元组

元组(tuple)和列表类似,只不过它们是不可变的(immutable):一旦被创建,它们的元素就无法被修改。虽然很多时候元组和列表可以互换使用,但对于那些在整个程序中都不会发生改变的集合来说,元组是不二之选。元组是通过多个被逗号分隔的值创建的:

mytuple = element1, element2, ...

使用圆括号通常更易于阅读:

In [76]: currencies = ("EUR", "GBP", "AUD")

可以使用访问数组的方法来访问元组,只是不能修改元组的元素。拼接元组会在“暗地里”创建一个新的元组,然后再把新元组绑定到你的变量上:

In [77]: currencies[0] # 访问第一个元素
Out[77]: 'EUR'
In [78]: # 拼接元组会返回一个新元组
 		 currencies + ("SGD",)
Out[78]: ('EUR', 'GBP', 'AUD', 'SGD')

附录 C 中会解释可变对象不可变对象之间的区别

3.3.4 集合

集合(set)是一种没有重复元素的集合(collection)。你自然可以把集合用于集合论的运算中,但在实践中它们经常被用于列表去重或者元组去重。使用花括号创建集合:

{element1, element2, ...}

要对列表或者元组进行去重,可以像下面这样使用 set 构造器

In [79]: set(["USD", "USD", "SGD", "EUR", "USD", "EUR"])
Out[79]: {'EUR', 'SGD', 'USD'}

还可以进行像交集和并集之类的集合论运算:

In [80]: portfolio1 = {"USD", "EUR", "SGD", "CHF"}
		 portfolio2 = {"EUR", "SGD", "CAD"}
In [81]: # 同 portfolio2.union(portfolio1)
		 portfolio1.union(portfolio2)
Out[81]: {'CAD', 'CHF', 'EUR', 'SGD', 'USD'}
In [82]: # 同 portfolio2.intersection(portfolio1)
		 portfolio1.intersection(portfolio2)
Out[82]: {'EUR', 'SGD'}

回顾一下刚刚认识的 4 种数据结构,使用前面用过的字面量(literal)记法。另外,列出它们的构造器。和字面量一样,构造器可以创建对应的数据结构,并且通常用于数据结构之间的相互转换。例如,要把元组转换为列表,可以执行以下操作:

In [83]: currencies = "USD", "EUR", "CHF"
 		 currencies
Out[83]: ('USD', 'EUR', 'CHF')
In [84]: list(currencies)
Out[84]: ['USD', 'EUR', 'CHF']

在这里插入图片描述

3.4 控制流

介绍 if 语句、for 循环和 while 循环。if 只会在满足特定条件时执行特定的代码,for 循环和 while 循环会反复执行代码块中的代码。

3.4.1 代码块和pass语句

代码块(code block)界定了一段源代码,这段代码会用于一些特定的目的。在 Python 中,代码块通过缩进来体现,而不像包括 VBA 在内的大部分编程语言那样——使用花括号。这就是所谓的有特殊含义的空白(significant white space)。Python 社区坚持使用 4 个空格作为缩进,不过你通常只需要敲一次 Tab 键就行了。Jupyter 笔记本和 VS Code 都会自动将 Tab 键转换为 4 个空格。代码块的前一行总是会以冒号结尾。一旦某一行没有被缩进,代码块就自然结束了。

3.4.2 if语句和条件表达式

在 Python 中,if 语句本身不需要任何的圆括号。要检查一个值是否为 True,并不需要显式地写这样一个表达式。

条件表达式(conditional expression)或者三元运算符(ternary operator)可以以一种更紧凑的形式编写 if/else 语句:

In [88]: is_important = False
		 print("important") if is_important else print("not important")
not important
3.4.3 for 循环和 while 循环

for 循环会对一个序列 [ 比如列表元组字符串(记住,字符串就是字符的序列)] 进行迭代。

In [89]: currencies = ["USD", "GBP", "AUD"]
		 for currency in currencies:
		 	 print(currency)
USD 
GBP
AUD

在 Python 中,如果你在 for 循环中需要一个计数器变量,那么可以用内置的 range 函数enumerate 函数。先来看看 range,它会提供一连串的数字。你可以只提供一个 stop 参数,也可以同时提供 start 参数和 stop 参数,还可以提供一个可选的 step 参数。和切片类似,range 产生的区间包含 start,但不包含 stop,step 决定了步长,默认为 1:

range(stop)
range(start, stop, step)

range 会延迟求值,意思就是说只要你不明确要求求值,它就不会产生指定的序列:

In [90]: range(5)
Out[90]: range(0, 5)

将 range 转换为列表可以解决这个问题:

In [91]: list(range(5)) # stop参数
Out[91]: [0, 1, 2, 3, 4]
In [92]: list(range(2, 5, 2)) # start、stop和step 3个参数
Out[92]: [2, 4]

不过大部分时候没必要把 range 包装成一个列表:

for i in range(3):
	print(i)
# 输出结果 	
0
1
2

如果在迭代序列时需要一个计数器变量,那么可以使用 enumerate。它会返回一系列 (index, element) 元组。 在默认情况下,索引从 0 开始,每次循环加 1。在循环中可以这样使用 enumerate:

for i, currency in enumerate(currencies):
    print(i, currency)
# 输出结果     
0 USD
1 GBP
2 AUD

在元组和集合中进行循环与在列表中类似。在字典中进行循环时,Python 会按照键进行循环:

exchange_rates = {"EURUSD": 1.1152,
                  "GBPUSD": 1.2454,
                  "AUDUSD": 0.6161}
for currency_pair in exchange_rates:
    print(currency_pair)
# 输出结果    
EURUSD
GBPUSD
AUDUSD

items 方法可以以元组的形式同时获得键和对应的值:

for currency_pair, exchange_rate in exchange_rates.items():
    print(currency_pair, exchange_rate)
    
# 输出结果 
EURUSD 1.1152
GBPUSD 1.2454
AUDUSD 0.6161

break 语句可以跳出循环:

for i in range(15):
    if i == 2:
        break 
    else:
        print(i)
        
# 输出结果
0
1

可以使用 continue 语句跳过本轮循环的剩余部分。即程序会使用下一个元素进入下一轮迭代:

for i in range(4):
    if i == 2:
        continue
    else:
        print(i)
        
# 输出结果
0 
1
3

使用 while 循环,循环会在条件不满足时停止:

n = 0
while n <= 2:
    print(n)
    n += 1
    
# 输出结果
0
1
2

增强赋值:上一个例子中使用了增强赋值(augmented assignment)的写法:n += 1。这和 n = n + 1 是一样的。前面介绍过的其他算术运算符也可以采用同样的写法,比如,可以写成 n -= 1。

3.4.4 列表、字典和集合推导式

假设有如下的货币名称对,你想把美元在后面的元素挑出来。你可能会写下面这样一个 for 循环:

currency_pairs = ["USDJPY", "USDGBP", "USDCHF",
                  "USDCAD", "AUDUSD", "NZDUSD"]
usd_quote = []
for pair in currency_pairs:
    if pair[3:] == "USD":
        usd_quote.append(pair[:3])
usd_quote、
# 输出结果
['AUD', 'NZD']

这种情况用列表推导式(list comprehension)会更简单。列表推导式是一种更简洁的列表创建方法。

[pair[:3] for pair in currency_pairs if pair[3:] == "USD"]
# 输出结果
['AUD', 'NZD']

字典也有字典推导式:

In [105]: exchange_rates = {"EURUSD": 1.1152,
 							"GBPUSD": 1.2454,
							"AUDUSD": 0.6161}	
		  {k: v * 100 for (k, v) in exchange_rates.items()}
Out[105]: {'EURUSD': 111.52, 'GBPUSD': 124.54, 'AUDUSD': 61.61}

集合也有集合推导式:

In [106]: {s + "USD" for s in ["EUR", "GBP", "EUR", "NZD", "NZD"]}
Out[106]: {'EURUSD', 'GBPUSD', 'NZDUSD'}

3.5 组织代码

如何让代码形成可维护的结构:首先会介绍函数的核心知识,然后如何将代码分成不同的 Python 模块。

3.5.1 函数

即使只是用 Python 来写一些简单的脚本,你仍然会经常编写函数。函数是所有编程语言中最重要的构造,它们可以让你在程序的任何地方重用同样的代码。

1. 定义函数

在 Python 中,需要使用 def 关键字来自定义函数,def 代表函数定义。函数定义的第一行以冒号结束,函数的主体需要缩进。

def function_name(required_argument, optional_argument=default_value, ...):
 	return value1, value2, ...

必需参数:必需参数(required argument)没有默认值。参数之间用逗号隔开。

可选参数:为参数提供默认值之后,它就成了可选参数(optional argument)。如果没有有意义的默认值,则通常用 None 作为可选参数的默认值。

返回值:return 语句定义了函数的返回值。如果省略了返回值,那么函数就会自动返回 None。Python 允许你返回以逗号隔开的多个返回值,这很方便。

来定义一个函数练习一下,这个函数可以将华氏度或者开氏度转换为摄氏度:

In [107]: def convert_to_celsius(degrees, source="fahrenheit"):
			  if source.lower() == "fahrenheit":
				  return (degrees-32) * (5/9)
			  elif source.lower() == "kelvin":
				  return degrees - 273.15
 			  else:
 				  return f"Don't know how to convert from {source}"

字符串的 lower 方法可以将给定字符串转换为小写,这样就可以在保持比较字符串的代码照常工作的前提下,接受任何大小写形式的 source 字符串了。完成 convert_to_celsius 的定义后,来看看如何调用它。

2. 调用函数

调用一个函数,可以在函数名后加上一对圆括号,并在其中给出参数。

value1, value2, ... = function_name(positional_arg, arg_name=value, ...)

位置参数:如果将一个值作为位置参数(positional argument,即上面的 positional_arg)传递,那么这个值会被传递给对应位置上的参数。

关键字参数:以 arg_name=value 这种形式传递的参数,就是关键字参数(keyword argument)。关键字参数的好处是可以以任意顺序传递参数,并且对于读者来说更加直观易懂。如果函数被定义成 f(a, b),则可以像这样调用:f(b=1, a=2)。

下面来尝试一下 convert_to_celsius 函数,看看它是如何工作的:

In [108]: convert_to_celsius(100, "fahrenheit") # 位置参数
Out[108]: 37.77777777777778
In [109]: convert_to_celsius(50) # 使用默认值(fahrenheit)
Out[109]: 10.0
In [110]: convert_to_celsius(source="kelvin", degrees=0) # 关键字参数
Out[110]: -273.15
3.5.2 模块和import语句

为大型项目编写代码时,在一定的时候会需要将代码分成不同的文件,从而保持一种可维护的结构。

Python 文件的扩展名为 .py,通常我们会把主要的文件称作脚本(script)。如果你想让你的主脚本获得来自其他文件的概念,则需要先导入(import)那个功能。在这种情况下,Python 源文件被称为模块(module)。

模块只会被导入一次:如果再一次运行 import temperature 单元格,你会注意到 print 函数不会输出任何内容。这是因为 Python 模块在每个会话中只会被导入一次。如果你要导入的模块发生了更改,则需要重启 Python 解释器才能让更改体现出来。在 Jupyter 笔记本中,需要点击“内核 > 重启”。

在这里插入图片描述

在使用 import x from y 这样的语法时,你只导入了指定的对象。这些对象被直接导入主脚本的命名空间(namespace)中,也就是说,如果不看这些 import 语句,你就说不清被导入的对象是在你的 Python 脚本(或者 Jupyter 笔记本)中还是在另一个模块中定义的。

不要让你的脚本和既存的包重名:一个常见的错误根源是给你的 Python 文件取一个和既存的包同样的名字。如果你要创建一个测试 pandas 功能的文件,那么不要将其命名为 pandas.py,因为这会造成冲突。

3.5.3 datetime类

要在 Python 中处理日期和时间,可以导入标准库中的 datetime 模块。这个模块包含了一个也叫 datetime 的类,可用于创建 datetime 对象。由于这个类和它所在的模块同名,可能会造成混淆,因此在本书中我会遵循这样的导入规则:import datetime as dt。这样可以更容易区分模块(dt)和类(datetime)

到目前为止,我们大部分时候是用字面量(literal)来创建列表和字典之类的对象。字面量指的是一种会被 Python 识别为特定类型对象的语法。对于列表来说就是像 [1, 2, 3] 这种写法。然而,大部分的对象需要调用对应的类来创建——这个过程被称为实例化(instantiation),因此对象也被称作类实例(class instance)。和调用函数一样,调用类也需要在类名后跟上一对圆括号,并在圆括号中提供参数。要实例化 datetime 对象,需要像下面这样调用对应的类:

import datetime as dt
dt.datetime(year, month, day, hour, minute, second, microsecond, timezone)
In [118]: # 将datetime模块导入为dt
		  import datetime as dt
In [119]: # 调用timestamp以创建datetime对象
 		  timestamp = dt.datetime(2020, 1, 31, 14, 30)
		  timestamp
Out[119]: datetime.datetime(2020, 1, 31, 14, 30)
In [120]: # datetime对象提供了多种属性,比如,想要知道它是几号
		  timestamp.day
Out[120]: 31 
In [121]: # 两个datetime对象求差会返回一个timedelta对象
		  timestamp - dt.datetime(2020, 1, 14, 12, 0)
Out[121]: datetime.timedelta(days=17, seconds=9000)
In [122]: # 也可以对timedelta进行同样的操作
		  timestamp + dt.timedelta(days=1, hours=4, minutes=11)
Out[122]: datetime.datetime(2020, 2, 1, 18, 41)

要将 datetime 对象格式化(format)成字符串,可以使用 strftime 方法;要解析(parse)字符串并将其转换为 datetime 对象,可以使用 strptime 函数:

In [123]: # 以特定方式格式化datetime对象
 		  # 也可以使用f字符串: f"{timestamp:%d/%m/%Y %H:%M}"
		  timestamp.strftime("%d/%m/%Y %H:%M")
Out[123]: '31/01/2020 14:30'
In [124]: # 将字符串解析为datetime对象
 		  dt.datetime.strptime("12.1.2020", "%d.%m.%Y")
Out[124]: datetime.datetime(2020, 1, 12, 0, 0)

3.6 PEP 8:Python风格指南

Python使用所谓的 Python 改进提案Python Enhancement ProposalsPEP)来讨论新语言特性的引入。Python 代码的风格指南就是其中之一。这些提案一般用数字来表示,代码风格指南就被称作 PEP 8。PEP 8 是一系列提供给 Python 社区的风格建议。如果使用相同代码的所有人都遵循相同的代码风格,那么写出的代码可读性就会更高。在开源的世界中,会有很多互不相识的程序员开发同一个项目,此时遵循相同的代码风格会显得尤为重要。

"""这个脚本展示了一些PEP 8的规则 ➊ 
"""
import datetime as dt ➋

TEMPERATURE_SCALES = ("fahrenheit", "kelvin",
 					  "celsius") ➌ 					  
➍

class TemperatureConverter: ➎
 	pass # 暂时不做任何事 ➏
def convert_to_celsius(degrees, source="fahrenheit"): ➐
 	"""这个函数将华氏度或开氏度转化为摄氏度 ➑
 	"""
 	if source.lower() == "fahrenheit": ➒
 		return (degrees-32) * (5/9) ➓
 	elif source.lower() == "kelvin":
 		return degrees - 273.15
 	else:
 		return f"Don't know how to convert from {source}"
 		
celsius = convert_to_celsius(44, source="fahrenheit")	11
non_celsius_scales = TEMPERATURE_SCALES[:-1]	12

print("Current time: " + dt.datetime.now().isoformat())
print(f"The temperature in Celsius is: {celsius}")

➊ 在文件顶部用文档字符串(docstring)解释这个脚本或者模块做了些什么。文档字符串是一种特殊的字符串,它用 3 个引号引用。除了作为代码的文档,它还可以用来编写跨越多行的字符串。如果你的字符串中有很多双引号或单引号,那么也可以用文档字符串来避免转义。

➋ 所有的导入语句都应该放在文件顶部,一行一个导入。从标准库导入的内容放在前面,然后是第三方包,最后是自己编写的模块。

➌ 用大写字母和下划线表示常量。每行的长度不超过 79 个字符。尽可能地利用圆括号、方括号或花括号隐式跨行

➍ 类、函数和其他代码之间用两个空行隔开。

➎ 尽管很多类像 datetime 一样使用小写字母命名,但是你自己编写的类也应该使用首字母大写的名称(CapitalizedWords)。有关类的更多内容请参见附录 C。

行内注释应该和代码间隔至少两个空格。代码块应该用 4 个空格缩进。

➐ 在能够提高可读性的情况下,函数和参数应该使用小写字母和下划线命名。不要在参数名和默认值之间使用空格。

➑ 函数的文档字符串应当列出函数参数并解释其意义

➒ 冒号前后不要使用空格。

➓ 可以在算术运算符前后使用空格。如果同时使用了优先级不同的运算符,则应当考虑在优先级最低的运算符前后添加空格。在本例中,由于乘号的优先级最低,因此它的前后被添加了空格。

11:变量名称使用小写字母。在可以提升可读性的前提下使用下划线。为变量赋值时,在等号前后添加空格。不过在调用函数时,不要在关键字参数前后使用空格。

12:在进行索引和切片时,不要在方括号前后使用空格。

3.6.1 PEP 8 和 VS Code

在使用 VS Code 时,确保代码严格遵循 PEP 8 的最简单方法是使用代码检查器(linter)。代码检查器会检查源代码中的语法和风格错误。vscode怎么检查代码 • Worktile社区

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.6.2 类型提示

Python 3.5 引入了一个叫作类型提示(type hint)的特性。类型提示也被称为类型标注(type annotation),它允许你声明变量的数据类型。类型提示并不是强制性的,它也不会影响 Python 解释器执行代码。其主要目的是让 VS Code 之类的文本编辑器可以在代码执行前捕获更多错误,不过它也可以增强编辑器的自动补全功能

mypy 是用于有类型标注的 Python 代码的最受欢迎的类型检查器,是 VS Code 提供的一种代码检查器。要理解类型标注如何工作,先来看下面这段没有类型提示的代码:

x = 1 
def hello(name):
 	return f"Hello {name}!"

现在加上类型提示:

x: int = 1 
def hello(name: str) -> str:
 	return f"Hello {name}!"

11:变量名称使用小写字母。在可以提升可读性的前提下使用下划线。为变量赋值时,在等号前后添加空格。不过在调用函数时,不要在关键字参数前后使用空格。

12:在进行索引和切片时,不要在方括号前后使用空格。

3.6.1 PEP 8 和 VS Code

在使用 VS Code 时,确保代码严格遵循 PEP 8 的最简单方法是使用代码检查器(linter)。代码检查器会检查源代码中的语法和风格错误。vscode怎么检查代码 • Worktile社区

在这里插入图片描述

3.6.2 类型提示

Python 3.5 引入了一个叫作类型提示(type hint)的特性。类型提示也被称为类型标注(type annotation),它允许你声明变量的数据类型。类型提示并不是强制性的,它也不会影响 Python 解释器执行代码。其主要目的是让 VS Code 之类的文本编辑器可以在代码执行前捕获更多错误,不过它也可以增强编辑器的自动补全功能

mypy 是用于有类型标注的 Python 代码的最受欢迎的类型检查器,是 VS Code 提供的一种代码检查器。要理解类型标注如何工作,先来看下面这段没有类型提示的代码:

x = 1 
def hello(name):
 	return f"Hello {name}!"

现在加上类型提示:

x: int = 1 
def hello(name: str) -> str:
 	return f"Hello {name}!"

一般来说,类型提示在较大的项目中才会更有用。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐