web-dev-qa-db-ja.com

キーワード引数を取らない関数の2番目の引数を部分的に適用できますか?

たとえば、python組み込みのpow()関数を例にとります。

xs = [1,2,3,4,5,6,7,8]

from functools import partial

list(map(partial(pow,2),xs))

>>> [2, 4, 8, 16, 32, 128, 256]

しかし、どうすればxsを2の累乗に上げることができますか?

取得するため [1, 4, 9, 16, 25, 49, 64]

list(map(partial(pow,y=2),xs))

TypeError: pow() takes no keyword arguments

リスト内包表記の方が簡単でしょう。

42
beoliver

番号

ドキュメント によると、 partialできませんこれを実行します(私自身の強調):

partial.args

位置引数の前に追加されるleftmost位置引数


キーワードargsを持つようにpowを常に「修正」することができます。

_pow = pow
pow = lambda x, y: _pow(x, y)
39
Eric

私はこの単純なワンライナーを使用すると思います:

_import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))
_

更新:

また、便利な解決策よりもおかしな方法を考え出しました。 Python3では、_..._リテラルがEllipsisを意味するという事実から、これは美しい構文上の砂糖です。これはpartialの変更バージョンであり、左端と右端の間の位置引数の一部を省略できます。唯一の欠点は、Ellipsisを引数として渡すことができないことです。

_import itertools
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(newfunc.leftmost_args + fargs + newfunc.rightmost_args), **newkeywords)
    newfunc.func = func
    args = iter(args)
    newfunc.leftmost_args = Tuple(itertools.takewhile(lambda v: v != Ellipsis, args))
    newfunc.rightmost_args = Tuple(args)
    newfunc.keywords = keywords
    return newfunc

>>> print partial(pow, ..., 2, 3)(5) # (5^2)%3
1
>>> print partial(pow, 2, ..., 3)(5) # (2^5)%3
2
>>> print partial(pow, 2, 3, ...)(5) # (2^3)%5
3
>>> print partial(pow, 2, 3)(5) # (2^3)%5
3
_

したがって、元の質問の解決策は、このバージョンのパーシャルlist(map(partial(pow, ..., 2),xs))を使用することです。

14
kosii

なぜ引数とその部分を並べ替える簡単なラムダ関数を作成しないのですか?

partial(lambda p, x: pow(x, p), 2)
13
nrob

このためのヘルパー関数を作成できます。

from functools import wraps
def foo(a, b, c, d, e):
    print('foo(a={}, b={}, c={}, d={}, e={})'.format(a, b, c, d, e))

def partial_at(func, index, value):
    @wraps(func)
    def result(*rest, **kwargs):
        args = []
        args.extend(rest[:index])
        args.append(value)
        args.extend(rest[index:])
        return func(*args, **kwargs)
    return result

if __name__ == '__main__':
    bar = partial_at(foo, 2, 'C')
    bar('A', 'B', 'D', 'E') 
    # Prints: foo(a=A, b=B, c=C, d=D, e=E)

免責事項:私はこれをキーワード引数でテストしていないため、どういうわけかそれらのために爆発するかもしれません。また、これが@wrapsの使用目的であるかどうかはわかりませんが、それは正しいようです。

6
millimoose

あなたはクロージャーを使うことができます

xs = [1,2,3,4,5,6,7,8]

def closure(method, param):
  def t(x):
    return method(x, param)
  return t

f = closure(pow, 2)
f(10)
f = closure(pow, 3)
f(10)
5
iruvar

それを行う1つの方法は次のとおりです。

def testfunc1(xs):
    from functools import partial
    def mypow(x,y): return x ** y
    return list(map(partial(mypow,y=2),xs))

ただし、これには、pow関数の再定義が含まれます。

パーシャルの使用が「必要」でない場合、単純なラムダがうまくいきます

def testfunc2(xs):
    return list(map(lambda x: pow(x,2), xs))

そして、2の捕虜をマップする特定の方法は、

def testfunc5(xs):
    from operator import mul
    return list(map(mul,xs,xs))

しかし、これらはいずれも、キーワード引数に関連する部分的な適用の問題に直接対処するものではありません。

2
beoliver

これは、functools.partial()よりも柔軟なlambdaを使用して行うことができます。

pow_two = lambda base: pow(base, 2)
print(pow_two(3))  # 9

より一般的には:

def bind_skip_first(func, *args, **kwargs):
  return lambda first: func(first, *args, **kwargs)

pow_two = bind_skip_first(pow, 2)
print(pow_two(3))  # 9

ラムダの欠点の1つは、一部のライブラリがシリアル化できないことです。

2
danijar

非常に用途の広い funcy には、この問題に正確に対処するrpartial関数が含まれています。

xs = [1,2,3,4,5,6,7,8]
from funcy import rpartial
list(map(rpartial(pow, 2), xs))
# [1, 4, 9, 16, 25, 36, 49, 64]

それはただのラムダです:

def rpartial(func, *args):
    """Partially applies last arguments."""
    return lambda *a: func(*(a + args))
1
Micah Smith

ラムダ関数を使用できない場合は、引数を並べ替える単純なラッパー関数を作成することもできます。

def _pow(y, x):
    return pow(x, y)

そして次に電話する

list(map(partial(_pow,2),xs))

>>> [1, 4, 9, 16, 25, 36, 49, 64]
0
Corvince

すでに述べたように、これは functools.partialpartialにする関数がキーワード引数を受け入れない場合。

外部ライブラリを使用してもかまわない場合 1iteration_utilities.partial プレースホルダーをサポートするパーシャルがあります:

>>> from iteration_utilities import partial
>>> square = partial(pow, partial._, 2)  # the partial._ attribute represents a placeholder
>>> list(map(square, xs))
[1, 4, 9, 16, 25, 36, 49, 64]

1 免責事項:私は iteration_utilities ライブラリ(インストール手順は ドキュメントにあります 興味がある場合に参照できます)。

0
MSeifert