我有一个动态重载基本算术运算符的类,如下所示...
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)或其他任何东西,才能让它正常工作。为什么?
发布于 2010-04-23 06:33:11
答案可以在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希望显式地传入两个值,因此内部函数有两个参数。
正如前面的评论者所提到的,如果可能,最好避免使用旧式类,并坚持使用新型类(在Python 3+中删除了旧式类)。不幸的是,在这种情况下,旧风格的类恰好可以为您工作,而新风格的类需要更多的代码。
编辑:
通过在类而不是实例上设置方法,您还可以按照最初尝试的方式执行此操作
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我担心我有时会在元类中思考,在元类中,简单的解决方案会更好:)
发布于 2010-04-23 05:33:47
一般来说,永远不要在实例上设置__方法--它们只在类上受支持。(在这种情况下,问题是它们碰巧在旧式类上工作。不要使用老式的类)。
您可能希望使用元类,而不是您在这里所做的奇怪的事情。
这里有一个元类教程:http://www.voidspace.org.uk/python/articles/metaclasses.shtml
发布于 2010-04-23 06:25:05
我不明白你想要实现什么,但我几乎可以肯定你的做法是错误的。我的一些观察结果:
,
- 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. - 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.
dict。继承内置类型没有什么好处。事实证明,它比它的价值更令人困惑,而且你不能重用太多。- 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的旗舰类,也是您想要使用的类。在这种情况下,它们实际上会导致您的技术不再有效。不过,这很好,因为在您发布的代码中没有理由在每个实例级别上设置魔术方法,也没有什么理由需要这样做。
https://stackoverflow.com/questions/2694619
复制相似问题