【发布时间】:2015-10-28 05:03:07
【问题描述】:
我正在创建几个类以将它们用作状态标志(这更像是一个练习,尽管我将在实际项目中使用它们),就像我们在 Python 中使用 None 一样,即
... some_var is None ...
NoneType 有几个特殊属性,最重要的是它是一个单例,即在任何解释器会话期间不能有多个NoneType 实例,并且它的实例(None 对象)是不可变的。我想出了两种可能的方法来在纯 Python 中实现一些类似的行为,我很想知道从架构的角度来看哪一种看起来更好。
1。根本不要使用实例。
这个想法是有一个元类,它产生不可变的类。禁止类有实例。
class FlagMetaClass(type):
def __setattr__(self, *args, **kwargs):
raise TypeError("{} class is immutable".format(self))
def __delattr__(self, *args, **kwargs):
self.__setattr__()
def __repr__(self):
return self.__name__
class BaseFlag(object):
__metaclass__ = FlagMetaClass
def __init__(self):
raise TypeError("Can't create {} instances".format(type(self)))
def __repr__(self):
return str(type(self))
class SomeFlag(BaseFlag):
pass
我们得到了想要的行为
a = BaseFlag
a is BaseFlag # -> True
a is SomeFlag # -> False
显然,任何在这些类上设置属性的尝试都会失败(当然有一些技巧可以克服这个问题,但直接的方法是关闭的)。并且类本身是加载在命名空间中的唯一对象。
2。一个合适的单例类
class FlagMetaClass(type):
_instances = {}
def __call__(cls):
if cls not in cls._instances:
cls._instances[cls] = super(FlagMetaClass, cls).__call__()
return cls._instances[cls] # This may be slightly modified to
# raise an error instead of returning
# the same object, e.g.
# def __call__(cls):
# if cls in cls._instances:
# raise TypeError("Can't have more than one {} instance".format(cls))
# cls._instances[cls] = super(FlagMetaClass, cls).__call__()
# return cls._instances[cls]
def __setattr__(self, *args, **kwargs):
raise TypeError("{} class is immutable".format(self))
def __delattr__(self, *args, **kwargs):
self.__setattr__()
def __repr__(self):
return self.__name__
class BaseFlag(object):
__metaclass__ = FlagMetaClass
__slots__ = []
def __repr__(self):
return str(type(self))
class SomeFlag(BaseFlag):
pass
这里的Flag 类是真正的单例。当我们尝试创建另一个实例时,这个特定的实现不会引发错误,而是返回相同的对象(尽管很容易改变这种行为)。类和实例都不能直接修改。重点是在导入时为每个类创建一个实例,就像使用None 一样。
这两种方法都给了我一些不可变的独特对象,可以用于比较,就像None。对我来说,第二个看起来更像NoneType-like,因为None 是一个实例,但我不确定增加思想复杂性是否值得。期待您的来信。
【问题讨论】:
-
如果您只是在寻找状态标志,那么为什么要尝试:pypi.python.org/pypi/enum34(我假设您使用的是 python 2.x)。
-
@Dunes 一个不错的链接,谢谢。
-
sqlalchemy 的
symbol类演示了“命名单例”的另一种可能性:bitbucket.org/zzzeek/sqlalchemy/src/… 这可能值得一看... -
最好使用类,因为如果你的标志之一是深度复制,你会失去单例引用,而如果输入是一个类,深度复制输出相同的引用。