Preamble: I have objects, some of them could be created by default constructor and left without modifications, so such objects could be considered as "empty". Sometimes I need to verify whether some object is "empty" or not. It could be done in the following way (majik methods are implemented in the base class Animal):

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

So the question: is it possible (and if yes then how) to achieve such syntax:

>>> a == Bird.default
True

At least this one (but the previous is sweeter):

>>> a == a.default
True

But: with implementation of default in the base class Animal (to not repeat it in all derived classes):

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

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

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

Of course I don't need solutions to have Bird() calling in Animal class :)

I'd like to have a kind of templating implemented in base class which will stamp out derived class default instance and store its unique copy in the derived class or instance property. I think it could be achieved by playing with metaclasses or so, but don't know how.

Class default instance could be considered as any object instantiated by __init__() of its class (without further object modification of course).

 

UPDATE

The system is flooded with objects and I just want to have a possibility to separate circulating of freshly (by default) created objects (which are useless to display for example) from already somehow modified one. I do it by:

if a == Bird():
    . . .

I don't want creation of new object for comparison, intuitevly, I'd like to have one instance copy as etalon for the instances of this class to compare with. Objects are JSON-like and contain only properties (besides implicit __str__, __call__, __eq__ methods), so I'd like to keep such style of using built-in Python features and avoid the using explicitly defined methods like is_empty() for example. It's like entering an object in the interactive shell and it prints it out calling __str__, it is implicit, but fun.

有帮助吗?

解决方案

To achieve the first solution you should use a metaclass.

For example:

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

And use it as(assuming python3. In python2 set the __metaclass__ attribute in the class body):

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

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

Note that you have repeat the metaclass=... for every subclass of Animal.

If instead of a function you use a class and its __new__ method to implement the metaclass, it can be inherited, i.e:

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

A different way to achieve the same effect is to use a class decorator:

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


@add_default
class Bird(Animal):
   # stuff

Again, you must use the decorator for every subclass.

If you want to achieve the second solution, i.e. to check a == a.default, then you can simply reimplement 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)

This will create the empty instance whenever the first instance of the class is created and it is stored in the default attribute.

This means that you can do both:

a == a.default

and

a == Bird.default

But accessing Bird.default gives AttributeError if you didn't create any Bird instance.


Style note: Bird.Default looks very bad to me. Default is an instance of Bird not a type, hence you should use lowercase_with_underscore according to PEP 8.

In fact the whole thing looks fishy for me. You could simply have an is_empty() method. It's pretty easy to implement:

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

Then when the subclasses create an empty instance that doesn't pass any arguments to the base class the _is_empty attribute will be True and hence the inherited method will return True accordingly, while in the other cases some argument would be passed to the base class which would set _is_empty to False.

You can play around with this in order to obtain a more robust condition that works better with your subclasses.

其他提示

Another possible metaclass:

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

You only need to set the metaclass attribute for the Animal class, as all derived classes will inherit it:

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

class Bird(Animal):
    # ...

This allows you to use both:

a == Bird.default

and:

a == a.default
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top