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

作为一个学习练习,我正在尝试实现一个类,它将模拟python的
复杂的
内置函数的行为,但是
\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)