2012-02-24 32 views
16

Tôi có một lớp học siêu hạng với nhiều lớp khác. Tôi muốn biết (trong init() của siêu lớp học của tôi nếu lớp con đã ghi đè một phương pháp cụ thểLàm thế nào để phát hiện quá tải phương thức trong các lớp con trong python?

Tôi cố gắng để thực hiện điều này với một phương pháp học, nhưng kết quả là sai:.

class Super: 
    def __init__(self): 
     if self.method == Super.method: 
     print 'same' 
     else: 
     print 'different' 

    @classmethod 
    def method(cls): 
     pass 

class Sub1(Super): 
    def method(self): 
     print 'hi' 

class Sub2(Super): 
    pass 

Super() # should be same 
Sub1() # should be different 
Sub2() # should be same 

>>> same 
>>> different 
>>> different 

có cách nào cho một siêu lớp để biết nếu một sub-class đã ghi đè một phương pháp?

+1

Could bạn nói một vài từ về * tại sao * bạn muốn làm điều đó? – NPE

+0

Không có gì đến với tâm trí ngay lập tức, nhưng nếu bạn bao gồm docstrings cho các phương pháp của bạn, sau đó họ nhận được ghi đè khi phương pháp được ghi đè. Do đó, bạn có thể theo dõi nó với 'MyClass.methodname .__ doc__'.Nhưng tôi thấy giải pháp này rất hack, đó là lý do tại sao tôi không đăng nó như là một câu trả lời – inspectorG4dget

+0

Về cơ bản tôi muốn có siêu lớp có các phương thức này được định nghĩa là 'pass' và có một phương thức riêng biệt (thực sự __init__) gọi những chức năng này. Tôi muốn có init đưa vào báo cáo in nói rằng chúng tôi đang bắt đầu hoặc kết thúc chức năng, nhưng nếu chức năng đó vẫn còn null, các báo cáo này nhìn ra khỏi vị trí, và tôi muốn làm đi với họ. – Brian

Trả lời

7

Bạn có thể sử dụng trang trí của riêng bạn. Nhưng đây là một thủ thuật và sẽ chỉ hoạt động trên các lớp mà bạn kiểm soát việc thực hiện.

def override(method): 
    method.is_overridden = True 
    return method 

class Super: 
    def __init__(self): 
     if hasattr(self.method, 'is_overridden'): 
     print 'different' 
     else: 
     print 'same' 
    @classmethod 
    def method(cls): 
     pass 

class Sub1(Super): 
    @override 
    def method(self): 
     print 'hi' 

class Sub2(Super): 
    pass 

Super() # should be same 
Sub1() # should be different 
Sub2() # should be same 

>>> same 
>>> different 
>>> same 
+4

Tôi đã đi theo một hướng hơi khác, trang trí phương pháp của tôi với @native, và giả sử các lớp con sẽ không. Tôi nghĩ tôi sẽ làm theo cách này vì tôi không có nhiều quyền kiểm soát các lớp phụ. Nó hoạt động tốt, cảm ơn sự giúp đỡ của bạn! – Brian

+0

Hoặc thậm chí đơn giản hơn đặt 'Super.method._original = True' là phần thân của lớp' Super() ', và sau đó kiểm tra xem' hasattr (self.method, '_original') '. –

3

bạn có thể so sánh bất cứ thứ gì trong __dict__ của lớp với các chức năng bên trong phương pháp bạn có thể lấy từ đối tượng - chức năng "detect_overriden" thấp làm điều đó - lừa là để vượt qua "lớp cha mẹ" cho tên của nó, giống như một trong những cuộc gọi đến "siêu" - khác nó không phải là dễ dàng để lấy các thuộc tính từ chính parentclass thay vì của lớp con:

# -*- coding: utf-8 -*- 
from types import FunctionType 

def detect_overriden(cls, obj): 
    res = [] 
    for key, value in cls.__dict__.items(): 
     if isinstance(value, classmethod): 
      value = getattr(cls, key).im_func 
     if isinstance(value, (FunctionType, classmethod)): 
      meth = getattr(obj, key) 
      if not meth.im_func is value: 
       res.append(key) 
    return res 


# Test and example 
class A(object): 
    def __init__(self): 
     print detect_overriden(A, self) 

    def a(self): pass 
    @classmethod 
    def b(self): pass 
    def c(self): pass 

class B(A): 
    def a(self): pass 
    #@classmethod 
    def b(self): pass 

chỉnh sửa thay đổi mã để làm việc tốt với classmethods cũng như: nếu nó phát hiện một classmethod trên lớp cha mẹ, chiết xuất các chức năng cơ bản trước khi tiếp tục.

- Một cách khác để làm điều này, không phải mã hóa cứng tên lớp, sẽ theo thứ tự độ phân giải phương thức của lớp (self.__class__) (được cho bởi thuộc tính __mro__) và tìm kiếm các bản sao của các phương pháp và các thuộc tính được định nghĩa trong mỗi lớp dọc theo chuỗi thừa kế.

+1

Trong Python 3, thuộc tính '__func__'. –

4

Có vẻ như đơn giản nhất và đủ để làm điều này bằng cách so sánh các tập hợp con chung của từ điển của một thể hiện và các lớp cơ sở riêng của mình, ví dụ:

def detect_overridden(cls, obj): 
    common = cls.__dict__.keys() & obj.__class__.__dict__.keys() 
    diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]] 
    print(diff) 

def f1(self): 
    pass 

class Foo: 
    def __init__(self): 
    detect_overridden(Foo, self) 
    def method1(self): 
    print("Hello foo") 
    method2=f1 

class Bar(Foo): 
    def method1(self): 
    print("Hello bar") 
    method2=f1 # This is pointless but not an override 
# def method2(self): 
# pass 

b=Bar() 
f=Foo() 

Chạy và đưa ra:

['method1'] 
[] 
4

Trả lời trả lời https://stackoverflow.com/a/9437273/1258307, vì tôi chưa có đủ tín dụng để nhận xét về nó, nó sẽ không hoạt động dưới python 3 trừ khi bạn thay thế im_func với __func__ và cũng sẽ không hoạt động trong python 3.4 (và nhiều khả năng trở đi) vì các hàm không còn có thuộc tính __func__, chỉ các phương thức bị ràng buộc.

EDIT: Đây là giải pháp cho câu hỏi ban đầu (mà làm việc trên 2,7 và 3,4, và tôi thừa nhận tất cả phiên bản khác ở giữa):

class Super: 
     def __init__(self): 
      if self.method.__code__ is Super.method.__code__: 
       print('same') 
      else: 
       print('different') 

     @classmethod 
     def method(cls): 
      pass 

    class Sub1(Super): 
     def method(self): 
      print('hi') 

    class Sub2(Super): 
     pass 

    Super() # should be same 
    Sub1() # should be different 
    Sub2() # should be same 

Và đây là kết quả:

same 
different 
same 
+0

Chỉ cần lưu ý: trong cython (ít nhất là trong python 3.4), 'self.method .__ code__' là vắng mặt, nhưng hoạt động trong Python sạch. Bởi một số lý do, '__code__' bị thiếu trong' dir (self.method) ' – socketpair

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