当存在冲突时,如何告诉Python我们总是希望将Foo类型的对象解释为Bar类型的对象?

2024-04-26 05:06:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我是一个新手,所以请原谅非标准术语,让我知道我是否应该添加代码,使这个问题更清楚。你知道吗

假设我们尝试在Python中创建一个类“Rational”。(我知道已经内置了一个,但在这个问题中忽略它。)

例如,我们可以使用__add____mul__来教Python如何解释a + ba * b形式的代码

其中ab是有理数。你知道吗

现在,可能会发生这样的情况,在其他地方,人们想要计算a + b,其中a是有理数,而b是整数。这可以通过修改Rational类中的__add__代码来实现,例如,包含if语句

def __add__(self, b):
    if isinstance(b, int):
        brat = rational(b, 1)
        return self + brat
    else:
        y = rational(self.num*b.den + b.num*self.den , b.den*self.den)
        y = y.lowest_terms()
        return y

我们可以类似地修改__mul__代码、__div__代码等,但这种解决方案至少有两个问题:

  1. 它只在第二个参数是int时起作用。第一个 争论仍然必须是理性的;没有办法写出一个合理的结论 方法,允许我们在 int和be是有理数。你知道吗
  2. 这是重复的。我们真正想要的是一些能够 在全球范围内,以某种方式说一次,“当你试图做一件事的时候 对多个对象的运算,其中有些是有理数,有些是有理数 其中是整数,通过映射n将整数视为有理数 到有理(n,1)。”

这种技术存在吗?(我为这个强制添加了标签,因为我认为在其他上下文中这就是所谓的强制,但我的理解是,在Python中不赞成强制。)


Tags: 代码selfaddreturnif整数numint
2条回答

可以通过在类的初始值设定项中进行映射来避免重复。下面是一个处理整数的简单演示。正确处理float将留给读者作为练习。:)但是,我已经演示了如何轻松地实现__radd____iadd__,这是处理+=的神奇方法(又称dunder方法)。你知道吗

我的代码从代码中保留rational作为类名,即使Python中的类名通常是大小写。你知道吗

def gcd(a, b):
    while b > 0:
        a, b = b, a%b
    return a

class rational(object):
    def __init__(self, num, den=1):
        if isinstance(num, rational):
            self.copy(num)
        else:
            self.num = num
            self.den = den

    def copy(self, other):
        self.num = other.num 
        self.den = other.den

    def __str__(self):
        return '{0} / {1}'.format(self.num, self.den)

    def lowest_terms(self):
        g = gcd(self.num, self.den)
        return rational(self.num // g, self.den // g)

    def __add__(self, other):
        other = rational(other)
        y = rational(self.num*other.den + other.num*self.den, other.den*self.den)
        return y.lowest_terms()

    def __radd__(self, other):
        return rational(other) + self

    def __iadd__(self, other):
        self.copy(self + rational(other))
        return self


a = rational(1, 4)
b = rational(2, 5)
c = a + b
print a
print b
print c
print c + 5
print 10 + c
c += 10
print c

输出

1 / 4
2 / 5
13 / 20
113 / 20
213 / 20
213 / 20

您可能希望保留copy方法供内部使用;通常的约定是在这些名称前面加一个下划线。你知道吗

与强制使用参数不同,更通用的方法是创建自己的多方法模块,类似于几年前Guido van Rossum撰写的题为Five-minute Multimethods in Python的文章中所描述的方法。这将允许您避免大量重复的代码。下面是一个版本的增强版,它支持按相反顺序接受参数的“关联\u multimemethod”函数:

# This is in the 'mm' module

_registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args)
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        print('registering: {!r} for args: {}'.format(function.__name__, types))
        self.typemap[types] = function

def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = _registry.get(name)
        if mm is None:
            mm = _registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

def associative_multimethod(*types):
    def register(function):
        name = function.__name__
        mm = _registry.get(name)
        if mm is None:
            mm = _registry[name] = MultiMethod(name)
        mm.register(types[::-1], lambda a, b: function(b, a))
        mm.register(types, function)
        return mm
    return register

这将允许您编写如下代码:

from mm import associative_multimethod, multimethod

class Rational(object):
    pass

@multimethod(int, int)
def foo(a, b):
    print('...code for two ints...')

@associative_multimethod(int, Rational)
def foo(a, b):
    print('...code for int and Rational...')

@multimethod(Rational, Rational)
def foo(a, b):
    print('...code two Rationals...')

a, b, c, d = 1, 2, Rational(), Rational()

foo(a, b)
foo(a, c)
foo(c, a)
foo(c, d)

相关问题 更多 >

    热门问题