我想拥有不可变类型,理想情况下,它们可以理清它们自己的散列和相等,但可以很容易地进行子类化。我开始使用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或0等),但没有什么复杂的。多重继承的好处并不重要。
答案 0 :(得分:1)
这是一个可以做你想做的元类(我想)。它的工作原理是将要继承的方法存储在字典中,并手动将它们插入到新的类字典中。它还存储传递给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__
,它查看元类字典并找到适当的方法。这需要以某种方式将基类硬编码到方法中,可能使用闭包。