在其基类中创建当前类的默认实例

时间:2013-09-04 12:11:53

标签: python oop python-2.7 static metaclass

序言:我有对象,其中一些可以通过默认构造函数创建并保留而不进行修改,因此这些对象可以被视为“空”。有时我需要验证某个对象是否为“空”。它可以通过以下方式完成(majik方法在基类Animal中实现):

>>> a = Bird()
>>> b = Bird()
>>> a == b
True
>>> a == Bird()
True

所以问题:是否有可能(如果是,那么如何)实现这样的语法:

>>> a == Bird.default
True

至少这一个(但前一个更甜):

>>> a == a.default
True

但是:在基类default中实现Animal不在所有派生类中重复):

class Animal(object):
   ... tech stuff ...
        - obj comparison
        - obj representation
        - etc

class Bird(Animal):
   ... all about birds ...

class Fish(Animal):
   ... all about fishes ...

当然我不需要在Bird()类中调用Animal的解决方案:)

我希望在基类中实现一种模板,它将标记派生类的默认实例,并将其唯一副本存储在派生类或实例属性中。我认为可以通过玩metaclasses左右来实现,但不知道如何。

类默认实例可以被视为由其类__init__()实例化的任何对象(当然没有进一步的对象修改)。

更新

系统充满了对象,我只想有可能将已经以某种方式修改过的新对象(默认情况下)创建的对象(例如无用的显示)分开。我是这样做的:

if a == Bird():
    . . .

我不想创建新对象进行比较,直观地说,我想将一个实例副本作为标准具用于此类的实例进行比较。对象类似于JSON并且只包含属性(除了隐式__str____call____eq__方法),因此我希望保持这种使用内置Python功能的方式并避免例如,使用明确定义的方法,如is_empty()。这就像在交互式shell中输入一个对象并将其打印出来调用__str__,这是隐含的,但很有趣。

2 个答案:

答案 0 :(得分:1)

要实现第一个解决方案,您应该使用metaclass

例如:

def add_default_meta(name, bases, attrs):
    cls = type(name, bases, attrs)
    cls.default = cls()
    return cls

并将其用作(假设python3。在python2中设置类主体中的__metaclass__属性):

class Animal(object, metaclass=add_default_meta):
    # stuff

class NameClass(Animal, metaclass=add_default_meta):
    # stuff

请注意,您已为metaclass=...的每个子类重复Animal

如果使用类及其__new__方法来实现元类而不是函数,它可以被继承,即:

class AddDefaultMeta(type):
    def __new__(cls, name, bases, attrs):
        cls = super(AddDefaultMeta, cls).__new__(cls, name, bases, attrs)
        cls.default = cls()
        return cls

实现相同效果的另一种方法是使用类装饰器:

def add_default(cls):
    cls.default = cls()
    return cls


@add_default
class Bird(Animal):
   # stuff

同样,你必须使用每个子类的装饰器。

如果您想要实现第二个解决方案,即检查a == a.default,那么您只需重新实现Animal.__new__

class Animal(object):
    def __new__(cls, *args, **kwargs):
        if not (args or kwargs) and not hasattr(cls, 'default'):
            cls.default = object.__new__(cls)
            return cls.default
        else:
            return object.__new__(cls)

每当创建第一个类实例并将其存储在default属性中时,这将创建空实例。

这意味着您可以同时执行这两项操作:

a == a.default

a == Bird.default

如果您没有创建任何Bird.default个实例,那么访问AttributeErrorBird


样式注释:Bird.Default对我来说非常糟糕。 DefaultBird不是类型的实例,因此根据PEP 8,您应该使用lowercase_with_underscore

事实上,对我来说,整件事看起来很可疑。你可以简单地使用is_empty()方法。它很容易实现:

class Animal(object):
    def __init__(self, *args, **kwargs):
        # might require more complex condition
        self._is_empty = not (bool(args) or bool(kwargs))
    def is_empty(self):
        return self._is_empty

然后,当子类创建一个没有将任何参数传递给基类的空实例时,_is_empty属性将为True,因此继承的方法将返回True因此,在其他情况下,一些参数将被传递给基类,该基类将_is_empty设置为False

您可以使用它来获得更健壮的条件,以便更好地使用子类。

答案 1 :(得分:1)

另一种可能的元类:

class DefaultType(type):
    def __new__(cls, name, bases, attrs):
        new_cls = super(DefaultType, cls).__new__(cls, name, bases, attrs)
        new_cls.default = new_cls()
        return new_cls

您只需要为Animal类设置元类属性,因为所有派生类都将继承它:

class Animal(object):
    __metaclass__ = DefaultType
    # ...

class Bird(Animal):
    # ...

这允许您同时使用:

a == Bird.default

a == a.default