在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"