Python 使用元类重写复杂内置的方法
作为一个学习练习,我正在尝试实现一个类,它将模拟python的Python 使用元类重写复杂内置的方法,python,metaprogramming,metaclass,complex-numbers,Python,Metaprogramming,Metaclass,Complex Numbers,作为一个学习练习,我正在尝试实现一个类,它将模拟python的复杂的内置函数的行为,但是\uuu str\uuuuuuuuu和\uuuu repr\uuuuu方法的不同行为:我希望它们以以下格式打印 (1.0,2.0) …而不是: (1+2j) 我首先尝试简单地从complex子类化,并重新定义\uuu str\uuu和\uuu repr\uu,但这存在一个问题,即当调用非重写方法时,将返回一个标准的complex,并以标准格式打印: >>> a = ComplexWrap
复杂的
内置函数的行为,但是\uuu str\uuuuuuuuu
和\uuuu repr\uuuuu
方法的不同行为:我希望它们以以下格式打印
(1.0,2.0)
…而不是:
(1+2j)
我首先尝试简单地从complex
子类化,并重新定义\uuu str\uuu
和\uuu repr\uu
,但这存在一个问题,即当调用非重写方法时,将返回一个标准的complex
,并以标准格式打印:
>>> a = ComplexWrapper(1.0,1.0)
>>> a
(1.0,1.0)
>>> b = ComplexWrapper(2.0,3.0)
>>> b
(2.0,3.0)
>>> a + b
(3+4j)
当所需输出为(3.0,4.0)
时
我在读有关元类的书,认为它们能解决我的问题。从中的答案开始,我当前的实现如下:
def complex_str(z):
return '(' + str(z.real) + ',' + str(z.imag) + ')'
def complex_repr(z):
return '(' + repr(z.real) + ',' + repr(z.imag) + ')'
class CmplxMeta(type):
def __new__(cls, name, bases, attrs):
attrs['__str__'] = complex_str
attrs['__repr__'] = complex_repr
return super(CmplxMeta, cls).__new__(cls, name, bases, attrs)
class ComplexWrapper(complex):
__metaclass__ = CmplxMeta
不幸的是,这似乎与前面的解决方案具有相同的行为(例如,当两个ComplexWrapper
实例相互添加时)
我承认,我不完全理解元类。也许我的问题可以用另一种方式解决
当然,我可以手动重新定义相关的方法,例如\uuuuuuuuuuuuuuuuuuuuuuuuuuu
,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
谢谢你的帮助
编辑:对agf答案的回应:
因此,关于您的代码,我有很多不理解的地方:
ReturnTypeWrapper
元类的\uuuu new\uuuu
方法从何处获取其参数?如果它们是自动传递的,我希望在这种情况下,name=“Complex”、base=(Complex)、dict={}
。对吗?这种自动传递类数据的方法是元类特有的吗
你为什么用这个
cls=type.\uuuu新的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
cls=类型(mcs、名称、基数、dct)
?
这只是为了避免与type()
的“其他含义”混淆吗
我复制了您的代码,并在您的ComplexWrapper
类中添加了我的特殊实现\uuuuu str\uuuuuu
和\uuu repr\uuuuuu
。但它不起作用;打印任何类型为Complex
的对象只需以标准Python格式打印即可。我不理解这一点,因为这两个方法应该在元类的for循环中得到,但是应该在以后被我的定义覆盖
我的代码的相关部分:
class Complex(complex):
__metaclass__ = ReturnTypeWrapper
wrapped_base = complex
def __str__(self):
return '(' + str(self.real) + ',' + str(self.imag) + ')'
def __repr__(self):
return '(' + repr(self.real) + ',' + repr(self.imag) + ')'
以及它的行为:
>>> type(a)
<class 'Cmplx2.Complex'>
>>> a.__str__
<bound method Complex.wrapper of (1+1j)>
>>> a.__str__()
'(1+1j)'
>>>
>类型(a)
>>>a.._u街__
>>>a.uuu str_uuuuu()
"(1+1j)"
>>>
再次感谢您的回答,如果您在回答中提到以上内容,请随意编辑/删除 您当前的方法行不通。如何定义类不是问题所在,complex
的方法在调用时创建complex
的新实例,而不是使用输入对象的类型。您总是会得到complex
的实例,而不是ComplexWrapper
,因此您的自定义方法不会被调用:
>>> type(ComplexWrapper(1.0,1.0) + ComplexWrapper(2.0,3.0))
<type 'complex'>
注意,这不会包装\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;如果需要,包装器可以很容易地转换为查看内部序列
未绑定方法的\uuuuu objclass\uuuuu
属性似乎没有文档记录,但它指向在其上定义方法的类,因此我使用它来过滤在我们转换的类以外的类上定义的方法。我在这里还使用它来过滤非未绑定方法的属性。非常感谢您的回答。然而,我不确定我是否明白发生了什么。由于评论的篇幅明显有限,我将对问题进行编辑以澄清。因此,还有两条评论和一个问题:1。repr的定义中有一个缩进错误,StackOverflow不会让我更正,因为它只是空白。2.除此之外,它还可以正常工作,但我不明白为什么要取str和repr中虚部的abs()。3.我想知道行包装器的情况。你添加的行。。。它只是为了美观吗?@mszep1。固定的。2.我的错误是,我认为它返回了一个复杂的。3.语义和修饰性——如果你打印复合词。uuu添加uuu,复合词。uuu子uu
结果应该是有意义的;没有那一行,它们都是复杂的。包装器。我把你的问题编号了。1.你几乎是对的dct
将类似于{code>模块:''code>'main','code元类:,'wrapped\u base':}
,一旦创建,dict
将成为类的dict
属性。试试看。你必须直接调用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。那是我的错。首先,我甚至包装了被重写的方法。这是固定的。其次,complex.real
和complex.imag
是属性/描述符,因此它们被包装起来。我对它进行了特殊的大小写,这样它们就不会出现,但是现在,像\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,.imag
返回一个复杂的,而不是您的子类。如果需要,这也可以解决。我还添加了\uu str\uuuuuuuu
和\uuuu repr\uuuuuuu
方法,向您展示如何获得所需的输出,并将\uuuuuu repr\uuuuuu
修改为eval
,从而能够返回对象(正如repr
输出应该是的那样)。再次感谢,这是一个很大的帮助。1.二,。我现在明白了。对于3,我认为您的修复效果比您想象的好,因为.imag实际上返回一个浮点,所以没有问题。我以后可能会尝试实施强制。
class ReturnTypeWrapper(type):
def __new__(mcs, name, bases, dct):
cls = type.__new__(mcs, name, bases, dct)
for attr, obj in cls.wrapped_base.__dict__.items():
# skip 'member descriptor's and overridden methods
if type(obj) == type(complex.real) or attr in dct:
continue
if getattr(obj, '__objclass__', None) is cls.wrapped_base:
setattr(cls, attr, cls.return_wrapper(obj))
return cls
def return_wrapper(cls, obj):
def convert(value):
return cls(value) if type(value) is cls.wrapped_base else value
def wrapper(*args, **kwargs):
return convert(obj(*args, **kwargs))
wrapper.__name__ = obj.__name__
return wrapper
class Complex(complex):
__metaclass__ = ReturnTypeWrapper
wrapped_base = complex
def __str__(self):
return '({0}, {1})'.format(self.real, self.imag)
def __repr__(self):
return '{0}({1!r}, {2!r})'.format(self.__class__.__name__,
self.real, self.imag)
a = Complex(1+1j)
b = Complex(2+2j)
print type(a + b)