简体   繁体   English

Python Lambda 计数/循环函数

[英]Python Lambda Count/Loop Function

I'm sorry if this is a question answered elsewhere.如果这是在其他地方回答的问题,我很抱歉。 Searching through Google and Stackforum I didn't find anything from which I could extrapolate the answers;通过 Google 和 Stackforum 搜索,我没有找到任何可以推断答案的内容; but I feel like part of that is me.但我觉得其中的一部分是我。

I'm trying to work out lambdas as a concept, and as part of that I'm kinda looking for ways to use it.我正在尝试将 lambdas 作为一个概念来解决,作为其中的一部分,我正在寻找使用它的方法。

SO, if this is a colossally stupid thing to do with lambda from a function standpoint, feel free to let me know and explain.所以,如果从函数的角度来看,这对于 lambda 来说是一件非常愚蠢的事情,请随时让我知道并解释。 But either way, I still want to know the answer/still want to know how to do this with the python language.但无论哪种方式,我仍然想知道答案/仍然想知道如何使用 python 语言来做到这一点。

So, for testing purposes I have:因此,出于测试目的,我有:

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

I'm looking to use lambda to create one function that loops through and returns the first test_name_# that isn't in the testlist.我希望使用 lambda 来创建一个循环并返回不在测试列表中的第一个 test_name_# 的函数。 The functionality will eventually be applied to filenames, but for testing purposes I had to get away from actually reading the filenames--gave me too many more ways to mess something up.该功能最终将应用于文件名,但出于测试目的,我不得不远离实际读取文件名——给了我太多的方法来搞砸了。

But my_test has to be able to change, and the test list will be a list of filepaths.但是 my_test 必须能够更改,并且测试列表将是文件路径列表。

So, I'm looking for a function like:所以,我正在寻找一个类似的功能:

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

But the initial value should be x = 1, and it should continue until new_name is not in testlist.但是初始值应该是 x = 1,它应该一直持续到 new_name 不在 testlist 中。 Seems like:似乎:

bool(new_name not in testlist)

might be something work with.可能是有用的东西。

But I can't figure out a way to set the initial x to 1, and have it loop through with (x+1) until the bool is true.但是我想不出一种方法来将初始 x 设置为 1,并让它循环使用 (x+1) 直到 bool 为真。

I know this is possible as I've found some CRAZY lambda examples out there that are looping through lines in a file.我知道这是可能的,因为我发现了一些 CRAZY lambda 示例,它们在文件中的行中循环。 I just couldn't quite make sense of them (and didn't have any way to play with them as they were dealing with things outside my programming level.我只是不能完全理解它们(并且没有任何方法可以与它们一起玩,因为它们正在处理我编程水平之外的事情。

On a related note, could I add values to the beginning of this loop?在相关说明中,我可以在此循环的开头添加值吗? (ie can I have it check for test_name, then test_name_dup, then test_name_dup_#)? (即我可以让它检查 test_name,然后是 test_name_dup,然后是 test_name_dup_#)?

Thanks in advance for the help!先谢谢您的帮助! Lambdas (while very cool) totally mess with my head. Lambdas(虽然很酷)完全搞砸了我的脑袋。

Lambdas are just another way of defining a function Lambda 只是定义函数的另一种方式

def foo(x):
    return x + x

is the same as是相同的

foo = lambda x: x + x

So let's start with a function to do what you want:所以让我们从一个函数开始做你想做的事:

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

The first thing to note is that you can't use loops inside a lambda.首先要注意的是,您不能在 lambda 中使用循环。 So we'll need to rewrite this without a loop.所以我们需要在没有循环的情况下重写它。 Instead, we'll use recursion:相反,我们将使用递归:

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)

Now, we also can't use an if/else block in a lambda.现在,我们也不能在 lambda 中使用 if/else 块。 But we can use a ternary expression:但是我们可以使用三元表达式:

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)

We can't have local variables in a lambda, so we'll use a trick, default arguments:我们不能在 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()

At this point we can rewrite inner as a lambda:此时我们可以将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()

We can combine two lines to get rid of the inner local variable:我们可以结合两行来去掉内部局部变量:

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))()

And at long last, we can make the whole thing into a lambda:最后,我们可以将整个事情变成一个 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))()

Hopefully that gives you some insight into what you can do.希望这能让您对自己可以做什么有所了解。 But don't ever do it because, as you can tell, lambdas can make your code really hard to read.但是永远不要这样做,因为正如您所知,lambda 会使您的代码非常难以阅读。

There's no need to use a lambda in this case, a simple for loop will do:在这种情况下不需要使用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

If you really, really want to use a lambda for this problem, you'll also have to learn about itertools, iterators, filters, etc. I'm gonna build on thg435's answer, writing it in a more idiomatic fashion and explaining it:如果你真的,真的想使用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

The key to understanding the above solution lies in the dropwhile() procedure.理解上述解决方案的关键在于dropwhile()过程。 It takes two parameters: a predicate and an iterable, and returns an iterator that drops elements from the iterable as long as the predicate is true;它接受两个参数:一个谓词和一个可迭代对象,并返回一个迭代器,只要谓词为真,它就会从可迭代对象中删除元素; afterwards, returns every element.之后,返回每个元素。

For the iterable, I'm passing count(1) , an iterator that produces an infinite number of integers starting from 1 .对于可迭代对象,我正在传递count(1) ,这是一个迭代器,它生成从1开始的无限数量的整数。

Then dropwhile() starts to consume the integers until the predicate is false;然后dropwhile()开始消耗整数,直到谓词为假; this is a good opportunity for passing an in-line defined function - and here's our lambda .这是传递内联定义函数的好机会 - 这是我们的lambda It receives each generated integer in turn, checking to see if the string test_name_dup_# is present in the list.它依次接收每个生成的整数,检查字符串 test_name_dup_# 是否存在于列表中。

When the predicate returns false , dropwhile() returns and we can retrieve the value that made it stop by calling next() on it.当谓词返回falsedropwhile()返回,我们可以通过调用next()来检索使其停止的值。

You can combine a lambda with itertools.dropwhile:您可以将 lambda 与 itertools.dropwhile 结合使用:

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

As to your last question, you can write a generator for names, like:至于您的最后一个问题,您可以为名称编写一个生成器,例如:

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

and then use this generator with dropwhile:然后在 dropwhile 中使用这个生成器:

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

You are a bit off the track.你有点跑题了。 Lambdas are nothing but "simple" functions, often used for their fast syntax in functional programming. Lambda 只不过是“简单”的函数,通常用于函数式编程中的快速语法。 They are the perfect companion built-in functions "map", "reduce", "filter" but also for more complicated functions defined into itertools .它们是完美的配套内置函数“map”、“reduce”、“filter”,也适用于定义到itertools 中的更复杂的函数。 Therefore the most useful thing to do with them is to generate/manipulate iterable objects (especially lists).因此,对它们做的最有用的事情是生成/操作可迭代对象(尤其是列表)。 Note that lambdas will slow down your code in most of the cases if compared to list comprehensions/normal loops and will make it harder to be read.请注意,与列表推导式/普通循环相比,在大多数情况下,lambda 会减慢您的代码速度,并且会使其更难阅读。 Here is an example of what you want to do with lambdas.下面是一个你想用 lambdas 做什么的例子。

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

Or you could use more complicated functions with itertools.或者您可以通过 itertools 使用更复杂的函数。 Anyhow I strongly suggest you not to use lambdas for this kind of assignments, as the readability is terrible.无论如何,我强烈建议您不要将 lambda 用于此类任务,因为可读性很差。 I'd rather use a well structured for loop, which is also faster.我宁愿使用结构良好的 for 循环,它也更快。

[Edit] [编辑]

To prove that lambdas+builtins are not faster than list comprehensions: consider a simple problem, for x in range(1000) create a list of x shifted by 5.为了证明 lambdas+builtins 并不比列表推导式快:考虑一个简单的问题,因为 x in range(1000) 创建一个 x 移位 5 的列表。

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

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

You have a >100% performance increase without lambdas.在没有 lambda 的情况下,您的性​​能提高了 100% 以上。

I prefer the list comprehension or iterator method.我更喜欢列表理解或迭代器方法。 Makes for easy one liners that I feel are pretty easy to read and maintain.使我觉得很容易阅读和维护的简单衬垫。 Quite frankly, lambdas belong some places, here I believe its less elegant a solution.坦率地说,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)

This returns the first not-found instance in the sequence, which I think is the cleanest.这将返回序列中第一个未找到的实例,我认为这是最干净的。

Of course, if you prefer a list from a definite range, you can modify it to become a list comprehension:当然,如果你更喜欢一个确定范围的列表,你可以修改它成为一个列表理解:

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

returns:返回:

['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