繁体   English   中英

Python Lambda 计数/循环函数

[英]Python Lambda Count/Loop Function

如果这是在其他地方回答的问题,我很抱歉。 通过 Google 和 Stackforum 搜索,我没有找到任何可以推断答案的内容; 但我觉得其中的一部分是我。

我正在尝试将 lambdas 作为一个概念来解决,作为其中的一部分,我正在寻找使用它的方法。

所以,如果从函数的角度来看,这对于 lambda 来说是一件非常愚蠢的事情,请随时让我知道并解释。 但无论哪种方式,我仍然想知道答案/仍然想知道如何使用 python 语言来做到这一点。

因此,出于测试目的,我有:

my_test = 'test_name'
testlist = ['test_name', 'test_name_dup', 'test_name_dup_1', 'test_name_dup_3']

我希望使用 lambda 来创建一个循环并返回不在测试列表中的第一个 test_name_# 的函数。 该功能最终将应用于文件名,但出于测试目的,我不得不远离实际读取文件名——给了我太多的方法来搞砸了。

但是 my_test 必须能够更改,并且测试列表将是文件路径列表。

所以,我正在寻找一个类似的功能:

new_name = lambda x: my_test + '_' + str(x)

但是初始值应该是 x = 1,它应该一直持续到 new_name 不在 testlist 中。 似乎:

bool(new_name not in testlist)

可能是有用的东西。

但是我想不出一种方法来将初始 x 设置为 1,并让它循环使用 (x+1) 直到 bool 为真。

我知道这是可能的,因为我发现了一些 CRAZY lambda 示例,它们在文件中的行中循环。 我只是不能完全理解它们(并且没有任何方法可以与它们一起玩,因为它们正在处理我编程水平之外的事情。

在相关说明中,我可以在此循环的开头添加值吗? (即我可以让它检查 test_name,然后是 test_name_dup,然后是 test_name_dup_#)?

先谢谢您的帮助! Lambdas(虽然很酷)完全搞砸了我的脑袋。

Lambda 只是定义函数的另一种方式

def foo(x):
    return x + x

是相同的

foo = lambda x: x + x

所以让我们从一个函数开始做你想做的事:

def first_missing(items, base):
    for number in itertools.count():
        text = base + '_' + str(number)
        if text not in items:
             return text

首先要注意的是,您不能在 lambda 中使用循环。 所以我们需要在没有循环的情况下重写它。 相反,我们将使用递归:

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        if text not in items:
             return text
        else:
             return first_missing(items, base, number + 1)

现在,我们也不能在 lambda 中使用 if/else 块。 但是我们可以使用三元表达式:

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        return text if text not in items else first_missing(items, base, number + 1)

我们不能在 lambda 中使用局部变量,所以我们将使用一个技巧,默认参数:

def first_missing(items, base, number = 0):
        def inner(text = base + '_' + str(number)):
            return text if text not in items else first_missing(items, base, number + 1)
        return inner()

此时我们可以将inner重写为一个lambda:

def first_missing(items, base, number = 0):
        inner = lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1)
        return inner()

我们可以结合两行来去掉内部局部变量:

def first_missing(items, base, number = 0):
    return (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

最后,我们可以将整个事情变成一个 lambda:

first_missing = lambda: items, base, number = 0: (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

希望这能让您对自己可以做什么有所了解。 但是永远不要这样做,因为正如您所知,lambda 会使您的代码非常难以阅读。

在这种情况下不需要使用lambda ,一个简单的for循环就可以:

my_test  = 'test_name_dup'  
testlist = ['test_name', 'test_name_dup','test_name_dup_1', 'test_name_dup_3']

for i in xrange(1, len(testlist)):
    if my_test + '_' + str(i) not in testlist:
        break

print my_test + '_' + str(i)
> test_name_dup_2

如果你真的,真的想使用lambda来解决这个问题,你还必须了解 itertools、迭代器、过滤器等。 我将建立在 thg435 的答案上,以更惯用的方式编写并解释它:

import itertools as it

iterator = it.dropwhile(
    lambda n: '{0}_{1}'.format(my_test, n) in testlist,
    it.count(1))

print my_test + '_' + str(iterator.next())
> test_name_dup_2

理解上述解决方案的关键在于dropwhile()过程。 它接受两个参数:一个谓词和一个可迭代对象,并返回一个迭代器,只要谓词为真,它就会从可迭代对象中删除元素; 之后,返回每个元素。

对于可迭代对象,我正在传递count(1) ,这是一个迭代器,它生成从1开始的无限数量的整数。

然后dropwhile()开始消耗整数,直到谓词为假; 这是传递内联定义函数的好机会 - 这是我们的lambda 它依次接收每个生成的整数,检查字符串 test_name_dup_# 是否存在于列表中。

当谓词返回falsedropwhile()返回,我们可以通过调用next()来检索使其停止的值。

您可以将 lambda 与 itertools.dropwhile 结合使用:

import itertools
n = itertools.dropwhile(lambda n: 'test_name_dup_%d' % n in testlist, range(1, len(testlist))).next()

至于您的最后一个问题,您可以为名称编写一个生成器,例如:

def possible_names(prefix):
    yield prefix
    yield prefix + '_dup'
    n = 0
    while True:
        n += 1
        yield '%s_dup_%d' % (prefix, n)

然后在 dropwhile 中使用这个生成器:

unique_name = itertools.dropwhile(lambda x: x in testlist, possible_names('test_name')).next()
print unique_name

你有点跑题了。 Lambda 只不过是“简单”的函数,通常用于函数式编程中的快速语法。 它们是完美的配套内置函数“map”、“reduce”、“filter”,也适用于定义到itertools 中的更复杂的函数。 因此,对它们做的最有用的事情是生成/操作可迭代对象(尤其是列表)。 请注意,与列表推导式/普通循环相比,在大多数情况下,lambda 会减慢您的代码速度,并且会使其更难阅读。 下面是一个你想用 lambdas 做什么的例子。

>>> filter(lambda i: i!=(0 if len(testlist[i].split("_"))==3 else int(testlist[i].split("_")[-1])), range(len(testlist)))[0]
2

或者您可以通过 itertools 使用更复杂的函数。 无论如何,我强烈建议您不要将 lambda 用于此类任务,因为可读性很差。 我宁愿使用结构良好的 for 循环,它也更快。

[编辑]

为了证明 lambdas+builtins 并不比列表推导式快:考虑一个简单的问题,因为 x in range(1000) 创建一个 x 移位 5 的列表。

$ python -m timeit 'map(lambda x: x>>5, range(1000))' 1000 个循环,最好的 3 个:每个循环225 微秒

$ python -m timeit '[x>>5 for x in range(1000)]'10000 个循环,最好的 3 个:每个循环99.1 usec

在没有 lambda 的情况下,您的性​​能提高了 100% 以上。

我更喜欢列表理解或迭代器方法。 使我觉得很容易阅读和维护的简单衬垫。 坦率地说,lambdas 属于某些地方,在这里我认为它不太优雅的解决方案。

my_test = 'test_name'
prefix = 'test_name_dup_'
testlist = ['test_name','test_name_dup','test_name_dup_1','test_name_dup_3']

from itertools import count
print next('%s%d' % (prefix, i) for i in count(1) if '%s%d' % (prefix, i) not in testlist)

这将返回序列中第一个未找到的实例,我认为这是最干净的。

当然,如果你更喜欢一个确定范围的列表,你可以修改它成为一个列表理解:

print ['%s%d' % (prefix, i) for i in xrange(0,5) if '%s%d' % (prefix, i) not in testlist]

返回:

['test_name_dup_0', 'test_name_dup_2', 'test_name_dup_4']

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM