首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Python中dict类的动态运算符重载

Python中dict类的动态运算符重载
EN

Stack Overflow用户
提问于 2010-04-23 05:23:53
回答 4查看 1.9K关注 0票数 3

我有一个动态重载基本算术运算符的类,如下所示...

代码语言:javascript
运行
复制
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)或其他任何东西,才能让它正常工作。为什么?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-04-23 06:33:11

答案可以在Python拥有的两种类型的类中找到。

您提供的第一个代码片段使用了一个遗留的“旧式”类(您可以分辨出来,因为它没有任何子类-冒号之前没有任何东西)。它的语义是独特的。具体来说,您可以为实例添加一个特殊的方法:

代码语言:javascript
运行
复制
class Foo:
   def __init__(self, num):
      self.num = num
      def _fn(other):
         return self.num + other.num
      self.__add__ = _fn

并得到一个有效的响应:

代码语言:javascript
运行
复制
>>> f = Foo(2)
>>> g = Foo(1)
>>> f + g
3

但是,对dict进行子类化意味着您正在生成一个新风格的类。并且操作符重载的语义是不同的:

代码语言:javascript
运行
复制
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的子类或任何其他类型),必须确保在类上定义了特殊的方法。您可以通过元类完成此操作:

代码语言:javascript
运行
复制
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

此外,语义上的差异意味着,在第一种情况下,我可以用一个参数定义我的本地add方法(它使用的self是从定义它的周围作用域捕获的),但是对于新样式的类,Python希望显式地传入两个值,因此内部函数有两个参数。

正如前面的评论者所提到的,如果可能,最好避免使用旧式类,并坚持使用新型类(在Python 3+中删除了旧式类)。不幸的是,在这种情况下,旧风格的类恰好可以为您工作,而新风格的类需要更多的代码。

编辑:

通过在类而不是实例上设置方法,您还可以按照最初尝试的方式执行此操作

代码语言:javascript
运行
复制
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

我担心我有时会在元类中思考,在元类中,简单的解决方案会更好:)

票数 4
EN

Stack Overflow用户

发布于 2010-04-23 05:33:47

一般来说,永远不要在实例上设置__方法--它们只在类上受支持。(在这种情况下,问题是它们碰巧在旧式类上工作。不要使用老式的类)。

您可能希望使用元类,而不是您在这里所做的奇怪的事情。

这里有一个元类教程:http://www.voidspace.org.uk/python/articles/metaclasses.shtml

票数 3
EN

Stack Overflow用户

发布于 2010-04-23 06:25:05

我不明白你想要实现什么,但我几乎可以肯定你的做法是错误的。我的一些观察结果:

  • ,我不明白你为什么要尝试动态生成这些算术方法。您不需要对它们执行任何特定于实例的操作,所以我不明白您为什么不在类中定义它们。

代码语言:javascript
运行
复制
- The only reason they work at all is because `IshyNum` is an old-style class; this isn't a good thing, since old-style classes are long-deprecated and not as nice as new-style classes. (I'll explain later why you should be especially interested in this.)
- If you wanted to automate the process of doing the same thing for multiple methods (probably not worth it in this case), you could just do this right after the class definition block.
代码语言:javascript
运行
复制
    - Don't use `map` to do that. `map` is for making a list; using it for side effects is silly. Just use a normal for loop.

如果您想在使用组合时使用组合自动将许多方法引用到同一属性,请使用__getattr__并重定向到该属性的methods.

  • Don't
  • dict。继承内置类型没有什么好处。事实证明,它比它的价值更令人困惑,而且你不能重用太多。

代码语言:javascript
运行
复制
- If your code above is anything close to the stuff in your post, you _really_ don't want to inherit `dict`. If it's not, try posting your real use case.

这是你真正想知道的:

  • 当你继承dict时,你就是在创建一个。IshyNum是一个老式的类,因为它不继承object (或它的一个子类)。

十年来,新型类一直是Python的旗舰类,也是您想要使用的类。在这种情况下,它们实际上会导致您的技术不再有效。不过,这很好,因为在您发布的代码中没有理由在每个实例级别上设置魔术方法,也没有什么理由需要这样做。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2694619

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档