Por que o código Python usa a function len () em vez de um método length?

Eu sei que python tem uma function len() que é usada para determinar o tamanho de uma string, mas eu estava me perguntando por que não é um método do object string.

Atualizar

Ok, percebi que estava embaraçosamente enganado. __len__() é na verdade um método de um object de string. Parece estranho ver código orientado a objects no Python usando a function len em objects string. Além disso, também é estranho ver __len__ como o nome em vez de apenas len.

Strings possuem um método length: __len__()

O protocolo em Python é implementar esse método em objects que têm um comprimento e usar a function len() __iter__() , que o chama para você, semelhante à maneira que você implementaria __iter__() e usaria o iter() integrado iter() function (ou ter o método chamado nos bastidores para você) em objects que são iteráveis.

Consulte Emulando Tipos de Contêiner para obter mais informações.

Aqui está uma boa leitura sobre o assunto dos protocolos em Python: Python e o Princípio do Menos Espanto

A resposta de Jim a esta pergunta pode ajudar; Eu copio aqui. Citando Guido van Rossum:

Primeiro de tudo, escolhi len (x) sobre x.len () por motivos de HCI (def __len __ () veio muito mais tarde). Existem duas razões interligadas na verdade, ambas HCI:

(a) Para algumas operações, a notação de prefixo apenas lê melhor do que as operações de prefixo – prefixo (e infixo!) têm uma longa tradição em matemática que gosta de notações onde os visuais ajudam o matemático a pensar sobre um problema. Compare o fácil com o qual reescrevemos uma fórmula como x * (a + b) em x a + x b para a falta de jeito de fazer a mesma coisa usando uma notação OO bruta.

(b) Quando eu leio código que diz len (x) eu sei que ele está pedindo a duração de algo. Isso me diz duas coisas: o resultado é um inteiro, e o argumento é algum tipo de contêiner. Ao contrário, quando eu leio x.len (), eu tenho que saber que x é algum tipo de contêiner implementando uma interface ou herdando de uma class que tem um len () padrão. Testemunhe a confusão que ocasionalmente temos quando uma class que não está implementando um mapeamento tem um método get () ou keys (), ou algo que não é um arquivo que possui um método write ().

Dizendo a mesma coisa de outra maneira, eu vejo ‘len’ como uma operação embutida. Eu odiaria perder isso. /… /

Existe um método len :

 >>> a = 'a string of some length' >>> a.__len__() 23 >>> a.__len__  

Python é uma linguagem de programação pragmática, e as razões para len() ser uma function e não um método de str , list , dict etc. são pragmáticas.

A function len() lida diretamente com tipos internos: a implementação CPython de len() na verdade retorna o valor do campo ob_size na estrutura C de PyVarObject que representa qualquer object PyVarObject de tamanho variável na memory. Isso é muito mais rápido do que chamar um método – nenhuma pesquisa de atributos precisa acontecer. Obter o número de itens em uma coleção é uma operação comum e deve funcionar de forma eficiente para tipos básicos e diversos como str , list , array.array etc.

No entanto, para promover consistência, ao aplicar len(o) a um tipo definido pelo usuário, o Python chama o.__len__() como um fallback. __len__ , __abs__ e todos os outros methods especiais documentados no Python Data Model facilitam a criação de objects que se comportam como os internos, permitindo as APIs expressivas e altamente consistentes que chamamos de “Pythonic”.

Ao implementar methods especiais, seus objects podem suportar iteração, sobrecarregar operadores de infix, gerenciar contextos with blocos etc. Você pode pensar no Modelo de Dados como uma maneira de usar a própria linguagem Python como uma estrutura onde os objects criados podem ser integrados perfeitamente.

Uma segunda razão, apoiada por citações de Guido van Rossum como este , é que é mais fácil ler e escrever len(s) que s.len() .

A notação len(s) é consistente com operadores unários com notação de prefixo, como abs(n) . len() é usado com mais frequência do que abs() , e merece ser tão fácil de escrever.

Também pode haver uma razão histórica: na linguagem ABC que precedeu o Python (e foi muito influente em seu design), havia um operador unário escrito como #s que significava len(s) .

 met% python -c 'import this' | grep 'only one' There should be one-- and preferably only one --obvious way to do it. 

Você também pode dizer

 >> x = 'test' >> len(x) 4 

Usando o Python 2.7.3.

Não faz?

 >>> "abc".__len__() 3 

Há algumas ótimas respostas aqui, e então antes de dar a minha, eu gostaria de destacar algumas das gemas (sem trocadilhos) que eu li aqui.

  • O Python não é uma linguagem OOP pura – é uma linguagem multi-paradigmática de uso geral que permite ao programador usar o paradigma com o qual se sente mais confortável e / ou o paradigma mais adequado para a sua solução.
  • Python tem funções de primeira class, então len é realmente um object. Ruby, por outro lado, não tem funções de primeira class. Portanto, o object de function len possui seus próprios methods que você pode inspecionar executando dir(len) .

Se você não gosta da maneira como isso funciona no seu próprio código, é trivial reimplementar os contêineres usando seu método preferido (veja o exemplo abaixo).

 >>> class List(list): ... def len(self): ... return len(self) ... >>> class Dict(dict): ... def len(self): ... return len(self) ... >>> class Tuple(tuple): ... def len(self): ... return len(self) ... >>> class Set(set): ... def len(self): ... return len(self) ... >>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F']) >>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'}) >>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'}) >>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F')) >>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple)) >>> >>> for container in my_containers: ... print container.len() ... 15 2 15 15