Есть ли способ создать функцию из метода внутри класса

#python #function #class #oop

Вопрос:

У меня есть класс number, в котором есть два метода:

     def digit_sum(self):
        """Returns the sum of all the digits"""
        str_num = str(self.num)
        ans = Number(0)
        for item in str_num:
            ans  = Number(item)
        return ans

    def digit_product(self):
        """Returns the sum of all the digits"""
        str_num = str(self.num)
        ans = Number(1)
        for item in str_num:
            ans *= Number(item)
        return ans
 

Я хотел бы, чтобы эти методы были доступны, как функции, например, я бы набрал x = digit_product(Number(54))
Есть ли способ сделать это в python?

Комментарии:

1. Можете ли вы объяснить, почему это желательно? Эти функции/методы явно не имеют значения для не- Number s, так почему бы просто не сделать Number(54).digit_product() это ? Это пахнет проблемой XY .

2. Не могли бы вы, пожалуйста, включить остальную часть определения Number класса, __init__ а именно соответствующие атрибуты и?

3. Я могу себе представить, что причина , по которой вы, возможно, захотите это сделать, заключается в передаче в качестве параметра a map sort или аналогичного. Например numbers.sort(key=Number.digit_product) , для сортировки списка чисел в соответствии с их цифровыми продуктами.

4. Реально, аргумент не обязательно должен быть a Number ; в этот момент мы в основном говорим об альтернативном конструкторе, доступ к которому, как x = Number.digit_product(54)

Ответ №1:

Вы уже можете получить доступ к функции как к атрибуту класса, например:

 Number.digit_product(Number(54))
 

Если вы хотите иметь доступ к нему как к имени digit_product , вы можете просто сделать:

 digit_product = Number.digit_product
 

Комментарии:

1. Если он будет доступен как атрибут класса, он должен быть оформлен @classmethod или @staticmethod

2. Если вы это сделаете, вы удалите возможность вызывать его как метод экземпляра как instance.method() . Нет необходимости делать его статическим или классовым методом, чтобы он также был доступен в качестве атрибута класса.

3. @sabik: И на самом деле, в данном конкретном случае использование staticmethod или classmethod было бы неправильным; self аргумент передается явно, а не неявно, как в синтаксисе вызова метода, и если его не получить (или попытаться передать класс, а затем экземпляр в качестве аргумента), произойдет сбой.

4. @sabik Это значение, к которому привязано self . Для метода экземпляра bar foo.bar() и type(foo).bar(foo) эквивалентны (игнорируя наследование). Для метода класса bar foo.bar() и type(foo).bar(type(foo)) эквивалентны. Для статического метода bar foo.bar() и type(foo).bar() эквивалентны.

5. self это просто имя параметра. Вы можете определить обычную функцию с вызываемым параметром self , и она будет работать просто отлично, и вы можете определить метод, в котором первый параметр называется чем-то иным self (он все равно будет привязан к экземпляру, если вы вызовете его как метод экземпляра). На самом деле вы не должны делать ни того, ни другого, потому что это сбивает с толку, но важно понимать, что вызов этого параметра self -это просто соглашение; имя self не изменяет семантику того, как работает вызов функции.

Ответ №2:

Предполагая , что эти методы определены Number , они уже являются функциями; вы уже можете сделать это, выполнив:

 x = Number.digit_product(Number(54))
 

Если у вас есть сценарий, в котором несколько классов могут предоставлять метод с одинаковым именем, но вы хотите вызвать только тот, который связан с этим классом, вы можете написать простую оболочку:

 def digit_product(x):
    return x.digit_product()
 

который отправит соответствующий метод, все еще позволяя вам вызывать его в синтаксисе функций верхнего уровня. Если это усложняется (не все типы имеют один и тот же метод, но вы хотите, чтобы он работал для всех типов), functools.singledispatch позволяет специализировать вызовы, относящиеся к определенному типу, в то время как все неспециализированные вызовы используют путь кода по умолчанию, например:

 from functools import singledispatch

@singledispatch
def digit_product(x):
    return x.digit_product()

@digit_product.register
def _(x: MyWeirdNumber):
    return x.my_weird_digit_product()