在Python中允许子类化的不可变类型
我希望拥有不可变的类型,理想情况下,这些类型可以对自己的哈希和相等进行排序,但可以轻松地进行子类化。我开始使用: …但这不适合子类化。直接子类化这意味着元组的名称保持不变(用于打印…没什么大不了的),但更重要的是,您不能添加字段:在Python中允许子类化的不可变类型,python,immutability,Python,Immutability,我希望拥有不可变的类型,理想情况下,这些类型可以对自己的哈希和相等进行排序,但可以轻松地进行子类化。我开始使用: …但这不适合子类化。直接子类化这意味着元组的名称保持不变(用于打印…没什么大不了的),但更重要的是,您不能添加字段: class LimitedLengthCommand(Command): # I want to have self.length! Where does it go? def valid_msg(msg): return len(msg) <
class LimitedLengthCommand(Command):
# I want to have self.length! Where does it go?
def valid_msg(msg):
return len(msg) <= self.length
class LimitedLengthCommand(命令):
#我想拥有自我长度!它去哪里了?
def有效消息(消息):
return len(msg)这里有一个元类来做你想做的事情(我想)。它的工作原理是将要继承的方法存储在字典中,然后手动将它们插入到新的类字典中。它还存储传递给namedtuple
构造函数的属性字符串,并将其与子类中的属性字符串合并。然后,它将其传递给namedtuple
,并返回一个从结果namedtuple
继承的类及其字典中的所有适当方法。因为元类是从abc.ABCMeta派生的,所以您可以免费获得工作类型检查。下面是构建两个类的方式:
class Foo(object):
__metaclass__ = ImmutableMeta
_attributes_ = 'a b'
def sayhi(self):
print "Hello from {0}".format(type(self).__name__)
class Bar(Foo):
_attributes_ = 'c'
def saybye(self):
print "Goodbye from {0}".format(type(self).__name__)
这是元类:
import collections as co
import abc
class ImmutableMeta(abc.ABCMeta):
_classes = {}
def __new__(meta, clsname, bases, clsdict):
attributes = clsdict.pop('_attributes_')
if bases[0] is object:
# 'new' class
methods = clsdict
else:
# we're 'inheriting' from an existing class
base = bases[0]
attributes = meta._classes[base]['attributes'] + ' ' + attributes
base_methods = meta._classes[base]['methods'].copy()
base_methods.update(clsdict)
methods = base_methods
# construct the actual base class and create the return class
new_base = co.namedtuple(clsname + 'Base', attributes)
cls = super(ImmutableMeta, meta).__new__(meta, clsname, (new_base,),
methods)
# register the data necessary to 'inherit' from the class
# and make sure that it passes typechecking
meta._classes[cls] = {'attributes': attributes,
'methods': methods}
if bases[0] is not object:
base.register(cls)
return cls
这里有一些微不足道的测试代码
a = Foo(1, 2)
a.sayhi()
b = Bar(1, 2, 3)
b.sayhi() # 'inherited' from class Foo
b.saybye()
try:
b.c = 1 # will raise an AttributeError
except AttributeError:
print "Immutable"
print "issubclass(Bar, Foo): {0}".format(issubclass(Bar, Foo))
try:
d = {b: 1} # No problems
except TypeError:
print "Cant put it in a dict"
else:
print "Can put it in a dict"
希望有帮助。如果您不希望将每个方法都附加到应该继承它的每个类上,那么您还可以提供一个默认的\uuuu getattr\uuuu
,它可以查看元类字典并从中找到适当的方法。这需要某种程度上将基类硬编码到方法中,可能使用闭包。我从来没有这样做过,它目前给我带来了麻烦,但我知道您可以通过使用\uuuuuuunew\uuuuuuuuu
而不是\uuuuuuuu init\uuuuuuuuuuuu
来进行任何修改。我知道你可以用\uuuu new\uuuuu
对元组进行子类化,但我也想要命名字段。你需要用issubclass测试它们吗?可能不需要。它们会有一定程度的反映(这样类可以为每个命令生成方法),但这不应该涉及类型层次结构信息。对不起,我的时间被占用了,所以给我几天时间,我会尝试一下。但这似乎是一个很好的解决方案:)
a = Foo(1, 2)
a.sayhi()
b = Bar(1, 2, 3)
b.sayhi() # 'inherited' from class Foo
b.saybye()
try:
b.c = 1 # will raise an AttributeError
except AttributeError:
print "Immutable"
print "issubclass(Bar, Foo): {0}".format(issubclass(Bar, Foo))
try:
d = {b: 1} # No problems
except TypeError:
print "Cant put it in a dict"
else:
print "Can put it in a dict"