2012-02-10 31 views
8
class ITestType(object): 
    """ Sample interface type """ 

    __metaclass__ = ABCMeta 

    @abstractmethod 
    def requiredCall(self): 
    return 

class TestType1(object): 
    """ Valid type? """ 
    def requiredCall(self): 
    pass 

class TestType2(ITestType): 
    """ Valid type """ 
    def requiredCall(self): 
    pass 

class TestType3(ITestType): 
    """ Invalid type """ 
    pass 

Trong ví dụ trên issubclass(TypeType*, ITestType) sẽ trở lại đúng đối với 2 và sai cho 1 và 3.Làm thế nào để bạn xác minh giao diện kiểu gõ trong python?

Có một cách khác để sử dụng issubclass, hoặc một phương pháp khác để thử nghiệm giao diện mà sẽ cho phép 1 2 để vượt qua, nhưng từ chối 3? Nó sẽ rất hữu ích cho tôi để có thể sử dụng gõ vịt hơn là ràng buộc ràng buộc các lớp học để trừu tượng các loại, nhưng cũng cho phép đối tượng kiểm tra khi vịt-typed đối tượng đi qua giao diện cụ thể. Có, tôi biết rằng người trăn không thích giao diện, và phương pháp chuẩn là "tìm nó khi nó không thành công và bọc mọi thứ trong ngoại lệ" nhưng cũng hoàn toàn không liên quan đến câu hỏi của tôi. Không, tôi không thể đơn giản không sử dụng giao diện trong dự án này.

chỉnh sửa:

Hoàn hảo! Đối với bất kỳ ai khác tìm thấy câu hỏi này, dưới đây là ví dụ về cách sử dụng subclasshook:

class ITestType(object): 
    """ Sample interface type """ 

    __metaclass__ = ABCMeta 

    @abstractmethod 
    def requiredCall(self): 
    return 

    @classmethod 
    def __subclasshook__(cls, C): 
    required = ["requiredCall"] 
    rtn = True 
    for r in required: 
     if not any(r in B.__dict__ for B in C.__mro__): 
     rtn = NotImplemented 
    return rtn 
+1

'Không, tôi không thể đơn giản không sử dụng giao diện trong dự án này.' Tại sao? (Tôi không phải là châm biếm, thực sự tò mò về trường hợp sử dụng có thể là gì) –

Trả lời

9

Kiểm tra ABC module. Bạn có thể định nghĩa một lớp cơ sở trừu tượng cung cấp phương thức __subclasshook__ xác định xem một lớp cụ thể "là lớp con" của lớp cơ sở trừu tượng dựa trên bất kỳ tiêu chí nào bạn thích - chẳng hạn như "nó có phương thức X, Y và Z" hay bất kỳ thứ gì . Sau đó, bạn có thể sử dụng issubclass() hoặc isinstance() để phát hiện giao diện trên các lớp và các phiên bản.

1

Dưới đây là một giải pháp thay thế hoạt động rất tốt trong thực tế, mà không cần kiểm tra toàn bộ từ điển trên mọi cá thể tạo lớp.

(py2 và py3 tương thích)

Cách sử dụng:

class Bar(): 
    required_property_1 = '' 

    def required_method(self): 
    pass 

# Module compile time check that Foo implements Bar 
@implements(Bar) 
class Foo(UnknownBaseClassUnrelatedToBar): 
    required_property_1 

    def required_method(self): 
    pass 

# Run time check that Foo uses @implements or defines its own __implements() member 
def accepts_bar(self, anything): 
    if not has_api(anything, Bar): 
    raise Exception('Target does not implement Bar') 
    ... 

Bạn cũng có thể làm những điều hiển nhiên như @implements (Stream, Folder, Bar), khi tất cả họ đều yêu cầu một số phương pháp tương tự, điều này hữu ích hơn là thừa kế.

Code:

import inspect 


def implements(*T): 
    def inner(cls): 
    cls.__implements = [] 
    for t in T: 

     # Look for required methods 
     t_methods = inspect.getmembers(t, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x)) 
     c_methods = inspect.getmembers(cls, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x)) 
     sig = {} 
     for i in t_methods: 
     name = 'method:%s' % i[0] 
     if not name.startswith("method:__"): 
      sig[name] = False 
     for i in c_methods: 
     name = 'method:%s' % i[0] 
     if name in sig.keys(): 
      sig[name] = True 

     # Look for required properties 
     t_props = [i for i in inspect.getmembers(t) if i not in t_methods] 
     c_props = [i for i in inspect.getmembers(cls) if i not in c_methods] 
     for i in t_props: 
     name = 'property:%s' % i[0] 
     if not name.startswith("property:__"): 
      sig[name] = False 
     for i in c_props: 
     name = 'property:%s' % i[0] 
     if name in sig.keys(): 
      sig[name] = True 

     missing = False 
     for i in sig.keys(): 
     if not sig[i]: 
      missing = True 
     if missing: 
     raise ImplementsException(cls, t, sig) 
     cls.__implements.append(t) 
    return cls 
    return inner 


def has_api(instance, T): 
    """ Runtime check for T in type identity """ 
    rtn = False 
    if instance is not None and T is not None: 
    if inspect.isclass(instance): 
     if hasattr(instance, "__implements"): 
     if T in instance.__implements: 
      rtn = True 
    else: 
     if hasattr(instance.__class__, "__implements"): 
     if T in instance.__class__.__implements: 
      rtn = True 
    return rtn 


class ImplementsException(Exception): 
    def __init__(self, cls, T, signature): 
    msg = "Invalid @implements decorator on '%s' for interface '%s': %r" % (cls.__name__, T.__name__, signature) 
    super(ImplementsException, self).__init__(msg) 
    self.signature = signature 
2

đây là một vài năm cuối nhưng đây là cách tôi đã làm nó:

import abc 

class MetaClass(object): 
    __metaclass__ = abc.ABCMeta 

    [...] 

    @classmethod 
    def __subclasshook__(cls, C): 
     if C.__abstractmethods__: 
      print C.__abstractmethods__ 
      return False 
     else: 
      return True 

Nếu C là một lớp cố gắng của MetaClass, sau đó C.__abstractmethods__ sẽ trống chỉ nếu C thực hiện tất cả các phương pháp trừu tượng.

Xem ở đây để biết chi tiết: https://www.python.org/dev/peps/pep-3119/#the-abc-module-an-abc-support-framework (đó là dưới "Thực hiện" nhưng một tìm kiếm cho __abstractmethods__ nên giúp bạn có được những đoạn phải)

đâu này đã làm việc cho tôi:

tôi có thể tạo MetaClass. Sau đó tôi có thể phân loại BaseClassMetaClass để tạo SubClass cần một số chức năng bổ sung.Nhưng tôi có một nhu cầu để đúc một thể hiện của BaseClass xuống đến SubClass bằng cách thay đổi các thuộc tính __cls__ vì tôi không sở hữu BaseClass nhưng tôi nhận được trường hợp của nó mà tôi muốn bỏ xuống.

Tuy nhiên, nếu tôi triển khai không đúng SubClass, tôi vẫn có thể bỏ qua trừ khi tôi sử dụng ở trên __subclasshook__ và chỉ thêm một kiểm tra lớp con khi tôi thực hiện quy trình truyền xuống (tôi nên làm như vậy vì tôi chỉ muốn thử truyền một lớp cha xuống). Nếu ai đó yêu cầu, tôi có thể cung cấp một MWE cho việc này.

ETA: Đây là MWE. Tôi nghĩ rằng những gì tôi đã đề xuất trước đây là không chính xác vì vậy sau đây xuất hiện để làm những gì tôi dự định.

Mục đích là để có thể chuyển đổi đối tượng BaseClass thành SubClass và ngược lại. Từ SubClass đến BaseClass thật dễ dàng. Nhưng từ BaseClass đến SubClass không quá nhiều. Điều tiêu chuẩn cần làm là cập nhật thuộc tính __class__ nhưng để lại mở khi SubClass không thực sự là phân lớp hoặc xuất phát từ lớp meta trừu tượng nhưng không được triển khai đúng cách.

Dưới đây, việc chuyển đổi được thực hiện theo phương pháp convert mà thực hiện BaseMetaClass. Tuy nhiên, trong logic đó, tôi kiểm tra hai thứ. Một, để chuyển đổi sang lớp con, tôi kiểm tra xem nó có thực sự là một phân lớp hay không. Thứ hai, tôi kiểm tra thuộc tính __abstractmethods__ để xem thuộc tính có trống không. Nếu có, thì nó cũng là một metaclass được thực hiện đúng cách. Thất bại dẫn đến một TypeError. Nếu không thì đối tượng được chuyển đổi.

import abc 


class BaseMetaClass(object): 
    __metaclass__ = abc.ABCMeta 

    @classmethod 
    @abc.abstractmethod 
    def convert(cls, obj): 
     if issubclass(cls, type(obj)): 
      if cls.__abstractmethods__: 
       msg = (
        '{0} not a proper subclass of BaseMetaClass: ' 
        'missing method(s)\n\t' 
       ).format(
        cls.__name__ 
       ) 
       mthd_list = ',\n\t'.join(
        map(
         lambda s: cls.__name__ + '.' + s, 
         sorted(cls.__abstractmethods__) 
        ) 
       ) 
       raise TypeError(msg + mthd_list) 

      else: 
       obj.__class__ = cls 
       return obj 
     else: 
      msg = '{0} not subclass of {1}'.format(
       cls.__name__, 
       type(obj).__name__ 
      ) 
      raise TypeError(msg) 

    @abc.abstractmethod 
    def abstractmethod(self): 
     return 


class BaseClass(object): 

    def __init__(self, x): 
     self.x = x 

    def __str__(self): 
     s0 = "BaseClass:\n" 
     s1 = "x: {0}".format(self.x) 
     return s0 + s1 


class AnotherBaseClass(object): 

    def __init__(self, z): 
     self.z = z 

    def __str__(self): 
     s0 = "AnotherBaseClass:\n" 
     s1 = "z: {0}".format(self.z) 
     return s0 + s1 


class GoodSubClass(BaseMetaClass, BaseClass): 

    def __init__(self, x, y): 
     super(GoodSubClass, self).__init__(x) 
     self.y = y 

    @classmethod 
    def convert(cls, obj, y): 
     super(GoodSubClass, cls).convert(obj) 
     obj.y = y 

    def to_base(self): 
     return BaseClass(self.x) 

    def abstractmethod(self): 
     print "This is the abstract method" 

    def __str__(self): 
     s0 = "SubClass:\n" 
     s1 = "x: {0}\n".format(self.x) 
     s2 = "y: {0}".format(self.y) 
     return s0 + s1 + s2 


class BadSubClass(BaseMetaClass, BaseClass): 

    def __init__(self, x, y): 
     super(BadSubClass, self).__init__(x) 
     self.y = y 

    @classmethod 
    def convert(cls, obj, y): 
     super(BadSubClass, cls).convert(obj) 
     obj.y = y 

    def __str__(self): 
     s0 = "SubClass:\n" 
     s1 = "x: {0}\n".format(self.x) 
     s2 = "y: {0}".format(self.y) 
     return s0 + s1 + s2 


base1 = BaseClass(1) 
print "BaseClass instance" 
print base1 
print 


GoodSubClass.convert(base1, 2) 
print "Successfully casting BaseClass to GoodSubClass" 
print base1 
print 

print "Cannot cast BaseClass to BadSubClass" 
base1 = BaseClass(1) 
try: 
    BadSubClass.convert(base1, 2) 
except TypeError as e: 
    print "TypeError: {0}".format(e.message) 
    print 


print "Cannot cast AnotherBaseCelass to GoodSubClass" 
anotherbase = AnotherBaseClass(5) 
try: 
    GoodSubClass.convert(anotherbase, 2) 
except TypeError as e: 
    print "TypeError: {0}".format(e.message) 
    print 

print "Cannot cast AnotherBaseCelass to BadSubClass" 
anotherbase = AnotherBaseClass(5) 
try: 
    BadSubClass.convert(anotherbase, 2) 
except TypeError as e: 
    print "TypeError: {0}".format(e.message) 
    print 


# BaseClass instance 
# BaseClass: 
# x: 1 

# Successfully casting BaseClass to GoodSubClass 
# SubClass: 
# x: 1 
# y: 2 

# Cannot cast BaseClass to BadSubClass 
# TypeError: BadSubClass not a proper subclass of BaseMetaClass: missing method(s) 
#  BadSubClass.abstractmethod 

# Cannot cast AnotherBaseCelass to GoodSubClass 
# TypeError: GoodSubClass not subclass of AnotherBaseClass 

# Cannot cast AnotherBaseCelass to BadSubClass 
# TypeError: BadSubClass not subclass of AnotherBaseClass 
+0

xin vui lòng, cung cấp một ví dụ – Konstantin

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