0

序言:我有对象,其中一些可以由默认构造函数创建,并且无需修改,因此可以将这些对象视为“空”。有时我需要验证某个对象是否为“空”。可以通过以下方式完成(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:)

我想在基类中实现一种模板,它将删除派生类默认实例并将其唯一副本存储在派生类或实例属性中。我认为这可以通过使用元类来实现,但不知道如何。

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

 

更新

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

if a == Bird():
    . . .

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

4

2 回答 2

1

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

例如:

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对我来说看起来很糟糕。Default是非类型的实例Bird,因此您应该lowercase_with_underscore根据 PEP 8 使用。

事实上,整件事对我来说看起来很可疑。你可以简单地有一个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_emptyFalse.

您可以使用它来获得更强大的条件,以便更好地与您的子类一起使用。

于 2013-09-04T12:41:31.173 回答
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
于 2013-09-04T13:18:25.297 回答