Python中的dict类的动态运算符重载 [英] Dynamic Operator Overloading on dict classes in Python

查看:155
本文介绍了Python中的dict类的动态运算符重载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类,可以动态重载基本的算术运算符,如此...

I have a class that dynamically overloads basic arithmetic operators like so...

import operator

class IshyNum:
    def __init__(self, n):
        self.num=n
        self.buildArith()

    def arithmetic(self, other, o):
        return o(self.num, other)

    def buildArith(self):
        map(lambda o: setattr(self, "__%s__"%o,lambda f: self.arithmetic(f, getattr(operator, o))), ["add", "sub", "mul", "div"])

if __name__=="__main__":
    number=IshyNum(5)
    print number+5
    print number/2
    print number*3
    print number-3

但是,如果我更改类继承自字典( class IshyNum(dict):)它不工作。我需要明确地 def __add __(self,other)或任何为了使其工作。为什么?

But if I change the class to inherit from the dictionary (class IshyNum(dict):) it doesn't work. I need to explicitly def __add__(self, other) or whatever in order for this to work. Why?

推荐答案

答案是在Python有两种类型的类中找到的。

The answer is found in the two types of class that Python has.

你提供的第一个代码片段使用了一个旧的旧式类(你可以知道,因为它没有子类 - 冒号之前没有)。它的语义是特殊的特别是,您可以向实例添加一个特殊方法:

The first code-snippet you provided uses a legacy "old-style" class (you can tell because it doesn't subclass anything - there's nothing before the colon). Its semantics are peculiar. In particular, you can add a special method to an instance:

class Foo:
   def __init__(self, num):
      self.num = num
      def _fn(other):
         return self.num + other.num
      self.__add__ = _fn

并获得有效的回复:

>>> f = Foo(2)
>>> g = Foo(1)
>>> f + g
3

但是,子类化 dict 意味着你正在生成一个新式的类。而运算符重载的语义是不同的:

But, subclassing dict means you are generating a new-style class. And the semantics of operator overloading are different:

class Foo (object):
   def __init__(self, num):
      self.num = num
      def _fn(other):
         return self.num + other.num
      self.__add__ = _fn
>>> f = Foo(2)
>>> g = Foo(1)
>>> f + g
Traceback ...
TypeError: unsupported operand type(s) for +: 'Foo' and 'Foo'

要使新的类(包括 dict 的子类或其他所有类型的类)工作,你必须确保在类上定义了特殊的方法。你可以通过元类来实现:

To make this work with new-style classes (which includes subclasses of dict or just about any other type you will find), you have to make sure the special method is defined on the class. You can do this through a metaclass:

class _MetaFoo(type):
    def __init__(cls, name, bases, args):
        def _fn(self, other):
            return self.num + other.num
        cls.__add__ = _fn

class Foo(object):
    __metaclass__ = _MetaFoo
    def __init__(self, num):
        self.num = num

>>> f = Foo(2)
>>> g = Foo(1)
>>> f+g
3

另外,语义差异意味着在第一种情况下可以使用一个参数( self )定义我的本地add方法,它使用的方法是从定义的周围范围中捕获),但是使用新的类,Python期望明确地传递这两个值,所以内部函数有两个参数。

Also, the semantic difference means that in the very first case I could define my local add method with one argument (the self it uses is captured from the surrounding scope in which it is defined), but with new-style classes, Python expects to pass in both values explicitly, so the inner function has two arguments.

正如之前的评论者所说,尽可能避免使用旧样式的类,并坚持使用新的类(Python 3+中删除旧类)。不幸的是,在这种情况下,旧式课程适合你,新式课程需要更多的代码。

As a previous commenter mentioned, best to avoid old-style classes if possible and stick with new-style classes (old-style classes are removed in Python 3+). Its unfortunate that the old-style classes happened to work for you in this case, where new-style classes will require more code.

编辑:

您还可以通过在上设置方法而不是实例

You can also do this more in the way you originally tried by setting the method on the class rather than the instance:

class Foo(object):
    def __init__(self, num):
        self.num = num
setattr(Foo, '__add__', (lambda self, other: self.num + other.num))
>>> f = Foo(2)
>>> g = Foo(1)
>>> f+g
3

恐怕我有时会在元类中考虑更简单的解决方案会更好:)

I'm afraid I sometimes think in Metaclasses, where simpler solutions would be better :)

这篇关于Python中的dict类的动态运算符重载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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