Python中dict类的动态运算符重载
我有一个动态重载基本算术运算符的类,如Python中dict类的动态运算符重载,python,dictionary,operator-overloading,Python,Dictionary,Operator Overloading,我有一个动态重载基本算术运算符的类,如 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: setatt
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\uuuu添加(self,other)
或其他任何东西,以使其工作。为什么?通常,不要在实例上设置方法——它们只在类上受支持。(在本例中,问题是它们恰好在旧式类上工作。不要使用旧式类)
你可能想使用元类,而不是你在这里做的奇怪的事情
这里有一个元类教程:我不明白您想要完成什么,但我几乎可以肯定您是以错误的方式完成的。我的一些观察:
- 我不明白您为什么要尝试动态生成这些算术方法。您没有对它们执行任何特定于实例的操作,因此我不明白您为什么不在类中定义它们
- 它们工作的唯一原因是,
是一个老式的类;这不是一件好事,因为旧样式的类早就被弃用了,而且不如新样式的类好。(我稍后将解释为什么您应该对此特别感兴趣。)IshyNum
- 如果您想自动化对多个方法执行相同操作的过程(在本例中可能不值得),您可以在类定义块之后立即执行此操作
- 不要使用
来执行此操作<代码>地图用于制作列表;用它做副作用是愚蠢的。只需使用普通for循环map
- 不要使用
- 它们工作的唯一原因是,
- 如果要在使用合成时使用合成自动地将许多方法引用到同一属性,请使用
并重定向到该属性的方法\uuu getattr\uuu
- 不要继承
。继承内置类型没有什么好处。事实证明,它比它的价值更让人困惑,而且你不需要重复使用太多dict
- 如果上面的代码与文章中的内容非常接近,那么您确实不想继承
。如果不是,试着发布你真正的用例dict
- 如果上面的代码与文章中的内容非常接近,那么您确实不想继承
- 当您继承
时,您正在创建一个dict
是一个旧式类,因为它不继承IshyNum
对象(或其子类之一) 十年来,新样式类一直是Python的旗舰类,并且是您想要使用的。在这种情况下,它们实际上会导致您的技术不再有效。不过,这很好,因为在您发布的代码中没有理由在每个实例级别设置魔术方法,也没有理由希望这样做
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
意味着您正在生成一个新样式的类。运算符重载的语义不同:
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
的子类或您将找到的任何其他类型),必须确保在类上定义了特殊方法。可以通过元类执行此操作:
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希望显式地传递这两个值,因此内部函数有两个参数
正如前面的一位评论员所提到的,如果可能的话,最好避免使用旧样式的类,并坚持使用新样式的类(旧样式的类在Python3+中被删除)。不幸的是,在这种情况下,旧样式的类恰好适合您,而新样式的类将需要更多的代码
编辑: 您还可以按照最初尝试的方式,通过在类而不是实例上设置方法来实现这一点:
class Foo:
def __init__(self, num):
self.num = num
def _fn(other):
return self.num + other.num
self.__add__ = _fn
恐怕我有时会认为在元类中,更简单的解决方案会更好:)对于新样式的类,Python在执行加法时不检查
\uuuuuuuuuuuuuuuuuuuuuuu>方法的实例,而是检查类。问题是,您将\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。(其他特殊方法也是如此,您只能将它们附加到类,而不能附加到实例)。因此,您可能希望使用元类来实现此功能(尽管我认为这是一件非常尴尬的事情,因为显式地阐述这些方法更具可读性)。无论如何,下面是一个元类示例:
import operator
class OperatorMeta(type):
def __new__(mcs, name, bases, attrs):
for opname in ["add", "sub", "mul", "div"]:
op = getattr(operator, opname)
attrs["__%s__" % opname] = mcs._arithmetic_func_factory(op)
return type.__new__(mcs, name, bases, attrs)
@staticmethod
def _arithmetic_func_factory(op):
def func(self, other):
return op(self.num, other)
return func
class IshyNum(dict):
__metaclass__ = OperatorMeta
def __init__(self, n):
dict.__init__(self)
self.num=n
if __name__=="__main__":
number=IshyNum(5)
print number+5
print number/2
print number*3
print number-3
@艾琳,他说了,但不是很直接。由于dict
是一个内置类型,继承它会生成一个新样式的类。@Erin:是的。当你做一些“不受支持”的事情时,奇怪的事情就会发生。解决方法不是做不受支持的事情,也不是去理解什么情况导致了什么奇怪的事情……谢谢。这就是我需要的解释。这也让我很容易以错误的方式完成我想要的任务,只需更改原始代码中的一行即可P我相信人们会为此对我大发雷霆。是的,没错,很抱歉我第一次错过了那个简单的版本!这可以通过Mixin或decorator来完成,而无需借助元类。