msz*_*zep 9 python metaprogramming metaclass complex-numbers
作为一个学习练习,我正在尝试实现一个类,它将模拟python的complex内置行为,但具有不同的行为__str__和__repr__方法:我希望它们以格式打印...
(1.0,2.0)
Run Code Online (Sandbox Code Playgroud)
...代替:
(1+2j)
Run Code Online (Sandbox Code Playgroud)
我第一次尝试只是从继承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)
Run Code Online (Sandbox Code Playgroud)
当所需的输出是(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
Run Code Online (Sandbox Code Playgroud)
不幸的是,这似乎与之前的解决方案具有相同的行为(例如,当两个ComplexWrapper实例相互添加时).
我承认,我不完全理解元类.也许我的问题可以用不同的方式解决?
当然,我可以手动重新定义相关的方法,如__add__,__subtract__等等.但这将是非常重复的,所以我更喜欢更优雅的解决方案.
任何帮助赞赏.
所以我对你的代码有很多不了解的事情:
元类的__new__方法ReturnTypeWrapper从哪里得到它的参数?如果他们自动通过,我希望在这种情况下name = "Complex", bases = (complex), dict = {}.那是对的吗?这种自动传递特定于元类的类数据的方法是什么?
为什么用
cls = type.__new__(mcs, name, bases, dct)而不是
cls = type(mcs, name, bases, dct)?是否只是为了避免与"其他意义"混淆type()?
我复制了你的代码,__str__并__repr__在你的ComplexWrapper课堂上添加了我的特殊实现.但它不起作用; 打印任何类型的对象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) + ')'
Run Code Online (Sandbox Code Playgroud)
它的行为:
>>> type(a)
<class 'Cmplx2.Complex'>
>>> a.__str__
<bound method Complex.wrapper of (1+1j)>
>>> a.__str__()
'(1+1j)'
>>>
Run Code Online (Sandbox Code Playgroud)
再次感谢您的回答,如果您在答案中解决上述问题,请随时编辑/删除上述内容!
您当前的方法无效.如何定义类不是问题 - 在调用它们时complex创建新实例的方法complex,而不是使用type输入对象的方法.您将始终返回complex而不是实例ComplexWrapper,因此不会调用您的自定义方法:
>>> type(ComplexWrapper(1.0,1.0) + ComplexWrapper(2.0,3.0))
<type 'complex'>
Run Code Online (Sandbox Code Playgroud)
相反,您需要将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)
Run Code Online (Sandbox Code Playgroud)
请注意,这不会换的__coerce__特殊方法,因为它返回tuple的complexS; 如果需要,可以很容易地将包装器转换为查看内部序列.
__objclass__未绑定方法的属性似乎是未记录的,但它指向定义方法的类,因此我使用它来过滤除了我们要转换的类之外的类上定义的方法.我也在这里使用它来过滤掉不是未绑定方法的属性.