2011-11-03 38 views
16

Tôi có một lớp siêu với một phương thức gọi các phương thức khác chỉ được định nghĩa trong các lớp con của nó. Đó là lý do tại sao, khi tôi tạo một thể hiện của lớp siêu của tôi và gọi phương thức của nó, nó không thể tìm ra phương thức và đưa ra một lỗi.Ngăn chặn một lớp từ instantiation trực tiếp trong Python

Dưới đây là một ví dụ:

class SuperClass(object): 

    def method_one(self): 
    value = self.subclass_method() 
    print value 


class SubClassOne(SuperClass): 

    def subclass_method(self): 
    return 'subclass 1' 


class SubClassTwo(SuperClass): 

    def subclass_method(self): 
    return 'nubclass 2' 


s1 = SubClassOne() 
s1.method_one() 

s2 = SubClassTwo() 
s2.method_one() 

c = SuperClass() 
c.method_one() 

# Results: 
# subclass 1 
# nubclass 2 
# Traceback (most recent call last): 
# File "abst.py", line 28, in <module> 
#  c.method_one() 
# File "abst.py", line 4, in method_one 
#  value = self.subclass_method() 
# AttributeError: 'SuperClass' object has no attribute 'subclass_method' 

Tôi đã suy nghĩ về việc thay đổi init của siêu lớp và xác minh các loại đối tượng, khi một trường hợp mới được tạo ra. Nếu đối tượng thuộc về super class thì sẽ gây ra lỗi. Tuy nhiên, tôi không chắc chắn nếu đó là cách Pythonic làm việc đó.

Bất kỳ đề xuất nào?

+3

Cách Pythonic là viết tài liệu hay giải thích cách sử dụng lớp học của bạn. – yak

+2

@chown: Điều này thực sự được thực hiện khá thường xuyên trong C++. Những gì anh ta làm là cơ bản gọi các phương thức ảo. Không có gì sai với họ, nếu họ là giải pháp thích hợp cho vấn đề. – rossipedia

+1

Tôi sẽ đồng ý rằng nó không phải là rất Pythonic – rossipedia

Trả lời

9

Bạn đang nói về Lớp cơ sở trừu tượng và ngôn ngữ Python không hỗ trợ chúng nguyên bản.

Tuy nhiên, trong thư viện chuẩn, có một mô-đun bạn có thể sử dụng để trợ giúp bạn. Xem tài liệu abc.

+1

Thực ra có rất nhiều cách để thực hiện trên Python của mình, nhưng tôi sẽ chỉ nhập mô-đun abc và đặt lớp của tôi là lớp trừu tượng. Tôi nhận thấy ngay cả khi lớp của tôi là trừu tượng, tôi vẫn có thể thực hiện các phương pháp trong đó, mà là rất tốt đẹp. – mohi666

16

Cách tiếp cận của bạn là điển hình framework pattern.

Sử dụng __init__ để xác minh rằng type(self) is not SuperClass là cách hợp lý để đảm bảo SuperClass chưa được tạo trực tiếp.

Cách tiếp cận phổ biến khác là cung cấp các phương pháp sơ khai là raise NotImplementedError khi được gọi. Điều đó đáng tin cậy hơn vì nó cũng xác nhận rằng các lớp con đã ghi đè các phương thức dự kiến.

7

Đây là những gì tôi có thể làm:

class SuperClass(object): 
    def __init__(self): 
     if type(self) == SuperClass: 
      raise Exception("<SuperClass> must be subclassed.") 
     # assert(type(self) == SuperClass) 

class SubClass(SuperClass): 
    def __init__(self): 
     SuperClass.__init__(self) 

subC = SubClassOne() 
supC = SuperClass() # This line should throw an exception 

Khi chạy (ngoại lệ được ném!):

[ 18:32 [email protected] ~/so/python ]$ ./preventing-direct-instantiation.py 
Traceback (most recent call last): 
    File "./preventing-direct-instantiation.py", line 15, in <module> 
    supC = SuperClass() 
    File "./preventing-direct-instantiation.py", line 7, in __init__ 
    raise Exception("<SuperClass> must be subclassed.") 
Exception: <SuperClass> must be subclassed. 

Chỉnh sửa (từ bình luận):

[ 20:13 [email protected] ~/SO/python ]$ cat preventing-direct-instantiation.py 
#!/usr/bin/python 

class SuperClass(object): 
    def __init__(self): 
     if type(self) == SuperClass: 
      raise Exception("<SuperClass> must be subclassed.") 

class SubClassOne(SuperClass): 
    def __init__(self): 
     SuperClass.__init__(self) 

class SubSubClass(SubClassOne): 
    def __init__(self): 
     SubClassOne.__init__(self) 

class SubClassTwo(SubClassOne, SuperClass): 
    def __init__(self): 
     SubClassOne.__init__(self) 
     SuperClass.__init__(self) 

subC = SubClassOne() 

try: 
    supC = SuperClass() 
except Exception, e: 
    print "FAILED: supC = SuperClass() - %s" % e 
else: 
    print "SUCCESS: supC = SuperClass()" 

try: 
    subSubC = SubSubClass() 
except Exception, e: 
    print "FAILED: subSubC = SubSubClass() - %s" % e 
else: 
    print "SUCCESS: subSubC = SubSubClass()" 

try: 
    subC2 = SubClassTwo() 
except Exception, e: 
    print "FAILED: subC2 = SubClassTwo() - %s" % e 
else: 
    print "SUCCESS: subC2 = SubClassTwo()" 

Bản in:

[ 20:12 [email protected] ~/SO/python ]$ ./preventing-direct-instantiation.py 
FAILED: supC = SuperClass() - <SuperClass> must be subclassed. 
SUCCESS: subSubC = SubSubClass() 
SUCCESS: subC2 = SubClassTwo() 
+0

Không nói điều này không phải là khá mát mẻ, nhưng tôi nghĩ rằng nó cho thấy chính xác lý do tại sao bạn không nên cố gắng để làm điều này loại điều. Tôi khá chắc chắn cả hai ví dụ thất bại với nhiều thừa kế. – sdolan

+0

@sdolan Tôi vừa thử nghiệm, và nó thực sự làm việc với 'lớp SubSubClass (SubClassOne):' và gọi 'SubClassOne .__ init __ (self)' từ '__init__'! – chown

+0

Ahh rất tuyệt. Cảm ơn bạn đã thực hiện cập nhật đó. – sdolan

22

Tôi sẽ ghi đè __new__() trong lớp cơ sở và chỉ đơn giản là không khởi tạo được nếu đó là lớp cơ sở.

class BaseClass(object): 

    def __new__(cls, *args, **kwargs): 
     if cls is BaseClass: 
      raise TypeError("base class may not be instantiated") 
     return object.__new__(cls, *args, **kwargs) 

Điều này tách mối quan tâm tốt hơn một chút so với việc có trong __init__() và "không nhanh".

+2

Mô-đun 'abc' có thể phù hợp với 90% trường hợp, nhưng kỹ thuật này rất hữu ích trong một số trường hợp khác. Một vài điều tôi đã gặp phải: 1.) bạn muốn có một lớp mixin tái sử dụng nhưng nó thực sự không có các phương thức trừu tượng, vì vậy 'ABCMeta' sẽ không ngăn chặn nó được khởi tạo; 2.) Bạn đang định nghĩa một lớp "cơ sở" hoặc mixin mà thực sự phân lớp một cái gì đó khác, nhưng bạn không muốn nó được trực tiếp tức thời. – ches

+0

Điều này thực sự rất hữu ích. Tôi muốn thêm rằng nếu BaseClass thừa hưởng VeryBaseClass, sau đó trả về VeryBaseClass .__ new __() thay vì đối tượng .__ new __() – cfchou

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