我正在编写一个对一个对象进行取幂的函数,即给定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方式吗?
答案 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