2009-10-19 44 views
63

lợi ích gì hay hàm ý chúng ta có thể nhận được với mã Python như thế này:Chức năng lồng nhau bằng Python

class some_class(parent_class): 
    def doOp(self, x, y): 
     def add(x, y): 
      return x + y 
     return add(x, y) 

Tôi thấy điều này trong một dự án mã nguồn mở, làm một cái gì đó hữu ích bên trong hàm lồng nhau, nhưng làm như hoàn toàn không có gì bên ngoài nó ngoại trừ việc gọi nó. (Mã thực tế có thể được tìm thấy here.) Tại sao một người nào đó có thể mã hóa nó như thế này? Có một số lợi ích hay tác dụng phụ nào khi viết mã bên trong hàm lồng nhau chứ không phải ở hàm bên ngoài, bình thường?

+4

Đây có phải là mã thực tế mà bạn tìm thấy, hay chỉ là một ví dụ đơn giản bạn xây dựng? – MAK

+0

Đó là một ví dụ đơn giản. Bạn có thể tìm thấy mã thực tế tại đây: http://bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/annotate/head%3A/bin/report/render/rml2pdf/trml2pdf.py#L685 –

+2

Liên kết của bạn (trỏ đến HEAD) hiện không chính xác. Thử: http://bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/1861/bin/report/render/rml2pdf/trml2pdf.py#L685 –

Trả lời

85

Thông thường bạn làm điều đó để làm cho closures:

def make_adder(x): 
    def add(y): 
     return x + y 
    return add 

plus5 = make_adder(5) 
print(plus5(12)) # prints 17 

chức năng Nội có thể truy cập các biến từ phạm vi kèm theo (trong trường hợp này, biến địa phương x) . Nếu bạn không truy cập bất kỳ biến nào từ phạm vi kèm theo, chúng thực sự chỉ là các hàm bình thường với một phạm vi khác.

+4

Đối với điều đó, tôi muốn partials: 'plus5 = functools.partial (operator.add, 5)'. Trang trí sẽ là một ví dụ tốt hơn cho đóng cửa. –

+3

Cảm ơn, nhưng như bạn có thể thấy trong đoạn trích tôi đăng, đó không phải là trường hợp ở đây: hàm lồng nhau đơn giản được gọi trong hàm bên ngoài. –

8

Tôi không thể định hình bất kỳ lý do chính đáng nào cho mã như thế.

Có thể có lý do cho chức năng bên trong trong các phiên bản cũ hơn, như các Op khác.

Ví dụ, điều này làm cho hơi ý nghĩa hơn:

class some_class(parent_class): 
    def doOp(self, op, x, y): 
     def add(x, y): 
      return x + y 
     def sub(x,y): 
      return x - y 
     return locals()[op](x,y) 

some_class().doOp('add', 1,2) 

nhưng sau đó các chức năng bên trong phải được ("private") phương pháp lớp học thay vì:

class some_class(object): 
    def _add(self, x, y): 
     return x + y 
    def doOp(self, x, y): 
     return self._add(x,y) 
+0

Vâng, có thể doOp lấy một chuỗi để xác định toán tử nào sử dụng trên đối số ... – Skilldrick

+4

bạn không nên sử dụng các lớp trong python đơn giản chỉ để tổ chức các hàm ... – aehlke

+0

@aehlke - tại sao lại không? – ArtOfWarfare

6

Ý tưởng đằng sau các phương pháp địa phương tương tự như biến cục bộ: không gây ô nhiễm không gian tên lớn hơn. Rõ ràng những lợi ích bị hạn chế vì hầu hết các ngôn ngữ cũng không cung cấp chức năng như vậy trực tiếp.

1

Bạn có chắc là mã này chính xác như thế này không? Lý do bình thường để làm một cái gì đó như thế này là để tạo ra một phần - một hàm với các tham số nướng. Việc gọi hàm bên ngoài trả về một hàm có thể gọi mà không cần tham số và do đó có thể được lưu trữ và sử dụng ở đâu đó mà không thể truyền tham số. Tuy nhiên, mã bạn đã đăng sẽ không làm điều đó - nó gọi hàm ngay lập tức và trả về kết quả, chứ không phải là có thể gọi được. Nó có thể hữu ích để đăng mã thực tế bạn đã thấy.

+1

Tôi đã thêm liên kết vào mã gốc trong nhận xét về câu hỏi của mình. Như bạn có thể thấy, tôi là một ví dụ đơn giản, nhưng nó vẫn gần như giống nhau. –

44

Ngoài bộ tạo hàm, nơi tạo chức năng nội bộ gần như là định nghĩa của trình tạo hàm, lý do tôi tạo hàm lồng nhau là để cải thiện khả năng đọc. Nếu tôi có một hàm nhỏ mà sẽ chỉ được gọi bởi hàm bên ngoài, sau đó tôi định nghĩa nội tuyến để bạn không phải bỏ qua để xác định hàm đó đang làm gì. Tôi luôn có thể di chuyển phương thức bên trong bên ngoài phương thức đóng gói nếu tôi thấy cần phải sử dụng lại hàm này vào một ngày sau đó.

Toy dụ:

import sys 

def Foo(): 
    def e(s): 
     sys.stderr.write('ERROR: ') 
     sys.stderr.write(s) 
     sys.stderr.write('\n') 
    e('I regret to inform you') 
    e('that a shameful thing has happened.') 
    e('Thus, I must issue this desultory message') 
    e('across numerous lines.') 
Foo() 
+2

Vấn đề duy nhất là điều này đôi khi có thể gây khó khăn hơn cho việc kiểm tra đơn vị, bởi vì bạn không thể truy cập chức năng bên trong. – Challenger5

+1

Chúng trông thật đáng yêu! – taper

21

Một lợi ích tiềm năng của việc sử dụng các phương thức bên trong là nó cho phép bạn sử dụng các biến cục bộ của phương thức bên ngoài mà không chuyển chúng làm đối số.

def helper(feature, resultBuffer): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

def save(item, resultBuffer): 

    helper(item.description, resultBuffer) 
    helper(item.size, resultBuffer) 
    helper(item.type, resultBuffer) 

có thể được viết như sau, mà cho là đọc tốt hơn

def save(item, resultBuffer): 

    def helper(feature): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

    helper(item.description) 
    helper(item.size) 
    helper(item.type) 
Các vấn đề liên quan