引用类的方法,而不是实例

时间:2009-06-04 11:53:41

标签: python

我正在编写一个对一个对象进行取幂的函数,即给定a和n,返回 n 。由于不需要是内置类型,因此该函数接受作为关键字参数的执行乘法的函数。如果未定义,则默认为对象__mul__方法,即对象本身应定义乘法。那部分很容易:

def bin_pow(a, n, **kwargs) :

    mul = kwargs.pop('mul',None)
    if mul is None :
        mul = lambda x,y : x*y

问题在于,在计算 n 的过程中,有很多中间角点,并且通常有更有效的方法来计算它们,而不仅仅是简单地将对象相乘。很容易定义另一个计算平方的函数,并将其作为另一个关键字参数传递,如:

def bin_pow(a, n, **kwargs) :
    mul = kwargs.pop('mul',None)
    sqr = kwargs.pop('sqr',None)

    if mul is None :
        mul = lambda x,y : x*y
    if sqr is None :
        sqr = lambda x : mul(x,x)

如果对象的方形函数不是一个独立的函数,而是一个对象被取幂的方法,那么问题就出现了,这是一件非常合理的事情。我能想到的唯一方法就是这样:

import inspect

def bin_pow(a, n, **kwargs) :
    mul = kwargs.pop('mul',None)
    sqr = kwargs.pop('sqr',None)

    if mul is None :
        mul = lambda x,y : x*y
    if sqr is None :
        sqr = lambda x : mul(x,x)
    elif inspect.isfunction(sqr) == False : # if not a function, it is a string
        sqr = lambda x : eval('x.'+sqr+'()')

它确实有效,但我发现这是一种非常不优雅的做事方式......我对OOP的掌握是有限的,但是如果有一种方法可以让sqr指向类的功能,而不是实例的那个,那么我可以逃避sqr = lambda x : sqr(x)sqr = lambda x: x.sqr()之类的事情。可以这样做吗?还有其他更多的pythonic方式吗?

5 个答案:

答案 0 :(得分:4)

您可以使用实例作为第一个参数调用未绑定的方法:

class A(int):
    def sqr(self):
        return A(self*self)

sqr = A.sqr
a = A(5)
print sqr(a) # Prints 25

因此,在您的情况下,您实际上不需要做任何具体的事情,只需以下内容:

bin_pow(a, n, sqr=A.sqr)

请注意,这是早期绑定,因此如果您有一个子类B覆盖sqr,那么仍然会调用A.sqr。对于后期绑定,您可以在调用点使用lambda:

bin_pow(a, n, sqr=lambda x: x.sqr())

答案 1 :(得分:3)

这是我如何做到的:

import operator

def bin_pow(a, n, **kwargs) :
    pow_function = kwargs.pop('pow' ,None)

    if pow_function is None:
        pow_function = operator.pow

    return pow_function(a, n)

这是最快的方式。另请参阅object.__pow__operator模块文档。

现在,要传递一个对象方法,你可以直接传递它,不需要传递一个带有名字的字符串。事实上,永远不要使用字符串来做这种事情,直接使用对象要好得多。

如果你想要未绑定的方法,你也可以传递它:

class MyClass(object):
    def mymethod(self, other):
        return do_something_with_self_and_other

m = MyClass()
n = MyClass()

bin_pow(m, n, pow=MyClass.mymethod)

如果你想要类方法,那么只需传递它:

class MyClass(object):
    @classmethod
    def mymethod(cls, x, y):
        return do_something_with_x_and_y

m = MyClass()
n = MyClass()

bin_pow(m, n, pow=MyClass.mymethod)

答案 2 :(得分:1)

如果你想调用类的方法,而不是(可能被覆盖的)实例的方法,你可以做

instance.__class__.method(instance)

而不是

instance.method()

我不确定这是不是你想要的。

答案 3 :(得分:0)

如果我理解库函数的设计目标,你想提供一个库“power”函数,它会将传递给它的任何对象提升到N次幂。但是你也希望提供效率的“捷径”。

设计目标看起来有点奇怪 - Python已经定义了 mul 方法,允许类的设计者将其乘以任意值,并且 pow 允许类的设计者支持将其提升为幂的方法。如果我正在构建这个,我希望并要求用户使用 mul 方法,我会做这样的事情:

def bin_or_pow(a, x):
    pow_func = getattr(a, '__pow__', None)
    if pow_func is None:
        def pow_func(n):
            v = 1
            for i in xrange(n):
                v = a * v

            return v

    return pow_func(x)

这将让您执行以下操作:

class Multable(object):
    def __init__(self, x):
        self.x = x

    def __mul__(self, n):
        print 'in mul'
        n = getattr(n, 'x', n)
        return type(self)(self.x * n)

class Powable(Multable):
    def __pow__(self, n):
        print 'in pow'
        n = getattr(n, 'x', n)
        return type(self)(self.x ** n)

print bin_or_pow(5, 3)
print
print bin_or_pow(Multable(5), 5).x
print
print bin_or_pow(Powable(5), 5).x

......你得到......

  

125

     

in mul
  在mul中   在mul中   在mul中   在mul中   3125

     

在战俘中   3125

答案 4 :(得分:-1)

据我所知,这是你要修复的最后一个sqr位。如果是这样,我建议getattr。例如:

class SquarableThingy:
  def __init__(self, i):
    self.i = i
  def squarify(self):
    return self.i**2

class MultipliableThingy:
  def __init__(self, i):
    self.i = i
  def __mul__(self, other):
    return self.i * other.i

x = SquarableThingy(3)
y = MultipliableThingy(4)
z = 5
sqr = 'squarify'
sqrFunX = getattr(x, sqr, lambda: x*x)
sqrFunY = getattr(y, sqr, lambda: y*y)
sqrFunZ = getattr(z, sqr, lambda: z*z)
assert sqrFunX() == 9
assert sqrFunY() == 16
assert sqrFunZ() == 25