2009-05-14 39 views
26

Tôi tự hỏi liệu có thể tạo ra một phương thức hoạt động khác khi được gọi là phương thức lớp hay không khi được gọi là phương thức thể hiện.Phương thức lớp hoạt động khác khi được gọi là phương thức thể hiện?

Ví dụ, như một dự án cải thiện kỹ năng, tôi đang viết một lớp Matrix (vâng, tôi biết có các lớp ma trận hoàn toàn tốt đã có sẵn). Tôi đã tạo một phương thức lớp cho nó được gọi là identity trả về ma trận nhận dạng của một kích thước được chỉ định.

Bây giờ, khi được gọi trên một ví dụ của Matrix, có vẻ hợp lý rằng kích thước không cần phải được chỉ định; nó sẽ trả về ma trận nhận dạng có cùng kích thước với tên gọi là Matrix.

Nói cách khác, tôi muốn xác định một phương pháp có thể xác định xem nó có được gọi thông qua một cá thể không và nếu có, hãy truy cập các thuộc tính của cá thể đó. Thật không may, ngay cả sau khi đào bới thông qua tài liệu và một vài tìm kiếm của Google, tôi đã không tìm thấy bất cứ điều gì cho thấy điều này là có thể. Có ai biết khác nhau không?

Edit:

Wow! Rõ ràng, tôi vẫn chưa quen với các chức năng hạng nhất. Đây là những gì tôi đã kết thúc với - nhờ không biết để cung cấp chìa khóa!

class Foo(object): 
    def __init__(self, bar): 
     self.baz = bar 
     self.bar = MethodType(lambda self: self.__class__.bar(self.baz), self, self.__class__) 

    @classmethod 
    def bar(cls, baz): 
     return 5 * baz 

Foo.bar(3) # returns 15 

foo = Foo(7) 
foo.bar() # returns 35 

Chỉnh sửa 2:

Chỉ cần một cách nhanh chóng lưu ý - kỹ thuật này (và hầu hết những trình bày dưới đây) sẽ không hoạt động trên lớp học mà xác định __slots__, như bạn không thể gán phương pháp.

+0

Câu hỏi được yêu cầu cho một đối tượng có thể gọi duy nhất có thể phát hiện nó được gọi như thế nào. (Điều này có thể thực hiện bằng cách sử dụng mô-đun kiểm tra để có được khung gọi.) 'Giải pháp' được trình bày là tạo một phương thức có thể gọi thứ hai, một phương thức ràng buộc, được ràng buộc với mỗi cá thể có cùng tên với tên gọi đầu tiên. Điều này có thể giải quyết vấn đề làm thế nào để có class và 'instance methods' có cùng tên. Nhưng nó không giống như có một cuộc gọi duy nhất có thể hành động như một trong hai. –

+0

@TerryJanReedy có thể kiểm tra khung gọi trong thời gian chạy không? – aayush

+0

Vâng, đây là những gì tôi có nghĩa là "sử dụng mô-đun kiểm tra để có được khung gọi". Có một hoặc nhiều chức năng cho việc này. –

Trả lời

38

Hacks hữu ích đáng ngờ là sở trường của tôi.

from types import * 

class Foo(object): 
    def __init__(self): 
     self.bar = methodize(bar, self) 
     self.baz = 999 

    @classmethod 
    def bar(cls, baz): 
     return 2 * baz 


def methodize(func, instance): 
    return MethodType(func, instance, instance.__class__) 

def bar(self): 
    return 4*self.baz 


>>> Foo.bar(5) 
10 
>>> a=Foo() 
>>> a.bar() 
3996 
+9

Kudo, và một upvote, cho nỗ lực trong mẫu mã, và đặc biệt là cho cụm từ "hacks Python hữu ích có vấn đề là sở trường của tôi" :-) –

+0

@ Jarret, nó không chỉ là một báo giá cho hiển thị một trong hai. Kiểm tra những cái khác của tôi như: http://stackoverflow.com/questions/813882/is-there-a-way-to-check-whether-function-output-is-assigned-to-a-variable-in- pyth/813938 # 813938 – Unknown

+1

Ok, thật tuyệt. Bây giờ làm thế nào để bạn chọn này? – Neoecos

3

Tôi nghĩ rằng vấn đề lớn hơn là bạn đang quá tải tên 'bar' trên lớp 'Foo', một cái gì đó python không cho phép. Định nghĩa thứ hai của 'bar' ghi đè định nghĩa đầu tiên của 'bar'.

Hãy thử nghĩ về các tên duy nhất cho phương pháp lớp và phương pháp thể hiện của bạn. tức là

@classmethod 
def create(cls, baz): 
    ... 

def rubber_stamp(self): 
    ... 
+1

+1: Và - tất nhiên - yếu tố nhầm lẫn vốn có của hành vi biến thể, ngay cả khi "liên quan chặt chẽ". –

+0

Và đề nghị của bạn để sửa chữa "mã khái niệm không làm việc" mà OP đã được xác định là một vấn đề lớn mà anh ta cần giúp đỡ? –

+2

IMO, mã khái niệm không hoạt động là một ý tưởng tồi - bản sửa lỗi là phải suy nghĩ lại về API. Không có "sửa chữa". –

7

[đã chỉnh sửa: sử dụng thuộc tính để trở thành câu trả lời trực tiếp hơn; xem nhận xét hữu ích bởi John Fouhy]

Bạn có thể sử dụng một mô tả để làm những gì bạn muốn:

class cls_or_inst_method(object): 
    def __init__(self, class_method, instance_method): 
     self.class_method = class_method 
     self.instance_method = instance_method 

    def __get__(self, obj, objtype): 
     if obj is None: 
      return self.class_method 
     else: 
      return lambda: self.instance_method(obj) 

def my_class_method(baz): 
    return baz + 1 

def my_instance_method(self): 
    return self.baz * 2 

class Foo(object): 
    baz = 10 
    bar = cls_or_inst_method(my_class_method, my_instance_method) 

Sử dụng trên:

>>> print Foo.bar(5) 
6 
>>> my_foo = Foo() 
>>> print my_foo.bar() 
20 
+1

Để trở thành một giải pháp, phương pháp thể hiện nên tham khảo self.baz và không lấy bất kỳ đối số nào (ngoại trừ tự), tôi nghĩ rằng .. –

7

@Unknown chênh lệch giữa của bạn là gì và này:

class Foo(object): 

    def _bar(self, baz): 
     print "_bar, baz:", baz 

    def __init__(self, bar): 
     self.bar = self._bar 
     self.baz = bar 

    @classmethod 
    def bar(cls, baz): 
     print "bar, baz:", baz 

In [1]: import foo 

In [2]: f = foo.Foo(42) 

In [3]: f.bar(1) 
_bar, baz: 1 

In [4]: foo.Foo.bar(1) 
bar, baz: 1 

In [5]: f.__class__.bar(1) 
bar, baz: 1 
+0

Oh! Cảm ơn ông không biết :-) – Ale

+1

Vâng sự khác biệt là bạn có thể ràng buộc bất cứ chức năng nào bạn muốn làm phương pháp. Tôi chỉ sử dụng cơ hội này để thể hiện mẹo đó. Và cũng không có f._bar() – Unknown

+4

@Unknown: nếu "dangling" 'f._bar()' là một vấn đề, thì chỉ cần định nghĩa nó bằng hàm lambda hoặc lồng nhau bên trong '__init __()' mã ở đây là trực quan rõ ràng cách nó hoạt động, trong khi các giải pháp khác liên quan đến tối nghĩa Python voodoo, chưa kể đến mô-đun có thể nhìn thấy chức năng cấp cao nhất 'methodize()' và 'bar()' –

2

Bạn có thể gán lại phương thức nhận dạng của mình trong init với chức năng lambda ngắn:

class Matrix(object): 
    def __init__(self): 
     self.identity = lambda s=self:s.__class__.identity(s) 

     #...whatever initialization code you have... 
     self.size = 10   

    @classmethod 
    def identity(self, other): 
     #...always do you matrix calculations on 'other', not 'self'... 
     return other.size 


m = Matrix() 
print m.identity() 
print Matrix.identity(m) 

Nếu bạn không quen thuộc với lambda, nó tạo ra một chức năng ẩn danh. Nó hiếm khi cần thiết, nhưng nó có thể làm cho mã của bạn ngắn gọn hơn. Dòng lambda ở trên có thể được viết lại:

def identity(self): 
     self.__class__.indentity(self) 
    self.identity = identity 
Các vấn đề liên quan