Python 中 Ruby 的 tap 习惯用法

发布于 2024-09-24 03:43:20 字数 811 浏览 5 评论 0原文

有一个有用的 Ruby 习惯用法,使用 tap ,它允许您创建一个对象,对其进行一些操作并返回它(我在这里仅使用列表作为示例,我的真实代码更复杂) :

def foo
  [].tap do |a|
    b = 1 + 2
    # ... and some more processing, maybe some logging, etc.
    a << b
  end
end

>> foo
=> [1]

Rails 有一个类似的方法,称为 returning,因此您可以这样写:

def foo
  returning([]) do |a|
    b = 1 + 2
    # ... and some more processing, maybe some logging, etc.
    a << b
  end
end

这不言而喻。无论你对对象进行多少处理,它仍然很明显是函数的返回值。

在 Python 中我必须这样写:

def foo():
  a = []
  b = 1 + 2
  # ... and some more processing, maybe some logging, etc.
  a.append(b)
  return a

我想知道是否有办法将这个 Ruby 习惯用法移植到 Python 中。我的第一个想法是使用 with 语句,但 return with 不是有效的语法。

There is a useful Ruby idiom that uses tap which allows you to create an object, do some operations on it and return it (I use a list here only as an example, my real code is more involved):

def foo
  [].tap do |a|
    b = 1 + 2
    # ... and some more processing, maybe some logging, etc.
    a << b
  end
end

>> foo
=> [1]

With Rails there's a similar method called returning, so you can write:

def foo
  returning([]) do |a|
    b = 1 + 2
    # ... and some more processing, maybe some logging, etc.
    a << b
  end
end

which speaks for itself. No matter how much processing you do on the object, it's still clear that it's the return value of the function.

In Python I have to write:

def foo():
  a = []
  b = 1 + 2
  # ... and some more processing, maybe some logging, etc.
  a.append(b)
  return a

and I wonder if there is a way to port this Ruby idiom into Python. My first thought was to use with statement, but return with is not valid syntax.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

硪扪都還晓 2024-10-01 03:43:21

如果你想要这个足够糟糕,你可以创建一个上下文管理器

class Tap(object):
    def __enter__(self, obj):
        return obj

    def __exit__(*args):
        pass

,你可以使用它,如下所示:

def foo():
    with Tap([]) as a:
        a.append(1)
        return a

没有办法绕过 return 语句,并且 with 在这里实际上不做任何事情。但你确实在开始时就有了 Tap ,它可以让你了解我认为该函数的含义。它比使用 lambda 更好,因为您不限于表达式,并且可以在 with 语句中拥有几乎任何您想要的内容。

总的来说,我想说,如果你想要点击那么糟糕,那么就坚持使用 ruby​​,如果你需要用 python 编程,请使用 python 来编写 python 而不是 ruby​​。当我开始学习 Ruby 时,我打算编写 Ruby ;)

If you want this bad enough, you can create a context manager

class Tap(object):
    def __enter__(self, obj):
        return obj

    def __exit__(*args):
        pass

which you can use like:

def foo():
    with Tap([]) as a:
        a.append(1)
        return a

There's no getting around the return statement and with really doesn't do anything here. But you do have Tap right at the start which clues you into what the function is about I suppose. It is better than using lambdas because you aren't limited to expressions and can have pretty much whatever you want in the with statement.

Overall, I would say that if you want tap that bad, then stick with ruby and if you need to program in python, use python to write python and not ruby. When I get around to learning ruby, I intend to write ruby ;)

街道布景 2024-10-01 03:43:21

我有一个想法使用函数装饰器来实现这一点,但由于Python中表达式和语句之间的区别,这最终仍然需要返回在最后。

根据我的经验,ruby 语法很少使用,而且其可读性远不如显式的 python 方法。如果 python 具有隐式返回或将多个语句包装成单个表达式的方法,那么这将是可行的 - 但它在设计上没有这些东西。

这是我的 - 有点毫无意义 - 装饰器方法,供参考:

class Tapper(object):
    def __init__(self, initial):
        self.initial = initial
    def __call__(self, func):
        func(self.initial)
        return self.initial

def tap(initial):
    return Tapper(initial)

if __name__ == "__main__":
    def tapping_example():
        @tap([])
        def tapping(t):
            t.append(1)
            t.append(2)
        return tapping

    print repr(tapping_example())

I had an idea to achieve this using function decorators, but due to the distinction in python between expressions and statements, this ended up still requiring the return to be at the end.

The ruby syntax is rarely used in my experience, and is far less readable than the explicit python approach. If python had implicit returns or a way to wrap multiple statements up into a single expression then this would be doable - but it has neither of those things by design.

Here's my - somewhat pointless - decorator approach, for reference:

class Tapper(object):
    def __init__(self, initial):
        self.initial = initial
    def __call__(self, func):
        func(self.initial)
        return self.initial

def tap(initial):
    return Tapper(initial)

if __name__ == "__main__":
    def tapping_example():
        @tap([])
        def tapping(t):
            t.append(1)
            t.append(2)
        return tapping

    print repr(tapping_example())
橘味果▽酱 2024-10-01 03:43:21

我部分同意其他人的观点,因为用 Python 实现这个没有多大意义。然而,恕我直言,Mark Byers 的方式就是方式,但为什么使用 lambda(以及它们附带的所有内容)?不能写一个单独的函数在需要的时候调用吗?

另一种方法可以实现基本相同的功能

map(afunction(), avariable)

,但据我所知,这个漂亮功能并不是 Python 3 中内置的。

I partly agree with others in that it doesn't make much sense to implement this in Python. However, IMHO, Mark Byers's way is the way, but why lambdas(and all that comes with them)? can't you write a separate function to be called when needed?

Another way to do basically the same could be

map(afunction(), avariable)

but this beautiful feature is not a built-in in Python 3, I hear.

最舍不得你 2024-10-01 03:43:21

几乎没有 Ruby 程序员以这种方式使用 tap。事实上,我认识的所有顶级 Ruby 程序员都说,tap 除了调试之外没有任何用处。

为什么不在你的代码中这样做呢?

[].push(1)

请记住 Array 支持流畅的接口,因此您甚至可以这样做:

[].push(1).push(2)

Hardly any Ruby programmers use tap in this way. In fact, all top Ruby programmers i know say tap has no use except in debugging.

Why not just do this in your code?

[].push(1)

and remember Array supports a fluent interface, so you can even do this:

[].push(1).push(2)
丢了幸福的猪 2024-10-01 03:43:20

简短的回答:Ruby 鼓励方法链接,而 Python 则不然。

我想正确的问题是:Ruby 的 tap 有什么用处?

现在我对 Ruby 了解不多,但通过谷歌搜索,我得到的印象是 tap 在概念上作为方法链接很有用。

在 Ruby 中,样式:SomeObject.doThis().doThat().andAnotherThing() 非常惯用。例如,它是流畅界面概念的基础。 Ruby 的 tap 是这种情况的一个特例,它不是使用 SomeObject.doThis(),而是动态定义 doThis

我为什么要解释这一切?因为它告诉我们为什么tap在Python中没有得到很好的支持。需要注意的是,Python 不执行调用链

例如,Python 列表方法通常返回 None 而不是返回变异列表。像 mapfilter 这样的函数不是列表方法。另一方面,许多 Ruby 数组方法确实返回修改后的数组。

除了某些 ORM 等某些情况之外,Python 代码不使用流畅的接口。

归根结底,这是惯用的 Ruby 和惯用的 Python 之间的区别。如果您要从一种语言转向另一种语言,则需要进行调整。

Short answer: Ruby encourages method chaining, Python doesn't.

I guess the right question is: What is Ruby's tap useful for?

Now I don't know a lot about Ruby, but by googling I got the impression that tap is conceptually useful as method chaining.

In Ruby, the style: SomeObject.doThis().doThat().andAnotherThing() is quite idiomatic. It underlies the concept of fluent interfaces, for example. Ruby's tap is a special case of this where instead of having SomeObject.doThis() you define doThis on the fly.

Why I am explaining all this? Because it tells us why tap doesn't have good support in Python. With due caveats, Python doesn't do call chaining.

For example, Python list methods generally return None rather than returning the mutated list. Functions like map and filter are not list methods. On the other hand, many Ruby array methods do return the modified array.

Other than certain cases like some ORMs, Python code doesn't use fluent interfaces.

In the end it is the difference between idiomatic Ruby and idiomatic Python. If you are going from one language to the other you need to adjust.

柒七 2024-10-01 03:43:20

您可以按如下方式在 Python 中实现它:

def tap(x, f):
    f(x)
    return x

用法:

>>> tap([], lambda x: x.append(1))
[1]

但是,它在 Python 2.x 中的用途不会像在 Ruby 中那么多,因为 Python 中的 lambda 函数非常受限制。例如,您无法内联对 print 的调用,因为它是关键字,因此您不能将其用于内联调试代码。您可以在 Python 3.x 中执行此操作,尽管它不如 Ruby 语法清晰。

>>> tap(2, lambda x: print(x)) + 3
2
5

You can implement it in Python as follows:

def tap(x, f):
    f(x)
    return x

Usage:

>>> tap([], lambda x: x.append(1))
[1]

However it won't be so much use in Python 2.x as it is in Ruby because lambda functions in Python are quite restrictive. For example you can't inline a call to print because it is a keyword, so you can't use it for inline debugging code. You can do this in Python 3.x although it isn't as clean as the Ruby syntax.

>>> tap(2, lambda x: print(x)) + 3
2
5
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文