Ссылка на метод класса, а не экземпляра - PullRequest
0 голосов
/ 04 июня 2009

Я пишу функцию, которая возводит в степень объект, то есть, учитывая a и n, возвращает n . Поскольку тип не должен быть встроенным, функция принимает в качестве ключевого аргумента функцию для выполнения умножений. Если значение не определено, по умолчанию используется метод objects __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+'()')

Это работает, но я считаю, что это крайне нелегкий способ делать вещи ... Мое владение ООП ограничено, но если бы был способ заставить sqr указывать на функцию класса, а не на функцию экземпляра, тогда я мог бы получить что-то вроде sqr = lambda x : sqr(x) или, может быть sqr = lambda x: x.sqr() Можно ли это сделать? Есть ли какой-нибудь другой более питонический способ?

Ответы [ 5 ]

4 голосов
/ 04 июня 2009

Вы можете вызывать несвязанные методы с экземпляром в качестве первого параметра:

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. Для позднего связывания вы можете использовать лямбду в месте вызова:

bin_pow(a, n, sqr=lambda x: x.sqr())
3 голосов
/ 04 июня 2009

вот как бы я это сделал:

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)
1 голос
/ 04 июня 2009

Если вы хотите вызвать метод класса, а не (возможно, переопределенный) метод экземпляра, вы можете сделать

instance.__class__.method(instance)

вместо

instance.method()

Я не уверен, что если ты этого хочешь.

0 голосов
/ 04 июня 2009

Если я понимаю цели разработки библиотечной функции, вы хотите предоставить библиотечную «мощную» функцию, которая возведет любой объект, переданный ей, в 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

в мул
в мул
в мул
в мул
в мул
3125

в пау
3125

0 голосов
/ 04 июня 2009

Я понимаю, что это 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...