2012-05-24 30 views
16

Xem xét một chức năng đơn giản nhưChuyển đổi chức năng được xây dựng trong loại để loại phương pháp (bằng Python 3)

def increment(self): 
    self.count += 1 

được điều hành thông qua Cython và biên soạn thành một mô-đun mở rộng. Giả sử bây giờ tôi muốn làm cho hàm này trở thành một phương thức trên một lớp. Ví dụ:

class Counter: 
    def __init__(self): 
     self.count = 0 

from compiled_extension import increment 
Counter.increment = increment 

Điều này sẽ không hoạt động, vì quy ước gọi điện ở cấp C sẽ bị hỏng. Ví dụ:

>>> c = Counter() 
>>> c.increment() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: increment() takes exactly one argument (0 given) 

Nhưng trong Python 2, chúng ta có thể chuyển đổi các chức năng để một phương pháp không ràng buộc bằng cách thực hiện:

Counter.increment = types.MethodType(increment, None, Counter) 

Làm thế nào tôi có thể thực hiện điều này tương tự bằng Python 3?

Một cách đơn giản là sử dụng một wrapper mỏng:

from functools import wraps 
def method_wraper(f): 
    def wrapper(*args, **kwargs): 
     return f(*args, **kwargs) 
    return wraps(f)(wrapper) 

Counter.increment = method_wrapper(increment) 

Có cách nào hiệu quả hơn để làm điều đó?

+2

Tôi có vấn đề này cố gắng xây dựng một lớp Heapq sử dụng xấu xí module 'heapq'. Giải pháp của bạn là tốt. Nó có thể được thực hiện trong một dòng, nhưng có cùng hiệu quả: 'def method_wraper (f): trả về functools.wraps (f) (lambda * a, ** kw: f (* a, ** kw))' – JBernardo

+0

. ..Interesting, việc gán dường như hoạt động tốt nếu hàm được định nghĩa trong cùng một mô-đun (phương thức không liên kết được gán cho một lớp và ràng buộc trên instantiation). Vì vậy, nó chỉ là một vấn đề với phần mở rộng C, hoặc với các chức năng trong các mô-đun khác nhau? Dù sao, bạn có thể muốn xem http://stackoverflow.com/questions/7490879/python3-bind-method-to-class-instance-with-get-it-works-but-why, điều này có thể giúp bạn một chút cũng. – JAB

+0

Ngoài ra, http://docs.python.org/py3k/howto/descriptor.html#functions-and-methods "việc thực hiện C thực tế của PyMethod_Type trong Objects/classobject.c là một đối tượng duy nhất có hai biểu diễn khác nhau tùy thuộc vào việc trường im_self được thiết lập hoặc là NULL (tương đương C của None). " Mà làm cho nó có vẻ như vấn đề này không nên xảy ra ở tất cả, trừ khi Python bằng cách nào đó không cập nhật trực tiếp lĩnh vực đó cho một phương pháp của đối tượng khi instantiating đối tượng. – JAB

Trả lời

0

nhập phần mở rộng như thế này:

import compiled_extension 

Trong lớp của bạn, bạn viết:

def increment: return compiled_extension.increment() 

Điều này có vẻ dễ đọc hơn và có thể hiệu quả hơn.

4

Điều đầu tiên là nhận được tên một cách chính xác:

>>> def increment(obj): 
...  obj.count += 1 
... 
>>> class A(object): 
...  def __init__(self): 
...   self.count = 0 
... 
>>> o = A() 
>>> o.__init__ 
<bound method A.__init__ of <__main__.A object at 0x0000000002766EF0>> 
>>> increment 
<function increment at 0x00000000027797C8> 

tên Vì vậy, đúng là chức năngphương pháp ràng buộc. Bây giờ bạn có thể tìm kiếm làm thế nào để Bind an Unbound Method và có thể bạn sẽ kết thúc đọc về descriptors:

Nói chung, một bộ mô tả là một thuộc tính đối tượng với "ràng buộc hành vi", một người có thuộc tính truy cập đã được ghi đè bằng các phương pháp trong giao thức mô tả. Những phương pháp đó là __get__, __set____delete__. Nếu bất kỳ phương thức nào trong số đó được định nghĩa cho một đối tượng, nó được gọi là một bộ mô tả.

Bạn có thể dễ dàng chuyển đổi chức năng để phương pháp bằng cách chỉ sử dụng gọi khác nhau của __get__

>>> increment.__get__(None, type(None)) 
<function increment at 0x00000000027797C8> 
>>> increment.__get__(o, type(o)) 
<bound method A.increment of <__main__.A object at 0x00000000027669B0>> 

Và nó hoạt động như một nét duyên dáng:

>>> o = A() 
>>> increment.__get__(None, type(None))(o) 
>>> o.count 
1 
>>> increment.__get__(o, type(o))() 
>>> o.count 
2 

Bạn có thể dễ dàng thêm các phương pháp mới bao bọc đối tượng:

def increment(obj): 
    obj.count += 1 

def addition(obj, number): 
    obj.count += number 

class A(object): 
    def __init__(self): 
     self.count = 0 

o = A() 
o.inc = increment.__get__(o) 
o.add = addition.__get__(o) 
print(o.count) # 0 
o.inc() 
print(o.count) # 1 
o.add(5) 
print(o.count) # 6 

Hoặc tạo riêng mô tả của bạn mà sẽ sẽ chuyển đổi chức năng-phương pháp ràng buộc:

class BoundMethod(object): 
    def __init__(self, function): 
     self.function = function 

    def __get__(self, obj, objtype=None): 
     print('Getting', obj, objtype) 
     return self.function.__get__(obj, objtype) 

class B(object): 
    def __init__(self): 
     self.count = 0 

    inc = BoundMethod(increment) 
    add = BoundMethod(addition) 


o = B() 
print(o.count) # 0 
o.inc() 
# Getting <__main__.B object at 0x0000000002677978> <class '__main__.B'> 
print(o.count) # 1 
o.add(5) 
# Getting <__main__.B object at 0x0000000002677978> <class '__main__.B'> 
print(o.count) # 6 

Và bạn cũng có thể thấy rằng đây là độc đáo phù hợp với function/bound method principles:

Lớp từ điển lưu trữ các phương thức như hàm. Trong định nghĩa lớp, các phương thức được viết bằng cách sử dụng def và lambda, các công cụ thông thường để tạo các hàm. Sự khác biệt duy nhất từ ​​các hàm thông thường là đối số đầu tiên được dành riêng cho cá thể đối tượng. Theo quy ước Python, tham chiếu cá thể được gọi là tự nhưng có thể được gọi là tên này hoặc bất kỳ tên biến nào khác.

Để hỗ trợ các cuộc gọi phương thức, các hàm bao gồm phương thức __get__() cho các phương thức liên kết trong khi truy cập thuộc tính. Điều này có nghĩa là tất cả các hàm đều là các bộ mô tả phi dữ liệu trả về các phương thức bị ràng buộc hoặc không liên kết phụ thuộc vào việc chúng được gọi từ một đối tượng hay một lớp.

chức năng trở thành phương pháp ràng buộc trong dụ khởi tạo:

>>> B.add 
# Getting None <class '__main__.B'> 
<function addition at 0x00000000025859C8> 
>>> o.add 
# Getting <__main__.B object at 0x00000000030B1128> <class '__main__.B'> 
<bound method B.addition of <__main__.B object at 0x00000000030B1128>> 
+0

Thông thường, một kiểu được xây dựng trong CPython sử dụng 'method_descriptor', được gắn kết là' builtin_function_or_method', ví dụ: 'str.upper .__ nhận __ ('a') .__ self__ == 'a''.Bản thân 'builtin_function_or_method' là * not * một bộ mô tả. Sau đó là những gì Cython tạo ra, vì vậy OP tìm kiếm một cách để bọc nó trong một bộ mô tả như ['partialmethod'] (https://docs.python.org/3/library/functools.html#functools.partialmethod) (3.4+). – eryksun

+0

Tôi nghĩ câu trả lời của bạn không phải là những gì OP muốn. – laike9m

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