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

Ленивая Загрузка.

9
задан user186477 27 October 2009 в 01:02
поделиться

4 ответа

The big advantage is that built-in functions (and operators) can apply extra logic when appropriate, beyond simply calling the special methods. For example, min can look at several arguments and apply the appropriate inequality checks, or it can accept a single iterable argument and proceed similarly; abs when called on an object without a special method __abs__ could try comparing said object with 0 and using the object change sign method if needed (though it currently doesn't); and so forth.

So, for consistency, all operations with wide applicability must always go through built-ins and/or operators, and it's those built-ins responsibility to look up and apply the appropriate special methods (on one or more of the arguments), use alternate logic where applicable, and so forth.

An example where this principle wasn't correctly applied (but the inconsistency was fixed in Python 3) is "step an iterator forward": in 2.5 and earlier, you needed to define and call the non-specially-named next method on the iterator. In 2.6 and later you can do it the right way: the iterator object defines __next__, the new next built-in can call it and apply extra logic, for example to supply a default value (in 2.6 you can still do it the bad old way, for backwards compatibility, though in 3.* you can't any more).

Another example: consider the expression x + y. In a traditional object-oriented language (able to dispatch only on the type of the leftmost argument -- like Python, Ruby, Java, C++, C#, &c) if x is of some built-in type and y is of your own fancy new type, you're sadly out of luck if the language insists on delegating all the logic to the method of type(x) that implements addition (assuming the language allows operator overloading;-).

In Python, the + operator (and similarly of course the builtin operator.add, if that's what you prefer) tries x's type's __add__, and if that one doesn't know what to do with y, then tries y's type's __radd__. So you can define your types that know how to add themselves to integers, floats, complex, etc etc, as well as ones that know how to add such built-in numeric types to themselves (i.e., you can code it so that x + y and y + x both work fine, when y is an instance of your fancy new type and x is an instance of some builtin numeric type).

"Generic functions" (as in PEAK) are a more elegant approach (allowing any overriding based on a combination of types, never with the crazy monomaniac focus on the leftmost arguments that OOP encourages!-), but (a) they were unfortunately not accepted for Python 3, and (b) they do of course require the generic function to be expressed as free-standing (it would be absolutely crazy to have to consider the function as "belonging" to any single type, where the whole POINT is that can be differently overridden/overloaded based on arbitrary combination of its several arguments' types!-). Anybody who's ever programmed in Common Lisp, Dylan, or PEAK, knows what I'm talking about;-).

So, free-standing functions and operators are just THE right, consistent way to go (even though the lack of generic functions, in bare-bones Python, does remove some fraction of the inherent elegance, it's still a reasonable mix of elegance and practicality!-).

19
ответ дан 4 December 2019 в 08:15
поделиться

It emphasizes the capabilities of an object, not its methods or type. Capabilites are declared by "helper" functions such as __iter__ and __len__ but they don't make up the interface. The interface is in the builtin functions, and beside this also in the buit-in operators like + and [] for indexing and slicing.

Sometimes, it is not a one-to-one correspondance: For example, iter(obj) returns an iterator for an object, and will work even if __iter__ is not defined. If not defined, it goes on to look if the object defines __getitem__ and will return an iterator accessing the object index-wise (like an array).

This goes together with Python's Duck Typing, we care only about what we can do with an object, not that it is of a particular type.

3
ответ дан 4 December 2019 в 08:15
поделиться

Actually, those aren't "static" methods in the way you are thinking about them. They are built-in functions that really just alias to certain methods on python objects that implement them.

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

These are always available to be called whether or not the object implements them or not. The point is to have some consistency. Instead of some class having a method called length() and another called size(), the convention is to implement len and let the callers always access it by the more readable len(obj) instead of obj.methodThatDoesSomethingCommon

3
ответ дан 4 December 2019 в 08:15
поделиться

Я думал, что причина в том, что эти базовые операции могут выполняться на итераторах с тем же интерфейсом, что и контейнеры. Однако на самом деле это не работает с 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 __") .

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

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

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, неуклюжему второразрядному обходному пути, распространенному в других языках.

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

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

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

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

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

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

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

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

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

1
ответ дан 4 December 2019 в 08:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: