在 OP 多次发表评论后,我决定从头开始重写我的答案。
可能的解决方法:
class MyUnicodeMetaClass(type):
autocast_methods = ('__add__', '__radd__', 'format')
def __init__(cls, name, bases, attrs):
super(MyUnicodeMetaClass, cls).__init__(name, bases, attrs)
for method_name in MyUnicodeMetaClass.autocast_methods:
try:
setattr(cls, method_name, cls.autocast_creator(method_name))
except AttributeError:
if method_name.startswith('__r'):
setattr(cls, method_name, cls.autocast_reverse(method_name))
else:
raise
def autocast_creator(cls, method_name):
method = unicode().__getattribute__(method_name)
def autocast_method(self, *args, **kwargs):
method = unicode(self).__getattribute__(method_name)
return cls(method(*args, **kwargs))
return autocast_method
def autocast_reverse(cls, method_name):
method_name = method_name.replace('__r', '__', 1)
def autocast_method(self, *args, **kwargs):
method = unicode(args[0]).__getattribute__(method_name)
return cls(method(self, *args[1:], **kwargs))
return autocast_method
class MyUnicode(unicode):
__metaclass__ = MyUnicodeMetaClass
a = MyUnicode(u'aaa {0}')
print a, type(a)
# aaa {0} <class '__main__.MyUnicode'>
b = a + u'bbb'
print b, type(b)
# aaa {0}bbb <class '__main__.MyUnicode'>
c = u'ddd' + a
print c, type(c)
# cccaaa {0} <class '__main__.MyUnicode'>
d = a.format(115)
print d, type(d)
# aaa 115 <class '__main__.MyUnicode'>
它可能需要扩展,但基本骨架已准备好。
这里发生了什么?
1.Metaclass
用于改变MyUnicode
类的创建。
2. Simpleautocast_creator
用于使用MyUnicode
应该返回MyUnicode
而不是返回的方法填充类unicode
。
3.更复杂一点autocast_reverse
的方法也用于提供反向方法(就像__radd__
第一个操作数是unicode
和第二个操作数时需要的那样MyUnicode
)
这样,您不必手动覆盖所有方法 - 只需将它们列在autocast_methods
元组中。
背景资料:
继承:
面向对象的编程旨在尽可能地反映真实的单词。
继承在这里也不例外。
在现实世界中,一群大象总是一群动物。这里没有疑问。
但是一群动物可能是一群大象,但也可以是一群不同的动物,甚至可能没有大象。
这是因为任何一群大象都是一种特殊的动物群。所以
它可以通过定义ElephantGroup
为.
怎么能延长?例如通过定义新字段和新方法。
考虑这么简单的操作。
预期结果的类别是什么?AnimalGroup
ElephantGroup
AnimalGroup
ivory_weight
toot()
ElephantGroup() + AnimalGroup()
AnimalGroup
- 这里没有魔法,老鼠、海豚等不会变成大象。老鼠和海豚不提供象牙,它们不会鸣叫,所以强迫它们这样做不是预期的行为。
让我们回到MyUnicode
和unicode
。
机器在矩阵或终结者中呈现的含义并不智能。
Python 解释器不明白MyUnicode
.
考虑一个扩展unicode
或str
(在这里并不重要)的类,它被命名EmailAddress
并旨在保存电子邮件地址。毫不奇怪 :)
我们现在有一个代码片段:
a = EmailAddress(u'example@example.com')
b = u'/!\n'
c = a + b
d = b + a
仍然期待c
并d
成为EmailAddress
? (或者MyUnicode
?)
如果您回答是,那么请告诉我:
1. 如果EmailAddress.__init__(...)
包含精心设计的逻辑检查参数是否可能是有效的电子邮件地址,该怎么办?如果不是,它会引发异常......
2.解释器如何知道它可以安全地MyUnicode
用任何 unicode
实例初始化实例而不执行__init__
?还请记住这是 Python,__init__
甚至可以在运行时动态更改。
请记住——我们总是可以将实例转换为它的任何祖先。无法隐式执行反向操作。如果解释器将object
实例隐式转换为objects
子类。
该strip()
方法遵循相同的规则 -unicode
从原始字符中删除字符并unicode
返回对新实例的引用(除非存在完全相同的实例unicode
- 在这种情况下返回对现有实例的引用)。
引用实例类:
在您的一条评论中,您所说cls
的指的是启动执行链的实例类......
cls
是一种命名约定,用于metaclasses
,classmethods
和 in__new__()
方法,表示我们没有实例- 我们只有一个类。
事实上,在任何这些情况下我们都无法访问实例 -__new__()
但是在大多数情况下应该返回新实例。
我猜你在考虑identifier.__class__
属性。它也与执行链无关。它指向由 引用的实例的实际类identifier
。
为什么您期望使用它的方法unicode
并str
使用它来创建子类?
将某些内容隐式转换为它的子类不是预期的行为 - 我知道,您的代码中的一个操作数是,MyUnicode
但另一个是unicode
- 即使在 中strip()
,默认参数是unicode
包含空白字符。
一些 unicode 实现细节:
Pythonunicode
和string
类型是不可变的和唯一的(解释来了)。不可变意味着对它们的任何修改操作分别返回或的其他实例。其他实例意味着新实例,但正如我所说,这些类型是唯一的。
这是什么意思?见代码:unicode
string
a = u'aaa'
b = u'aaa'
这里发生了什么?
有一个created 的新实例来unicode
初始化a
。找到要初始化
的对象,因此没有创建新实例。
相反,对象持有的引用计数器增加了。 unicode
b
unicode
u'aaa'
现在,当我们知道它时,请考虑以下代码:
a = u'aaa'
b = MyUnicode(u'aa')
c = b + u'a'
c
变量中究竟存储了什么?对unicode
object 的引用 - 由 引用的同一对象a
。
为什么更改c
不会影响a
?因为unicode
是不可变的,底层对象保持不变。
如果下一行是c = c + u'b'
,那么c
将获得对新/其他实例的引用,并且引用的对象a
将减少它的引用计数器。
结论:
Pythonunicode
和str
类是一致的和可预测的。
由于优化、特殊目的或实现细节,有些类型很难派生。
unicode
并且str
不打算被子类化,尽管它可以通过例如metaclass
我的代码片段来实现。
与往常一样,我正在寻找任何建设性的批评和评论。
祝你好运!