#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()