Преимущества наличия статических функций, таких как len(), max() и min(), перед вызовами унаследованных методов

StackOverflow https://stackoverflow.com/questions/1628222

  •  06-07-2019
  •  | 
  •  

Вопрос

я новичок в python, и я не уверен, почему python реализовал len(obj), max (obj) и min (obj) как статические подобные функции (я из языка java) поверх obj.len(), obj.max() и obj.min()

каковы преимущества и недостатки (кроме очевидной непоследовательности) наличия len()...из-за вызовов метода?

почему guido предпочел это вызовам метода?(при необходимости это можно было бы решить в python3, но в python3 это не было изменено, так что должны быть веские причины...я надеюсь)

Спасибо!!

Это было полезно?

Решение

Большим преимуществом является то, что встроенные функции (и операторы) могут применять дополнительную логику, когда это необходимо, помимо простого вызова специальных методов.Например, min может просмотреть несколько аргументов и применить соответствующие проверки неравенства, или он может принять один повторяющийся аргумент и действовать аналогичным образом; abs при вызове объекта без специального метода __abs__ можно попробовать сравнить указанный объект с 0 и при необходимости использовать метод изменения знака объекта (хотя в настоящее время этого не происходит);и так далее.

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

Примером, где этот принцип был применен некорректно (но несоответствие было исправлено в Python 3), является "продвинуть итератор вперед".:в версии 2.5 и более ранних версиях вам нужно было определить и вызвать не имеющий специального имени next метод на итераторе.В версии 2.6 и более поздних версиях вы можете сделать это правильным способом:объект iterator определяет __next__, новый next встроенный может вызвать это и примените дополнительную логику, например, для указания значения по умолчанию (в версии 2.6 вы все еще можете сделать это старым старым способом для обратной совместимости, хотя в 3.* ты больше не можешь).

Другой пример:рассмотрим выражение x + y.В традиционном объектно-ориентированном языке (способном отправлять только тип крайнего левого аргумента - например, Python, Ruby, Java, C ++, C # и c), если x имеет некоторый встроенный тип и y относится к вашему собственному причудливому новому типу, вам, к сожалению, не повезло, если язык настаивает на делегировании всей логики методу type(x) который реализует сложение (предполагая, что язык допускает перегрузку оператора;-).

В Python, the + оператор (и аналогично, конечно, встроенный operator.add, если это то, что вы предпочитаете) пробует тип x's __add__, и если этот человек не знает , что делать с y, затем пробует тип y's __radd__.Таким образом, вы можете определить свои типы, которые знают, как добавлять себя к целым числам, числам с плавающей запятой, сложным и т.д. И т.п., А также те, которые знают, как добавлять к себе такие встроенные числовые типы (т. Е. вы можете закодировать это так, чтобы x + y и y + x оба работают нормально, когда y является экземпляром вашего необычного нового типа и x является экземпляром некоторого встроенного числового типа).

"Универсальные функции" (как в PEAK) представляют собой более элегантный подход (допускающий любое переопределение на основе комбинации типов, никогда с сумасшедшим акцентом на крайних левых аргументах, который поощряет ООП!-), но (а) они, к сожалению, не были приняты для Python 3, и (б) они, конечно, требуют, чтобы универсальная функция была выражена как автономная (было бы абсолютно безумно рассматривать функцию как "принадлежащую" какому-либо одному типу, где весь СМЫСЛ в том, что она может быть по-разному переопределена / перегружена на основе произвольной комбинации типов ее нескольких аргументов!-).Любой, кто когда-либо программировал на Common Lisp, Dylan или PEAK, знает, о чем я говорю;-).

Таким образом, автономные функции и операторы - это просто правильный, последовательный путь (даже несмотря на то, что отсутствие универсальных функций в bare-bones Python устраняет некоторые несмотря на присущую ему элегантность, это все же разумное сочетание элегантности и практичности!-).

Другие советы

Он подчеркивает возможности объекта, а не его методы или тип.Возможности объявляются "вспомогательными" функциями, такими как __iter__ и __len__ но они не составляют интерфейс.Интерфейс находится во встроенных функциях, а помимо этого также во встроенных операторах, таких как + и [] для индексации и нарезки.

Иногда это не является взаимно однозначным соответствием:Например, iter(obj) возвращает итератор для объекта и будет работать, даже если __iter__ не определено.Если не определено, он переходит к просмотру, определяет ли объект __getitem__ и вернет итератор, обращающийся к объекту по индексу (например, к массиву).

Это сочетается с утиной типизацией Python, мы заботимся только о том, что мы можем сделать с объектом, а не о том, что он определенного типа.

На самом деле, это не "статические" методы в том смысле, в каком вы о них думаете.Они такие встроенные функции это действительно просто псевдоним для определенных методов в объектах python, которые их реализуют.

>>> class Foo(object):
...     def __len__(self):
...             return 42
... 
>>> f = Foo()
>>> len(f)
42

Они всегда доступны для вызова, независимо от того, реализует их объект или нет.Смысл в том, чтобы иметь некоторую последовательность.Вместо некоторого класса, имеющего метод с именем length() и другой с именем size(), соглашение заключается в реализации лен и пусть вызывающие пользователи всегда обращаются к нему с помощью более читаемого len(obj) вместо obj.methodthat doessomethingcommon

Я думал, причина в том, что эти основные операции могут быть выполнены на итераторах с тем же интерфейсом, что и у контейнеров.Однако на самом деле это не работает с len:

def foo():
    for i in range(10):
        yield i
print len(foo())

...сбой из-за TypeError.len() не будет использовать и подсчитывать итератор;это работает только с объектами, которые имеют __len__ звони.

Итак, насколько я понимаю, len() не должен существовать.Гораздо естественнее говорить obj.len, чем len(obj), и гораздо более соответствует остальному языку и стандартной библиотеке.Мы не говорим append(lst, 1);мы говорим lst.append(1).Наличие отдельного глобального метода для определения длины является странным, непоследовательным частным случаем и потребляет очень очевидное имя в глобальном пространстве имен, что является очень плохой привычкой Python.

Это не связано с утиным набором текста;вы можете сказать getattr(obj, "len") чтобы решить, можете ли вы использовать len для объекта так же легко - и гораздо более последовательно - чем вы можете использовать getattr(obj, "__len__").

Все это говорит о том, что бородавки на языке исчезают - для тех, кто считает это бородавкой, - с этим очень легко жить.

С другой стороны, минимальное и максимальное делай работайте с итераторами, что дает им возможность использования отдельно от какого-либо конкретного объекта.Это просто, поэтому я просто приведу пример:

import random
def foo():
    for i in range(10):
        yield random.randint(0, 100)
print max(foo())

Однако никаких __min__ или __max__ методы переопределяют его поведение, поэтому не существует согласованного способа обеспечить эффективный поиск отсортированных контейнеров.Если контейнер отсортирован по тому же ключу, который вы ищете, min / max - это O (1) операций вместо O (n), и единственный способ предоставить это другим, несовместимым методом.(Конечно, это можно было бы относительно легко исправить в языке.)

Чтобы разобраться с другой проблемой, связанной с этим:это предотвращает использование привязки метода Python.В качестве простого, надуманного примера вы можете сделать это, чтобы предоставить функцию для добавления значений в список:

def add(f):
    f(1)
    f(2)
    f(3)
lst = []
add(lst.append)
print lst

и это работает со всеми функциями-членами.Однако вы не можете сделать это с помощью min, max или len, поскольку они не являются методами объекта, с которым они работают.Вместо этого вам придется прибегнуть к functools.partial , неуклюжему второразрядному обходному пути, распространенному в других языках.

Конечно, это необычный случай;но именно необычные случаи говорят нам о согласованности языка.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top