Python是一种了不起的编程语言!它是开发人工智能和机器学习应用程序的最流行语言之一。凭借非常容易学习的语法,Python有一些特殊的功能,使其区别于其他语言。在本教程中,我们将谈论Python编程语言的一些特殊属性。
完成本教程后,你将知道。
- 用于列表和字典理解的结构
- 如何使用 zip 和枚举函数
- 什么是函数上下文和装饰器
- Python 中生成器的作用是什么?
让我们开始吧。
教程概述
本教程分为4个部分,它们是:
- 列表和字典的理解
- Zip和枚举函数
- 函数上下文和装饰器
- Python中的生成器,以Keras生成器为例
导入部分
本教程中使用的库在下面的代码中被导入。
from tensorflow import keras
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np
import matplotlib.pyplot as plt
import math
列表理解
列表理解提供了一种简短的语法,用于从现有列表中创建新的列表。例如,假设我们需要一个新的列表,其中每个新项是旧项乘以3。一种方法是使用for 循环,如下所示。
original_list = [1, 2, 3, 4]
times3_list = []
for i in original_list:
times3_list.append(i*3)
print(times3_list)
[3, 6, 9, 12]
使用列表理解的较短方法只需要一行代码。
time3_list_awesome_method = [i*3 for i in original_list]
print(time3_list_awesome_method)
[3, 6, 9, 12]
你甚至可以根据一个特殊的标准来创建一个新的列表。例如,如果我们希望只有偶数被添加到新的列表中。
even_list_awesome_method = [i for i in original_list if i%2==0]
print(even_list_awesome_method)
[2, 4]
也可以有一个与上述相关的else 。例如,我们可以保留所有的偶数,并将奇数替换为零。
new_list_awesome_method = [i if i%2==0 else 0 for i in original_list]
print(new_list_awesome_method)
[0, 2, 0, 4]
列表理解也可以用来替换嵌套的循环。例如下面这个。
colors = ["red", "green", "blue"]
animals = ["cat", "dog", "bird"]
newlist = []
for c in colors:
for a in animals:
newlist.append(c + " " + a)
print(newlist)
['red cat', 'red dog', 'red bird', 'green cat', 'green dog', 'green bird', 'blue cat', 'blue dog', 'blue bird']
可以这样做,在列表理解的内部有两个 "for"。
colors = ["red", "green", "blue"]
animals = ["cat", "dog", "bird"]
newlist = [c+" "+a for c in colors for a in animals]
print(newlist)
语法
列表理解的语法如下。
newlist = [expression for item in iterable if condition == True] 。
或者
newList = [expression if condition == True else expression for item in iterable]。
字典理解
字典理解与列表理解类似,只是现在我们有(键,值)对。这里有一个例子;我们将通过将字符串 'number' 连接到每个值来修改字典的每个值。
original_dict = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
new_dict = {key:'number ' + value for (key, value) in original_dict.items()}
print(new_dict)
{1: 'number one', 2: 'number two', 3: 'number three', 4: 'number four'}
同样,条件式也是可以的。我们可以根据新字典中的一个标准来选择添加 (key, value) 对。
#Only add keys which are greater than 2
new_dict_high_keys = {key:'number ' + value for (key, value) in original_dict.items() if key>2}
print(new_dict_high_keys)
# Only change values with key>2
new_dict_2 = {key:('number ' + value if key>2 else value) for (key, value) in original_dict.items() }
print(new_dict_2)
{3: 'number three', 4: 'number four'}
{1: 'one', 2: 'two', 3: 'number three', 4: 'number four'}
Python 中的枚举器和 Zip
在 Python 中,迭代器被定义为任何可以返回其所有项目的数据结构,一次一个。这样你就可以使用一个for 循环来进一步逐一处理所有的项目。Python有两个额外的结构,使for 循环更容易使用,即:enumerate() 和zip() 。
枚举
在传统的编程语言中,你需要一个循环变量来遍历一个容器的不同值。在Python中,这被简化为让你访问一个循环变量和一个可迭代对象的值。enumerate(x) 函数返回两个可迭代对象。一个可迭代对象从0到len(x)-1不等。另一个是迭代对象,其值等于x的项。下面是一个例子。
name = ['Triangle', 'Square', 'Hexagon', 'Pentagon']
# enumerate returns two iterables
for i, n in enumerate(name):
print(i, 'name: ', n)
0 name: Triangle
1 name: Square
2 name: Hexagon
3 name: Pentagon
默认情况下,enumerate从0开始,但如果我们指定的话,我们可以从其他数字开始。这在某些情况下是很有用的,比如说。
data = [1,4,1,5,9,2,6,5,3,5,8,9,7,9,3]
for n, digit in enumerate(data[5:], 6):
print("The %d-th digit is %d" % (n, digit))
The 6-th digit is 2
The 7-th digit is 6
The 8-th digit is 5
The 9-th digit is 3
The 10-th digit is 5
The 11-th digit is 8
The 12-th digit is 9
The 13-th digit is 7
The 14-th digit is 9
The 15-th digit is 3
Zip
Zip允许你创建一个图元的可迭代对象。Zip将多个容器作为参数,并通过从每个容器中配对一个项目来创建第i个元组。第i个元组就是。如果传递的对象有不同的长度,那么形成的元组总数的长度等于传递对象的最小长度。
下面是同时使用zip() 和enumerate() 的例子。
sides = [3, 4, 6, 5]
colors = ['red', 'green', 'yellow', 'blue']
shapes = zip(name, sides, colors)
# Tuples are created from one item from each list
print(set(shapes))
# Easy to use enumerate and zip together for iterating through multiple lists in one go
for i, (n, s, c) in enumerate(zip(name, sides, colors)):
print(i, 'Shape- ', n, '; Sides ', s)
{('Triangle', 3, 'red'), ('Square', 4, 'green'), ('Hexagon', 6, 'yellow'), ('Pentagon', 5, 'blue')}
0 Shape- Triangle ; Sides 3
1 Shape- Square ; Sides 4
2 Shape- Hexagon ; Sides 6
3 Shape- Pentagon ; Sides 5
函数上下文
Python 允许嵌套函数,你可以在一个外层函数中定义一个内部函数。在Python中,有一些与嵌套函数有关的很棒的特性。
- 外层函数可以向内层函数返回一个句柄
- 即使外层函数结束执行,内层函数也会保留它的所有环境和它的局部变量,以及它的外层函数中的变量。
下面给出了一个例子,并在注释中作了解释。
def circle(r):
area = 0
def area_obj():
nonlocal area
area = math.pi * r * r
print("area_obj")
return area_obj
def circle(r):
area_val = math.pi * r * r
def area():
print(area_val)
return area
# returns area_obj(). The value of r passed is retained
circle_1 = circle(1)
circle_2 = circle(2)
# Calling area_obj() with radius = 1
circle_1()
# Calling area_obj() with radius = 2
circle_2()
3.141592653589793
12.566370614359172
Python中的装饰器
装饰器是Python的一个强大特性。你可以使用装饰器来定制一个类或一个函数的工作。可以把它们看作是一个应用于另一个函数的函数。使用带有@ 符号的函数名来定义被装饰函数上的装饰器函数。装饰器以一个函数作为参数,给人以很大的灵活性。
考虑下面这个函数square_decorator() ,它接受一个函数作为参数,同时也返回一个函数。
- 内部嵌套的函数
square_it()需要一个参数arg. square_it()函数将该函数应用于arg,并将结果平方化。- 我们可以将诸如
sin这样的函数传递给square_decorator(),它反过来会返回 。 - 你也可以编写你自己的自定义函数,并使用特殊的@符号对其使用
square_decorator()函数,如下图所示。函数plus_one(x)返回x+1。这个函数由square_decorator()来装饰,因此,我们得到。
def square_decorator(function):
def square_it(arg):
x = function(arg)
return x*x
return square_it
size_sq = square_decorator(len)
print(size_sq([1,2,3]))
sin_sq = square_decorator(math.sin)
print(sin_sq(math.pi/4))
@square_decorator
def plus_one(a):
return a+1
a = plus_one(3)
print(a)
9
0.4999999999999999
16
Python中的生成器
Python中的生成器允许你生成序列。生成器不是写一个return 语句,而是通过多个yield 语句返回多个值。对函数的第一次调用从产量中返回第一个值。第二次调用会从yield中返回第二个值,以此类推。
生成器函数可以通过next().每次调用next() ,都会返回下一个yield 的值。下面是一个生成斐波那契数列的例子,直到一个给定的数字x 。
def get_fibonacci(x):
x0 = 0
x1 = 1
for i in range(x):
yield x0
temp = x0 + x1
x0 = x1
x1 = temp
f = get_fibonacci(6)
for i in range(6):
print(next(f))
0
1
1
2
3
5
Keras中数据生成器的例子
生成器的一个用途是Keras中的数据生成器。它之所以有用,是因为我们不想把所有的数据都保存在内存中,而是想在训练循环需要它的时候临时创建。请记住,在Keras中,神经网络模型是分批训练的,所以生成器是为了发出成批的数据。下面的函数来自我们之前的文章《使用CNN进行金融时间序列预测》。
def datagen(data, seq_len, batch_size, targetcol, kind):
"As a generator to produce samples for Keras model"
batch = []
while True:
# Pick one dataframe from the pool
key = random.choice(list(data.keys()))
df = data[key]
input_cols = [c for c in df.columns if c != targetcol]
index = df.index[df.index < TRAIN_TEST_CUTOFF]
split = int(len(index) * TRAIN_VALID_RATIO)
if kind == 'train':
index = index[:split] # range for the training set
elif kind == 'valid':
index = index[split:] # range for the validation set
# Pick one position, then clip a sequence length
while True:
t = random.choice(index) # pick one time step
n = (df.index == t).argmax() # find its position in the dataframe
if n-seq_len+1 < 0:
continue # can't get enough data for one sequence length
frame = df.iloc[n-seq_len+1:n+1]
batch.append([frame[input_cols].values, df.loc[t, targetcol]])
break
# if we get enough for a batch, dispatch
if len(batch) == batch_size:
X, y = zip(*batch)
X, y = np.expand_dims(np.array(X), 3), np.array(y)
yield X, y
batch = []
上面的函数是在pandas数据框架中随机选取一行作为起点,并将接下来的几行作为一个时间间隔的样本。这个过程重复多次,以收集许多时间间隔为一个批次。当我们收集到足够数量的时间间隔样本时,在上述函数的倒数第二行,使用yield 命令对该批次进行调度。你可能已经注意到,生成器函数没有返回语句。在这个例子中,该函数甚至会永远运行。这很有用,也很有必要,因为它允许我们的Keras训练过程随心所欲地运行许多epoch。
如果我们不使用生成器,我们将需要把数据帧转换为所有可能的时间间隔,并把它们保存在内存中用于训练循环。这将是大量的重复数据(因为时间间隔是重叠的),并占用大量的内存。
因为它很有用,所以Keras在库中预先定义了一些生成器函数。下面是ImageDataGenerator() 的例子。我们在x_train 中加载了32×32图像的cifar10 数据集。该数据通过flow() 方法连接到生成器。next() 函数返回下一批数据。在下面的例子中,有4次对next() 的调用。每次都返回8张图片,因为批次大小为8。
下面是整个代码,在每次调用next() 后都会显示所有的图像。
(x_train, y_train), _ = keras.datasets.cifar10.load_data()
datagen = ImageDataGenerator()
data_iterator = datagen.flow(x_train, y_train, batch_size=8)
fig,ax = plt.subplots(nrows=4, ncols=8,figsize=(18,6),subplot_kw=dict(xticks=[], yticks=[]))
for i in range(4):
# The next() function will load 8 images from CIFAR
X, Y = data_iterator.next()
for j, img in enumerate(X):
ax[i, j].imshow(img.astype('int'))
摘要
在本教程中,你发现了Python的特殊功能
具体来说,你学到了
- 列表和字典理解的目的
- 如何使用 zip 和 enumerate
- 嵌套函数、函数上下文和装饰器
- Python 中的生成器和 Python 中的 ImageDataGenerator

