2012-06-23 30 views
21

Lấy ví dụ về con trăn được xây dựng trong hàm pow().Có thể một phần áp dụng đối số thứ hai của hàm không có đối số từ khóa không?

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] 

nhưng làm cách nào để tăng xs lên sức mạnh của 2?

để có được [1, 4, 9, 16, 25, 49, 64]

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

TypeError: pow() takes no keyword arguments 

Tôi biết danh sách comprehensions sẽ dễ dàng hơn.

+0

sử dụng một phần khác bắt đầu từ đối số 2 nd là một phần cho (các) phương thức bỏ qua đối số tự –

Trả lời

26

Không

Theo the documentation, partialkhông thể làm điều này (nhấn mạnh của riêng tôi):

một phần.args

Các tận cùng bên trái đối số vị trí sẽ được thêm vào phía trước đối số vị trí


Bạn có thể luôn luôn chỉ là "sửa chữa" pow có args keyword:

_pow = pow 
pow = lambda x, y: _pow(x, y) 
+1

tại sao tôi không đọc tài liệu. P.S định dạng đẹp của câu trả lời này. +1 – beoliver

+0

nếu trong python 4.X pow() là "cố định", tất cả chúng ta sẽ biết nơi họ có ý tưởng của họ từ :) – beoliver

+0

@beoliver: Trên thực tế, kể từ [PEP-457] (https: //www.python .org/dev/peps/pep-0457 /), đối số chỉ định vị trí được chính thức hóa thành những gì có vẻ là một tính năng, vì vậy tôi sẽ không bao giờ mong đợi một sửa chữa cho 'pow' – Eric

2

Một cách để làm việc đó sẽ là:

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

nhưng điều này liên quan đến việc tái định chức năng pow.

nếu việc sử dụng một phần không 'cần thiết' sau đó một lambda đơn giản sẽ làm các trick

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

Và một cách cụ thể để lập bản đồ pow của 2 sẽ

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

nhưng không ai trong số này giải quyết đầy đủ vấn đề trực tiếp của ứng dụng một phần liên quan đến đối số từ khóa

+0

Điều này sẽ được thêm vào câu hỏi, không được đăng dưới dạng câu trả lời. – user545424

+0

ngày hôm qua Tôi được khuyên nên thêm nỗ lực của tôi làm câu trả lời thay vì câu hỏi. Đó là một tình huống không thắng! – beoliver

+1

Tôi nghĩ ở đây là OK. Bạn nên cụm từ nó nhiều hơn như "Đây là một cách để làm điều đó" hơn là một sự bổ sung cho câu hỏi. Đến một mức độ nhất định, giả vờ bạn là người khác trả lời. – Eric

6

Bạn có thể tạo chức năng trợ giúp cho việc này:

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) 

Tuyên bố miễn trừ trách nhiệm: Tôi chưa thử nghiệm điều này với các đối số từ khóa để nó có thể phát nổ vì chúng bằng cách nào đó. Ngoài ra tôi không chắc chắn nếu điều này là những gì @wraps nên được sử dụng cho nhưng nó có vẻ đúng.

+0

Chính +1. Tôi đã bị cám dỗ để chọn câu trả lời này, nhưng tôi cho rằng tôi đã suy nghĩ nhiều hơn về 'functools.partial' sẵn có. Nhưng điều này chắc chắn đang được cứu. Tôi thích :) – beoliver

5

bạn có thể sử dụng một đóng cửa

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) 
+1

+1 tốt đẹp và đơn giản, tôi cho rằng tôi có thể xây dựng một thư viện nhỏ của các bao đóng khác nhau. :) ... bây giờ cái nào tôi đang tìm kiếm lần nữa. – beoliver

11

Tôi nghĩ tôi chỉ sử dụng này đơn giản một-liner:

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

Cập nhật:

Tôi cũng đã đưa ra một giải pháp hữu ích buồn cười hơn. Đó là một đường cú pháp đẹp, thu lợi từ thực tế là các phương tiện theo nghĩa đen ... nghĩa là Ellipsis trong Python3. Đây là phiên bản sửa đổi của partial, cho phép bỏ qua một số đối số vị trí giữa các đối số ngoài cùng bên trái và ngoài cùng bên phải. Hạn chế duy nhất là bạn không thể vượt qua được nữa Dấu ba chấm làm đối số.

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 

Vì vậy, các giải pháp cho câu hỏi ban đầu sẽ là với phiên bản này của phần list(map(partial(pow, ..., 2),xs))

+1

tốt đẹp, vì lý do nào đó tôi không bao giờ sử dụng lặp lại. và như tôi là 3.X, nó chỉ là 'danh sách (bản đồ (pow, [1, 2, 3], itertools.repeat (2)))' – beoliver

+0

tốt đẹp, tôi không biết họ đã thay đổi 'map' trong Python3 – kosii

+0

izip cũng bây giờ là zip EDIT ... Tôi chỉ nhìn thấy bản cập nhật của bạn !!! ;) – beoliver

5

Tại sao không chỉ cần tạo một hàm lambda nhanh chóng mà sắp xếp lại các args và một phần mà

partial(lambda p, x: pow(x, p), 2) 
0

Như đã nói rằng đó là giới hạn của functools.partial nếu hàm bạn muốn partial không chấp nhận các đối số từ khóa.

Nếu bạn không nhớ sử dụng một thư viện bên ngoài bạn có thể sử dụng iteration_utilities.partial trong đó có một phần hỗ trợ placeholders:

>>> 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] 

Disclaimer: Tôi là tác giả của thư viện iteration_utilities (hướng dẫn cài đặt có thể được tìm thấy in the documentation trong trường hợp bạn quan tâm).

1

Bạn có thể làm điều này với lambda, đó là linh hoạt hơn functools.partial():

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

Tổng quát hơn:

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 

Một xuống phía của lambda là một số thư viện không thể serialize nó.

+0

Bạn có ý nghĩa gì với một số thư viện? Tôi nghĩ rằng không có thư viện nào có thể tuần tự hóa chúng. – MSeifert

+0

Tôi thường sử dụng ruamel.yaml và nó nối tiếp lambdas tốt. – danijar

+0

Được rồi, tôi đã suy nghĩ về các mô-đun tích hợp sẵn. Cảm ơn bạn đã làm rõ :) – MSeifert

Các vấn đề liên quan