序言:我有对象,其中一些可以通过默认构造函数创建并保留而不进行修改,因此这些对象可以被视为“空”。有时我需要验证某个对象是否为“空”。它可以通过以下方式完成(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__
,这是隐含的,但很有趣。
答案 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
个实例,那么访问AttributeError
会Bird
。
样式注释:Bird.Default
对我来说非常糟糕。 Default
是Bird
不是类型的实例,因此根据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