#python #pandas #dataframe
#python #pandas #фрейм данных
Вопрос:
Зачем нам нужны три способа работы?
(Я использую умножение для примеров)
Первый способ:
df['a'] * 5
Второй способ:
df['a'].mul(5)
Третий способ:
df['a'].__mul__(5)
Разве не достаточно двух, не нужно mul
, мне было интересно, может ли это быть как обычные способы, например, целое число
Первый способ:
3 * 5
Второй способ:
(3).__mul__(5)
Но в обычных базах inetger:
(3).mul(5)
Сломается.
Мне просто любопытно, зачем нам столько всего в Pandas, то же самое со сложением, вычитанием и делением.
Ответ №1:
*
и mul
сделайте то же самое, но __mul__
по-другому.
*
и mul
выполните некоторые проверки перед делегированием __mul__
. Есть две вещи, о которых вы должны знать.
NotImplemented
Существует специальное одноэлементное значение NotImplemented
, которое возвращается классом __mul__
в тех случаях, когда он не может обработать другой операнд. Затем это говорит Python попробовать __rmul__
. Если это тоже не удается, возникает общий TypeError
. Если вы используете __mul__
напрямую, вы не получите эту логику. Наблюдайте:
class TestClass:
def __mul__(self, other):
return NotImplemented
TestClass() * 1
Вывод:
TypeError: unsupported operand type(s) for *: 'TestClass' and 'int'
Сравните это с этим:
TestClass().__mul__(1)
Вывод:
NotImplemented
Вот почему, как правило, вам следует избегать прямого вызова методов dunder (magic): вы обходите определенные проверки, которые выполняет Python.
- Обработка оператора производного класса
Когда вы пытаетесь выполнить что-то вроде Base() * Derived()
where Derived
inherits from Base
, вы ожидаете Base.__mul__(Derived())
, что вас вызовут первым. Это может создать проблемы, поскольку Derived.__mul__
, скорее всего, знает, как обращаться с такими ситуациями.
Поэтому при использовании *
Python проверяет, является ли тип правого операнда более производным, чем тип левого, и если да, вызывает метод правого операнда __rmul__
напрямую.
Наблюдайте:
class Base:
def __mul__(self, other):
print('base mul')
class Derived(Base):
def __rmul__(self, other):
print('derived rmul')
Base() * Derived()
Вывод:
derived rmul
Обратите внимание, что, хотя Base.__mul__
не возвращает NotImplemented
и может четко обрабатывать объект типа Derived
, Python даже не смотрит на него первым; он делегирует Derived.__rmul__
сразу.
Для полноты картины, есть одно различие между *
и mul
, в контексте pandas
: mul
является функцией, и поэтому может передаваться в переменной и использоваться независимо. Например:
import pandas as pd
pandas_mul = pd.DataFrame.mul
pandas_mul(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
С другой стороны, это приведет к сбою:
*(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
Ответ №2:
И «волшебный метод» __mul__
, и оператор *
одинаковы в базовом python ( *
просто вызовы __mul__
), и, как вы указали, это способ, которым python stadarized обрабатывает вещи. Другой метод mul
— это метод, который вы можете использовать для сопоставления (использования map
) и избегать использования lambda x, y: x*mul
, например. Да, вы все еще можете использовать __mul__
, но обычно эти методы ( __x__
) не предназначены для использования в качестве обычных функций, а простой mul
делает код более понятным.
Итак, вам это действительно не «нужно», но приятно иметь и использовать.
Ответ №3:
Во-первых, third way ( df['a'].__mul__(5)
) никогда не следует использовать, поскольку это внутренний метод, вызываемый классом Python. Как правило, пользователи не используют ни один из методов «dunder».
Что касается двух других способов, первый способ очевиден; вы просто умножаете это. Это стандартная математика.
Второй способ становится немного более интересным. Один из примеров того, как я использовал этот метод, — это когда функция, которую вы хотите применить, является переменной.
Например:
def pandas_math(series, func, val):
return getattr(series, func)(val)
pandas_math(df['a'], 'mul', 5)
даст тот же результат, df['a'].mul(5)
что и, но теперь вы можете передавать mul
как переменную или любую другую функцию, которую вы хотите использовать. Это намного проще, чем жестко кодировать все символы.
Комментарии:
1. Вы могли бы использовать
operator
модуль, еслиdf['a'].mul
бы он не существовал, так что на самом деле он не служит цели в этом отношении.2. Верно, но по какой-то причине Pandas решил встроить его. Я понял, что главный вопрос заключается в том, «почему я должен делать
df['a'].mul(5)
вместоdf['a']*5
?». Аналогичный вопрос «зачем вообще использоватьoperator
модуль?»