Идиома Руби в Python - PullRequest
       43

Идиома Руби в Python

10 голосов
/ 16 сентября 2010

Существует полезная идиома 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 - недопустимый синтаксис.

Ответы [ 6 ]

23 голосов
/ 16 сентября 2010

Краткий ответ: Ruby поощряет объединение методов, Python нет.

Наверное, правильный вопрос: для чего нужна tap Руби?

Сейчас я не знаю много о Ruby, но, прибегая к гуглу, у меня создалось впечатление, что tap концептуально полезен как метод цепочки.

В Ruby стиль: SomeObject.doThis().doThat().andAnotherThing() довольно идиоматичен. Это лежит в основе концепции плавных интерфейсов , например. tap в Ruby является частным случаем, когда вместо SomeObject.doThis() вы определяете doThis на лету.

Почему я все это объясняю? Потому что это говорит нам, почему tap не имеет хорошей поддержки в Python. С учетом предостережений, Python не делает цепочку вызовов .

Например, методы списка Python обычно возвращают None, а не возвращают измененный список. Такие функции, как map и filter, не являются методами списка. С другой стороны, многие методы массива Ruby возвращают модифицированный массив.

За исключением некоторых случаев, таких как некоторые ORM, код Python не использует свободно распространяемые интерфейсы.

В конце концов, это разница между идиоматическим Ruby и идиоматическим Python. Если вы переходите с одного языка на другой, вам нужно отрегулировать.

7 голосов
/ 16 сентября 2010

Если вы хотите, чтобы это было достаточно плохо, вы можете создать менеджер контекста

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 в самом начале, что подсказывает вам, о чем эта функция, я полагаю.Это лучше, чем использование лямбда-выражений, потому что вы не ограничены выражениями и можете иметь почти все, что хотите в выражении with.

В целом, я бы сказал, что если вы хотите tap, то это плохозатем придерживайтесь ruby, и если вам нужно программировать на python, используйте python для написания python, а не ruby.Когда я приступаю к изучению ruby, я собираюсь написать ruby;)

7 голосов
/ 16 сентября 2010

Вы можете реализовать его в Python следующим образом:

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

Использование:

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

Однако в Python 2.x он не будет так полезен, как в Ruby, потому что лямбда-функции в Python довольно ограничительны. Например, вы не можете встроить вызов для печати, потому что это ключевое слово, поэтому вы не можете использовать его для встроенного кода отладки. Вы можете сделать это в Python 3.x, хотя он не так чист, как синтаксис Ruby.

>>> tap(2, lambda x: print(x)) + 3
2
5
1 голос
/ 16 сентября 2010

У меня была идея достичь этого с помощью декораторов функций, но из-за различий в выражениях между выражениями и операторами в 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())
0 голосов
/ 16 сентября 2010

Вряд ли кто-либо из программистов на Ruby использует tap таким образом.На самом деле, все известные мне программисты на Ruby говорят, что tap бесполезен, кроме как в отладке.

Почему бы просто не сделать это в своем коде?

[].push(1)

и помнить, что поддержка Arrayсвободный интерфейс, так что вы даже можете сделать это:

[].push(1).push(2)
0 голосов
/ 16 сентября 2010

Я частично согласен с другими в том, что не имеет смысла реализовывать это в Python.Впрочем, ИМХО, путь Марка Байерса - это путь, но зачем лямбды (и все, что с ними идет)?Вы не можете написать отдельную функцию, которая будет вызываться при необходимости?

Другим способом сделать то же самое можно было бы

map(afunction(), avariable)

, но эта красивая функцияЯ слышал, встроенный в Python 3.

...