如何创建抽象类属性(可能是只读的)

时间:2017-05-04 18:13:11

标签: python exception abstract-class class-properties

我花了很多时间研究这个,但没有一个答案看起来像我想要的那样。

我有一个带有class属性的抽象类,我希望每个子类都被强制实现

ArrayList<String> dealList = new ArrayList<>();

List<Webelement> deals = driver.findelements(By.xpath//"div[@class='xyz']");
// It pulls out all the Fare with same Xpath which is almost 10 value.

for(Webelement w: deals)
{
deallist.add(w.gettext());
}

ArrayList<String> newDeaList = new ArrayList<>();

for(String s: dealList)
{
newDealList.add(s);
}

Collections.sort(newDealList);
Assert.Assertequals(dealList,newDealList);

所以当我这样做时

class AbstractFoo():
    forceThis = 0

它会抛出一个错误,告诉我在实现class RealFoo(AbstractFoo): pass 之前它无法创建类。

我该怎么做?

(我不希望该属性是只读的,但如果这是唯一的解决方案,我会接受它。)

对于类方法,我发现我可以做

forceThis

这样

from abc import ABCMeta, abstractmethod

class AbstractFoo(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def forceThis():
        """This must be implemented"""

至少抛出错误class RealFoo(AbstractFoo): pass

(虽然它不强制TypeError: Can't instantiate abstract class EZ with abstract methods forceThis成为类方法。)

如何为类属性弹出类似的错误?

3 个答案:

答案 0 :(得分:4)

您可以通过定义自己的元类来完成此操作。类似的东西:

 class ForceMeta(type):
     required = ['foo', 'bar']

     def __new__(mcls, name, bases, namespace):
         cls = super().__new__(mcls, name, bases, namespace)
         for prop in mcls.required:
            if not hasattr(cls, prop):
               raise NotImplementedError('must define {}'.format(prop))
         return cls

现在您可以将它用作您自己的类的元类:

class RequiredClass(metaclass=ForceMeta):
     foo = 1

会引发错误'必须定义条'。

答案 1 :(得分:1)

我想出了一个基于之前发布的解决方案。 (谢谢@Daniel Roseman和@martineau)

我创建了一个名为ABCAMeta的元类(最后一个&#39; A&#39;代表&#39;属性&#39;)。

班级有两种工作方式。

  1. 只使用ABCAMeta作为元类的类必须有一个名为requiredAttributes的属性,该属性应该包含您希望在该类的未来子类中需要的所有属性的名称列表

  2. 其父元素为ABCAMeta的类必须具有其父类指定的所有必需属性。

  3. 例如:

    class AbstractFoo(metaclass=ABCAMeta):
        requiredAttributes = ['forceThis']
    
    class RealFoo(AbstractFoo):
        pass
    

    会抛出错误:

    NameError: Class RealFoo has not implemented the following attributes: 'forceThis'

    正是我想要的。

    class NoRequirements(RuntimeError):
            def __init__(self, message):
                RuntimeError.__init__(self, message)
    
    class ABCAMeta(ABCMeta):
        def __init__(mcls, name, bases, namespace):
            ABCMeta.__init__(mcls, name, bases, namespace)
    
        def __new__(mcls, name, bases, namespace):
            def getRequirements(c):
                """c is a class that should have a 'requiredAttributes' attribute
                this function will get that list of required attributes or
                raise a NoRequirements error if it doesn't find one.
                """
    
                if hasattr(c, 'requiredAttributes'):
                    return c.requiredAttributes
                else:
                    raise NoRequirements("Class {} has no requiredAttributes property".format(c.__name__))
    
            cls = super().__new__(mcls, name, bases, namespace)
            #true if no parents of the class being created have ABCAMeta as their metaclass
            basicMetaclass = True 
            #list of attributes the class being created must implement
            #should stay empty if basicMetaclass stays True
            reqs = [] 
            for parent in bases:
                parentMeta = type(parent)
                if parentMeta==ABCAMeta:
                    #the class being created has a parent whose metaclass is ABCAMeta
                    #the class being created must contain the requirements of the parent class
                    basicMetaclass=False
                    try:
                        reqs.extend(getRequirements(parent))
                    except NoRequirements:
                        raise
            #will force subclasses of the created class to define
            #the atrributes listed in the requiredAttributes attribute of the created class
            if basicMetaclass:
                getRequirements(cls) #just want it to raise an error if it doesn't have the attributes
            else:
                missingreqs = []
                for req in reqs:
                    if not hasattr(cls, req):
                        missingreqs.append(req)
                if len(missingreqs)!=0:
                    raise NameError("Class {} has not implemented the following attributes: {}".format(cls.__name__, str(missingreqs)[1:-1]))
            return cls
    

    欢迎提出任何改进建议。

答案 2 :(得分:0)

虽然您可以使用元类进行非常类似的操作,如@Daniel Roseman的answer所示,但也可以使用类装饰器完成。它们具有的一些优点是,在定义类时会发生错误,而不是在创建一个实例时发生错误,并且在Python 2和3中指定它们的语法相同。有些人也发现它们更简单,更容易理解。

def check_reqs(cls):
    requirements = 'must_have',

    missing = [req for req in requirements if not hasattr(cls, req)]
    if missing:
        raise NotImplementedError(
            'class {} did not define required attribute{} named {}'.format(
                cls.__name__, 's' if len(missing) > 1 else '',
                ', '.join('"{}"'.format(name) for name in missing)))
    return cls

@check_reqs
class Foo(object):  # OK
    must_have = 42

@check_reqs
class Bar(object):  # raises a NotImplementedError
    pass