5

I'm writing an plugin framework and I want to be able to write a decorator interface, which will convert user class to ABC class and substitute all methods with abstractmethods. I cannot get it working and I suppose the problem is connected with wrong mro, but I can be wrong.

I basically need to be albe to write:

@interface
class X:
    def test(self):
        pass

x = X() # should fail, because test will be abstract method.

substituting methods with their abstract versions is straightforward (you have to iterate over func's and replace them with abc.abstractmethod(func)), but I've got problem with creating dynamic type, which will be an ABCmeta metaclass.

Right now I've got something like:

from abc import ABCMeta

class Interface(metaclass=ABCMeta):
    pass

def interface(cls):
    newcls = type(cls.__name__, (Interface, cls), {})
    # substitute all methods with abstract ones
    for name, func in inspect.getmembers(newcls, predicate=inspect.isfunction):
        setattr(newcls, name, abstractmethod(func))
    return newcls

but it doesnot work - Ican initialize class X without errors.

With standard usage of ABC in Python, we can write:

class X(metaclass=ABCMeta):
    @abstractmethod
    def test(self):
        pass

x = X() # it will fail

How can I create dynamic type in Python3, which will behave like it will have metaclass ABCmeta and will substitute all functions with abstract ones?

4

1 回答 1

5

The trick is not to use setattr to reset each of the attributes, but instead to pass those modified attributes to the type function as a dictionary:

import inspect
from abc import ABCMeta, abstractmethod

class Interface(metaclass=ABCMeta):
    pass

def interface(cls):
    attrs = {n: abstractmethod(f)
             for n, f in inspect.getmembers(cls, predicate=inspect.isfunction)}

    return type(cls.__name__, (Interface, cls), attrs)

@interface
class X(metaclass=ABCMeta):
    def test(self):
        pass

x = X()
# does fail:
# Traceback (most recent call last):
#   File "test.py", line 19, in <module>
#     x = X() # should fail, because test will be abstract method.
# TypeError: Can't instantiate abstract class X with abstract methods test
于 2013-01-08T16:11:25.620 回答