python - 允许在 Python 中子类化的不可变类型

标签 python immutability

我想要不可变类型,理想情况下,它们可以整理出自己的散列和相等性,但可以很容易地子类化。我开始使用 namedtuple :

class Command(namedtuple('Command', 'cmd_string')):

  def valid_msg(msg):
    return True

  def make_command(msg):
    if self.valid_msg(msg):
      return '%s:%s' % (self.cmd_string, msg)
    else:
      raise ValueError(INVALID_MSG)

...但这不适合子类化。直接子类化这意味着元组的名称保持不变(对于打印......没什么大不了的),但更重要的是你不能添加字段:

class LimitedLengthCommand(Command):

  # I want to have self.length! Where does it go?

  def valid_msg(msg):
    return len(msg) <= self.length

简单地创建另一个命名元组(根据文档)意味着我不继承任何方法!

做这样的事情最简单易行的方法是什么?我打算拥有 Command 的多个子类(例如,十六进制文字、1-or-0 等),但并不复杂。玩好多重继承并不是必需的。

最佳答案

这是一个元类来做你想做的事(我认为)。它的工作原理是将要继承的方法存储在字典中,然后手动将它们插入到新的类字典中。它还存储传递给 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"

希望对您有所帮助。如果您不希望将每个方法附加到应该继承它的每个类,您还可以提供一个默认的 __getattr__ 来查看元类字典并从中找到合适的方法。这将需要以某种方式将基类硬编码到方法中,可能使用闭包。

关于python - 允许在 Python 中子类化的不可变类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3904441/

相关文章:

java - 在核心 Java 中的其他相关类中共享不可变类的内部示例

python - 如何在Python 2中实现中缀运算符矩阵乘法?

python - 如何在不使用抽象方法的情况下使用 abc 创建抽象类?

python - 单类机争夺计划

python - 无法加载 pickle 的自定义估算器 sklearn 管道

javascript - Redux 不只是美化了全局状态吗?

python - 使用列表理解从列表列表中删除重复项

Java Generic - 找不到对象方法

java - 所有不可变对象(immutable对象)都可以重用吗?

javascript - Immutable.js - toJS 不是函数