Generics/templates in python?

前端 未结 10 1428
深忆病人
深忆病人 2020-12-22 19:16

How does python handle generic/template type scenarios? Say I want to create an external file \"BinaryTree.py\" and have it handle binary trees, but for any data type.

相关标签:
10条回答
  • 2020-12-22 19:47

    Actually now you can use generics in Python 3.5+. See PEP-484 and typing module documentation.

    According to my practice it is not very seamless and clear especially for those who are familiar with Java Generics, but still usable.

    0 讨论(0)
  • 2020-12-22 19:47

    Look at how the built-in containers do it. dict and list and so on contain heterogeneous elements of whatever types you like. If you define, say, an insert(val) function for your tree, it will at some point do something like node.value = val and Python will take care of the rest.

    0 讨论(0)
  • 2020-12-22 19:50

    Here's a variant of this answer that uses metaclasses to avoid the messy syntax, and use the typing-style List[int] syntax:

    class template(type):
        def __new__(metacls, f):
            cls = type.__new__(metacls, f.__name__, (), {
                '_f': f,
                '__qualname__': f.__qualname__,
                '__module__': f.__module__,
                '__doc__': f.__doc__
            })
            cls.__instances = {}
            return cls
    
        def __init__(cls, f):  # only needed in 3.5 and below
            pass
    
        def __getitem__(cls, item):
            if not isinstance(item, tuple):
                item = (item,)
            try:
                return cls.__instances[item]
            except KeyError:
                cls.__instances[item] = c = cls._f(*item)
                item_repr = '[' + ', '.join(repr(i) for i in item) + ']'
                c.__name__ = cls.__name__ + item_repr
                c.__qualname__ = cls.__qualname__ + item_repr
                c.__template__ = cls
                return c
    
        def __subclasscheck__(cls, subclass):
            for c in subclass.mro():
                if getattr(c, '__template__', None) == cls:
                    return True
            return False
    
        def __instancecheck__(cls, instance):
            return cls.__subclasscheck__(type(instance))
    
        def __repr__(cls):
            import inspect
            return '<template {!r}>'.format('{}.{}[{}]'.format(
                cls.__module__, cls.__qualname__, str(inspect.signature(cls._f))[1:-1]
            ))
    

    With this new metaclass, we can rewrite the example in the answer I link to as:

    @template
    def List(member_type):
        class List(list):
            def append(self, member):
                if not isinstance(member, member_type):
                    raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                        type(member).__name__,
                        type(self).__name__,
                        member_type.__name__ 
                    ))
    
                    list.append(self, member)
        return List
    
    l = List[int]()
    l.append(1)  # ok
    l.append("one")  # error
    

    This approach has some nice benefits

    print(List)  # <template '__main__.List[member_type]'>
    print(List[int])  # <class '__main__.List[<class 'int'>, 10]'>
    assert List[int] is List[int]
    assert issubclass(List[int], List)  # True
    
    0 讨论(0)
  • 2020-12-22 19:51

    Python uses duck typing, so it doesn't need special syntax to handle multiple types.

    If you're from a C++ background, you'll remember that, as long as the operations used in the template function/class are defined on some type T (at the syntax level), you can use that type T in the template.

    So, basically, it works the same way:

    1. define a contract for the type of items you want to insert in the binary tree.
    2. document this contract (i.e. in the class documentation)
    3. implement the binary tree using only operations specified in the contract
    4. enjoy

    You'll note however, that unless you write explicit type checking (which is usually discouraged), you won't be able to enforce that a binary tree contains only elements of the chosen type.

    0 讨论(0)
提交回复
热议问题