3

这个问题的启发,我认为将“MutableNum”类放在一起会很有趣,因为它在尽可能多的情况下就像标准数字类型一样,但它是可变的,所以类似于以下内容会工作:

def double(x): x *= 2

x = MutableNum(9)
print(x)             # 9
double(x)
print(x)             # 18

我得到以下内容:

class MutableNum():
    val = None
    def __init__(self, v): self.val = v
    # Comparison Methods
    def __eq__(self, x):    return self.val == x
    def __ne__(self, x):    return self.val != x
    def __lt__(self, x):    return self.val <  x
    def __gt__(self, x):    return self.val >  x
    def __le__(self, x):    return self.val <= x
    def __ge__(self, x):    return self.val >= x
    # Arithmetic
    def __mul__(self, x):   return self.__class__(self.val * x)
    def __rmul__(self, x):  return self.__class__(self.val * x)
    # Casts
    def __int__(self):      return self.val
    # Represenation
    def __str__(self):      return "%d" % (self.val)
    def __repr__(self):     return "%s(%d)" % (self.__class__.__name__, self.val)

哪个有效(到目前为止,据我所知),但我发现自己想要“抓住”魔法方法,因为它们中的许多都会遵循非常相似的结构。

例如,我想以类似的方式捕捉__mul__, __add__,__sub__等:

def catch(self, method, x): return MutableNum(self.val.method(x))

所以对于__add__,catch()会返回

return MutableNum(self.val.__add__(x))

这样的事情可能吗?还是我应该像我已经完成的那样实现所有的魔法方法?

编辑:我已经尝试了一些尝试捕捉魔术方法的方法__getattr__(self,key),但我得到的结果好坏参半。

提前致谢。

编辑 2

在大家的帮助下,这就是我想出的:

class MutableNum(object):
    __val__ = None
    def __init__(self, v): self.__val__ = v
    # Comparison Methods
    def __eq__(self, x):        return self.__val__ == x
    def __ne__(self, x):        return self.__val__ != x
    def __lt__(self, x):        return self.__val__ <  x
    def __gt__(self, x):        return self.__val__ >  x
    def __le__(self, x):        return self.__val__ <= x
    def __ge__(self, x):        return self.__val__ >= x
    def __cmp__(self, x):       return 0 if self.__val__ == x else 1 if self.__val__ > 0 else -1
    # Unary Ops
    def __pos__(self):          return self.__class__(+self.__val__)
    def __neg__(self):          return self.__class__(-self.__val__)
    def __abs__(self):          return self.__class__(abs(self.__val__))
    # Bitwise Unary Ops
    def __invert__(self):       return self.__class__(~self.__val__)
    # Arithmetic Binary Ops
    def __add__(self, x):       return self.__class__(self.__val__ + x)
    def __sub__(self, x):       return self.__class__(self.__val__ - x)
    def __mul__(self, x):       return self.__class__(self.__val__ * x)
    def __div__(self, x):       return self.__class__(self.__val__ / x)
    def __mod__(self, x):       return self.__class__(self.__val__ % x)
    def __pow__(self, x):       return self.__class__(self.__val__ ** x)
    def __floordiv__(self, x):  return self.__class__(self.__val__ // x)
    def __divmod__(self, x):    return self.__class__(divmod(self.__val__, x))
    def __truediv__(self, x):   return self.__class__(self.__val__.__truediv__(x))
    # Reflected Arithmetic Binary Ops
    def __radd__(self, x):      return self.__class__(x + self.__val__)
    def __rsub__(self, x):      return self.__class__(x - self.__val__)
    def __rmul__(self, x):      return self.__class__(x * self.__val__)
    def __rdiv__(self, x):      return self.__class__(x / self.__val__)
    def __rmod__(self, x):      return self.__class__(x % self.__val__)
    def __rpow__(self, x):      return self.__class__(x ** self.__val__)
    def __rfloordiv__(self, x): return self.__class__(x // self.__val__)
    def __rdivmod__(self, x):   return self.__class__(divmod(x, self.__val__))
    def __rtruediv__(self, x):  return self.__class__(x.__truediv__(self.__val__))
    # Bitwise Binary Ops
    def __and__(self, x):       return self.__class__(self.__val__ & x)
    def __or__(self, x):        return self.__class__(self.__val__ | x)
    def __xor__(self, x):       return self.__class__(self.__val__ ^ x)
    def __lshift__(self, x):    return self.__class__(self.__val__ << x)
    def __rshift__(self, x):    return self.__class__(self.__val__ >> x)
    # Reflected Bitwise Binary Ops
    def __rand__(self, x):      return self.__class__(x & self.__val__)
    def __ror__(self, x):       return self.__class__(x | self.__val__)
    def __rxor__(self, x):      return self.__class__(x ^ self.__val__)
    def __rlshift__(self, x):   return self.__class__(x << self.__val__)
    def __rrshift__(self, x):   return self.__class__(x >> self.__val__)
    # Compound Assignment
    def __iadd__(self, x):      self.__val__ += x; return self
    def __isub__(self, x):      self.__val__ -= x; return self
    def __imul__(self, x):      self.__val__ *= x; return self
    def __idiv__(self, x):      self.__val__ /= x; return self
    def __imod__(self, x):      self.__val__ %= x; return self
    def __ipow__(self, x):      self.__val__ **= x; return self
    # Casts
    def __nonzero__(self):      return self.__val__ != 0
    def __int__(self):          return self.__val__.__int__()               # XXX
    def __float__(self):        return self.__val__.__float__()             # XXX
    def __long__(self):         return self.__val__.__long__()              # XXX
    # Conversions
    def __oct__(self):          return self.__val__.__oct__()               # XXX
    def __hex__(self):          return self.__val__.__hex__()               # XXX
    def __str__(self):          return self.__val__.__str__()               # XXX
    # Random Ops
    def __index__(self):        return self.__val__.__index__()             # XXX
    def __trunc__(self):        return self.__val__.__trunc__()             # XXX
    def __coerce__(self, x):    return self.__val__.__coerce__(x)
    # Represenation
    def __repr__(self):         return "%s(%d)" % (self.__class__.__name__, self.__val__)
    # Define innertype, a function that returns the type of the inner value self.__val__
    def innertype(self):        return type(self.__val__)
    # Define set, a function that you can use to set the value of the instance
    def set(self, x):
        if   isinstance(x, (int, long, float)): self.__val__ = x
        elif isinstance(x, self.__class__): self.__val__ = x.__val__
        else: raise TypeError("expected a numeric type")
    # Pass anything else along to self.__val__
    def __getattr__(self, attr):
        print("getattr: " + attr)
        return getattr(self.__val__, attr)

我把整个班级,使用标题和粗略的测试套件放在这里

mgilson 的使用建议@total_ordering将简化这一点。

只要您遵循使用指南(例如,使用x *= 2而不是x = x * 2),似乎就可以了。

虽然,简单地将参数包装在一个列表中然后进行修改x[0]似乎更容易——仍然是一个有趣的项目。

4

2 回答 2

2

最简单的方法是手动实现它们。如果这是您要添加到许多类中的东西,那么您可能会查看元类(可能是大脑融化)或类装饰器(更容易处理),但您应该手动完成一次,这样您就知道发生了什么.

有时只起作用的原因是,只有在类或其任何基__getattr__类上找不到它要查找的名称时才调用它。所以如果可以在 上找到,将不会被调用。__xyz__object__getattr__

于 2013-09-21T23:58:59.740 回答
2

大多数魔术方法都是在类型而不是实例上查找的。您既不能在实例中覆盖它们,也不能用__getattr__. 为了挂钩到双重功能,您必须实现它。

例子:

obj < 1

不打电话

obj.__lt__(1)

相反,调用通过类型。它几乎等于(除了它也跳过了元类上的 getattr )。

type(obj).__lt__(obj, 1)

查找记录在http://docs.python.org/3/reference/datamodel.html#special-method-lookup

于 2013-09-22T01:27:17.933 回答