gpt4 book ai didi

python - 使用元类覆盖复杂内置方法

转载 作者:太空狗 更新时间:2023-10-29 20:23:10 25 4
gpt4 key购买 nike

作为一个学习练习,我正在尝试实现一个模拟 python 的 complex 内置行为的类,但 __str__ 的行为不同>__repr__ 方法:我希望它们以格式打印...

(1.0,2.0)

...而不是:

(1+2j)

我首先尝试简单地从 complex 子类化并重新定义 __str____repr__,但这有一个问题,当调用非覆盖方法时,返回一个标准的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)时。

我正在阅读有关元类的内容,并认为它们可以解决我的问题。从Python Class Decorator中的答案开始,我目前的实现如下:

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 实例相互添加时)。

我承认,我并不完全理解元类。也许我的问题可以用不同的方式解决?

当然,我可以手动重新定义相关方法,如__add____subtract__等,但这样会很重复,所以我更喜欢更优雅的解决方案.

感谢任何帮助。


编辑:对 agf 回答的回应:

所以我对你的代码有很多不理解的地方:

  1. ReturnTypeWrapper 元类的 __new__ 方法从哪里获取参数?如果它们是自动传递的,我希望在这种情况下 name = "Complex", bases = (complex), dict = {}。那是对的吗?这种自动传递类数据的方法是metaclass特有的吗?

  2. 你为什么使用 cls = type.__new__(mcs, name, bases, dct) 而不是 cls = type(mcs, name, bases, dct)?是否只是为了避免与type()的“其他含义”混淆?

  3. 我复制了您的代码,并在您的 ComplexWrapper 类中添加了我对 __str____repr__ 的特殊实现。但它不起作用;打印任何 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)'
>>>

再次感谢您的回答,如果您在回答中提及以上内容,请随时编辑/删除它们!

最佳答案

您当前的方法行不通。你如何定义你的类不是问题 - complex 的方法在你调用它们时创建 complex 的新实例,而不是使用 type 输入对象。您将始终取回 complex 而不是 ComplexWrapper 的实例,因此不会调用您的自定义方法:

>>> type(ComplexWrapper(1.0,1.0) + ComplexWrapper(2.0,3.0))
<type 'complex'>

相反,您需要将complex 的方法返回的新complex 对象转换为返回派生类的对象。

这个元类包装了指定基类的所有方法,并将包装的方法附加到类中。包装器检查要返回的值是否是基类的实例(但不包括子类的实例),如果是,则将其转换为派生类的实例。

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)

请注意,这不会包装 __coerce__ 特殊方法,因为它返回 complextuple;如有必要,可以轻松转换包装器以查看序列内部。

未绑定(bind)方法的 __objclass__ 属性似乎没有记录,但它指向定义该方法的类,所以我用它来过滤掉定义在非我们的类上的方法重新转换自。我在这里也使用它来过滤掉不是未绑定(bind)方法的属性。

关于python - 使用元类覆盖复杂内置方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10771010/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com