使用元类覆盖复杂的内置方法 [英] Using metaclasses to override methods of complex builtin

查看:59
本文介绍了使用元类覆盖复杂的内置方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为一项学习练习,我正在尝试实现一个类,该类将模拟python的complex内置行为,但是__str____repr__方法的行为不同:我希望它们在格式...

As a learning exercise, I'm trying to implement a class which will emulate the behavior of python's complex builtin, but with different behavior of the __str__ and __repr__ methods: I want them to print in the format...

(1.0,2.0)

...而不是:

(1+2j)

我首先尝试简单地从complex进行子类化,然后重新定义__str____repr__,但这存在以下问题:调用非重写方法时,将返回标准complex,并以标准格式打印:

I first tried simply subclassing from complex and redefining __str__ and __repr__, but this has the problem that when non-overridden methods are called, a standard complex is returned, and printed in the standard format:

>>> 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类装饰器中的答案开始,我当前的实现如下:

I was reading about metaclasses and thought they would solve my problem. Starting from the answer in Python Class Decorator, my current implementation is as follows:

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

Unfortunately, this seems to have the same behavior as the previous solution (e.g. when two ComplexWrapper instances are added to each other).

我承认,我不完全理解元类.也许我的问题可以通过其他方式解决?

I admit, I don't fully understand metaclasses. Maybe my problem can be solved in a different way?

当然,我可以手动重新定义相关的方法,例如__add____subtract__等.但这将是非常重复的,所以我希望有一个更优雅的解决方案.

Of course, I could manually redefine the relevant methods such as __add__, __subtract__, etc. But that would be very repetitive, so I would prefer a more elegant solution.

任何帮助表示赞赏.

所以我对您的代码有很多不了解的地方:

So a number of things I don't understand about your code:

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

  1. Where does the __new__ method of the ReturnTypeWrapper metaclass get its arguments from? If they are passed automatically, I would expect in this case that name = "Complex", bases = (complex), dict = {}. Is that correct? Is this method of automatic passing of class data specific to metaclasses?

为什么使用 cls = type.__new__(mcs, name, bases, dct)代替 cls = type(mcs, name, bases, dct)? 只是为了避免与type()的其他含义"混淆?

Why do you use cls = type.__new__(mcs, name, bases, dct) instead of cls = type(mcs, name, bases, dct)? Is it just to avoid confusion with the "other meaning" of type()?

我复制了您的代码,并在ComplexWrapper类中添加了__str____repr__的特殊实现.但这是行不通的.打印任何类型为Complex的对象只会以标准Python格式打印.我不明白,因为这两个方法应该在元类的for循环中使用,但之后应该被我的定义所覆盖.

I copied your code, and added my special implementations of __str__ and __repr__ in your ComplexWrapper class. But it doesn't work; printing any object of type Complex just prints in the standard Python format. I don't understand that, as the two methods should have been picked up in the for loop of the metaclass, but should have been overridden by my definitions afterward.

我的代码的相关部分:

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)'
>>> 

再次感谢您的回答,如果您在回答中解决上述问题,请随时进行编辑/删除!

Thanks again for your answer and feel free to edit/remove the above if you address them in your answer!

推荐答案

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

Your current approach won't work. How you define your class isn't the issue -- the methods of complex are creating new instances of complex when you call them, rather than using the type of the input objects. You'll always get back instances of complex rather than ComplexWrapper, so your customized methods won't be called:

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


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


Instead, you need to convert the new complex objects returned by the methods of complex to return objects of the derived class.

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

This metaclass wraps all the methods of the specified base class and attaches the wrapped methods to the class. The wrapper checks if the value to be returned is an instance of the base class (but excluding instances of subclasses), and if it is, converts it to an instance of the derived class.

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__特殊方法,因为它会返回complex s的tuple.如有必要,可以轻松地将包装器转换为内部序列.

Note that this won't wrap the __coerce__ special method, since it returns a tuple of complexs; the wrapper can easily be converted to look inside sequences if necessary.

未绑定方法的__objclass__属性似乎没有记录,但它指向定义该方法的类,因此我用它来筛选出在非要转换的类上定义的方法.我还在这里使用它来过滤不是非绑定方法的属性.

The __objclass__ attribute of unbound methods seems to be undocumented, but it points to the class the method is defined on, so I used it to filter out methods defined on classes other than the one we're converting from. I also use it here to filter out attributes that aren't unbound methods.

这篇关于使用元类覆盖复杂的内置方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆