2012-01-22 40 views
5

Python không hỗ trợ các chức năng ẩn danh phức tạp. Một lựa chọn tốt là gì? Ví dụ:python: thay thế cho các chức năng ẩn danh

class Calculation: 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, data): 
     try: 
     # check if the value has already been calculated 
     # if it has, it would be cached under key = self.func 
      return data[self.func] 
     except KeyError: 
      pass # first-time call; calculate and cache the values 
     data[self.func] = self.func(data) 
     return data[self.func] 

# with a simple function, which can be represented using lambda, this works great 
f1 = Calculation(lambda data : data['a'] * data['b']) 

# with a complicated function, I can do this: 
def f2_aux: 
    # some complicated calculation, which isn't suitable for a lambda one-liner 
f2 = Calculation(f2_aux) 

Đây có phải là thiết kế hợp lý để bắt đầu không?

Nếu có, có cách nào để tránh sự xấu xí của f * _aux cho mỗi f * mà tôi xác định trong mô-đun không?

UPDATE:

Ví dụ về sử dụng:

d = {'a' : 3, 'b' : 6} 

# computes 3 * 6 
# stores 18 in d under a key <function <lambda> at ...> 
# returns 18 
f1(d) 

# retrieves 18 from d[<function <lambda> at ...>] 
# returns 18, without having to recalculate it 
f1(d) 

UPDATE:

Chỉ cần cho sự hiểu biết của tôi, tôi đã thêm một phiên bản sử dụng các chức năng bên trong.

def memoize(func): 
    def new_func(data): 
     try: 
     # check if the value has already been calculated 
     # if it has, it would be cached under key = self.func 
      return data[func] 
     except KeyError: 
      pass # first-time call; calculate and cache the values 
     data[func] = func(data) 
     return data[func] 
    return new_func 

@memoize 
def f1(data): 
    return data['a'] * data['b'] 
+5

Không, đây không phải là thiết kế hợp lý. Dường như bạn đang thực hiện việc ghi nhớ (không liên quan đến các chức năng ẩn danh) và làm việc đó rất tệ. 'f1 = lambda a: expression' luôn giống hệt' def f1 (a): biểu thức trả về'. [Đây là một trang trí ghi nhớ tốt để nghiên cứu] (http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize). BTW, '@ decorator def function: pass' luôn chính xác giống như' def function: pass; function = decorator (function) ' –

+2

Điều gì là xấu là memoizer của bạn bị hỏng. Bộ nhớ cache kết quả 'dữ liệu' cũng là đối số cho hàm của bạn ... –

+0

@FrancisAvila: Tôi dự định lưu trữ giá trị của' f (dữ liệu) 'trong phần tử từ điển' data [f] '. Tôi biết rằng 'dữ liệu' được truyền cho tôi sẽ không bao giờ sử dụng khóa như vậy - và xung đột với một khóa khác không nên xảy ra vì giá trị của một hàm khác sẽ được lưu trong một khóa khác. Dường như nó hoạt động (về mặt kỹ thuật); là vấn đề chỉ trong mã khó hiểu không cần thiết? – max

Trả lời

5

Bạn không cần chức năng ẩn danh. Ngoài ra, ghi nhớ đã được thực hiện tốt hơn so với điều này, có lẽ không có lý do để bạn cuộn của riêng bạn.

Nhưng để trả lời câu hỏi: Bạn có thể sử dụng lớp của bạn làm trang trí.

@Calculation 
def f2(): 
    ... 

này chỉ đơn giản là xác định chức năng, kết thúc tốt đẹp nó trong Calculation và lưu trữ kết quả đó như f2. Cú pháp trang trí được định nghĩa là tương đương với:

_decorator = Calculation # a fresh identifier 
# not needed here, but in other cases (think properties) it's useful 
def f2(): 
    ... 
f2 = _decorator(f2) 
+0

Tôi hiểu chính xác rằng trong khi bình thường một trang trí là một hàm nhận một hàm làm đối số và trả về một hàm khác - trong trường hợp này, 'tính toán lớp' phù hợp với vai trò này do chữ ký hàm tạo và phương thức '__call__' của nó? – max

+0

@max: Không có giới hạn đối với người trang trí, ngoại trừ tất nhiên là chúng phải được gọi và có thể hữu ích * chúng phải có giá trị trả về có ý nghĩa. Các lớp được gọi thông qua '__new__' /' __init__', và như các thể hiện của lớp bạn có thể gọi (mặc dù '__call__'), giá trị trả về là hữu ích. – delnan

+0

@max "có thể gọi", theo cách nói Python. –

1

Một đóng cửa có thể là một thay thế ngắn gọn để viết một lớp như trong ví dụ của bạn. Kỹ thuật này bao gồm việc đặt def vào một số khác def. Hàm bên trong có thể có quyền truy cập vào biến trong hàm kèm theo. Trong Python 3, từ khóa nonlocal cho phép bạn ghi quyền truy cập vào biến đó. Trong Python 2, bạn cần phải sử dụng một giá trị có thể thay đổi cho biến phi địa phương để có thể cập nhật nó từ hàm bên trong.

Về câu hỏi liên quan đến chức năng ẩn danh, ngôn ngữ cố ý đẩy bạn trở lại sử dụng def cho bất kỳ điều gì phức tạp hơn lambda có thể xử lý.

+0

Bạn có ý nghĩa giống như nội dung tôi đã thêm trong lần cập nhật cuối cùng cho câu hỏi của mình? – max

+0

Vâng, đó là đóng cửa. –

4

Cách thay thế cho chức năng ẩn danh là một chức năng không ẩn danh. Một hàm ẩn danh chỉ ẩn danh trong ngữ cảnh mà nó được định nghĩa. Nhưng nó không thực sự vô danh, bởi vì sau đó bạn không thể sử dụng nó.

Trong Python, bạn thực hiện các chức năng ẩn danh với câu lệnh lambda. Bạn có thể ví dụ như làm điều này:

output = mysort(input, lambda x: x.lastname) 

Lambda sẽ tạo ra một chức năng, nhưng chức năng mà không có tên trong không gian địa phương, và tên riêng của nó cho chính nó chỉ '<lambda>' là. Nhưng nếu chúng ta nhìn vào mysort, nó sẽ phải được xác định một cái gì đó như thế này:

def mysort(input, getterfunc): 
    blahblahblah 

Như chúng ta thấy ở đây, trong bối cảnh này chức năng không phải là vô danh ở tất cả. Nó có tên, getterfunc.Từ quan điểm của hàm này, nó không quan trọng nếu hàm được truyền vào là vô danh hay không. Điều này cũng hoạt động tốt và hoàn toàn tương đương với tất cả các cách quan trọng:

def get_lastname(x): 
    return x.lastname 

output = mysort(input, get_lastname) 

Chắc chắn, nó sử dụng nhiều mã hơn, nhưng không chậm hơn hoặc bất cứ điều gì tương tự. Trong Python, do đó các hàm ẩn danh không là gì ngoài cú pháp đường cho các hàm bình thường.

Một chức năng thực sự nặc danh sẽ

lambda x: x.lastname 

Nhưng như chúng ta không chỉ định các chức năng dẫn đến bất cứ điều gì, chúng ta không có được một tên định chức năng, và sau đó chúng ta không thể sử dụng nó. Tất cả các hàm ẩn danh thực sự đều không sử dụng được.

Vì lý do đó, nếu bạn cần một hàm không thể là lambda, hãy biến nó thành hàm bình thường. Nó không bao giờ có thể ẩn danh theo bất kỳ cách có ý nghĩa nào, vậy tại sao lại làm cho nó ẩn danh? Lambdas rất hữu ích khi bạn muốn có một chức năng một dòng nhỏ và bạn không muốn lãng phí không gian bằng cách xác định một hàm đầy đủ. Rằng họ là vô danh là không thích hợp.

+0

Cảm ơn .. Tôi đoán tôi đã cố gắng để tiết kiệm số lượng mã viết (và do đó số lượng lỗi chính tả tiềm năng), mà tôi đồng ý không phải là một mối quan tâm lớn để bắt đầu với. – max

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