簡體   English   中英

Python用任意數量的變量進行curry

[英]Python currying with any number of variables

我正在嘗試使用currying在Python中進行簡單的功能添加。 我在這里找到了這個咖喱裝飾。

def curry(func):     
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= func.__code__.co_argcount:
            return func(*args, **kwargs)
        return (lambda *args2, **kwargs2:
            curried(*(args + args2), **dict(kwargs, **kwargs2)))
    return curried

@curry
def foo(a, b, c):
    return a + b + c

現在這很棒,因為我可以做一些簡單的討論:

>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6

但這僅適用於三個變量。 如何編寫函數foo以便它可以接受任意數量的變量並且仍然可以調整結果? 我嘗試過使用* args的簡單解決方案,但它沒有用。

編輯:我已經查看了答案,但仍然無法弄清楚如何編寫一個可以執行如下所示的函數:

>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6
>>> foo(1)(2)
3
>>> foo(1)(2)(3)(4)
10

可以說, explicit is better than implicit

from functools import partial

def example(*args):
    print("This is an example function that was passed:", args)

one_bound = partial(example, 1)
two_bound = partial(one_bound, 2)
two_bound(3)

@JohnKugelman用你正在嘗試做的事情解釋了設計問題 - 在“添加更多curried參數”和“調用邏輯”之間調用curried函數將是模棱兩可的。 這個問題在Haskell(概念來自於)中不是問題的原因是語言懶惰地評估所有內容 ,因此在“名為x的函數之間沒有任何參數並且只返回3”之間沒有區別 “和”對上述函數的調用“,或甚至在那些和”整數3“之間。 Python不是那樣的。 (例如,您可以使用零參數調用來表示“立即調用邏輯”;但是這會打破special cases aren't special enough ,並且對於您實際上special cases aren't special enough需要額外的一對括號想做任何討論。)

functools.partial是一個開箱即用的解決方案,用於在Python中部分應用函數。 不幸的是,反復調用partial以添加更多“curried”參數並不是那么有效(在引擎蓋下會有嵌套的partial對象)。 但是,它更靈活; 特別是,您可以將它用於沒有任何特殊裝飾的現有功能。

您可以為自己實現與functools.partial示例相同的功能:

def curry (prior, *additional):
    def curried(*args):
        return prior(*(args + additional))
    return curried

def add(*args):
    return sum(args)

x = curry(add, 3,4,5)
y = curry(b, 100)
print y(200)
# 312

curry視為功能工廠而不是裝飾者可能更容易; 從技術上講,這是裝飾器的所有功能,但裝飾器使用模式是靜態的,其中工廠是您希望作為操作鏈的一部分調用的東西。

你可以在這里看到我開始使用add作為咖喱的參數而不是add(1)或者其他東西:工廠簽名是<callable>, *<args> 這可以解決原始帖子的評論中的問題。

事實1:對於可變功能實現自動曲線功能根本不可能。

事實2:你可能不是在尋找咖喱,如果你想要傳遞給它的功能*知道*它將被咖喱,以使其表現不同。

如果您需要的是一種方法來討論可變參數函數,您應該在下面的這些行中使用某些東西(使用您自己的剪切):

def curryN(arity, func):
    """curries a function with a pre-determined number of arguments"""
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= arity:
            return func(*args, **kwargs)
        return (lambda *args2, **kwargs2:
            curried(*(args + args2), **dict(kwargs, **kwargs2)))
    return curried

def curry(func):
    """automatically curries a function"""
    return curryN(func.__code__.co_argcount, func);

這樣你可以做到:

def summation(*numbers):
    return sum(numbers);

sum_two_numbers = curryN(2, summation)
sum_three_numbers = curryN(3, summation)
increment = curryN(2, summation)(1)
decrement = curryN(2, summation)(-1)

我認為這是一個不錯的解決方案:

from copy import copy
import functools


def curry(function):

  def inner(*args, **kwargs):
    partial = functools.partial(function, *args, **kwargs)
    signature = inspect.signature(partial.func)
    try:
      signature.bind(*partial.args, **partial.keywords)
    except TypeError as e:
      return curry(copy(partial))
    else:
      return partial()

  return inner

這只是允許您以自動方式遞歸調用functools.partial

def f(x, y, z, info=None):
  if info:
    print(info, end=": ")
  return x + y + z

g = curry_function(f)
print(g)
print(g())
print(g(2))
print(g(2,3))
print(g(2)(3))
print(g(2, 3)(4))
print(g(2)(3)(4))
print(g(2)(3, 4))
print(g(2, info="test A")(3, 4))
print(g(2, info="test A")(3, 4, info="test B"))

輸出:

<function curry.<locals>.inner at 0x7f6019aa6f28>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a158>
<function curry.<locals>.inner at 0x7f6019a9a0d0>
9
9
9
test A: 9
test B: 9

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM