理解这一行:list_of_tuples = [,x,y)x,y,data_one中的标签]

oba*_*311 8 python numpy list-comprehension

正如你已经明白我是一个初学者,我正在努力理解编写这个函数的"Pythonic方式"是什么.我知道其他线程可能包含对此的部分答案,但我不知道该寻找什么,因为我不明白这里发生了什么.

这行是我朋友发给我的代码,用于改进我的代码:

import numpy as np

#load_data:
def load_data():
    data_one = np.load ('/Users/usr/... file_name.npy') 
    list_of_tuples = []
    for x, y, label in data_one:
        list_of_tuples.append( (x,y) )
    return list_of_tuples

print load_data()
Run Code Online (Sandbox Code Playgroud)

"改进"版本:

import numpy as np

#load_data:
def load_data():
    data_one = np.load ('/Users/usr.... file_name.npy') 
    list_of_tuples = [(x,y) for x, y, label in data_one]
    return list_of_tuples

print load_data()
Run Code Online (Sandbox Code Playgroud)

我想知道:

  1. 这里发生了什么?
  2. 是更好还是更差的方式?因为它是"Pythonic"我认为它不适用于其他语言,所以也许最好习惯更普通的方式?

piR*_*red 16

list_of_tuples = [(x,y) for x, y, label in data_one]
Run Code Online (Sandbox Code Playgroud)

(x, y)是一个tuple< - 链接教程.

这是列表理解

    [(x,y) for x, y, label in data_one]
#   ^                                 ^
#   |       ^comprehension syntax^    |
# begin list                       end list   
Run Code Online (Sandbox Code Playgroud)

data_one是一个iterable并且是列表理解所必需的.在封面下,它们是循环,必须迭代某些东西.

x, y, label in data_one告诉我,我可以从data_one迭代器提供的每个元素中"解包"这三个项目.这就像for循环的局部变量,它在每次迭代时都会发生变化.

总的来说,这说:

列出一个元组列表,这些元组看起来就像(x, y)x, y, and label从iterable提供的每个项目中得到的位置data_one.把每一个xy一个叫做列表内的元组list_of_tuples.是的我知道我"打开包装" label并且从未使用它,我不在乎.

  • @TemporalWolf Python没有这样的"不关心"语法,`_`只是一个常规变量名. (6认同)
  • 我喜欢你的解释,只是我要添加的一件事:python支持`_`为"我知道你还有其他东西需要解压缩我不关心,所以只需扔掉它们:`[(x,y)for x_y,_ in data_one]`是有效的,虽然可读性较差. (4认同)
  • @StefanoSanfilippo虽然你是正确的,因为它不是一种特殊的语法,但值得注意的是,许多编辑者将`_`变量识别为"不关心"占位符并抑制与其相关的"未使用的变量"警告. (4认同)

Xav*_*ino 8

两种方式都是正确和有效的.你可能会把第一种方式与用C和其他语言完成的方式联系起来.这就是,你基本上运行for循环来遍历所有值,然后将它附加到元组列表中.

第二种方式是pythonic,但做同样的事情.如果你看一下[(x,y) for x, y, label in data_one](这是一个列表解析),你会发现你也在同一个数据上运行for循环,但结果将是(x, y),所有这些结果都将形成一个列表.所以它实现了同样的目标.

第三种方式(作为注释的响应添加)使用切片方法.

我准备了一个类似于你的小例子:

data = [(1, 2, 3), (2, 3, 4), (4, 5, 6)]

def load_data():
    list_of_tuples = []
    for x, y, label in data:
        list_of_tuples.append((x,y))
    return list_of_tuples

def load_data_2():
    return [(x,y) for x, y, label in data]

def load_data_3():
    return [t[:2] for t in data]
Run Code Online (Sandbox Code Playgroud)

他们都做同样的事情并返回,[(1, 2), (2, 3), (4, 5)]但他们的运行时间是不同的.这就是为什么列表理解是更好的方法.

当我运行第一种方法时,load_data()我得到:

%%timeit
load_data()
1000000 loops, best of 3: 1.36 µs per loop
Run Code Online (Sandbox Code Playgroud)

当我运行第二种方法时,load_data_2()我得到:

%%timeit
load_data_2()
1000000 loops, best of 3: 969 ns per loop
Run Code Online (Sandbox Code Playgroud)

当我运行第三种方法时,load_data_3()我得到:

%%timeit 
load_data_3()
1000000 loops, best of 3: 981 ns per loop
Run Code Online (Sandbox Code Playgroud)

第二个方式,列表解析,速度更快!


Lee*_*ton 5

"改进的"版本使用列表理解.这使代码声明性(描述你想要的)而不是命令式(描述如何得到你想要的).

声明性编程的优点是实现细节大多被遗漏,底层类和数据结构可以以最佳方式执行操作.例如,python解释器在上面的示例中可以进行的一个优化是预先分配正确的数组大小,list_of_tuples而不是在append()操作期间不断地继续调整数组大小.

为了帮助您开始列表推导,我将解释我通常开始编写它们的方式.对于列表L写这样的东西:

output = [x for x in L]
Run Code Online (Sandbox Code Playgroud)

对于每个元素L,提取变量(中心x)并可用于形成输出列表(x左侧).上面的表达式实际上没有任何作用,并且output相同L.势不可挡,它类似于:

output = []
for x in L:
    output.append(x)
Run Code Online (Sandbox Code Playgroud)

从这里开始,您可以意识到每个x实际上都是一个可以使用元组赋值解压缩的元组:

output = [x for x, y, label in L]
Run Code Online (Sandbox Code Playgroud)

这将创建一个新列表,仅包含列表中x每个元组的元素.

如果您想在输出列表中打包一个不同的元组,只需将其打包在左侧:

output = [(x,y) for x, y, label in L]
Run Code Online (Sandbox Code Playgroud)

这基本上就是您在优化版本中的最终结果.

您可以使用列表推导执行其他有用的操作,例如仅插入符合特定条件的值:

output = [(x,y) for x, y, label in L if x > 10]
Run Code Online (Sandbox Code Playgroud)

这是一个关于列表推导的有用教程,你可能会觉得有趣:http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/