Python:强制新式的类

buk*_*zor 8 python oop class coerce

我希望这段代码"正常工作":

def main():
    c = Castable()
    print c/3
    print 2-c
    print c%7
    print c**2
    print "%s" % c
    print "%i" % c
    print "%f" % c
Run Code Online (Sandbox Code Playgroud)

当然,简单的方法是编写int(c)/3,但我想为配置迷你语言启用更简单的perl-ish语法.

值得注意的是,如果我使用"旧式"类(不从对象继承),我可以通过定义一个__coerce__方法来做到这一点,但旧式类已被弃用,将在python3中删除.

当我使用新式类做同样的事情时,我收到此错误:

TypeError: unsupported operand type(s) for /: 'Castable' and 'int'
Run Code Online (Sandbox Code Playgroud)

我相信这是设计的,但是我怎么能用__coerce__新式的类来模拟旧式的行为呢?您可以在下面找到我当前的解决方案,但它非常难看且冗长.

这是相关文件:(我认为)

奖励积分:

    print pow(c, 2, 100)
Run Code Online (Sandbox Code Playgroud)

Ned*_*der 8

您需要定义__div__是否要c/3工作.Python不会首先将您的对象转换为数字.

  • http://docs.python.org/reference/datamodel.html#new-style-special-lookup说"对于新式类,只有在对象的类型上定义时,才能保证特殊方法的隐式调用正常工作"和后来"隐式特殊方法查找通常也绕过`__getattribute __()`方法,甚至是对象的元类".所以,不,你必须定义每个操作. (7认同)

buk*_*zor 5

这有效,经过几次改进(@jchl的道具)后不那么严重,但似乎应该是不必要的,特别是考虑到你可以免费使用"旧式"课程.

我还在寻找更好的答案.如果没有更好的方法,这在我看来就像Python语言中的回归.

def ops_list():
    "calculate the list of overloadable operators"
    #<type 'object'> has functions but no operations
    not_ops = dir(object)

    #calculate the list of operation names
    ops = set()
    for mytype in (int, float, str):
        for op in dir(mytype):
            if op.endswith("__") and op not in not_ops:
                ops.add(op)
    return sorted(ops)

class MetaCastable(type):
    __ops = ops_list()

    def __new__(mcs, name, bases, dict):
        #pass any undefined ops to self.__op__
        def add_op(op):
            if op in dict:
                return
            fn = lambda self, *args: self.__op__(op, args)
            fn.__name__ = op
            dict[op] = fn

        for op in mcs.__ops:
            add_op( op )
        return type.__new__(mcs, name, bases, dict)


class Castable(object):
    __metaclass__ = MetaCastable
    def __str__(self):
        print "str!"
        return "<Castable>"
    def __int__(self):
        print "int!"
        return 42
    def __float__(self):
        print "float!"
        return 2.718281828459045

    def __op__(self, op, args):
        try:
            other = args[0]
        except IndexError:
            other = None
        print "%s %s %s" % (self, op, other)
        self, other = coerce(self, other)
        return getattr(self, op)(*args)

    def __coerce__(self, other):
        print "coercing like %r!" % other
        if other is None: other = 0.0
        return (type(other)(self), other)
Run Code Online (Sandbox Code Playgroud)